@skippr/live-agent-sdk 0.34.0 → 0.35.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 +8 -1
- package/dist/esm/lib-exports.js +516 -122
- package/dist/skippr-sdk.css +1 -1
- package/dist/skippr-sdk.js +130 -130
- package/dist/types/components/MeetingControls.d.ts +5 -1
- package/dist/types/components/StartSessionPrompt.d.ts +3 -1
- package/dist/types/context/LiveAgentContext.d.ts +14 -0
- package/dist/types/hooks/useLiveAgent.d.ts +1 -0
- package/dist/types/hooks/useSession.d.ts +15 -0
- package/dist/types/lib/react-compat.d.ts +26 -0
- package/package.json +1 -1
package/dist/esm/lib-exports.js
CHANGED
|
@@ -8,7 +8,7 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
8
8
|
|
|
9
9
|
// src/components/LiveAgent.tsx
|
|
10
10
|
import { LiveKitRoom, RoomAudioRenderer } from "@livekit/components-react";
|
|
11
|
-
import { useCallback as useCallback8, useMemo as
|
|
11
|
+
import { useCallback as useCallback8, useMemo as useMemo6, useRef as useRef10, useState as useState11 } from "react";
|
|
12
12
|
|
|
13
13
|
// src/context/LiveAgentContext.tsx
|
|
14
14
|
import { createContext } from "react";
|
|
@@ -203,8 +203,41 @@ function useAvailableModules({
|
|
|
203
203
|
}
|
|
204
204
|
|
|
205
205
|
// src/hooks/useSession.ts
|
|
206
|
-
import { useCallback as useCallback3, useEffect as useEffect3, useState as useState3 } from "react";
|
|
206
|
+
import { useCallback as useCallback3, useEffect as useEffect3, useRef, useState as useState3 } from "react";
|
|
207
207
|
var API_URL3 = "https://specialist.skippr.ai/api";
|
|
208
|
+
async function fetchSessionMessages(sessionId, bearerToken) {
|
|
209
|
+
try {
|
|
210
|
+
const resp = await fetch(`${API_URL3}/v1/sessions/${sessionId}/messages`, {
|
|
211
|
+
credentials: "omit",
|
|
212
|
+
headers: { Authorization: `Bearer ${bearerToken}` }
|
|
213
|
+
});
|
|
214
|
+
if (!resp.ok)
|
|
215
|
+
return [];
|
|
216
|
+
const { messages } = await resp.json();
|
|
217
|
+
return messages.filter((message) => message.role !== "system").map((message) => ({
|
|
218
|
+
id: message.id,
|
|
219
|
+
role: message.role === "user" ? "user" : "assistant",
|
|
220
|
+
content: message.content,
|
|
221
|
+
source: "chat",
|
|
222
|
+
timestamp: new Date(message.createdAt).getTime()
|
|
223
|
+
}));
|
|
224
|
+
} catch {
|
|
225
|
+
return [];
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
async function requestScreenShare() {
|
|
229
|
+
try {
|
|
230
|
+
return await navigator.mediaDevices.getDisplayMedia({ video: { displaySurface: "browser" } });
|
|
231
|
+
} catch {
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
function stopStream(stream) {
|
|
236
|
+
if (!stream)
|
|
237
|
+
return;
|
|
238
|
+
for (const track of stream.getTracks())
|
|
239
|
+
track.stop();
|
|
240
|
+
}
|
|
208
241
|
async function exchangeForBearerToken(appKey, userToken) {
|
|
209
242
|
const resp = await fetch(`${API_URL3}/v1/auth/token-exchange`, {
|
|
210
243
|
method: "POST",
|
|
@@ -238,6 +271,10 @@ function useSession({
|
|
|
238
271
|
const [sessionId, setSessionId] = useState3(null);
|
|
239
272
|
const [bearerToken, setBearerToken] = useState3(authToken ?? null);
|
|
240
273
|
const [pendingScreenStream, setPendingScreenStream] = useState3(null);
|
|
274
|
+
const [isPaused, setIsPaused] = useState3(false);
|
|
275
|
+
const [isPausing, setIsPausing] = useState3(false);
|
|
276
|
+
const [resumableSessions, setResumableSessions] = useState3([]);
|
|
277
|
+
const [historyMessages, setHistoryMessages] = useState3([]);
|
|
241
278
|
useEffect3(() => {
|
|
242
279
|
let stale = false;
|
|
243
280
|
if (authToken) {
|
|
@@ -257,6 +294,46 @@ function useSession({
|
|
|
257
294
|
stale = true;
|
|
258
295
|
};
|
|
259
296
|
}, [authToken, appKey, userToken]);
|
|
297
|
+
const refetchResumable = useCallback3(async () => {
|
|
298
|
+
if (!bearerToken) {
|
|
299
|
+
setResumableSessions([]);
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
try {
|
|
303
|
+
const resp = await fetch(`${API_URL3}/v1/sessions/resumable`, {
|
|
304
|
+
credentials: "omit",
|
|
305
|
+
headers: { Authorization: `Bearer ${bearerToken}` }
|
|
306
|
+
});
|
|
307
|
+
if (!resp.ok) {
|
|
308
|
+
setResumableSessions([]);
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
const { sessions } = await resp.json();
|
|
312
|
+
setResumableSessions(sessions);
|
|
313
|
+
} catch {
|
|
314
|
+
setResumableSessions([]);
|
|
315
|
+
}
|
|
316
|
+
}, [bearerToken]);
|
|
317
|
+
useEffect3(() => {
|
|
318
|
+
refetchResumable();
|
|
319
|
+
}, [refetchResumable]);
|
|
320
|
+
const pauseOnUnloadRef = useRef(null);
|
|
321
|
+
pauseOnUnloadRef.current = connection !== null && !isPaused && sessionId && bearerToken ? { sessionId, bearerToken } : null;
|
|
322
|
+
useEffect3(() => {
|
|
323
|
+
const onPageHide = () => {
|
|
324
|
+
const pending = pauseOnUnloadRef.current;
|
|
325
|
+
if (!pending)
|
|
326
|
+
return;
|
|
327
|
+
fetch(`${API_URL3}/v1/sessions/${pending.sessionId}/pause`, {
|
|
328
|
+
method: "POST",
|
|
329
|
+
credentials: "omit",
|
|
330
|
+
keepalive: true,
|
|
331
|
+
headers: { Authorization: `Bearer ${pending.bearerToken}` }
|
|
332
|
+
}).catch(() => {});
|
|
333
|
+
};
|
|
334
|
+
window.addEventListener("pagehide", onPageHide);
|
|
335
|
+
return () => window.removeEventListener("pagehide", onPageHide);
|
|
336
|
+
}, []);
|
|
260
337
|
const startSession = useCallback3(async ({ agentId, agentControls }) => {
|
|
261
338
|
if (!bearerToken) {
|
|
262
339
|
setError("No auth token available");
|
|
@@ -272,13 +349,7 @@ function useSession({
|
|
|
272
349
|
onStart?.();
|
|
273
350
|
let screenStream = null;
|
|
274
351
|
if (captureMode === "screenshare") {
|
|
275
|
-
|
|
276
|
-
screenStream = await navigator.mediaDevices.getDisplayMedia({
|
|
277
|
-
video: { displaySurface: "browser" }
|
|
278
|
-
});
|
|
279
|
-
} catch {
|
|
280
|
-
screenStream = null;
|
|
281
|
-
}
|
|
352
|
+
screenStream = await requestScreenShare();
|
|
282
353
|
}
|
|
283
354
|
const requestAgentControls = agentControls?.highlight === true ? { highlight: true } : undefined;
|
|
284
355
|
const headers = { Authorization: `Bearer ${bearerToken}` };
|
|
@@ -311,23 +382,97 @@ function useSession({
|
|
|
311
382
|
}
|
|
312
383
|
const { connection: conn } = await startResp.json();
|
|
313
384
|
setSessionId(session.id);
|
|
385
|
+
setHistoryMessages([]);
|
|
314
386
|
setConnection({
|
|
315
387
|
livekitUrl: conn.livekitUrl,
|
|
316
388
|
token: conn.token
|
|
317
389
|
});
|
|
318
390
|
setPendingScreenStream(screenStream);
|
|
319
391
|
setShouldConnect(true);
|
|
392
|
+
setIsPaused(false);
|
|
320
393
|
} catch (e) {
|
|
321
|
-
|
|
322
|
-
for (const track of screenStream.getTracks())
|
|
323
|
-
track.stop();
|
|
324
|
-
}
|
|
394
|
+
stopStream(screenStream);
|
|
325
395
|
setError(e instanceof Error ? e.message : "Failed to start session");
|
|
326
396
|
onStartError?.();
|
|
327
397
|
} finally {
|
|
328
398
|
setIsStarting(false);
|
|
329
399
|
}
|
|
330
400
|
}, [captureMode, bearerToken, onStart, onStartError]);
|
|
401
|
+
const pauseSession = useCallback3(async () => {
|
|
402
|
+
if (!sessionId || !bearerToken)
|
|
403
|
+
return;
|
|
404
|
+
setIsPausing(true);
|
|
405
|
+
try {
|
|
406
|
+
const resp = await fetch(`${API_URL3}/v1/sessions/${sessionId}/pause`, {
|
|
407
|
+
method: "POST",
|
|
408
|
+
credentials: "omit",
|
|
409
|
+
headers: { Authorization: `Bearer ${bearerToken}` }
|
|
410
|
+
});
|
|
411
|
+
if (!resp.ok) {
|
|
412
|
+
const body = await resp.json().catch(() => null);
|
|
413
|
+
throw new Error(body?.detail || `Failed to pause: ${resp.status}`);
|
|
414
|
+
}
|
|
415
|
+
} catch (e) {
|
|
416
|
+
setError(e instanceof Error ? e.message : "Failed to pause session");
|
|
417
|
+
setIsPausing(false);
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
const history2 = await fetchSessionMessages(sessionId, bearerToken);
|
|
421
|
+
stopStream(pendingScreenStream);
|
|
422
|
+
setPendingScreenStream(null);
|
|
423
|
+
setShouldConnect(false);
|
|
424
|
+
setConnection(null);
|
|
425
|
+
setHistoryMessages(history2);
|
|
426
|
+
setIsPaused(true);
|
|
427
|
+
setIsPausing(false);
|
|
428
|
+
await refetchResumable();
|
|
429
|
+
}, [sessionId, bearerToken, pendingScreenStream, refetchResumable]);
|
|
430
|
+
const resumeSession = useCallback3(async ({
|
|
431
|
+
sessionId: resumeId,
|
|
432
|
+
agentControls
|
|
433
|
+
}) => {
|
|
434
|
+
if (!bearerToken) {
|
|
435
|
+
setError("No auth token available");
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
setIsStarting(true);
|
|
439
|
+
setError("");
|
|
440
|
+
setErrorCode(null);
|
|
441
|
+
onStart?.();
|
|
442
|
+
let screenStream = null;
|
|
443
|
+
if (captureMode === "screenshare") {
|
|
444
|
+
screenStream = await requestScreenShare();
|
|
445
|
+
}
|
|
446
|
+
const requestAgentControls = agentControls?.highlight === true ? { highlight: true } : undefined;
|
|
447
|
+
try {
|
|
448
|
+
const resp = await fetch(`${API_URL3}/v1/sessions/${resumeId}/resume`, {
|
|
449
|
+
method: "POST",
|
|
450
|
+
credentials: "omit",
|
|
451
|
+
headers: { "Content-Type": "application/json", Authorization: `Bearer ${bearerToken}` },
|
|
452
|
+
body: JSON.stringify({ captureMode, agentControls: requestAgentControls })
|
|
453
|
+
});
|
|
454
|
+
if (!resp.ok) {
|
|
455
|
+
const body = await resp.json().catch(() => null);
|
|
456
|
+
setErrorCode(resp.status);
|
|
457
|
+
throw new Error(body?.detail || `Failed to resume: ${resp.status}`);
|
|
458
|
+
}
|
|
459
|
+
const { session, connection: conn } = await resp.json();
|
|
460
|
+
const history2 = await fetchSessionMessages(session.id, bearerToken);
|
|
461
|
+
setSessionId(session.id);
|
|
462
|
+
setConnection({ livekitUrl: conn.livekitUrl, token: conn.token });
|
|
463
|
+
setPendingScreenStream(screenStream);
|
|
464
|
+
setHistoryMessages(history2);
|
|
465
|
+
setShouldConnect(true);
|
|
466
|
+
setIsPaused(false);
|
|
467
|
+
setResumableSessions((prev) => prev.filter((s) => s.id !== resumeId));
|
|
468
|
+
} catch (e) {
|
|
469
|
+
stopStream(screenStream);
|
|
470
|
+
setError(e instanceof Error ? e.message : "Failed to resume session");
|
|
471
|
+
onStartError?.();
|
|
472
|
+
} finally {
|
|
473
|
+
setIsStarting(false);
|
|
474
|
+
}
|
|
475
|
+
}, [captureMode, bearerToken, onStart, onStartError]);
|
|
331
476
|
const disconnect = useCallback3(async () => {
|
|
332
477
|
setIsDisconnecting(true);
|
|
333
478
|
try {
|
|
@@ -341,20 +486,20 @@ function useSession({
|
|
|
341
486
|
});
|
|
342
487
|
} catch {}
|
|
343
488
|
}
|
|
344
|
-
|
|
345
|
-
for (const track of pendingScreenStream.getTracks())
|
|
346
|
-
track.stop();
|
|
347
|
-
}
|
|
489
|
+
stopStream(pendingScreenStream);
|
|
348
490
|
setError("");
|
|
349
491
|
setShouldConnect(false);
|
|
350
492
|
setConnection(null);
|
|
351
493
|
setSessionId(null);
|
|
352
494
|
setPendingScreenStream(null);
|
|
495
|
+
setHistoryMessages([]);
|
|
496
|
+
setIsPaused(false);
|
|
353
497
|
onDisconnect?.();
|
|
498
|
+
await refetchResumable();
|
|
354
499
|
} finally {
|
|
355
500
|
setIsDisconnecting(false);
|
|
356
501
|
}
|
|
357
|
-
}, [sessionId, bearerToken, pendingScreenStream, onDisconnect]);
|
|
502
|
+
}, [sessionId, bearerToken, pendingScreenStream, onDisconnect, refetchResumable]);
|
|
358
503
|
return {
|
|
359
504
|
connection,
|
|
360
505
|
shouldConnect,
|
|
@@ -363,7 +508,14 @@ function useSession({
|
|
|
363
508
|
error,
|
|
364
509
|
errorCode,
|
|
365
510
|
startSession,
|
|
511
|
+
pauseSession,
|
|
512
|
+
resumeSession,
|
|
366
513
|
disconnect,
|
|
514
|
+
isPaused,
|
|
515
|
+
isPausing,
|
|
516
|
+
resumableSessions,
|
|
517
|
+
historyMessages,
|
|
518
|
+
refetchResumable,
|
|
367
519
|
pendingScreenStream,
|
|
368
520
|
bearerToken
|
|
369
521
|
};
|
|
@@ -382,11 +534,11 @@ var NAME_MAX_CHARS = 80;
|
|
|
382
534
|
// src/components/AutoStartMedia.tsx
|
|
383
535
|
import { useConnectionState, useLocalParticipant } from "@livekit/components-react/hooks";
|
|
384
536
|
import { ConnectionState, Track } from "livekit-client";
|
|
385
|
-
import { useEffect as useEffect4, useRef } from "react";
|
|
537
|
+
import { useEffect as useEffect4, useRef as useRef2 } from "react";
|
|
386
538
|
function AutoStartMedia({ pendingScreenStream }) {
|
|
387
539
|
const { localParticipant } = useLocalParticipant();
|
|
388
540
|
const connectionState = useConnectionState();
|
|
389
|
-
const didStartRef =
|
|
541
|
+
const didStartRef = useRef2(false);
|
|
390
542
|
useEffect4(() => {
|
|
391
543
|
if (didStartRef.current)
|
|
392
544
|
return;
|
|
@@ -412,7 +564,7 @@ function AutoStartMedia({ pendingScreenStream }) {
|
|
|
412
564
|
// src/components/DomCapture.tsx
|
|
413
565
|
import { useConnectionState as useConnectionState2, useLocalParticipant as useLocalParticipant2 } from "@livekit/components-react/hooks";
|
|
414
566
|
import { ConnectionState as ConnectionState2, ScreenSharePresets, Track as Track2 } from "livekit-client";
|
|
415
|
-
import { useEffect as useEffect5, useRef as
|
|
567
|
+
import { useEffect as useEffect5, useRef as useRef3 } from "react";
|
|
416
568
|
|
|
417
569
|
// src/capture/a11yUtils.ts
|
|
418
570
|
var ROLE_BY_TAG = {
|
|
@@ -1159,7 +1311,7 @@ async function unpublishAndStopTrack(localParticipant, videoTrack) {
|
|
|
1159
1311
|
function DomCapture() {
|
|
1160
1312
|
const { localParticipant } = useLocalParticipant2();
|
|
1161
1313
|
const connectionState = useConnectionState2();
|
|
1162
|
-
const didStartRef =
|
|
1314
|
+
const didStartRef = useRef3(false);
|
|
1163
1315
|
useEffect5(() => {
|
|
1164
1316
|
if (didStartRef.current)
|
|
1165
1317
|
return;
|
|
@@ -1499,8 +1651,14 @@ var __iconNode18 = [
|
|
|
1499
1651
|
]
|
|
1500
1652
|
];
|
|
1501
1653
|
var MousePointer2 = createLucideIcon("mouse-pointer-2", __iconNode18);
|
|
1502
|
-
// ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/
|
|
1654
|
+
// ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/pause.js
|
|
1503
1655
|
var __iconNode19 = [
|
|
1656
|
+
["rect", { x: "14", y: "3", width: "5", height: "18", rx: "1", key: "kaeet6" }],
|
|
1657
|
+
["rect", { x: "5", y: "3", width: "5", height: "18", rx: "1", key: "1wsw3u" }]
|
|
1658
|
+
];
|
|
1659
|
+
var Pause = createLucideIcon("pause", __iconNode19);
|
|
1660
|
+
// ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/phone-off.js
|
|
1661
|
+
var __iconNode20 = [
|
|
1504
1662
|
[
|
|
1505
1663
|
"path",
|
|
1506
1664
|
{
|
|
@@ -1517,9 +1675,20 @@ var __iconNode19 = [
|
|
|
1517
1675
|
}
|
|
1518
1676
|
]
|
|
1519
1677
|
];
|
|
1520
|
-
var PhoneOff = createLucideIcon("phone-off",
|
|
1678
|
+
var PhoneOff = createLucideIcon("phone-off", __iconNode20);
|
|
1679
|
+
// ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/play.js
|
|
1680
|
+
var __iconNode21 = [
|
|
1681
|
+
[
|
|
1682
|
+
"path",
|
|
1683
|
+
{
|
|
1684
|
+
d: "M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z",
|
|
1685
|
+
key: "10ikf1"
|
|
1686
|
+
}
|
|
1687
|
+
]
|
|
1688
|
+
];
|
|
1689
|
+
var Play = createLucideIcon("play", __iconNode21);
|
|
1521
1690
|
// ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/rocket.js
|
|
1522
|
-
var
|
|
1691
|
+
var __iconNode22 = [
|
|
1523
1692
|
["path", { d: "M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5", key: "qeys4" }],
|
|
1524
1693
|
[
|
|
1525
1694
|
"path",
|
|
@@ -1537,9 +1706,9 @@ var __iconNode20 = [
|
|
|
1537
1706
|
],
|
|
1538
1707
|
["path", { d: "M9 12H4s.55-3.03 2-4c1.62-1.08 5 .05 5 .05", key: "92ym6u" }]
|
|
1539
1708
|
];
|
|
1540
|
-
var Rocket = createLucideIcon("rocket",
|
|
1709
|
+
var Rocket = createLucideIcon("rocket", __iconNode22);
|
|
1541
1710
|
// ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/send.js
|
|
1542
|
-
var
|
|
1711
|
+
var __iconNode23 = [
|
|
1543
1712
|
[
|
|
1544
1713
|
"path",
|
|
1545
1714
|
{
|
|
@@ -1549,17 +1718,17 @@ var __iconNode21 = [
|
|
|
1549
1718
|
],
|
|
1550
1719
|
["path", { d: "m21.854 2.147-10.94 10.939", key: "12cjpa" }]
|
|
1551
1720
|
];
|
|
1552
|
-
var Send = createLucideIcon("send",
|
|
1721
|
+
var Send = createLucideIcon("send", __iconNode23);
|
|
1553
1722
|
// ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/user-plus.js
|
|
1554
|
-
var
|
|
1723
|
+
var __iconNode24 = [
|
|
1555
1724
|
["path", { d: "M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2", key: "1yyitq" }],
|
|
1556
1725
|
["circle", { cx: "9", cy: "7", r: "4", key: "nufk8" }],
|
|
1557
1726
|
["line", { x1: "19", x2: "19", y1: "8", y2: "14", key: "1bvyxn" }],
|
|
1558
1727
|
["line", { x1: "22", x2: "16", y1: "11", y2: "11", key: "1shjgl" }]
|
|
1559
1728
|
];
|
|
1560
|
-
var UserPlus = createLucideIcon("user-plus",
|
|
1729
|
+
var UserPlus = createLucideIcon("user-plus", __iconNode24);
|
|
1561
1730
|
// src/components/HighlightOverlay.tsx
|
|
1562
|
-
import { useCallback as useCallback4, useEffect as useEffect6, useRef as
|
|
1731
|
+
import { useCallback as useCallback4, useEffect as useEffect6, useRef as useRef4, useState as useState4 } from "react";
|
|
1563
1732
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
1564
1733
|
var Z_INDEX = 2147483646;
|
|
1565
1734
|
var HIGHLIGHT_PADDING = 6;
|
|
@@ -1664,8 +1833,8 @@ function findScrollableAncestor(el) {
|
|
|
1664
1833
|
}
|
|
1665
1834
|
function HighlightOverlay() {
|
|
1666
1835
|
const [overlayState, setOverlayState] = useState4(null);
|
|
1667
|
-
const targetElementRef =
|
|
1668
|
-
const pendingFrameRef =
|
|
1836
|
+
const targetElementRef = useRef4(null);
|
|
1837
|
+
const pendingFrameRef = useRef4(null);
|
|
1669
1838
|
const clearOverlay = useCallback4(() => {
|
|
1670
1839
|
targetElementRef.current = null;
|
|
1671
1840
|
setOverlayState(null);
|
|
@@ -1792,14 +1961,27 @@ function HighlightOverlay() {
|
|
|
1792
1961
|
// src/components/MinimizedBubble.tsx
|
|
1793
1962
|
import { useEffect as useEffect7 } from "react";
|
|
1794
1963
|
|
|
1964
|
+
// src/lib/react-compat.ts
|
|
1965
|
+
import * as React from "react";
|
|
1966
|
+
function selectContextHook(use2, useContext3) {
|
|
1967
|
+
return use2 ?? useContext3;
|
|
1968
|
+
}
|
|
1969
|
+
var useContextValue = selectContextHook(React.use, React.useContext);
|
|
1970
|
+
|
|
1795
1971
|
// src/hooks/useLiveAgent.ts
|
|
1796
|
-
import { use } from "react";
|
|
1797
1972
|
function useLiveAgent() {
|
|
1798
|
-
const ctx =
|
|
1973
|
+
const ctx = useContextValue(LiveAgentContext);
|
|
1799
1974
|
if (!ctx) {
|
|
1800
1975
|
throw new Error("useLiveAgent must be used within a <LiveAgent> provider");
|
|
1801
1976
|
}
|
|
1802
|
-
const {
|
|
1977
|
+
const {
|
|
1978
|
+
connection,
|
|
1979
|
+
shouldConnect,
|
|
1980
|
+
historyMessages,
|
|
1981
|
+
phasesSnapshot,
|
|
1982
|
+
setPhasesSnapshot,
|
|
1983
|
+
...publicValue
|
|
1984
|
+
} = ctx;
|
|
1803
1985
|
return publicValue;
|
|
1804
1986
|
}
|
|
1805
1987
|
|
|
@@ -1861,14 +2043,29 @@ function pillStatusFromAgent(state, canSeePage) {
|
|
|
1861
2043
|
return canSeePage ? "observing" : "connected";
|
|
1862
2044
|
}
|
|
1863
2045
|
function LauncherStatusPill() {
|
|
1864
|
-
const { isConnected, isStarting, expandPanel, setSidebarTab, position, captureMode } = useLiveAgent();
|
|
2046
|
+
const { isConnected, isStarting, isPaused, expandPanel, setSidebarTab, position, captureMode } = useLiveAgent();
|
|
1865
2047
|
const { state } = useAgentVoiceState();
|
|
1866
2048
|
const { isScreenSharing } = useMediaControls();
|
|
1867
2049
|
const isStandingBy = isStarting && !isConnected;
|
|
1868
|
-
if (!isConnected && !isStandingBy)
|
|
2050
|
+
if (!isConnected && !isStandingBy && !isPaused)
|
|
1869
2051
|
return null;
|
|
1870
2052
|
const canSeePage = captureMode === "auto" || isScreenSharing;
|
|
1871
|
-
|
|
2053
|
+
let status;
|
|
2054
|
+
if (isPaused) {
|
|
2055
|
+
status = "paused";
|
|
2056
|
+
} else if (isStandingBy) {
|
|
2057
|
+
status = "standing-by";
|
|
2058
|
+
} else {
|
|
2059
|
+
status = pillStatusFromAgent(state, canSeePage);
|
|
2060
|
+
}
|
|
2061
|
+
let ariaLabel;
|
|
2062
|
+
if (isStandingBy) {
|
|
2063
|
+
ariaLabel = "Skippr is standing by — click to open";
|
|
2064
|
+
} else if (isPaused) {
|
|
2065
|
+
ariaLabel = "Session paused — click to open";
|
|
2066
|
+
} else {
|
|
2067
|
+
ariaLabel = `Skippr is ${status} — click to open chat`;
|
|
2068
|
+
}
|
|
1872
2069
|
const handleClick = () => {
|
|
1873
2070
|
if (!isStandingBy)
|
|
1874
2071
|
setSidebarTab("chat");
|
|
@@ -1878,7 +2075,7 @@ function LauncherStatusPill() {
|
|
|
1878
2075
|
type: "button",
|
|
1879
2076
|
onClick: handleClick,
|
|
1880
2077
|
className: cn("skippr:fixed skippr:bottom-20 skippr:z-[9999]", "skippr:flex skippr:items-center skippr:gap-2", "skippr:rounded-full skippr:bg-bubble/95 skippr:backdrop-blur-sm", "skippr:px-3 skippr:py-1.5", "skippr:text-xs skippr:font-medium skippr:text-white", "skippr:shadow-[0_8px_24px_rgba(45,43,61,0.35)]", "skippr:cursor-pointer skippr:transition-colors skippr:hover:bg-bubble", "skippr:animate-[skippr-bubble-in_0.28s_ease-out]", position === "right" ? "skippr:right-6" : "skippr:left-6"),
|
|
1881
|
-
"aria-label":
|
|
2078
|
+
"aria-label": ariaLabel,
|
|
1882
2079
|
children: /* @__PURE__ */ jsxs2("span", {
|
|
1883
2080
|
className: "skippr:flex skippr:items-center skippr:gap-2 skippr:animate-[skippr-pill-content_0.22s_ease-out]",
|
|
1884
2081
|
children: [
|
|
@@ -1921,6 +2118,14 @@ function LauncherStatusPill() {
|
|
|
1921
2118
|
children: "Skippr is thinking"
|
|
1922
2119
|
})
|
|
1923
2120
|
]
|
|
2121
|
+
}),
|
|
2122
|
+
status === "paused" && /* @__PURE__ */ jsxs2(Fragment, {
|
|
2123
|
+
children: [
|
|
2124
|
+
/* @__PURE__ */ jsx2(PausedDot, {}),
|
|
2125
|
+
/* @__PURE__ */ jsx2("span", {
|
|
2126
|
+
children: "Session paused"
|
|
2127
|
+
})
|
|
2128
|
+
]
|
|
1924
2129
|
})
|
|
1925
2130
|
]
|
|
1926
2131
|
}, status)
|
|
@@ -1939,6 +2144,11 @@ function ConnectedDot() {
|
|
|
1939
2144
|
]
|
|
1940
2145
|
});
|
|
1941
2146
|
}
|
|
2147
|
+
function PausedDot() {
|
|
2148
|
+
return /* @__PURE__ */ jsx2("span", {
|
|
2149
|
+
className: "skippr:inline-flex skippr:size-2 skippr:rounded-full skippr:bg-amber-400"
|
|
2150
|
+
});
|
|
2151
|
+
}
|
|
1942
2152
|
function ObservingIcon() {
|
|
1943
2153
|
return /* @__PURE__ */ jsxs2("svg", {
|
|
1944
2154
|
viewBox: "0 0 24 24",
|
|
@@ -2090,34 +2300,60 @@ import { jsx as jsx4, jsxs as jsxs4, Fragment as Fragment2 } from "react/jsx-run
|
|
|
2090
2300
|
var CONTROL_BUTTON = "skippr:flex skippr:size-12 skippr:items-center skippr:justify-center skippr:rounded-[14px] skippr:cursor-pointer skippr:transition-all skippr:hover:-translate-y-0.5 skippr:active:translate-y-0";
|
|
2091
2301
|
var CONTROL_SHADOW = "skippr:shadow-[0_4px_16px_rgba(0,0,0,0.15),0_2px_4px_rgba(0,0,0,0.1)]";
|
|
2092
2302
|
function ConnectedLauncher() {
|
|
2093
|
-
const {
|
|
2303
|
+
const {
|
|
2304
|
+
expandPanel,
|
|
2305
|
+
disconnect,
|
|
2306
|
+
pauseSession,
|
|
2307
|
+
resumeSession,
|
|
2308
|
+
isPaused,
|
|
2309
|
+
isPausing,
|
|
2310
|
+
captureMode,
|
|
2311
|
+
setSidebarTab
|
|
2312
|
+
} = useLiveAgent();
|
|
2094
2313
|
const { isMuted, toggleMute, isScreenSharing, toggleScreenShare } = useMediaControls();
|
|
2095
2314
|
const showScreenShareToggle = captureMode === "screenshare";
|
|
2315
|
+
const showPaused = isPaused || isPausing;
|
|
2096
2316
|
const openChat = () => {
|
|
2097
2317
|
setSidebarTab("chat");
|
|
2098
2318
|
expandPanel();
|
|
2099
2319
|
};
|
|
2100
2320
|
return /* @__PURE__ */ jsxs4(Fragment2, {
|
|
2101
2321
|
children: [
|
|
2102
|
-
/* @__PURE__ */
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2322
|
+
!showPaused && /* @__PURE__ */ jsxs4(Fragment2, {
|
|
2323
|
+
children: [
|
|
2324
|
+
/* @__PURE__ */ jsx4("button", {
|
|
2325
|
+
type: "button",
|
|
2326
|
+
onClick: toggleMute,
|
|
2327
|
+
"aria-label": isMuted ? "Unmute" : "Mute",
|
|
2328
|
+
className: cn(CONTROL_BUTTON, CONTROL_SHADOW, isMuted ? "skippr:bg-destructive/10 skippr:text-destructive skippr:hover:bg-destructive/20" : "skippr:bg-white skippr:text-foreground skippr:hover:bg-muted"),
|
|
2329
|
+
children: isMuted ? /* @__PURE__ */ jsx4(MicOff, {
|
|
2330
|
+
className: "skippr:size-5"
|
|
2331
|
+
}) : /* @__PURE__ */ jsx4(Mic, {
|
|
2332
|
+
className: "skippr:size-5"
|
|
2333
|
+
})
|
|
2334
|
+
}),
|
|
2335
|
+
showScreenShareToggle && /* @__PURE__ */ jsx4("button", {
|
|
2336
|
+
type: "button",
|
|
2337
|
+
onClick: toggleScreenShare,
|
|
2338
|
+
"aria-label": isScreenSharing ? "Stop sharing screen" : "Share screen",
|
|
2339
|
+
className: cn(CONTROL_BUTTON, CONTROL_SHADOW, isScreenSharing ? "skippr:bg-primary skippr:text-primary-foreground skippr:hover:bg-primary/90" : "skippr:bg-white skippr:text-foreground skippr:hover:bg-muted"),
|
|
2340
|
+
children: isScreenSharing ? /* @__PURE__ */ jsx4(MonitorOff, {
|
|
2341
|
+
className: "skippr:size-5"
|
|
2342
|
+
}) : /* @__PURE__ */ jsx4(Monitor, {
|
|
2343
|
+
className: "skippr:size-5"
|
|
2344
|
+
})
|
|
2345
|
+
})
|
|
2346
|
+
]
|
|
2112
2347
|
}),
|
|
2113
|
-
|
|
2348
|
+
/* @__PURE__ */ jsx4("button", {
|
|
2114
2349
|
type: "button",
|
|
2115
|
-
onClick:
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2350
|
+
onClick: () => isPaused ? resumeSession() : pauseSession(),
|
|
2351
|
+
disabled: isPausing,
|
|
2352
|
+
"aria-label": showPaused ? "Resume session" : "Pause session",
|
|
2353
|
+
className: cn(CONTROL_BUTTON, CONTROL_SHADOW, "skippr:bg-white skippr:text-foreground skippr:hover:bg-muted skippr:disabled:opacity-60"),
|
|
2354
|
+
children: showPaused ? /* @__PURE__ */ jsx4(Play, {
|
|
2119
2355
|
className: "skippr:size-5"
|
|
2120
|
-
}) : /* @__PURE__ */ jsx4(
|
|
2356
|
+
}) : /* @__PURE__ */ jsx4(Pause, {
|
|
2121
2357
|
className: "skippr:size-5"
|
|
2122
2358
|
})
|
|
2123
2359
|
}),
|
|
@@ -2130,7 +2366,7 @@ function ConnectedLauncher() {
|
|
|
2130
2366
|
className: "skippr:size-5"
|
|
2131
2367
|
})
|
|
2132
2368
|
}),
|
|
2133
|
-
/* @__PURE__ */ jsx4("button", {
|
|
2369
|
+
!showPaused && /* @__PURE__ */ jsx4("button", {
|
|
2134
2370
|
type: "button",
|
|
2135
2371
|
onClick: openChat,
|
|
2136
2372
|
"aria-label": "Open chat",
|
|
@@ -2181,8 +2417,8 @@ function MinimizedBubble({
|
|
|
2181
2417
|
welcomeDismissed,
|
|
2182
2418
|
onDismissWelcome
|
|
2183
2419
|
}) {
|
|
2184
|
-
const { isConnected, isStarting, position } = useLiveAgent();
|
|
2185
|
-
const inSession = isConnected;
|
|
2420
|
+
const { isConnected, isStarting, isPaused, isPausing, position } = useLiveAgent();
|
|
2421
|
+
const inSession = isConnected || isPaused || isPausing;
|
|
2186
2422
|
return /* @__PURE__ */ jsxs4(Fragment2, {
|
|
2187
2423
|
children: [
|
|
2188
2424
|
/* @__PURE__ */ jsx4(LauncherStatusPill, {}),
|
|
@@ -2202,7 +2438,7 @@ function MinimizedBubble({
|
|
|
2202
2438
|
}
|
|
2203
2439
|
|
|
2204
2440
|
// src/components/Sidebar.tsx
|
|
2205
|
-
import { useEffect as
|
|
2441
|
+
import { useEffect as useEffect16 } from "react";
|
|
2206
2442
|
|
|
2207
2443
|
// src/hooks/useCombinedMessages.ts
|
|
2208
2444
|
import { useMemo as useMemo4 } from "react";
|
|
@@ -2273,18 +2509,25 @@ function useCombinedMessages() {
|
|
|
2273
2509
|
const { transcriptMessages } = useStreamingTranscript();
|
|
2274
2510
|
const { chatMessages, sendChatMessage, isSendingChat } = useChatMessages();
|
|
2275
2511
|
const { state: agentState } = useAgentVoiceState();
|
|
2276
|
-
const
|
|
2512
|
+
const historyMessages = useContextValue(LiveAgentContext)?.historyMessages ?? [];
|
|
2513
|
+
const liveMessages = useMemo4(() => {
|
|
2277
2514
|
if (chatMessages.length === 0)
|
|
2278
2515
|
return transcriptMessages;
|
|
2279
2516
|
if (transcriptMessages.length === 0)
|
|
2280
2517
|
return chatMessages;
|
|
2281
2518
|
return mergeChatsIntoTranscripts(transcriptMessages, chatMessages);
|
|
2282
2519
|
}, [transcriptMessages, chatMessages]);
|
|
2520
|
+
const allMessages = useMemo4(() => {
|
|
2521
|
+
if (historyMessages.length === 0)
|
|
2522
|
+
return liveMessages;
|
|
2523
|
+
const seenIds = new Set(liveMessages.map((message) => message.id));
|
|
2524
|
+
return [...historyMessages.filter((message) => !seenIds.has(message.id)), ...liveMessages];
|
|
2525
|
+
}, [historyMessages, liveMessages]);
|
|
2283
2526
|
return { allMessages, agentState, sendChatMessage, isSendingChat };
|
|
2284
2527
|
}
|
|
2285
2528
|
|
|
2286
2529
|
// src/hooks/usePhaseUpdates.ts
|
|
2287
|
-
import { useCallback as useCallback6 } from "react";
|
|
2530
|
+
import { useCallback as useCallback6, useEffect as useEffect9 } from "react";
|
|
2288
2531
|
|
|
2289
2532
|
// src/hooks/useAgentState.ts
|
|
2290
2533
|
import { useRemoteParticipants } from "@livekit/components-react/hooks";
|
|
@@ -2339,12 +2582,18 @@ function parsePhases(json) {
|
|
|
2339
2582
|
}
|
|
2340
2583
|
function usePhaseUpdates() {
|
|
2341
2584
|
const parse = useCallback6(parsePhases, []);
|
|
2342
|
-
const
|
|
2585
|
+
const livePhases = useAgentState("phases", parse, []);
|
|
2586
|
+
const ctx = useContextValue(LiveAgentContext);
|
|
2587
|
+
useEffect9(() => {
|
|
2588
|
+
if (livePhases.length > 0)
|
|
2589
|
+
ctx?.setPhasesSnapshot(livePhases);
|
|
2590
|
+
}, [livePhases, ctx?.setPhasesSnapshot]);
|
|
2591
|
+
const phases = livePhases.length > 0 ? livePhases : ctx?.phasesSnapshot ?? [];
|
|
2343
2592
|
return { phases };
|
|
2344
2593
|
}
|
|
2345
2594
|
|
|
2346
2595
|
// src/hooks/useSessionRemaining.ts
|
|
2347
|
-
import { useEffect as
|
|
2596
|
+
import { useEffect as useEffect10, useRef as useRef5, useState as useState6 } from "react";
|
|
2348
2597
|
|
|
2349
2598
|
// src/lib/format.ts
|
|
2350
2599
|
function formatTime(seconds) {
|
|
@@ -2360,9 +2609,9 @@ function parseNumber(s) {
|
|
|
2360
2609
|
// src/hooks/useSessionRemaining.ts
|
|
2361
2610
|
function useSessionRemaining() {
|
|
2362
2611
|
const maxCallDuration = useAgentState("maxCallDuration", parseNumber, null);
|
|
2363
|
-
const endTimeRef =
|
|
2612
|
+
const endTimeRef = useRef5(null);
|
|
2364
2613
|
const [remaining, setRemaining] = useState6(null);
|
|
2365
|
-
|
|
2614
|
+
useEffect10(() => {
|
|
2366
2615
|
if (maxCallDuration === null || endTimeRef.current !== null)
|
|
2367
2616
|
return;
|
|
2368
2617
|
const endTime = Date.now() + maxCallDuration * 1000;
|
|
@@ -2379,10 +2628,10 @@ function useSessionRemaining() {
|
|
|
2379
2628
|
}
|
|
2380
2629
|
|
|
2381
2630
|
// src/hooks/useElapsedSeconds.ts
|
|
2382
|
-
import { useEffect as
|
|
2631
|
+
import { useEffect as useEffect11, useState as useState7 } from "react";
|
|
2383
2632
|
function useElapsedSeconds(isRunning) {
|
|
2384
2633
|
const [elapsed, setElapsed] = useState7(0);
|
|
2385
|
-
|
|
2634
|
+
useEffect11(() => {
|
|
2386
2635
|
if (!isRunning) {
|
|
2387
2636
|
setElapsed(0);
|
|
2388
2637
|
return;
|
|
@@ -2480,7 +2729,7 @@ function LoadingDots({ label }) {
|
|
|
2480
2729
|
}
|
|
2481
2730
|
|
|
2482
2731
|
// src/components/LoginFlow.tsx
|
|
2483
|
-
import { useCallback as useCallback7, useEffect as
|
|
2732
|
+
import { useCallback as useCallback7, useEffect as useEffect12, useRef as useRef6, useState as useState8 } from "react";
|
|
2484
2733
|
|
|
2485
2734
|
// src/components/ui/button.tsx
|
|
2486
2735
|
import { forwardRef as forwardRef3 } from "react";
|
|
@@ -2608,16 +2857,16 @@ function EmailStep({ email, onEmailChange, onSubmit, error, isSubmitting }) {
|
|
|
2608
2857
|
function OtpStep({ email, onSubmit, onResend, onBack, error, isSubmitting }) {
|
|
2609
2858
|
const [digits, setDigits] = useState8(Array(OTP_LENGTH).fill(""));
|
|
2610
2859
|
const [resendCooldown, setResendCooldown] = useState8(0);
|
|
2611
|
-
const inputRefs =
|
|
2612
|
-
const submittedRef =
|
|
2613
|
-
|
|
2860
|
+
const inputRefs = useRef6([]);
|
|
2861
|
+
const submittedRef = useRef6(false);
|
|
2862
|
+
useEffect12(() => {
|
|
2614
2863
|
inputRefs.current[0]?.focus();
|
|
2615
2864
|
}, []);
|
|
2616
|
-
|
|
2865
|
+
useEffect12(() => {
|
|
2617
2866
|
if (error)
|
|
2618
2867
|
submittedRef.current = false;
|
|
2619
2868
|
}, [error]);
|
|
2620
|
-
|
|
2869
|
+
useEffect12(() => {
|
|
2621
2870
|
if (resendCooldown <= 0)
|
|
2622
2871
|
return;
|
|
2623
2872
|
const timer = setTimeout(() => setResendCooldown((c) => c - 1), 1000);
|
|
@@ -2758,35 +3007,66 @@ function OtpStep({ email, onSubmit, onResend, onBack, error, isSubmitting }) {
|
|
|
2758
3007
|
}
|
|
2759
3008
|
|
|
2760
3009
|
// src/components/MeetingControls.tsx
|
|
2761
|
-
import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
2762
|
-
var CONTROL_BUTTON2 = "skippr:flex skippr:size-11 skippr:cursor-pointer skippr:items-center skippr:justify-center skippr:rounded-full skippr:transition-colors";
|
|
2763
|
-
|
|
3010
|
+
import { jsx as jsx9, jsxs as jsxs8, Fragment as Fragment3 } from "react/jsx-runtime";
|
|
3011
|
+
var CONTROL_BUTTON2 = "skippr:flex skippr:size-11 skippr:cursor-pointer skippr:items-center skippr:justify-center skippr:rounded-full skippr:transition-colors skippr:disabled:cursor-not-allowed skippr:disabled:opacity-60";
|
|
3012
|
+
var MUTED_BUTTON = "skippr:bg-muted skippr:text-foreground skippr:hover:bg-muted/80";
|
|
3013
|
+
function MeetingControls({
|
|
3014
|
+
onHangUp,
|
|
3015
|
+
onPause,
|
|
3016
|
+
onResume,
|
|
3017
|
+
isPaused = false,
|
|
3018
|
+
isPausing = false,
|
|
3019
|
+
showScreenShareToggle = true
|
|
3020
|
+
}) {
|
|
2764
3021
|
const { isMuted, toggleMute, isScreenSharing, toggleScreenShare } = useMediaControls();
|
|
3022
|
+
const showPaused = isPaused || isPausing;
|
|
2765
3023
|
return /* @__PURE__ */ jsxs8("div", {
|
|
2766
3024
|
className: "skippr:shrink-0 skippr:border-t skippr:border-border skippr:bg-background skippr:px-4 skippr:py-4",
|
|
2767
3025
|
children: [
|
|
2768
3026
|
/* @__PURE__ */ jsxs8("div", {
|
|
2769
3027
|
className: "skippr:flex skippr:items-center skippr:justify-center skippr:gap-3",
|
|
2770
3028
|
children: [
|
|
2771
|
-
/* @__PURE__ */
|
|
3029
|
+
!showPaused && /* @__PURE__ */ jsxs8(Fragment3, {
|
|
3030
|
+
children: [
|
|
3031
|
+
/* @__PURE__ */ jsx9("button", {
|
|
3032
|
+
type: "button",
|
|
3033
|
+
onClick: toggleMute,
|
|
3034
|
+
"aria-label": isMuted ? "Unmute" : "Mute",
|
|
3035
|
+
className: cn(CONTROL_BUTTON2, isMuted ? "skippr:bg-destructive/15 skippr:text-destructive skippr:hover:bg-destructive/25" : MUTED_BUTTON),
|
|
3036
|
+
children: isMuted ? /* @__PURE__ */ jsx9(MicOff, {
|
|
3037
|
+
className: "skippr:size-5"
|
|
3038
|
+
}) : /* @__PURE__ */ jsx9(Mic, {
|
|
3039
|
+
className: "skippr:size-5"
|
|
3040
|
+
})
|
|
3041
|
+
}),
|
|
3042
|
+
showScreenShareToggle && /* @__PURE__ */ jsx9("button", {
|
|
3043
|
+
type: "button",
|
|
3044
|
+
onClick: toggleScreenShare,
|
|
3045
|
+
"aria-label": isScreenSharing ? "Stop sharing screen" : "Share screen",
|
|
3046
|
+
className: cn(CONTROL_BUTTON2, isScreenSharing ? "skippr:bg-bubble skippr:text-white skippr:hover:brightness-110" : MUTED_BUTTON),
|
|
3047
|
+
children: isScreenSharing ? /* @__PURE__ */ jsx9(MonitorOff, {
|
|
3048
|
+
className: "skippr:size-5"
|
|
3049
|
+
}) : /* @__PURE__ */ jsx9(Monitor, {
|
|
3050
|
+
className: "skippr:size-5"
|
|
3051
|
+
})
|
|
3052
|
+
})
|
|
3053
|
+
]
|
|
3054
|
+
}),
|
|
3055
|
+
showPaused ? onResume && /* @__PURE__ */ jsx9("button", {
|
|
2772
3056
|
type: "button",
|
|
2773
|
-
onClick:
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
}) : /* @__PURE__ */ jsx9(Mic, {
|
|
3057
|
+
onClick: onResume,
|
|
3058
|
+
disabled: isPausing,
|
|
3059
|
+
"aria-label": "Resume session",
|
|
3060
|
+
className: cn(CONTROL_BUTTON2, MUTED_BUTTON),
|
|
3061
|
+
children: /* @__PURE__ */ jsx9(Play, {
|
|
2779
3062
|
className: "skippr:size-5"
|
|
2780
3063
|
})
|
|
2781
|
-
}),
|
|
2782
|
-
showScreenShareToggle && /* @__PURE__ */ jsx9("button", {
|
|
3064
|
+
}) : onPause && /* @__PURE__ */ jsx9("button", {
|
|
2783
3065
|
type: "button",
|
|
2784
|
-
onClick:
|
|
2785
|
-
"aria-label":
|
|
2786
|
-
className: cn(CONTROL_BUTTON2,
|
|
2787
|
-
children:
|
|
2788
|
-
className: "skippr:size-5"
|
|
2789
|
-
}) : /* @__PURE__ */ jsx9(Monitor, {
|
|
3066
|
+
onClick: onPause,
|
|
3067
|
+
"aria-label": "Pause session",
|
|
3068
|
+
className: cn(CONTROL_BUTTON2, MUTED_BUTTON),
|
|
3069
|
+
children: /* @__PURE__ */ jsx9(Pause, {
|
|
2790
3070
|
className: "skippr:size-5"
|
|
2791
3071
|
})
|
|
2792
3072
|
}),
|
|
@@ -2810,17 +3090,17 @@ function MeetingControls({ onHangUp, showScreenShareToggle = true }) {
|
|
|
2810
3090
|
}
|
|
2811
3091
|
|
|
2812
3092
|
// src/components/MessageList.tsx
|
|
2813
|
-
import { useEffect as
|
|
3093
|
+
import { useEffect as useEffect14, useRef as useRef8 } from "react";
|
|
2814
3094
|
|
|
2815
3095
|
// src/components/ChatInput.tsx
|
|
2816
|
-
import { useEffect as
|
|
3096
|
+
import { useEffect as useEffect13, useRef as useRef7, useState as useState9 } from "react";
|
|
2817
3097
|
import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2818
3098
|
var MAX_INPUT_HEIGHT = 60;
|
|
2819
3099
|
function ChatInput({ sendChatMessage, isSendingChat, autoFocus = false }) {
|
|
2820
3100
|
const [inputText, setInputText] = useState9("");
|
|
2821
|
-
const textareaRef =
|
|
3101
|
+
const textareaRef = useRef7(null);
|
|
2822
3102
|
const canSend = inputText.trim().length > 0 && !isSendingChat;
|
|
2823
|
-
|
|
3103
|
+
useEffect13(() => {
|
|
2824
3104
|
if (autoFocus)
|
|
2825
3105
|
textareaRef.current?.focus();
|
|
2826
3106
|
}, [autoFocus]);
|
|
@@ -2965,9 +3245,9 @@ function MessageList({
|
|
|
2965
3245
|
isSendingChat,
|
|
2966
3246
|
autoFocus = false
|
|
2967
3247
|
}) {
|
|
2968
|
-
const scrollRef =
|
|
3248
|
+
const scrollRef = useRef8(null);
|
|
2969
3249
|
const lastMessage = messages.length > 0 ? messages[messages.length - 1] : undefined;
|
|
2970
|
-
|
|
3250
|
+
useEffect14(() => {
|
|
2971
3251
|
scrollRef.current?.scrollIntoView({ behavior: "smooth" });
|
|
2972
3252
|
}, [messages.length, lastMessage?.content]);
|
|
2973
3253
|
const showTyping = isStreaming && lastMessage?.role === "assistant" && lastMessage.content === "";
|
|
@@ -2999,7 +3279,7 @@ function MessageList({
|
|
|
2999
3279
|
}
|
|
3000
3280
|
|
|
3001
3281
|
// src/components/ModuleSelector.tsx
|
|
3002
|
-
import { useEffect as
|
|
3282
|
+
import { useEffect as useEffect15, useMemo as useMemo5, useRef as useRef9, useState as useState10 } from "react";
|
|
3003
3283
|
import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
3004
3284
|
var AGENT_TYPE_ICONS = {
|
|
3005
3285
|
onboarding: UserPlus,
|
|
@@ -3019,13 +3299,15 @@ function ModuleSelector() {
|
|
|
3019
3299
|
selectModule,
|
|
3020
3300
|
isStarting,
|
|
3021
3301
|
isDisconnecting,
|
|
3022
|
-
error
|
|
3302
|
+
error,
|
|
3303
|
+
resumableSessions
|
|
3023
3304
|
} = useLiveAgent();
|
|
3024
3305
|
const isBusy = isStarting || isDisconnecting;
|
|
3025
|
-
const
|
|
3306
|
+
const resumableAgentIds = useMemo5(() => new Set(resumableSessions.map((s) => s.agentId)), [resumableSessions]);
|
|
3307
|
+
const scrollRef = useRef9(null);
|
|
3026
3308
|
const [showScrollHint, setShowScrollHint] = useState10(false);
|
|
3027
3309
|
const [isScrolled, setIsScrolled] = useState10(false);
|
|
3028
|
-
|
|
3310
|
+
useEffect15(() => {
|
|
3029
3311
|
const el = scrollRef.current;
|
|
3030
3312
|
if (!el)
|
|
3031
3313
|
return;
|
|
@@ -3109,6 +3391,7 @@ function ModuleSelector() {
|
|
|
3109
3391
|
const isFeatured = index2 === 0;
|
|
3110
3392
|
const isWide = index2 === availableModules.length - 1 && availableModules.length % 2 === 1;
|
|
3111
3393
|
const Icon2 = getAgentIcon(module.type);
|
|
3394
|
+
const canResume = resumableAgentIds.has(module.id);
|
|
3112
3395
|
const base = "skippr:group skippr:flex skippr:cursor-pointer skippr:gap-3 skippr:rounded-xl skippr:text-left skippr:transition-colors skippr:disabled:cursor-not-allowed skippr:disabled:opacity-50";
|
|
3113
3396
|
const layout = isWide ? "skippr:items-center skippr:p-3.5 skippr:pb-5" : "skippr:flex-col skippr:items-start skippr:p-3.5";
|
|
3114
3397
|
const variant = isFeatured ? "skippr:bg-primary skippr:text-primary-foreground skippr:hover:bg-primary/90" : "skippr:bg-background skippr:ring-1 skippr:ring-foreground/10 skippr:hover:bg-muted/50";
|
|
@@ -3128,9 +3411,23 @@ function ModuleSelector() {
|
|
|
3128
3411
|
/* @__PURE__ */ jsxs13("div", {
|
|
3129
3412
|
className: "skippr:min-w-0 skippr:w-full skippr:space-y-0.5",
|
|
3130
3413
|
children: [
|
|
3131
|
-
/* @__PURE__ */
|
|
3132
|
-
className: "skippr:
|
|
3133
|
-
children:
|
|
3414
|
+
/* @__PURE__ */ jsxs13("div", {
|
|
3415
|
+
className: "skippr:flex skippr:items-center skippr:gap-1.5",
|
|
3416
|
+
children: [
|
|
3417
|
+
/* @__PURE__ */ jsx14("p", {
|
|
3418
|
+
className: "skippr:line-clamp-1 skippr:text-sm skippr:font-semibold",
|
|
3419
|
+
children: module.name
|
|
3420
|
+
}),
|
|
3421
|
+
canResume && /* @__PURE__ */ jsxs13("span", {
|
|
3422
|
+
className: `skippr:inline-flex skippr:shrink-0 skippr:items-center skippr:gap-0.5 skippr:rounded-full skippr:px-1.5 skippr:py-0.5 skippr:text-[9px] skippr:font-medium skippr:uppercase skippr:tracking-wide ${isFeatured ? "skippr:bg-primary-foreground/20 skippr:text-primary-foreground" : "skippr:bg-bubble/15 skippr:text-bubble"}`,
|
|
3423
|
+
children: [
|
|
3424
|
+
/* @__PURE__ */ jsx14(Play, {
|
|
3425
|
+
className: "skippr:size-2.5"
|
|
3426
|
+
}),
|
|
3427
|
+
"Resume"
|
|
3428
|
+
]
|
|
3429
|
+
})
|
|
3430
|
+
]
|
|
3134
3431
|
}),
|
|
3135
3432
|
module.description && /* @__PURE__ */ jsx14("p", {
|
|
3136
3433
|
className: isFeatured ? "skippr:line-clamp-2 skippr:text-[11px] skippr:leading-snug skippr:text-primary-foreground/70" : "skippr:line-clamp-2 skippr:text-[11px] skippr:leading-snug skippr:text-muted-foreground",
|
|
@@ -3227,18 +3524,27 @@ function SessionWarningBanner({ remaining }) {
|
|
|
3227
3524
|
|
|
3228
3525
|
// src/components/StartSessionPrompt.tsx
|
|
3229
3526
|
import { jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
3527
|
+
var PROMPT_BUTTON = "skippr:cursor-pointer skippr:rounded-xl skippr:bg-primary skippr:px-8 skippr:py-3 skippr:text-sm skippr:font-medium skippr:text-primary-foreground skippr:transition-all skippr:hover:bg-primary/90 skippr:disabled:cursor-not-allowed skippr:disabled:opacity-60";
|
|
3230
3528
|
function StartSessionPrompt({
|
|
3231
3529
|
onStartSession,
|
|
3232
3530
|
agentId,
|
|
3233
3531
|
agentControls,
|
|
3234
3532
|
isStarting,
|
|
3235
3533
|
error,
|
|
3236
|
-
label = "Talk to Skippr"
|
|
3534
|
+
label = "Talk to Skippr",
|
|
3535
|
+
canResume = false,
|
|
3536
|
+
onResume
|
|
3237
3537
|
}) {
|
|
3238
3538
|
return /* @__PURE__ */ jsxs15("div", {
|
|
3239
3539
|
className: "skippr:flex skippr:flex-1 skippr:flex-col skippr:items-center skippr:justify-center skippr:gap-3 skippr:px-4",
|
|
3240
3540
|
children: [
|
|
3241
|
-
/* @__PURE__ */ jsx17("button", {
|
|
3541
|
+
canResume && onResume ? /* @__PURE__ */ jsx17("button", {
|
|
3542
|
+
type: "button",
|
|
3543
|
+
onClick: onResume,
|
|
3544
|
+
disabled: isStarting,
|
|
3545
|
+
className: PROMPT_BUTTON,
|
|
3546
|
+
children: isStarting ? "Resuming..." : "Resume session"
|
|
3547
|
+
}) : /* @__PURE__ */ jsx17("button", {
|
|
3242
3548
|
type: "button",
|
|
3243
3549
|
onClick: () => {
|
|
3244
3550
|
if (!agentId)
|
|
@@ -3246,7 +3552,7 @@ function StartSessionPrompt({
|
|
|
3246
3552
|
onStartSession(agentControls ? { agentId, agentControls } : { agentId });
|
|
3247
3553
|
},
|
|
3248
3554
|
disabled: isStarting || !agentId,
|
|
3249
|
-
className:
|
|
3555
|
+
className: PROMPT_BUTTON,
|
|
3250
3556
|
children: isStarting ? "Starting..." : label
|
|
3251
3557
|
}),
|
|
3252
3558
|
error && /* @__PURE__ */ jsx17("p", {
|
|
@@ -3258,7 +3564,7 @@ function StartSessionPrompt({
|
|
|
3258
3564
|
}
|
|
3259
3565
|
|
|
3260
3566
|
// src/components/Sidebar.tsx
|
|
3261
|
-
import { jsx as jsx18, jsxs as jsxs16, Fragment as
|
|
3567
|
+
import { jsx as jsx18, jsxs as jsxs16, Fragment as Fragment4 } from "react/jsx-runtime";
|
|
3262
3568
|
function Sidebar({
|
|
3263
3569
|
hideControls = false,
|
|
3264
3570
|
hideHeader = false,
|
|
@@ -3268,8 +3574,14 @@ function Sidebar({
|
|
|
3268
3574
|
variant,
|
|
3269
3575
|
isConnected,
|
|
3270
3576
|
isStarting,
|
|
3577
|
+
isDisconnecting,
|
|
3578
|
+
isPausing,
|
|
3579
|
+
isPaused,
|
|
3271
3580
|
error,
|
|
3272
3581
|
startSession,
|
|
3582
|
+
pauseSession,
|
|
3583
|
+
resumeSession,
|
|
3584
|
+
resumableSession,
|
|
3273
3585
|
disconnect,
|
|
3274
3586
|
isPanelOpen,
|
|
3275
3587
|
position,
|
|
@@ -3289,7 +3601,7 @@ function Sidebar({
|
|
|
3289
3601
|
} = useLiveAgent();
|
|
3290
3602
|
const isFloating = variant === "floating";
|
|
3291
3603
|
const isSidebar = variant === "sidebar";
|
|
3292
|
-
|
|
3604
|
+
useEffect16(() => {
|
|
3293
3605
|
if (!isSidebar)
|
|
3294
3606
|
return;
|
|
3295
3607
|
const prop = position === "right" ? "marginRight" : "marginLeft";
|
|
@@ -3320,7 +3632,13 @@ function Sidebar({
|
|
|
3320
3632
|
isSubmitting: isAuthSubmitting
|
|
3321
3633
|
}) : /* @__PURE__ */ jsx18(AuthenticatedContent, {
|
|
3322
3634
|
isConnected,
|
|
3635
|
+
isPaused,
|
|
3636
|
+
isPausing,
|
|
3637
|
+
isDisconnecting,
|
|
3638
|
+
canResume: resumableSession !== null,
|
|
3323
3639
|
onStartSession: startSession,
|
|
3640
|
+
onPause: pauseSession,
|
|
3641
|
+
onResume: resumeSession,
|
|
3324
3642
|
onDisconnect: disconnect,
|
|
3325
3643
|
isStarting,
|
|
3326
3644
|
error,
|
|
@@ -3339,7 +3657,13 @@ function Sidebar({
|
|
|
3339
3657
|
}
|
|
3340
3658
|
function AuthenticatedContent({
|
|
3341
3659
|
isConnected,
|
|
3660
|
+
isPaused,
|
|
3661
|
+
isPausing,
|
|
3662
|
+
isDisconnecting,
|
|
3663
|
+
canResume,
|
|
3342
3664
|
onStartSession,
|
|
3665
|
+
onPause,
|
|
3666
|
+
onResume,
|
|
3343
3667
|
onDisconnect,
|
|
3344
3668
|
isStarting,
|
|
3345
3669
|
error,
|
|
@@ -3353,9 +3677,18 @@ function AuthenticatedContent({
|
|
|
3353
3677
|
agentId,
|
|
3354
3678
|
agentControls
|
|
3355
3679
|
}) {
|
|
3356
|
-
const
|
|
3680
|
+
const inSession = isConnected || isStarting || isPaused || isPausing;
|
|
3681
|
+
const showSelectorAsPrompt = hasModuleSelector && !inSession && !isDisconnecting;
|
|
3357
3682
|
const showTabBar = !showSelectorAsPrompt;
|
|
3358
|
-
|
|
3683
|
+
let transitionLabel = null;
|
|
3684
|
+
if (isPausing) {
|
|
3685
|
+
transitionLabel = "Pausing...";
|
|
3686
|
+
} else if (isStarting && !isConnected) {
|
|
3687
|
+
transitionLabel = "Reconnecting...";
|
|
3688
|
+
} else if (isPaused) {
|
|
3689
|
+
transitionLabel = "Paused";
|
|
3690
|
+
}
|
|
3691
|
+
return /* @__PURE__ */ jsxs16(Fragment4, {
|
|
3359
3692
|
children: [
|
|
3360
3693
|
isConnected && /* @__PURE__ */ jsx18(ConnectedBanner, {}),
|
|
3361
3694
|
showTabBar && /* @__PURE__ */ jsxs16("div", {
|
|
@@ -3393,9 +3726,22 @@ function AuthenticatedContent({
|
|
|
3393
3726
|
}),
|
|
3394
3727
|
/* @__PURE__ */ jsx18("div", {
|
|
3395
3728
|
className: "skippr:flex skippr:min-h-0 skippr:flex-1 skippr:flex-col",
|
|
3396
|
-
children:
|
|
3397
|
-
|
|
3398
|
-
|
|
3729
|
+
children: isDisconnecting ? /* @__PURE__ */ jsx18("div", {
|
|
3730
|
+
className: "skippr:flex skippr:flex-1 skippr:items-center skippr:justify-center",
|
|
3731
|
+
children: /* @__PURE__ */ jsx18(LoadingDots, {
|
|
3732
|
+
label: "Ending session..."
|
|
3733
|
+
})
|
|
3734
|
+
}) : showSelectorAsPrompt ? /* @__PURE__ */ jsx18(ModuleSelector, {}) : inSession ? /* @__PURE__ */ jsxs16(Fragment4, {
|
|
3735
|
+
children: [
|
|
3736
|
+
transitionLabel && /* @__PURE__ */ jsx18("div", {
|
|
3737
|
+
className: "skippr:shrink-0 skippr:border-b skippr:border-border skippr:bg-muted/50 skippr:px-3 skippr:py-1.5 skippr:text-center skippr:text-xs skippr:text-muted-foreground",
|
|
3738
|
+
children: transitionLabel
|
|
3739
|
+
}),
|
|
3740
|
+
/* @__PURE__ */ jsx18(ConnectedBody, {
|
|
3741
|
+
activeTab,
|
|
3742
|
+
autoFocusChat
|
|
3743
|
+
})
|
|
3744
|
+
]
|
|
3399
3745
|
}) : /* @__PURE__ */ jsx18("div", {
|
|
3400
3746
|
className: "skippr:flex skippr:min-h-0 skippr:flex-1 skippr:flex-col skippr:animate-skippr-tab-fade",
|
|
3401
3747
|
children: /* @__PURE__ */ jsx18(StartSessionPrompt, {
|
|
@@ -3404,12 +3750,18 @@ function AuthenticatedContent({
|
|
|
3404
3750
|
agentControls,
|
|
3405
3751
|
isStarting,
|
|
3406
3752
|
error,
|
|
3407
|
-
label: startSessionLabel
|
|
3753
|
+
label: startSessionLabel,
|
|
3754
|
+
canResume,
|
|
3755
|
+
onResume
|
|
3408
3756
|
})
|
|
3409
3757
|
}, `${activeTab}-empty`)
|
|
3410
3758
|
}),
|
|
3411
|
-
isConnected && !hideControls && /* @__PURE__ */ jsx18(MeetingControls, {
|
|
3759
|
+
(isConnected || isPaused) && !isDisconnecting && !hideControls && /* @__PURE__ */ jsx18(MeetingControls, {
|
|
3412
3760
|
onHangUp: onDisconnect,
|
|
3761
|
+
onPause,
|
|
3762
|
+
onResume,
|
|
3763
|
+
isPaused,
|
|
3764
|
+
isPausing,
|
|
3413
3765
|
showScreenShareToggle
|
|
3414
3766
|
})
|
|
3415
3767
|
]
|
|
@@ -3527,10 +3879,16 @@ function LiveAgent(props) {
|
|
|
3527
3879
|
shouldConnect,
|
|
3528
3880
|
isStarting,
|
|
3529
3881
|
isDisconnecting,
|
|
3882
|
+
isPausing,
|
|
3530
3883
|
error,
|
|
3531
3884
|
errorCode,
|
|
3532
3885
|
startSession,
|
|
3886
|
+
pauseSession,
|
|
3887
|
+
resumeSession: resumeSessionById,
|
|
3533
3888
|
disconnect,
|
|
3889
|
+
isPaused,
|
|
3890
|
+
resumableSessions,
|
|
3891
|
+
historyMessages,
|
|
3534
3892
|
pendingScreenStream,
|
|
3535
3893
|
bearerToken
|
|
3536
3894
|
} = useSession({
|
|
@@ -3542,9 +3900,23 @@ function LiveAgent(props) {
|
|
|
3542
3900
|
onStartError: expandOnSessionStartError,
|
|
3543
3901
|
onDisconnect: minimizeOnSessionDisconnect
|
|
3544
3902
|
});
|
|
3903
|
+
const teardownInFlightRef = useRef10(false);
|
|
3904
|
+
teardownInFlightRef.current = isPaused || isPausing || isDisconnecting;
|
|
3905
|
+
const handleRoomDisconnected = useCallback8(() => {
|
|
3906
|
+
if (teardownInFlightRef.current)
|
|
3907
|
+
return;
|
|
3908
|
+
disconnect();
|
|
3909
|
+
}, [disconnect]);
|
|
3910
|
+
const resumableSession = resumableSessions.find((s) => s.agentId === agentId) ?? null;
|
|
3911
|
+
const resumeSession = useCallback8(async () => {
|
|
3912
|
+
if (!resumableSession)
|
|
3913
|
+
return;
|
|
3914
|
+
await resumeSessionById({ sessionId: resumableSession.id, agentControls });
|
|
3915
|
+
}, [resumableSession, resumeSessionById, agentControls]);
|
|
3545
3916
|
const [isPanelOpen, setIsPanelOpen] = useState11(defaultOpen);
|
|
3546
3917
|
const [isMinimized, setIsMinimized] = useState11(minimizable && !defaultOpen);
|
|
3547
3918
|
const [sidebarTab, setSidebarTab] = useState11("agenda");
|
|
3919
|
+
const [phasesSnapshot, setPhasesSnapshot] = useState11([]);
|
|
3548
3920
|
const {
|
|
3549
3921
|
modules: availableModules,
|
|
3550
3922
|
isLoading: isLoadingModules,
|
|
@@ -3559,8 +3931,13 @@ function LiveAgent(props) {
|
|
|
3559
3931
|
if (!found)
|
|
3560
3932
|
return;
|
|
3561
3933
|
setActiveModule(found);
|
|
3562
|
-
|
|
3563
|
-
|
|
3934
|
+
const resumable = resumableSessions.find((s) => s.agentId === found.id);
|
|
3935
|
+
if (resumable) {
|
|
3936
|
+
resumeSessionById({ sessionId: resumable.id, agentControls: found.controls });
|
|
3937
|
+
} else {
|
|
3938
|
+
startSession({ agentId: found.id, agentControls: found.controls });
|
|
3939
|
+
}
|
|
3940
|
+
}, [availableModules, resumableSessions, startSession, resumeSessionById]);
|
|
3564
3941
|
const [welcomeDismissed, setWelcomeDismissed] = useState11(false);
|
|
3565
3942
|
const dismissWelcome = useCallback8(() => setWelcomeDismissed(true), []);
|
|
3566
3943
|
const [currentPosition, setCurrentPosition] = useState11(() => {
|
|
@@ -3592,16 +3969,25 @@ function LiveAgent(props) {
|
|
|
3592
3969
|
}, [minimizable]);
|
|
3593
3970
|
const isConnected = connection !== null;
|
|
3594
3971
|
const isAuthenticated = !!userToken || !!authTokenProp || auth.isAuthenticated;
|
|
3595
|
-
const ctx =
|
|
3972
|
+
const ctx = useMemo6(() => ({
|
|
3596
3973
|
connection,
|
|
3597
3974
|
shouldConnect,
|
|
3598
3975
|
isConnected,
|
|
3599
3976
|
isStarting,
|
|
3600
3977
|
isDisconnecting,
|
|
3978
|
+
isPausing,
|
|
3601
3979
|
error,
|
|
3602
3980
|
errorCode,
|
|
3603
3981
|
startSession,
|
|
3982
|
+
pauseSession,
|
|
3983
|
+
resumeSession,
|
|
3604
3984
|
disconnect,
|
|
3985
|
+
isPaused,
|
|
3986
|
+
resumableSession,
|
|
3987
|
+
resumableSessions,
|
|
3988
|
+
historyMessages,
|
|
3989
|
+
phasesSnapshot,
|
|
3990
|
+
setPhasesSnapshot,
|
|
3605
3991
|
isPanelOpen,
|
|
3606
3992
|
openPanel,
|
|
3607
3993
|
closePanel,
|
|
@@ -3639,10 +4025,18 @@ function LiveAgent(props) {
|
|
|
3639
4025
|
isConnected,
|
|
3640
4026
|
isStarting,
|
|
3641
4027
|
isDisconnecting,
|
|
4028
|
+
isPausing,
|
|
3642
4029
|
error,
|
|
3643
4030
|
errorCode,
|
|
3644
4031
|
startSession,
|
|
4032
|
+
pauseSession,
|
|
4033
|
+
resumeSession,
|
|
3645
4034
|
disconnect,
|
|
4035
|
+
isPaused,
|
|
4036
|
+
resumableSession,
|
|
4037
|
+
resumableSessions,
|
|
4038
|
+
historyMessages,
|
|
4039
|
+
phasesSnapshot,
|
|
3646
4040
|
isPanelOpen,
|
|
3647
4041
|
openPanel,
|
|
3648
4042
|
closePanel,
|
|
@@ -3681,7 +4075,7 @@ function LiveAgent(props) {
|
|
|
3681
4075
|
token: connection?.token,
|
|
3682
4076
|
connect: shouldConnect,
|
|
3683
4077
|
audio: true,
|
|
3684
|
-
onDisconnected:
|
|
4078
|
+
onDisconnected: handleRoomDisconnected,
|
|
3685
4079
|
children: [
|
|
3686
4080
|
connection && /* @__PURE__ */ jsx20(RoomAudioRenderer, {}),
|
|
3687
4081
|
connection && captureMode === "screenshare" && /* @__PURE__ */ jsx20(AutoStartMedia, {
|