@skippr/live-agent-sdk 0.32.0 → 0.34.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 +45 -24
- package/dist/esm/lib-exports.js +846 -493
- package/dist/skippr-sdk.css +1 -1
- package/dist/skippr-sdk.js +144 -144
- package/dist/types/components/LiveAgent.d.ts +1 -1
- package/dist/types/components/ModuleSelector.d.ts +1 -0
- package/dist/types/components/StartSessionPrompt.d.ts +8 -2
- package/dist/types/context/LiveAgentContext.d.ts +21 -1
- package/dist/types/hooks/useAvailableModules.d.ts +13 -0
- package/dist/types/hooks/useSession.d.ts +11 -4
- package/dist/types/lib-exports.d.ts +1 -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
|
|
11
|
+
import { useCallback as useCallback8, useMemo as useMemo5, useState as useState11 } from "react";
|
|
12
12
|
|
|
13
13
|
// src/context/LiveAgentContext.tsx
|
|
14
14
|
import { createContext } from "react";
|
|
@@ -155,11 +155,58 @@ function useAuth({ appKey }) {
|
|
|
155
155
|
};
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
-
// src/hooks/
|
|
158
|
+
// src/hooks/useAvailableModules.ts
|
|
159
159
|
import { useCallback as useCallback2, useEffect as useEffect2, useState as useState2 } from "react";
|
|
160
160
|
var API_URL2 = "https://specialist.skippr.ai/api";
|
|
161
|
+
function useAvailableModules({
|
|
162
|
+
enabled,
|
|
163
|
+
bearerToken
|
|
164
|
+
}) {
|
|
165
|
+
const [modules, setModules] = useState2([]);
|
|
166
|
+
const [isLoading, setIsLoading] = useState2(false);
|
|
167
|
+
const [error, setError] = useState2(null);
|
|
168
|
+
const fetchModules = useCallback2(async () => {
|
|
169
|
+
if (!bearerToken) {
|
|
170
|
+
setError("No auth token available");
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
setIsLoading(true);
|
|
174
|
+
setError(null);
|
|
175
|
+
try {
|
|
176
|
+
const resp = await fetch(`${API_URL2}/v1/modules/available`, {
|
|
177
|
+
credentials: "omit",
|
|
178
|
+
headers: { Authorization: `Bearer ${bearerToken}` }
|
|
179
|
+
});
|
|
180
|
+
if (!resp.ok) {
|
|
181
|
+
const body = await resp.json().catch(() => null);
|
|
182
|
+
throw new Error(body?.detail || `Failed to load modules: ${resp.status}`);
|
|
183
|
+
}
|
|
184
|
+
const data = await resp.json();
|
|
185
|
+
setModules(Array.isArray(data?.modules) ? data.modules : []);
|
|
186
|
+
} catch (e) {
|
|
187
|
+
setError(e instanceof Error ? e.message : "Failed to load modules");
|
|
188
|
+
} finally {
|
|
189
|
+
setIsLoading(false);
|
|
190
|
+
}
|
|
191
|
+
}, [bearerToken]);
|
|
192
|
+
useEffect2(() => {
|
|
193
|
+
if (!enabled || !bearerToken)
|
|
194
|
+
return;
|
|
195
|
+
fetchModules();
|
|
196
|
+
}, [enabled, bearerToken, fetchModules]);
|
|
197
|
+
return {
|
|
198
|
+
modules,
|
|
199
|
+
isLoading,
|
|
200
|
+
error,
|
|
201
|
+
refetch: fetchModules
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// src/hooks/useSession.ts
|
|
206
|
+
import { useCallback as useCallback3, useEffect as useEffect3, useState as useState3 } from "react";
|
|
207
|
+
var API_URL3 = "https://specialist.skippr.ai/api";
|
|
161
208
|
async function exchangeForBearerToken(appKey, userToken) {
|
|
162
|
-
const resp = await fetch(`${
|
|
209
|
+
const resp = await fetch(`${API_URL3}/v1/auth/token-exchange`, {
|
|
163
210
|
method: "POST",
|
|
164
211
|
credentials: "omit",
|
|
165
212
|
headers: {
|
|
@@ -174,22 +221,24 @@ async function exchangeForBearerToken(appKey, userToken) {
|
|
|
174
221
|
return token;
|
|
175
222
|
}
|
|
176
223
|
function useSession({
|
|
177
|
-
agentId,
|
|
178
224
|
captureMode = "screenshare",
|
|
179
|
-
agentControls,
|
|
180
225
|
authToken,
|
|
181
226
|
appKey,
|
|
182
|
-
userToken
|
|
227
|
+
userToken,
|
|
228
|
+
onStart,
|
|
229
|
+
onStartError,
|
|
230
|
+
onDisconnect
|
|
183
231
|
}) {
|
|
184
|
-
const [connection, setConnection] =
|
|
185
|
-
const [shouldConnect, setShouldConnect] =
|
|
186
|
-
const [isStarting, setIsStarting] =
|
|
187
|
-
const [
|
|
188
|
-
const [
|
|
189
|
-
const [
|
|
190
|
-
const [
|
|
191
|
-
const [
|
|
192
|
-
|
|
232
|
+
const [connection, setConnection] = useState3(null);
|
|
233
|
+
const [shouldConnect, setShouldConnect] = useState3(false);
|
|
234
|
+
const [isStarting, setIsStarting] = useState3(false);
|
|
235
|
+
const [isDisconnecting, setIsDisconnecting] = useState3(false);
|
|
236
|
+
const [error, setError] = useState3("");
|
|
237
|
+
const [errorCode, setErrorCode] = useState3(null);
|
|
238
|
+
const [sessionId, setSessionId] = useState3(null);
|
|
239
|
+
const [bearerToken, setBearerToken] = useState3(authToken ?? null);
|
|
240
|
+
const [pendingScreenStream, setPendingScreenStream] = useState3(null);
|
|
241
|
+
useEffect3(() => {
|
|
193
242
|
let stale = false;
|
|
194
243
|
if (authToken) {
|
|
195
244
|
setBearerToken(authToken);
|
|
@@ -208,15 +257,19 @@ function useSession({
|
|
|
208
257
|
stale = true;
|
|
209
258
|
};
|
|
210
259
|
}, [authToken, appKey, userToken]);
|
|
211
|
-
const
|
|
212
|
-
const startSession = useCallback2(async () => {
|
|
260
|
+
const startSession = useCallback3(async ({ agentId, agentControls }) => {
|
|
213
261
|
if (!bearerToken) {
|
|
214
262
|
setError("No auth token available");
|
|
215
263
|
return;
|
|
216
264
|
}
|
|
265
|
+
if (!agentId) {
|
|
266
|
+
setError("No agent selected");
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
217
269
|
setIsStarting(true);
|
|
218
270
|
setError("");
|
|
219
271
|
setErrorCode(null);
|
|
272
|
+
onStart?.();
|
|
220
273
|
let screenStream = null;
|
|
221
274
|
if (captureMode === "screenshare") {
|
|
222
275
|
try {
|
|
@@ -227,10 +280,10 @@ function useSession({
|
|
|
227
280
|
screenStream = null;
|
|
228
281
|
}
|
|
229
282
|
}
|
|
230
|
-
const requestAgentControls =
|
|
283
|
+
const requestAgentControls = agentControls?.highlight === true ? { highlight: true } : undefined;
|
|
231
284
|
const headers = { Authorization: `Bearer ${bearerToken}` };
|
|
232
285
|
try {
|
|
233
|
-
const createResp = await fetch(`${
|
|
286
|
+
const createResp = await fetch(`${API_URL3}/v1/sessions`, {
|
|
234
287
|
method: "POST",
|
|
235
288
|
credentials: "omit",
|
|
236
289
|
headers: { "Content-Type": "application/json", ...headers },
|
|
@@ -246,7 +299,7 @@ function useSession({
|
|
|
246
299
|
throw new Error(body?.detail || `Failed to create session: ${createResp.status}`);
|
|
247
300
|
}
|
|
248
301
|
const { session } = await createResp.json();
|
|
249
|
-
const startResp = await fetch(`${
|
|
302
|
+
const startResp = await fetch(`${API_URL3}/v1/sessions/${session.id}/start`, {
|
|
250
303
|
method: "POST",
|
|
251
304
|
credentials: "omit",
|
|
252
305
|
headers
|
|
@@ -270,40 +323,49 @@ function useSession({
|
|
|
270
323
|
track.stop();
|
|
271
324
|
}
|
|
272
325
|
setError(e instanceof Error ? e.message : "Failed to start session");
|
|
326
|
+
onStartError?.();
|
|
273
327
|
} finally {
|
|
274
328
|
setIsStarting(false);
|
|
275
329
|
}
|
|
276
|
-
}, [
|
|
277
|
-
const disconnect =
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
330
|
+
}, [captureMode, bearerToken, onStart, onStartError]);
|
|
331
|
+
const disconnect = useCallback3(async () => {
|
|
332
|
+
setIsDisconnecting(true);
|
|
333
|
+
try {
|
|
334
|
+
if (sessionId && bearerToken) {
|
|
335
|
+
try {
|
|
336
|
+
await fetch(`${API_URL3}/v1/sessions/${sessionId}/complete`, {
|
|
337
|
+
method: "POST",
|
|
338
|
+
credentials: "omit",
|
|
339
|
+
headers: { "Content-Type": "application/json", Authorization: `Bearer ${bearerToken}` },
|
|
340
|
+
body: JSON.stringify({})
|
|
341
|
+
});
|
|
342
|
+
} catch {}
|
|
343
|
+
}
|
|
344
|
+
if (pendingScreenStream) {
|
|
345
|
+
for (const track of pendingScreenStream.getTracks())
|
|
346
|
+
track.stop();
|
|
347
|
+
}
|
|
348
|
+
setError("");
|
|
349
|
+
setShouldConnect(false);
|
|
350
|
+
setConnection(null);
|
|
351
|
+
setSessionId(null);
|
|
352
|
+
setPendingScreenStream(null);
|
|
353
|
+
onDisconnect?.();
|
|
354
|
+
} finally {
|
|
355
|
+
setIsDisconnecting(false);
|
|
291
356
|
}
|
|
292
|
-
|
|
293
|
-
setShouldConnect(false);
|
|
294
|
-
setConnection(null);
|
|
295
|
-
setSessionId(null);
|
|
296
|
-
setPendingScreenStream(null);
|
|
297
|
-
}, [sessionId, bearerToken, pendingScreenStream]);
|
|
357
|
+
}, [sessionId, bearerToken, pendingScreenStream, onDisconnect]);
|
|
298
358
|
return {
|
|
299
359
|
connection,
|
|
300
360
|
shouldConnect,
|
|
301
361
|
isStarting,
|
|
362
|
+
isDisconnecting,
|
|
302
363
|
error,
|
|
303
364
|
errorCode,
|
|
304
365
|
startSession,
|
|
305
366
|
disconnect,
|
|
306
|
-
pendingScreenStream
|
|
367
|
+
pendingScreenStream,
|
|
368
|
+
bearerToken
|
|
307
369
|
};
|
|
308
370
|
}
|
|
309
371
|
|
|
@@ -320,12 +382,12 @@ var NAME_MAX_CHARS = 80;
|
|
|
320
382
|
// src/components/AutoStartMedia.tsx
|
|
321
383
|
import { useConnectionState, useLocalParticipant } from "@livekit/components-react/hooks";
|
|
322
384
|
import { ConnectionState, Track } from "livekit-client";
|
|
323
|
-
import { useEffect as
|
|
385
|
+
import { useEffect as useEffect4, useRef } from "react";
|
|
324
386
|
function AutoStartMedia({ pendingScreenStream }) {
|
|
325
387
|
const { localParticipant } = useLocalParticipant();
|
|
326
388
|
const connectionState = useConnectionState();
|
|
327
389
|
const didStartRef = useRef(false);
|
|
328
|
-
|
|
390
|
+
useEffect4(() => {
|
|
329
391
|
if (didStartRef.current)
|
|
330
392
|
return;
|
|
331
393
|
if (connectionState !== ConnectionState.Connected)
|
|
@@ -350,7 +412,7 @@ function AutoStartMedia({ pendingScreenStream }) {
|
|
|
350
412
|
// src/components/DomCapture.tsx
|
|
351
413
|
import { useConnectionState as useConnectionState2, useLocalParticipant as useLocalParticipant2 } from "@livekit/components-react/hooks";
|
|
352
414
|
import { ConnectionState as ConnectionState2, ScreenSharePresets, Track as Track2 } from "livekit-client";
|
|
353
|
-
import { useEffect as
|
|
415
|
+
import { useEffect as useEffect5, useRef as useRef2 } from "react";
|
|
354
416
|
|
|
355
417
|
// src/capture/a11yUtils.ts
|
|
356
418
|
var ROLE_BY_TAG = {
|
|
@@ -1098,7 +1160,7 @@ function DomCapture() {
|
|
|
1098
1160
|
const { localParticipant } = useLocalParticipant2();
|
|
1099
1161
|
const connectionState = useConnectionState2();
|
|
1100
1162
|
const didStartRef = useRef2(false);
|
|
1101
|
-
|
|
1163
|
+
useEffect5(() => {
|
|
1102
1164
|
if (didStartRef.current)
|
|
1103
1165
|
return;
|
|
1104
1166
|
if (connectionState !== ConnectionState2.Connected)
|
|
@@ -1194,255 +1256,6 @@ function DomCapture() {
|
|
|
1194
1256
|
|
|
1195
1257
|
// src/components/HighlightOverlay.tsx
|
|
1196
1258
|
import { useDataChannel } from "@livekit/components-react/hooks";
|
|
1197
|
-
import { useCallback as useCallback3, useEffect as useEffect5, useRef as useRef3, useState as useState3 } from "react";
|
|
1198
|
-
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
1199
|
-
var TOOLTIP_MAX_WIDTH = 280;
|
|
1200
|
-
var TOOLTIP_GAP = 8;
|
|
1201
|
-
var VIEWPORT_MARGIN = 8;
|
|
1202
|
-
var TOOLTIP_TOP_THRESHOLD = 48;
|
|
1203
|
-
var Z_INDEX = 2147483646;
|
|
1204
|
-
var RING_INNER = "oklab(0.585 0.0288678 -0.231205 / 0.95)";
|
|
1205
|
-
var RING_OUTER = "oklab(0.585 0.0288678 -0.231205 / 0.25)";
|
|
1206
|
-
var textDecoder = new TextDecoder;
|
|
1207
|
-
function parseHighlightMessage(payload) {
|
|
1208
|
-
try {
|
|
1209
|
-
const json = JSON.parse(textDecoder.decode(payload));
|
|
1210
|
-
if (!json || typeof json !== "object")
|
|
1211
|
-
return null;
|
|
1212
|
-
if (json.type === "show" && typeof json.ref === "string") {
|
|
1213
|
-
return {
|
|
1214
|
-
type: "show",
|
|
1215
|
-
ref: json.ref,
|
|
1216
|
-
message: typeof json.message === "string" ? json.message : ""
|
|
1217
|
-
};
|
|
1218
|
-
}
|
|
1219
|
-
if (json.type === "clear")
|
|
1220
|
-
return { type: "clear" };
|
|
1221
|
-
return null;
|
|
1222
|
-
} catch {
|
|
1223
|
-
return null;
|
|
1224
|
-
}
|
|
1225
|
-
}
|
|
1226
|
-
function safeGetIframeDocument(iframe) {
|
|
1227
|
-
try {
|
|
1228
|
-
return iframe.contentDocument;
|
|
1229
|
-
} catch {
|
|
1230
|
-
return null;
|
|
1231
|
-
}
|
|
1232
|
-
}
|
|
1233
|
-
function safeQuerySelector(doc, selector) {
|
|
1234
|
-
try {
|
|
1235
|
-
return doc.querySelector(selector);
|
|
1236
|
-
} catch {
|
|
1237
|
-
return null;
|
|
1238
|
-
}
|
|
1239
|
-
}
|
|
1240
|
-
function safeQueryAllIframes(doc) {
|
|
1241
|
-
try {
|
|
1242
|
-
return Array.from(doc.querySelectorAll("iframe"));
|
|
1243
|
-
} catch {
|
|
1244
|
-
return [];
|
|
1245
|
-
}
|
|
1246
|
-
}
|
|
1247
|
-
function findElementInDocumentTree(rootDoc, selector) {
|
|
1248
|
-
const hit = safeQuerySelector(rootDoc, selector);
|
|
1249
|
-
if (hit)
|
|
1250
|
-
return hit;
|
|
1251
|
-
for (const iframe of safeQueryAllIframes(rootDoc)) {
|
|
1252
|
-
const innerDoc = safeGetIframeDocument(iframe);
|
|
1253
|
-
if (!innerDoc)
|
|
1254
|
-
continue;
|
|
1255
|
-
const nested = findElementInDocumentTree(innerDoc, selector);
|
|
1256
|
-
if (nested)
|
|
1257
|
-
return nested;
|
|
1258
|
-
}
|
|
1259
|
-
return null;
|
|
1260
|
-
}
|
|
1261
|
-
function findElementByRef(ref) {
|
|
1262
|
-
const selector = `[${REF_ATTR}="${CSS.escape(ref)}"]`;
|
|
1263
|
-
return findElementInDocumentTree(document, selector);
|
|
1264
|
-
}
|
|
1265
|
-
function findIframeHostingDocument(targetDoc) {
|
|
1266
|
-
const queue = [document];
|
|
1267
|
-
while (queue.length > 0) {
|
|
1268
|
-
const doc = queue.shift();
|
|
1269
|
-
if (!doc)
|
|
1270
|
-
continue;
|
|
1271
|
-
for (const iframe of safeQueryAllIframes(doc)) {
|
|
1272
|
-
if (safeGetIframeDocument(iframe) === targetDoc)
|
|
1273
|
-
return iframe;
|
|
1274
|
-
const innerDoc = safeGetIframeDocument(iframe);
|
|
1275
|
-
if (innerDoc)
|
|
1276
|
-
queue.push(innerDoc);
|
|
1277
|
-
}
|
|
1278
|
-
}
|
|
1279
|
-
return null;
|
|
1280
|
-
}
|
|
1281
|
-
function getRectInTopViewport(el) {
|
|
1282
|
-
let rect = el.getBoundingClientRect();
|
|
1283
|
-
let ownerDoc = el.ownerDocument;
|
|
1284
|
-
while (ownerDoc && ownerDoc !== document) {
|
|
1285
|
-
const hostingIframe = findIframeHostingDocument(ownerDoc);
|
|
1286
|
-
if (!hostingIframe)
|
|
1287
|
-
return rect;
|
|
1288
|
-
const iframeRect = hostingIframe.getBoundingClientRect();
|
|
1289
|
-
rect = new DOMRect(rect.left + iframeRect.left, rect.top + iframeRect.top, rect.width, rect.height);
|
|
1290
|
-
ownerDoc = hostingIframe.ownerDocument;
|
|
1291
|
-
}
|
|
1292
|
-
return rect;
|
|
1293
|
-
}
|
|
1294
|
-
function findScrollableAncestor(el) {
|
|
1295
|
-
const win = el.ownerDocument?.defaultView ?? window;
|
|
1296
|
-
let node = el.parentElement;
|
|
1297
|
-
while (node) {
|
|
1298
|
-
const overflow = win.getComputedStyle(node).overflow;
|
|
1299
|
-
if (/auto|scroll|overlay/.test(overflow))
|
|
1300
|
-
return node;
|
|
1301
|
-
node = node.parentElement;
|
|
1302
|
-
}
|
|
1303
|
-
return el.ownerDocument?.documentElement ?? document.documentElement;
|
|
1304
|
-
}
|
|
1305
|
-
function HighlightOverlay() {
|
|
1306
|
-
const [overlayState, setOverlayState] = useState3(null);
|
|
1307
|
-
const targetElementRef = useRef3(null);
|
|
1308
|
-
const pendingFrameRef = useRef3(null);
|
|
1309
|
-
const clearOverlay = useCallback3(() => {
|
|
1310
|
-
targetElementRef.current = null;
|
|
1311
|
-
setOverlayState(null);
|
|
1312
|
-
}, []);
|
|
1313
|
-
const recomputeRect = useCallback3(() => {
|
|
1314
|
-
pendingFrameRef.current = null;
|
|
1315
|
-
const target = targetElementRef.current;
|
|
1316
|
-
if (!target)
|
|
1317
|
-
return;
|
|
1318
|
-
if (!target.isConnected) {
|
|
1319
|
-
clearOverlay();
|
|
1320
|
-
return;
|
|
1321
|
-
}
|
|
1322
|
-
const rect2 = getRectInTopViewport(target);
|
|
1323
|
-
setOverlayState((prev) => prev ? { ...prev, rect: rect2 } : prev);
|
|
1324
|
-
}, [clearOverlay]);
|
|
1325
|
-
const scheduleRecompute = useCallback3(() => {
|
|
1326
|
-
if (pendingFrameRef.current !== null)
|
|
1327
|
-
return;
|
|
1328
|
-
pendingFrameRef.current = window.requestAnimationFrame(recomputeRect);
|
|
1329
|
-
}, [recomputeRect]);
|
|
1330
|
-
const onHighlightMessage = useCallback3((msg) => {
|
|
1331
|
-
const parsed = parseHighlightMessage(msg.payload);
|
|
1332
|
-
if (!parsed)
|
|
1333
|
-
return;
|
|
1334
|
-
if (parsed.type === "clear") {
|
|
1335
|
-
clearOverlay();
|
|
1336
|
-
return;
|
|
1337
|
-
}
|
|
1338
|
-
const target = findElementByRef(parsed.ref);
|
|
1339
|
-
if (!target) {
|
|
1340
|
-
clearOverlay();
|
|
1341
|
-
return;
|
|
1342
|
-
}
|
|
1343
|
-
targetElementRef.current = target;
|
|
1344
|
-
setOverlayState({
|
|
1345
|
-
ref: parsed.ref,
|
|
1346
|
-
message: parsed.message ?? "",
|
|
1347
|
-
rect: getRectInTopViewport(target)
|
|
1348
|
-
});
|
|
1349
|
-
}, [clearOverlay]);
|
|
1350
|
-
useDataChannel(HIGHLIGHT_TOPIC, onHighlightMessage);
|
|
1351
|
-
useEffect5(() => {
|
|
1352
|
-
if (!overlayState)
|
|
1353
|
-
return;
|
|
1354
|
-
const target = targetElementRef.current;
|
|
1355
|
-
if (!target)
|
|
1356
|
-
return;
|
|
1357
|
-
const onScroll = () => scheduleRecompute();
|
|
1358
|
-
const onResize = () => scheduleRecompute();
|
|
1359
|
-
const watchedWindows = new Set;
|
|
1360
|
-
watchedWindows.add(window);
|
|
1361
|
-
const ownerWindow = target.ownerDocument?.defaultView;
|
|
1362
|
-
if (ownerWindow && ownerWindow !== window)
|
|
1363
|
-
watchedWindows.add(ownerWindow);
|
|
1364
|
-
for (const win of watchedWindows) {
|
|
1365
|
-
win.addEventListener("scroll", onScroll, { capture: true, passive: true });
|
|
1366
|
-
win.addEventListener("resize", onResize, { passive: true });
|
|
1367
|
-
}
|
|
1368
|
-
const resizeObserver = new ResizeObserver(scheduleRecompute);
|
|
1369
|
-
resizeObserver.observe(target);
|
|
1370
|
-
const scrollContainer = findScrollableAncestor(target);
|
|
1371
|
-
const mutationObserver = new MutationObserver(() => {
|
|
1372
|
-
if (!targetElementRef.current?.isConnected) {
|
|
1373
|
-
clearOverlay();
|
|
1374
|
-
return;
|
|
1375
|
-
}
|
|
1376
|
-
scheduleRecompute();
|
|
1377
|
-
});
|
|
1378
|
-
mutationObserver.observe(scrollContainer, { childList: true, subtree: true });
|
|
1379
|
-
return () => {
|
|
1380
|
-
for (const win of watchedWindows) {
|
|
1381
|
-
win.removeEventListener("scroll", onScroll, { capture: true });
|
|
1382
|
-
win.removeEventListener("resize", onResize);
|
|
1383
|
-
}
|
|
1384
|
-
resizeObserver.disconnect();
|
|
1385
|
-
mutationObserver.disconnect();
|
|
1386
|
-
if (pendingFrameRef.current !== null) {
|
|
1387
|
-
window.cancelAnimationFrame(pendingFrameRef.current);
|
|
1388
|
-
pendingFrameRef.current = null;
|
|
1389
|
-
}
|
|
1390
|
-
};
|
|
1391
|
-
}, [overlayState, scheduleRecompute, clearOverlay]);
|
|
1392
|
-
if (!overlayState)
|
|
1393
|
-
return null;
|
|
1394
|
-
const { rect, message } = overlayState;
|
|
1395
|
-
const ringStyle = {
|
|
1396
|
-
position: "fixed",
|
|
1397
|
-
left: `${rect.left}px`,
|
|
1398
|
-
top: `${rect.top}px`,
|
|
1399
|
-
width: `${rect.width}px`,
|
|
1400
|
-
height: `${rect.height}px`,
|
|
1401
|
-
pointerEvents: "none",
|
|
1402
|
-
zIndex: Z_INDEX,
|
|
1403
|
-
boxShadow: `0 0 0 2px ${RING_INNER}, 0 0 0 6px ${RING_OUTER}`,
|
|
1404
|
-
borderRadius: "4px",
|
|
1405
|
-
transition: "none"
|
|
1406
|
-
};
|
|
1407
|
-
const showTooltipBelow = rect.top < TOOLTIP_TOP_THRESHOLD;
|
|
1408
|
-
const tooltipTop = showTooltipBelow ? rect.bottom + TOOLTIP_GAP : rect.top - TOOLTIP_GAP;
|
|
1409
|
-
const tooltipTransform = showTooltipBelow ? "translateY(0)" : "translateY(-100%)";
|
|
1410
|
-
const tooltipLeft = Math.min(Math.max(rect.left, VIEWPORT_MARGIN), window.innerWidth - TOOLTIP_MAX_WIDTH - VIEWPORT_MARGIN);
|
|
1411
|
-
const tooltipStyle = {
|
|
1412
|
-
position: "fixed",
|
|
1413
|
-
left: `${tooltipLeft}px`,
|
|
1414
|
-
top: `${tooltipTop}px`,
|
|
1415
|
-
transform: tooltipTransform,
|
|
1416
|
-
maxWidth: `${TOOLTIP_MAX_WIDTH}px`,
|
|
1417
|
-
pointerEvents: "none",
|
|
1418
|
-
zIndex: Z_INDEX,
|
|
1419
|
-
background: "rgba(45, 43, 61, 0.96)",
|
|
1420
|
-
color: "#ffffff",
|
|
1421
|
-
padding: "6px 10px",
|
|
1422
|
-
borderRadius: "8px",
|
|
1423
|
-
fontSize: "13px",
|
|
1424
|
-
lineHeight: "1.35",
|
|
1425
|
-
fontFamily: 'ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Inter, sans-serif',
|
|
1426
|
-
boxShadow: "0 6px 20px rgba(0, 0, 0, 0.25)",
|
|
1427
|
-
whiteSpace: "normal",
|
|
1428
|
-
wordWrap: "break-word"
|
|
1429
|
-
};
|
|
1430
|
-
return /* @__PURE__ */ jsxs(Fragment, {
|
|
1431
|
-
children: [
|
|
1432
|
-
/* @__PURE__ */ jsx("div", {
|
|
1433
|
-
"data-skippr-private": "true",
|
|
1434
|
-
style: ringStyle,
|
|
1435
|
-
"aria-hidden": "true"
|
|
1436
|
-
}),
|
|
1437
|
-
message && /* @__PURE__ */ jsx("div", {
|
|
1438
|
-
"data-skippr-private": "true",
|
|
1439
|
-
style: tooltipStyle,
|
|
1440
|
-
role: "tooltip",
|
|
1441
|
-
children: message
|
|
1442
|
-
})
|
|
1443
|
-
]
|
|
1444
|
-
});
|
|
1445
|
-
}
|
|
1446
1259
|
// ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/createLucideIcon.js
|
|
1447
1260
|
import { forwardRef as forwardRef2, createElement as createElement3 } from "react";
|
|
1448
1261
|
|
|
@@ -1557,28 +1370,63 @@ var __iconNode3 = [
|
|
|
1557
1370
|
["circle", { cx: "4", cy: "20", r: "2", key: "6kqj1y" }]
|
|
1558
1371
|
];
|
|
1559
1372
|
var Sparkles = createLucideIcon("sparkles", __iconNode3);
|
|
1560
|
-
// ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/
|
|
1373
|
+
// ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/bot.js
|
|
1561
1374
|
var __iconNode4 = [
|
|
1375
|
+
["path", { d: "M12 8V4H8", key: "hb8ula" }],
|
|
1376
|
+
["rect", { width: "16", height: "12", x: "4", y: "8", rx: "2", key: "enze0r" }],
|
|
1377
|
+
["path", { d: "M2 14h2", key: "vft8re" }],
|
|
1378
|
+
["path", { d: "M20 14h2", key: "4cs60a" }],
|
|
1379
|
+
["path", { d: "M15 13v2", key: "1xurst" }],
|
|
1380
|
+
["path", { d: "M9 13v2", key: "rq6x2g" }]
|
|
1381
|
+
];
|
|
1382
|
+
var Bot = createLucideIcon("bot", __iconNode4);
|
|
1383
|
+
// ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/calendar.js
|
|
1384
|
+
var __iconNode5 = [
|
|
1562
1385
|
["path", { d: "M8 2v4", key: "1cmpym" }],
|
|
1563
1386
|
["path", { d: "M16 2v4", key: "4m81vk" }],
|
|
1564
1387
|
["rect", { width: "18", height: "18", x: "3", y: "4", rx: "2", key: "1hopcy" }],
|
|
1565
1388
|
["path", { d: "M3 10h18", key: "8toen8" }]
|
|
1566
1389
|
];
|
|
1567
|
-
var Calendar = createLucideIcon("calendar",
|
|
1390
|
+
var Calendar = createLucideIcon("calendar", __iconNode5);
|
|
1568
1391
|
// ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/chevron-down.js
|
|
1569
|
-
var
|
|
1570
|
-
var ChevronDown = createLucideIcon("chevron-down",
|
|
1392
|
+
var __iconNode6 = [["path", { d: "m6 9 6 6 6-6", key: "qrunsl" }]];
|
|
1393
|
+
var ChevronDown = createLucideIcon("chevron-down", __iconNode6);
|
|
1571
1394
|
// ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/circle.js
|
|
1572
|
-
var
|
|
1573
|
-
var Circle = createLucideIcon("circle",
|
|
1395
|
+
var __iconNode7 = [["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }]];
|
|
1396
|
+
var Circle = createLucideIcon("circle", __iconNode7);
|
|
1397
|
+
// ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/graduation-cap.js
|
|
1398
|
+
var __iconNode8 = [
|
|
1399
|
+
[
|
|
1400
|
+
"path",
|
|
1401
|
+
{
|
|
1402
|
+
d: "M21.42 10.922a1 1 0 0 0-.019-1.838L12.83 5.18a2 2 0 0 0-1.66 0L2.6 9.08a1 1 0 0 0 0 1.832l8.57 3.908a2 2 0 0 0 1.66 0z",
|
|
1403
|
+
key: "j76jl0"
|
|
1404
|
+
}
|
|
1405
|
+
],
|
|
1406
|
+
["path", { d: "M22 10v6", key: "1lu8f3" }],
|
|
1407
|
+
["path", { d: "M6 12.5V16a6 3 0 0 0 12 0v-3.5", key: "1r8lef" }]
|
|
1408
|
+
];
|
|
1409
|
+
var GraduationCap = createLucideIcon("graduation-cap", __iconNode8);
|
|
1410
|
+
// ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/headset.js
|
|
1411
|
+
var __iconNode9 = [
|
|
1412
|
+
[
|
|
1413
|
+
"path",
|
|
1414
|
+
{
|
|
1415
|
+
d: "M3 11h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-5Zm0 0a9 9 0 1 1 18 0m0 0v5a2 2 0 0 1-2 2h-1a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2h3Z",
|
|
1416
|
+
key: "12oyoe"
|
|
1417
|
+
}
|
|
1418
|
+
],
|
|
1419
|
+
["path", { d: "M21 16v2a4 4 0 0 1-4 4h-5", key: "1x7m43" }]
|
|
1420
|
+
];
|
|
1421
|
+
var Headset = createLucideIcon("headset", __iconNode9);
|
|
1574
1422
|
// ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/mail.js
|
|
1575
|
-
var
|
|
1423
|
+
var __iconNode10 = [
|
|
1576
1424
|
["path", { d: "m22 7-8.991 5.727a2 2 0 0 1-2.009 0L2 7", key: "132q7q" }],
|
|
1577
1425
|
["rect", { x: "2", y: "4", width: "20", height: "16", rx: "2", key: "izxlao" }]
|
|
1578
1426
|
];
|
|
1579
|
-
var Mail = createLucideIcon("mail",
|
|
1427
|
+
var Mail = createLucideIcon("mail", __iconNode10);
|
|
1580
1428
|
// ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/message-circle.js
|
|
1581
|
-
var
|
|
1429
|
+
var __iconNode11 = [
|
|
1582
1430
|
[
|
|
1583
1431
|
"path",
|
|
1584
1432
|
{
|
|
@@ -1587,9 +1435,9 @@ var __iconNode8 = [
|
|
|
1587
1435
|
}
|
|
1588
1436
|
]
|
|
1589
1437
|
];
|
|
1590
|
-
var MessageCircle = createLucideIcon("message-circle",
|
|
1438
|
+
var MessageCircle = createLucideIcon("message-circle", __iconNode11);
|
|
1591
1439
|
// ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/message-square.js
|
|
1592
|
-
var
|
|
1440
|
+
var __iconNode12 = [
|
|
1593
1441
|
[
|
|
1594
1442
|
"path",
|
|
1595
1443
|
{
|
|
@@ -1598,9 +1446,9 @@ var __iconNode9 = [
|
|
|
1598
1446
|
}
|
|
1599
1447
|
]
|
|
1600
1448
|
];
|
|
1601
|
-
var MessageSquare = createLucideIcon("message-square",
|
|
1449
|
+
var MessageSquare = createLucideIcon("message-square", __iconNode12);
|
|
1602
1450
|
// ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/mic-off.js
|
|
1603
|
-
var
|
|
1451
|
+
var __iconNode13 = [
|
|
1604
1452
|
["path", { d: "M12 19v3", key: "npa21l" }],
|
|
1605
1453
|
["path", { d: "M15 9.34V5a3 3 0 0 0-5.68-1.33", key: "1gzdoj" }],
|
|
1606
1454
|
["path", { d: "M16.95 16.95A7 7 0 0 1 5 12v-2", key: "cqa7eg" }],
|
|
@@ -1608,71 +1456,341 @@ var __iconNode10 = [
|
|
|
1608
1456
|
["path", { d: "m2 2 20 20", key: "1ooewy" }],
|
|
1609
1457
|
["path", { d: "M9 9v3a3 3 0 0 0 5.12 2.12", key: "r2i35w" }]
|
|
1610
1458
|
];
|
|
1611
|
-
var MicOff = createLucideIcon("mic-off",
|
|
1459
|
+
var MicOff = createLucideIcon("mic-off", __iconNode13);
|
|
1612
1460
|
// ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/mic.js
|
|
1613
|
-
var
|
|
1461
|
+
var __iconNode14 = [
|
|
1614
1462
|
["path", { d: "M12 19v3", key: "npa21l" }],
|
|
1615
1463
|
["path", { d: "M19 10v2a7 7 0 0 1-14 0v-2", key: "1vc78b" }],
|
|
1616
1464
|
["rect", { x: "9", y: "2", width: "6", height: "13", rx: "3", key: "s6n7sd" }]
|
|
1617
1465
|
];
|
|
1618
|
-
var Mic = createLucideIcon("mic",
|
|
1466
|
+
var Mic = createLucideIcon("mic", __iconNode14);
|
|
1619
1467
|
// ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/minimize-2.js
|
|
1620
|
-
var
|
|
1468
|
+
var __iconNode15 = [
|
|
1621
1469
|
["path", { d: "m14 10 7-7", key: "oa77jy" }],
|
|
1622
1470
|
["path", { d: "M20 10h-6V4", key: "mjg0md" }],
|
|
1623
1471
|
["path", { d: "m3 21 7-7", key: "tjx5ai" }],
|
|
1624
1472
|
["path", { d: "M4 14h6v6", key: "rmj7iw" }]
|
|
1625
1473
|
];
|
|
1626
|
-
var Minimize2 = createLucideIcon("minimize-2",
|
|
1474
|
+
var Minimize2 = createLucideIcon("minimize-2", __iconNode15);
|
|
1627
1475
|
// ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/monitor-off.js
|
|
1628
|
-
var
|
|
1476
|
+
var __iconNode16 = [
|
|
1629
1477
|
["path", { d: "M12 17v4", key: "1riwvh" }],
|
|
1630
1478
|
["path", { d: "M17 17H4a2 2 0 0 1-2-2V5a2 2 0 0 1 1.184-1.826", key: "cv7jms" }],
|
|
1631
1479
|
["path", { d: "m2 2 20 20", key: "1ooewy" }],
|
|
1632
1480
|
["path", { d: "M8 21h8", key: "1ev6f3" }],
|
|
1633
1481
|
["path", { d: "M8.656 3H20a2 2 0 0 1 2 2v10a2 2 0 0 1-.293 1.042", key: "z8ni2w" }]
|
|
1634
1482
|
];
|
|
1635
|
-
var MonitorOff = createLucideIcon("monitor-off",
|
|
1483
|
+
var MonitorOff = createLucideIcon("monitor-off", __iconNode16);
|
|
1636
1484
|
// ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/monitor.js
|
|
1637
|
-
var
|
|
1485
|
+
var __iconNode17 = [
|
|
1638
1486
|
["rect", { width: "20", height: "14", x: "2", y: "3", rx: "2", key: "48i651" }],
|
|
1639
1487
|
["line", { x1: "8", x2: "16", y1: "21", y2: "21", key: "1svkeh" }],
|
|
1640
1488
|
["line", { x1: "12", x2: "12", y1: "17", y2: "21", key: "vw1qmm" }]
|
|
1641
1489
|
];
|
|
1642
|
-
var Monitor = createLucideIcon("monitor",
|
|
1490
|
+
var Monitor = createLucideIcon("monitor", __iconNode17);
|
|
1491
|
+
// ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/mouse-pointer-2.js
|
|
1492
|
+
var __iconNode18 = [
|
|
1493
|
+
[
|
|
1494
|
+
"path",
|
|
1495
|
+
{
|
|
1496
|
+
d: "M4.037 4.688a.495.495 0 0 1 .651-.651l16 6.5a.5.5 0 0 1-.063.947l-6.124 1.58a2 2 0 0 0-1.438 1.435l-1.579 6.126a.5.5 0 0 1-.947.063z",
|
|
1497
|
+
key: "edeuup"
|
|
1498
|
+
}
|
|
1499
|
+
]
|
|
1500
|
+
];
|
|
1501
|
+
var MousePointer2 = createLucideIcon("mouse-pointer-2", __iconNode18);
|
|
1643
1502
|
// ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/phone-off.js
|
|
1644
|
-
var
|
|
1503
|
+
var __iconNode19 = [
|
|
1645
1504
|
[
|
|
1646
1505
|
"path",
|
|
1647
1506
|
{
|
|
1648
1507
|
d: "M10.1 13.9a14 14 0 0 0 3.732 2.668 1 1 0 0 0 1.213-.303l.355-.465A2 2 0 0 1 17 15h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2 18 18 0 0 1-12.728-5.272",
|
|
1649
1508
|
key: "1wngk7"
|
|
1650
1509
|
}
|
|
1651
|
-
],
|
|
1652
|
-
["path", { d: "M22 2 2 22", key: "y4kqgn" }],
|
|
1653
|
-
[
|
|
1654
|
-
"path",
|
|
1655
|
-
{
|
|
1656
|
-
d: "M4.76 13.582A18 18 0 0 1 2 4a2 2 0 0 1 2-2h3a2 2 0 0 1 2 2v3a2 2 0 0 1-.8 1.6l-.468.351a1 1 0 0 0-.292 1.233 14 14 0 0 0 .244.473",
|
|
1657
|
-
key: "10hv5p"
|
|
1510
|
+
],
|
|
1511
|
+
["path", { d: "M22 2 2 22", key: "y4kqgn" }],
|
|
1512
|
+
[
|
|
1513
|
+
"path",
|
|
1514
|
+
{
|
|
1515
|
+
d: "M4.76 13.582A18 18 0 0 1 2 4a2 2 0 0 1 2-2h3a2 2 0 0 1 2 2v3a2 2 0 0 1-.8 1.6l-.468.351a1 1 0 0 0-.292 1.233 14 14 0 0 0 .244.473",
|
|
1516
|
+
key: "10hv5p"
|
|
1517
|
+
}
|
|
1518
|
+
]
|
|
1519
|
+
];
|
|
1520
|
+
var PhoneOff = createLucideIcon("phone-off", __iconNode19);
|
|
1521
|
+
// ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/rocket.js
|
|
1522
|
+
var __iconNode20 = [
|
|
1523
|
+
["path", { d: "M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5", key: "qeys4" }],
|
|
1524
|
+
[
|
|
1525
|
+
"path",
|
|
1526
|
+
{
|
|
1527
|
+
d: "M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09",
|
|
1528
|
+
key: "u4xsad"
|
|
1529
|
+
}
|
|
1530
|
+
],
|
|
1531
|
+
[
|
|
1532
|
+
"path",
|
|
1533
|
+
{
|
|
1534
|
+
d: "M9 12a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.4 22.4 0 0 1-4 2z",
|
|
1535
|
+
key: "676m9"
|
|
1536
|
+
}
|
|
1537
|
+
],
|
|
1538
|
+
["path", { d: "M9 12H4s.55-3.03 2-4c1.62-1.08 5 .05 5 .05", key: "92ym6u" }]
|
|
1539
|
+
];
|
|
1540
|
+
var Rocket = createLucideIcon("rocket", __iconNode20);
|
|
1541
|
+
// ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/send.js
|
|
1542
|
+
var __iconNode21 = [
|
|
1543
|
+
[
|
|
1544
|
+
"path",
|
|
1545
|
+
{
|
|
1546
|
+
d: "M14.536 21.686a.5.5 0 0 0 .937-.024l6.5-19a.496.496 0 0 0-.635-.635l-19 6.5a.5.5 0 0 0-.024.937l7.93 3.18a2 2 0 0 1 1.112 1.11z",
|
|
1547
|
+
key: "1ffxy3"
|
|
1548
|
+
}
|
|
1549
|
+
],
|
|
1550
|
+
["path", { d: "m21.854 2.147-10.94 10.939", key: "12cjpa" }]
|
|
1551
|
+
];
|
|
1552
|
+
var Send = createLucideIcon("send", __iconNode21);
|
|
1553
|
+
// ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/user-plus.js
|
|
1554
|
+
var __iconNode22 = [
|
|
1555
|
+
["path", { d: "M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2", key: "1yyitq" }],
|
|
1556
|
+
["circle", { cx: "9", cy: "7", r: "4", key: "nufk8" }],
|
|
1557
|
+
["line", { x1: "19", x2: "19", y1: "8", y2: "14", key: "1bvyxn" }],
|
|
1558
|
+
["line", { x1: "22", x2: "16", y1: "11", y2: "11", key: "1shjgl" }]
|
|
1559
|
+
];
|
|
1560
|
+
var UserPlus = createLucideIcon("user-plus", __iconNode22);
|
|
1561
|
+
// src/components/HighlightOverlay.tsx
|
|
1562
|
+
import { useCallback as useCallback4, useEffect as useEffect6, useRef as useRef3, useState as useState4 } from "react";
|
|
1563
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
1564
|
+
var Z_INDEX = 2147483646;
|
|
1565
|
+
var HIGHLIGHT_PADDING = 6;
|
|
1566
|
+
var textDecoder = new TextDecoder;
|
|
1567
|
+
function parseHighlightMessage(payload) {
|
|
1568
|
+
try {
|
|
1569
|
+
const json = JSON.parse(textDecoder.decode(payload));
|
|
1570
|
+
if (!json || typeof json !== "object")
|
|
1571
|
+
return null;
|
|
1572
|
+
if (json.type === "show" && typeof json.ref === "string") {
|
|
1573
|
+
return {
|
|
1574
|
+
type: "show",
|
|
1575
|
+
ref: json.ref,
|
|
1576
|
+
message: typeof json.message === "string" ? json.message : undefined
|
|
1577
|
+
};
|
|
1578
|
+
}
|
|
1579
|
+
if (json.type === "clear")
|
|
1580
|
+
return { type: "clear" };
|
|
1581
|
+
return null;
|
|
1582
|
+
} catch {
|
|
1583
|
+
return null;
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
function safeGetIframeDocument(iframe) {
|
|
1587
|
+
try {
|
|
1588
|
+
return iframe.contentDocument;
|
|
1589
|
+
} catch {
|
|
1590
|
+
return null;
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
function safeQuerySelector(doc, selector) {
|
|
1594
|
+
try {
|
|
1595
|
+
return doc.querySelector(selector);
|
|
1596
|
+
} catch {
|
|
1597
|
+
return null;
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
function safeQueryAllIframes(doc) {
|
|
1601
|
+
try {
|
|
1602
|
+
return Array.from(doc.querySelectorAll("iframe"));
|
|
1603
|
+
} catch {
|
|
1604
|
+
return [];
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
function findElementInDocumentTree(rootDoc, selector) {
|
|
1608
|
+
const hit = safeQuerySelector(rootDoc, selector);
|
|
1609
|
+
if (hit)
|
|
1610
|
+
return hit;
|
|
1611
|
+
for (const iframe of safeQueryAllIframes(rootDoc)) {
|
|
1612
|
+
const innerDoc = safeGetIframeDocument(iframe);
|
|
1613
|
+
if (!innerDoc)
|
|
1614
|
+
continue;
|
|
1615
|
+
const nested = findElementInDocumentTree(innerDoc, selector);
|
|
1616
|
+
if (nested)
|
|
1617
|
+
return nested;
|
|
1618
|
+
}
|
|
1619
|
+
return null;
|
|
1620
|
+
}
|
|
1621
|
+
function findElementByRef(ref) {
|
|
1622
|
+
const selector = `[${REF_ATTR}="${CSS.escape(ref)}"]`;
|
|
1623
|
+
return findElementInDocumentTree(document, selector);
|
|
1624
|
+
}
|
|
1625
|
+
function findIframeHostingDocument(targetDoc) {
|
|
1626
|
+
const queue = [document];
|
|
1627
|
+
while (queue.length > 0) {
|
|
1628
|
+
const doc = queue.shift();
|
|
1629
|
+
if (!doc)
|
|
1630
|
+
continue;
|
|
1631
|
+
for (const iframe of safeQueryAllIframes(doc)) {
|
|
1632
|
+
if (safeGetIframeDocument(iframe) === targetDoc)
|
|
1633
|
+
return iframe;
|
|
1634
|
+
const innerDoc = safeGetIframeDocument(iframe);
|
|
1635
|
+
if (innerDoc)
|
|
1636
|
+
queue.push(innerDoc);
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
return null;
|
|
1640
|
+
}
|
|
1641
|
+
function getRectInTopViewport(el) {
|
|
1642
|
+
let rect = el.getBoundingClientRect();
|
|
1643
|
+
let ownerDoc = el.ownerDocument;
|
|
1644
|
+
while (ownerDoc && ownerDoc !== document) {
|
|
1645
|
+
const hostingIframe = findIframeHostingDocument(ownerDoc);
|
|
1646
|
+
if (!hostingIframe)
|
|
1647
|
+
return rect;
|
|
1648
|
+
const iframeRect = hostingIframe.getBoundingClientRect();
|
|
1649
|
+
rect = new DOMRect(rect.left + iframeRect.left, rect.top + iframeRect.top, rect.width, rect.height);
|
|
1650
|
+
ownerDoc = hostingIframe.ownerDocument;
|
|
1651
|
+
}
|
|
1652
|
+
return rect;
|
|
1653
|
+
}
|
|
1654
|
+
function findScrollableAncestor(el) {
|
|
1655
|
+
const win = el.ownerDocument?.defaultView ?? window;
|
|
1656
|
+
let node = el.parentElement;
|
|
1657
|
+
while (node) {
|
|
1658
|
+
const overflow = win.getComputedStyle(node).overflow;
|
|
1659
|
+
if (/auto|scroll|overlay/.test(overflow))
|
|
1660
|
+
return node;
|
|
1661
|
+
node = node.parentElement;
|
|
1662
|
+
}
|
|
1663
|
+
return el.ownerDocument?.documentElement ?? document.documentElement;
|
|
1664
|
+
}
|
|
1665
|
+
function HighlightOverlay() {
|
|
1666
|
+
const [overlayState, setOverlayState] = useState4(null);
|
|
1667
|
+
const targetElementRef = useRef3(null);
|
|
1668
|
+
const pendingFrameRef = useRef3(null);
|
|
1669
|
+
const clearOverlay = useCallback4(() => {
|
|
1670
|
+
targetElementRef.current = null;
|
|
1671
|
+
setOverlayState(null);
|
|
1672
|
+
}, []);
|
|
1673
|
+
const recomputeRect = useCallback4(() => {
|
|
1674
|
+
pendingFrameRef.current = null;
|
|
1675
|
+
const target = targetElementRef.current;
|
|
1676
|
+
if (!target)
|
|
1677
|
+
return;
|
|
1678
|
+
if (!target.isConnected) {
|
|
1679
|
+
clearOverlay();
|
|
1680
|
+
return;
|
|
1681
|
+
}
|
|
1682
|
+
const rect2 = getRectInTopViewport(target);
|
|
1683
|
+
setOverlayState((prev) => prev ? { ...prev, rect: rect2 } : prev);
|
|
1684
|
+
}, [clearOverlay]);
|
|
1685
|
+
const scheduleRecompute = useCallback4(() => {
|
|
1686
|
+
if (pendingFrameRef.current !== null)
|
|
1687
|
+
return;
|
|
1688
|
+
pendingFrameRef.current = window.requestAnimationFrame(recomputeRect);
|
|
1689
|
+
}, [recomputeRect]);
|
|
1690
|
+
const onHighlightMessage = useCallback4((msg) => {
|
|
1691
|
+
const parsed = parseHighlightMessage(msg.payload);
|
|
1692
|
+
if (!parsed)
|
|
1693
|
+
return;
|
|
1694
|
+
if (parsed.type === "clear") {
|
|
1695
|
+
clearOverlay();
|
|
1696
|
+
return;
|
|
1658
1697
|
}
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
var __iconNode16 = [
|
|
1664
|
-
[
|
|
1665
|
-
"path",
|
|
1666
|
-
{
|
|
1667
|
-
d: "M14.536 21.686a.5.5 0 0 0 .937-.024l6.5-19a.496.496 0 0 0-.635-.635l-19 6.5a.5.5 0 0 0-.024.937l7.93 3.18a2 2 0 0 1 1.112 1.11z",
|
|
1668
|
-
key: "1ffxy3"
|
|
1698
|
+
const target = findElementByRef(parsed.ref);
|
|
1699
|
+
if (!target) {
|
|
1700
|
+
clearOverlay();
|
|
1701
|
+
return;
|
|
1669
1702
|
}
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1703
|
+
targetElementRef.current = target;
|
|
1704
|
+
setOverlayState({
|
|
1705
|
+
ref: parsed.ref,
|
|
1706
|
+
message: parsed.message || "Click here",
|
|
1707
|
+
rect: getRectInTopViewport(target)
|
|
1708
|
+
});
|
|
1709
|
+
}, [clearOverlay]);
|
|
1710
|
+
useDataChannel(HIGHLIGHT_TOPIC, onHighlightMessage);
|
|
1711
|
+
useEffect6(() => {
|
|
1712
|
+
if (!overlayState)
|
|
1713
|
+
return;
|
|
1714
|
+
const target = targetElementRef.current;
|
|
1715
|
+
if (!target)
|
|
1716
|
+
return;
|
|
1717
|
+
const onScroll = () => scheduleRecompute();
|
|
1718
|
+
const onResize = () => scheduleRecompute();
|
|
1719
|
+
const watchedWindows = new Set;
|
|
1720
|
+
watchedWindows.add(window);
|
|
1721
|
+
const ownerWindow = target.ownerDocument?.defaultView;
|
|
1722
|
+
if (ownerWindow && ownerWindow !== window)
|
|
1723
|
+
watchedWindows.add(ownerWindow);
|
|
1724
|
+
for (const win of watchedWindows) {
|
|
1725
|
+
win.addEventListener("scroll", onScroll, { capture: true, passive: true });
|
|
1726
|
+
win.addEventListener("resize", onResize, { passive: true });
|
|
1727
|
+
}
|
|
1728
|
+
const resizeObserver = new ResizeObserver(scheduleRecompute);
|
|
1729
|
+
resizeObserver.observe(target);
|
|
1730
|
+
const scrollContainer = findScrollableAncestor(target);
|
|
1731
|
+
const mutationObserver = new MutationObserver(() => {
|
|
1732
|
+
if (!targetElementRef.current?.isConnected) {
|
|
1733
|
+
clearOverlay();
|
|
1734
|
+
return;
|
|
1735
|
+
}
|
|
1736
|
+
scheduleRecompute();
|
|
1737
|
+
});
|
|
1738
|
+
mutationObserver.observe(scrollContainer, { childList: true, subtree: true });
|
|
1739
|
+
return () => {
|
|
1740
|
+
for (const win of watchedWindows) {
|
|
1741
|
+
win.removeEventListener("scroll", onScroll, { capture: true });
|
|
1742
|
+
win.removeEventListener("resize", onResize);
|
|
1743
|
+
}
|
|
1744
|
+
resizeObserver.disconnect();
|
|
1745
|
+
mutationObserver.disconnect();
|
|
1746
|
+
if (pendingFrameRef.current !== null) {
|
|
1747
|
+
window.cancelAnimationFrame(pendingFrameRef.current);
|
|
1748
|
+
pendingFrameRef.current = null;
|
|
1749
|
+
}
|
|
1750
|
+
};
|
|
1751
|
+
}, [overlayState, scheduleRecompute, clearOverlay]);
|
|
1752
|
+
if (!overlayState)
|
|
1753
|
+
return null;
|
|
1754
|
+
const { rect, message } = overlayState;
|
|
1755
|
+
const containerStyle = {
|
|
1756
|
+
position: "fixed",
|
|
1757
|
+
left: `${rect.left - HIGHLIGHT_PADDING}px`,
|
|
1758
|
+
top: `${rect.top - HIGHLIGHT_PADDING}px`,
|
|
1759
|
+
width: `${rect.width + HIGHLIGHT_PADDING * 2}px`,
|
|
1760
|
+
height: `${rect.height + HIGHLIGHT_PADDING * 2}px`,
|
|
1761
|
+
pointerEvents: "none",
|
|
1762
|
+
zIndex: Z_INDEX
|
|
1763
|
+
};
|
|
1764
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1765
|
+
"data-skippr-private": "true",
|
|
1766
|
+
style: containerStyle,
|
|
1767
|
+
className: "skippr:relative",
|
|
1768
|
+
children: [
|
|
1769
|
+
/* @__PURE__ */ jsx("div", {
|
|
1770
|
+
"aria-hidden": "true",
|
|
1771
|
+
className: "skippr:absolute skippr:inset-0 skippr:rounded-[1rem] skippr:border-2 skippr:border-cyan-400 skippr:animate-skippr-annotation-pulse"
|
|
1772
|
+
}),
|
|
1773
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1774
|
+
role: "tooltip",
|
|
1775
|
+
className: "skippr:absolute skippr:-bottom-9 skippr:left-1/2 skippr:-translate-x-1/2 skippr:flex skippr:items-center skippr:gap-1.5 skippr:rounded-full skippr:bg-cyan-400 skippr:px-2.5 skippr:py-1 skippr:shadow-[0_4px_12px_rgba(0,0,0,0.12)]",
|
|
1776
|
+
children: [
|
|
1777
|
+
/* @__PURE__ */ jsx(MousePointer2, {
|
|
1778
|
+
"aria-hidden": "true",
|
|
1779
|
+
className: "skippr:h-3 skippr:w-3 skippr:text-cyan-900",
|
|
1780
|
+
fill: "currentColor"
|
|
1781
|
+
}),
|
|
1782
|
+
/* @__PURE__ */ jsx("span", {
|
|
1783
|
+
className: "skippr:text-[11px] skippr:font-medium skippr:text-cyan-900 skippr:whitespace-nowrap",
|
|
1784
|
+
children: message
|
|
1785
|
+
})
|
|
1786
|
+
]
|
|
1787
|
+
})
|
|
1788
|
+
]
|
|
1789
|
+
});
|
|
1790
|
+
}
|
|
1791
|
+
|
|
1674
1792
|
// src/components/MinimizedBubble.tsx
|
|
1675
|
-
import { useEffect as
|
|
1793
|
+
import { useEffect as useEffect7 } from "react";
|
|
1676
1794
|
|
|
1677
1795
|
// src/hooks/useLiveAgent.ts
|
|
1678
1796
|
import { use } from "react";
|
|
@@ -1688,7 +1806,7 @@ function useLiveAgent() {
|
|
|
1688
1806
|
// src/hooks/useMediaControls.ts
|
|
1689
1807
|
import { useLocalParticipant as useLocalParticipant3 } from "@livekit/components-react/hooks";
|
|
1690
1808
|
import { ScreenSharePresets as ScreenSharePresets2 } from "livekit-client";
|
|
1691
|
-
import { useCallback as
|
|
1809
|
+
import { useCallback as useCallback5 } from "react";
|
|
1692
1810
|
var SCREEN_SHARE_OPTIONS = {
|
|
1693
1811
|
video: { displaySurface: "browser" },
|
|
1694
1812
|
resolution: ScreenSharePresets2.h720fps30.resolution,
|
|
@@ -1698,14 +1816,14 @@ function useMediaControls() {
|
|
|
1698
1816
|
const { localParticipant } = useLocalParticipant3();
|
|
1699
1817
|
const isMuted = !localParticipant.isMicrophoneEnabled;
|
|
1700
1818
|
const isScreenSharing = localParticipant.isScreenShareEnabled;
|
|
1701
|
-
const toggleMute =
|
|
1819
|
+
const toggleMute = useCallback5(async () => {
|
|
1702
1820
|
try {
|
|
1703
1821
|
await localParticipant.setMicrophoneEnabled(isMuted);
|
|
1704
1822
|
} catch (error) {
|
|
1705
1823
|
console.error("Failed to toggle microphone:", error);
|
|
1706
1824
|
}
|
|
1707
1825
|
}, [localParticipant, isMuted]);
|
|
1708
|
-
const toggleScreenShare =
|
|
1826
|
+
const toggleScreenShare = useCallback5(async () => {
|
|
1709
1827
|
try {
|
|
1710
1828
|
await localParticipant.setScreenShareEnabled(!isScreenSharing, SCREEN_SHARE_OPTIONS);
|
|
1711
1829
|
} catch (error) {
|
|
@@ -1734,7 +1852,7 @@ function useAgentVoiceState() {
|
|
|
1734
1852
|
}
|
|
1735
1853
|
|
|
1736
1854
|
// src/components/LauncherStatusPill.tsx
|
|
1737
|
-
import { jsx as jsx2, jsxs as jsxs2, Fragment
|
|
1855
|
+
import { jsx as jsx2, jsxs as jsxs2, Fragment } from "react/jsx-runtime";
|
|
1738
1856
|
function pillStatusFromAgent(state, canSeePage) {
|
|
1739
1857
|
if (state === "speaking")
|
|
1740
1858
|
return "talking";
|
|
@@ -1743,26 +1861,36 @@ function pillStatusFromAgent(state, canSeePage) {
|
|
|
1743
1861
|
return canSeePage ? "observing" : "connected";
|
|
1744
1862
|
}
|
|
1745
1863
|
function LauncherStatusPill() {
|
|
1746
|
-
const { isConnected, expandPanel, setSidebarTab, position, captureMode } = useLiveAgent();
|
|
1864
|
+
const { isConnected, isStarting, expandPanel, setSidebarTab, position, captureMode } = useLiveAgent();
|
|
1747
1865
|
const { state } = useAgentVoiceState();
|
|
1748
1866
|
const { isScreenSharing } = useMediaControls();
|
|
1749
|
-
|
|
1867
|
+
const isStandingBy = isStarting && !isConnected;
|
|
1868
|
+
if (!isConnected && !isStandingBy)
|
|
1750
1869
|
return null;
|
|
1751
1870
|
const canSeePage = captureMode === "auto" || isScreenSharing;
|
|
1752
|
-
const status = pillStatusFromAgent(state, canSeePage);
|
|
1753
|
-
const
|
|
1754
|
-
|
|
1871
|
+
const status = isStandingBy ? "standing-by" : pillStatusFromAgent(state, canSeePage);
|
|
1872
|
+
const handleClick = () => {
|
|
1873
|
+
if (!isStandingBy)
|
|
1874
|
+
setSidebarTab("chat");
|
|
1755
1875
|
expandPanel();
|
|
1756
1876
|
};
|
|
1757
1877
|
return /* @__PURE__ */ jsx2("button", {
|
|
1758
1878
|
type: "button",
|
|
1759
|
-
onClick:
|
|
1879
|
+
onClick: handleClick,
|
|
1760
1880
|
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"),
|
|
1761
|
-
"aria-label": `Skippr is ${status} — click to open chat`,
|
|
1881
|
+
"aria-label": isStandingBy ? "Skippr is standing by — click to open" : `Skippr is ${status} — click to open chat`,
|
|
1762
1882
|
children: /* @__PURE__ */ jsxs2("span", {
|
|
1763
1883
|
className: "skippr:flex skippr:items-center skippr:gap-2 skippr:animate-[skippr-pill-content_0.22s_ease-out]",
|
|
1764
1884
|
children: [
|
|
1765
|
-
status === "
|
|
1885
|
+
status === "standing-by" && /* @__PURE__ */ jsxs2(Fragment, {
|
|
1886
|
+
children: [
|
|
1887
|
+
/* @__PURE__ */ jsx2("span", {
|
|
1888
|
+
children: "Standing by"
|
|
1889
|
+
}),
|
|
1890
|
+
/* @__PURE__ */ jsx2(ThinkingDots, {})
|
|
1891
|
+
]
|
|
1892
|
+
}),
|
|
1893
|
+
status === "observing" && /* @__PURE__ */ jsxs2(Fragment, {
|
|
1766
1894
|
children: [
|
|
1767
1895
|
/* @__PURE__ */ jsx2(ObservingIcon, {}),
|
|
1768
1896
|
/* @__PURE__ */ jsx2("span", {
|
|
@@ -1770,7 +1898,7 @@ function LauncherStatusPill() {
|
|
|
1770
1898
|
})
|
|
1771
1899
|
]
|
|
1772
1900
|
}),
|
|
1773
|
-
status === "connected" && /* @__PURE__ */ jsxs2(
|
|
1901
|
+
status === "connected" && /* @__PURE__ */ jsxs2(Fragment, {
|
|
1774
1902
|
children: [
|
|
1775
1903
|
/* @__PURE__ */ jsx2(ConnectedDot, {}),
|
|
1776
1904
|
/* @__PURE__ */ jsx2("span", {
|
|
@@ -1778,7 +1906,7 @@ function LauncherStatusPill() {
|
|
|
1778
1906
|
})
|
|
1779
1907
|
]
|
|
1780
1908
|
}),
|
|
1781
|
-
status === "talking" && /* @__PURE__ */ jsxs2(
|
|
1909
|
+
status === "talking" && /* @__PURE__ */ jsxs2(Fragment, {
|
|
1782
1910
|
children: [
|
|
1783
1911
|
/* @__PURE__ */ jsx2(SpeakingBars, {}),
|
|
1784
1912
|
/* @__PURE__ */ jsx2("span", {
|
|
@@ -1786,7 +1914,7 @@ function LauncherStatusPill() {
|
|
|
1786
1914
|
})
|
|
1787
1915
|
]
|
|
1788
1916
|
}),
|
|
1789
|
-
status === "thinking" && /* @__PURE__ */ jsxs2(
|
|
1917
|
+
status === "thinking" && /* @__PURE__ */ jsxs2(Fragment, {
|
|
1790
1918
|
children: [
|
|
1791
1919
|
/* @__PURE__ */ jsx2(ThinkingDots, {}),
|
|
1792
1920
|
/* @__PURE__ */ jsx2("span", {
|
|
@@ -1958,7 +2086,7 @@ function Logo({ className }) {
|
|
|
1958
2086
|
}
|
|
1959
2087
|
|
|
1960
2088
|
// src/components/MinimizedBubble.tsx
|
|
1961
|
-
import { jsx as jsx4, jsxs as jsxs4, Fragment as
|
|
2089
|
+
import { jsx as jsx4, jsxs as jsxs4, Fragment as Fragment2 } from "react/jsx-runtime";
|
|
1962
2090
|
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";
|
|
1963
2091
|
var CONTROL_SHADOW = "skippr:shadow-[0_4px_16px_rgba(0,0,0,0.15),0_2px_4px_rgba(0,0,0,0.1)]";
|
|
1964
2092
|
function ConnectedLauncher() {
|
|
@@ -1969,7 +2097,7 @@ function ConnectedLauncher() {
|
|
|
1969
2097
|
setSidebarTab("chat");
|
|
1970
2098
|
expandPanel();
|
|
1971
2099
|
};
|
|
1972
|
-
return /* @__PURE__ */ jsxs4(
|
|
2100
|
+
return /* @__PURE__ */ jsxs4(Fragment2, {
|
|
1973
2101
|
children: [
|
|
1974
2102
|
/* @__PURE__ */ jsx4("button", {
|
|
1975
2103
|
type: "button",
|
|
@@ -2031,7 +2159,7 @@ function WelcomeBubble({
|
|
|
2031
2159
|
position,
|
|
2032
2160
|
onDismiss
|
|
2033
2161
|
}) {
|
|
2034
|
-
|
|
2162
|
+
useEffect7(() => {
|
|
2035
2163
|
const timer = setTimeout(onDismiss, 5000);
|
|
2036
2164
|
return () => clearTimeout(timer);
|
|
2037
2165
|
}, [onDismiss]);
|
|
@@ -2054,14 +2182,14 @@ function MinimizedBubble({
|
|
|
2054
2182
|
onDismissWelcome
|
|
2055
2183
|
}) {
|
|
2056
2184
|
const { isConnected, isStarting, position } = useLiveAgent();
|
|
2057
|
-
const inSession = isConnected
|
|
2058
|
-
return /* @__PURE__ */ jsxs4(
|
|
2185
|
+
const inSession = isConnected;
|
|
2186
|
+
return /* @__PURE__ */ jsxs4(Fragment2, {
|
|
2059
2187
|
children: [
|
|
2060
2188
|
/* @__PURE__ */ jsx4(LauncherStatusPill, {}),
|
|
2061
2189
|
/* @__PURE__ */ jsxs4("div", {
|
|
2062
2190
|
className: cn("skippr:fixed skippr:bottom-6 skippr:z-[9999]", "skippr:flex skippr:items-center skippr:gap-2", position === "right" ? "skippr:right-6" : "skippr:left-6"),
|
|
2063
2191
|
children: [
|
|
2064
|
-
welcomeMessage && !
|
|
2192
|
+
welcomeMessage && !isConnected && !isStarting && !welcomeDismissed && /* @__PURE__ */ jsx4(WelcomeBubble, {
|
|
2065
2193
|
message: welcomeMessage,
|
|
2066
2194
|
position,
|
|
2067
2195
|
onDismiss: onDismissWelcome
|
|
@@ -2074,7 +2202,7 @@ function MinimizedBubble({
|
|
|
2074
2202
|
}
|
|
2075
2203
|
|
|
2076
2204
|
// src/components/Sidebar.tsx
|
|
2077
|
-
import { useEffect as
|
|
2205
|
+
import { useEffect as useEffect15 } from "react";
|
|
2078
2206
|
|
|
2079
2207
|
// src/hooks/useCombinedMessages.ts
|
|
2080
2208
|
import { useMemo as useMemo4 } from "react";
|
|
@@ -2156,15 +2284,15 @@ function useCombinedMessages() {
|
|
|
2156
2284
|
}
|
|
2157
2285
|
|
|
2158
2286
|
// src/hooks/usePhaseUpdates.ts
|
|
2159
|
-
import { useCallback as
|
|
2287
|
+
import { useCallback as useCallback6 } from "react";
|
|
2160
2288
|
|
|
2161
2289
|
// src/hooks/useAgentState.ts
|
|
2162
2290
|
import { useRemoteParticipants } from "@livekit/components-react/hooks";
|
|
2163
|
-
import { useEffect as
|
|
2291
|
+
import { useEffect as useEffect8, useState as useState5 } from "react";
|
|
2164
2292
|
function useAgentState(attributeKey, parse, initial) {
|
|
2165
|
-
const [value, setValue] =
|
|
2293
|
+
const [value, setValue] = useState5(initial);
|
|
2166
2294
|
const remoteParticipants = useRemoteParticipants();
|
|
2167
|
-
|
|
2295
|
+
useEffect8(() => {
|
|
2168
2296
|
const agentParticipant = remoteParticipants.find((p) => p.attributes?.[attributeKey]);
|
|
2169
2297
|
if (agentParticipant) {
|
|
2170
2298
|
const attr = agentParticipant.attributes?.[attributeKey];
|
|
@@ -2210,13 +2338,13 @@ function parsePhases(json) {
|
|
|
2210
2338
|
return null;
|
|
2211
2339
|
}
|
|
2212
2340
|
function usePhaseUpdates() {
|
|
2213
|
-
const parse =
|
|
2341
|
+
const parse = useCallback6(parsePhases, []);
|
|
2214
2342
|
const phases = useAgentState("phases", parse, []);
|
|
2215
2343
|
return { phases };
|
|
2216
2344
|
}
|
|
2217
2345
|
|
|
2218
2346
|
// src/hooks/useSessionRemaining.ts
|
|
2219
|
-
import { useEffect as
|
|
2347
|
+
import { useEffect as useEffect9, useRef as useRef4, useState as useState6 } from "react";
|
|
2220
2348
|
|
|
2221
2349
|
// src/lib/format.ts
|
|
2222
2350
|
function formatTime(seconds) {
|
|
@@ -2233,8 +2361,8 @@ function parseNumber(s) {
|
|
|
2233
2361
|
function useSessionRemaining() {
|
|
2234
2362
|
const maxCallDuration = useAgentState("maxCallDuration", parseNumber, null);
|
|
2235
2363
|
const endTimeRef = useRef4(null);
|
|
2236
|
-
const [remaining, setRemaining] =
|
|
2237
|
-
|
|
2364
|
+
const [remaining, setRemaining] = useState6(null);
|
|
2365
|
+
useEffect9(() => {
|
|
2238
2366
|
if (maxCallDuration === null || endTimeRef.current !== null)
|
|
2239
2367
|
return;
|
|
2240
2368
|
const endTime = Date.now() + maxCallDuration * 1000;
|
|
@@ -2251,10 +2379,10 @@ function useSessionRemaining() {
|
|
|
2251
2379
|
}
|
|
2252
2380
|
|
|
2253
2381
|
// src/hooks/useElapsedSeconds.ts
|
|
2254
|
-
import { useEffect as
|
|
2382
|
+
import { useEffect as useEffect10, useState as useState7 } from "react";
|
|
2255
2383
|
function useElapsedSeconds(isRunning) {
|
|
2256
|
-
const [elapsed, setElapsed] =
|
|
2257
|
-
|
|
2384
|
+
const [elapsed, setElapsed] = useState7(0);
|
|
2385
|
+
useEffect10(() => {
|
|
2258
2386
|
if (!isRunning) {
|
|
2259
2387
|
setElapsed(0);
|
|
2260
2388
|
return;
|
|
@@ -2352,7 +2480,7 @@ function LoadingDots({ label }) {
|
|
|
2352
2480
|
}
|
|
2353
2481
|
|
|
2354
2482
|
// src/components/LoginFlow.tsx
|
|
2355
|
-
import { useCallback as
|
|
2483
|
+
import { useCallback as useCallback7, useEffect as useEffect11, useRef as useRef5, useState as useState8 } from "react";
|
|
2356
2484
|
|
|
2357
2485
|
// src/components/ui/button.tsx
|
|
2358
2486
|
import { forwardRef as forwardRef3 } from "react";
|
|
@@ -2388,20 +2516,20 @@ import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
|
2388
2516
|
var OTP_LENGTH = 6;
|
|
2389
2517
|
var DIGIT_KEYS = ["d0", "d1", "d2", "d3", "d4", "d5"];
|
|
2390
2518
|
function LoginFlow({ requestOtp, verifyOtp, error, isSubmitting }) {
|
|
2391
|
-
const [step, setStep] =
|
|
2392
|
-
const [email, setEmail] =
|
|
2393
|
-
const handleRequestOtp =
|
|
2519
|
+
const [step, setStep] = useState8("email");
|
|
2520
|
+
const [email, setEmail] = useState8("");
|
|
2521
|
+
const handleRequestOtp = useCallback7(async (emailValue) => {
|
|
2394
2522
|
const success = await requestOtp(emailValue);
|
|
2395
2523
|
if (success)
|
|
2396
2524
|
setStep("otp");
|
|
2397
2525
|
}, [requestOtp]);
|
|
2398
|
-
const handleVerifyOtp =
|
|
2526
|
+
const handleVerifyOtp = useCallback7(async (code) => {
|
|
2399
2527
|
await verifyOtp(email, code);
|
|
2400
2528
|
}, [verifyOtp, email]);
|
|
2401
|
-
const handleBack =
|
|
2529
|
+
const handleBack = useCallback7(() => {
|
|
2402
2530
|
setStep("email");
|
|
2403
2531
|
}, []);
|
|
2404
|
-
const handleResend =
|
|
2532
|
+
const handleResend = useCallback7(async () => {
|
|
2405
2533
|
await requestOtp(email);
|
|
2406
2534
|
}, [requestOtp, email]);
|
|
2407
2535
|
if (step === "otp") {
|
|
@@ -2478,30 +2606,30 @@ function EmailStep({ email, onEmailChange, onSubmit, error, isSubmitting }) {
|
|
|
2478
2606
|
});
|
|
2479
2607
|
}
|
|
2480
2608
|
function OtpStep({ email, onSubmit, onResend, onBack, error, isSubmitting }) {
|
|
2481
|
-
const [digits, setDigits] =
|
|
2482
|
-
const [resendCooldown, setResendCooldown] =
|
|
2609
|
+
const [digits, setDigits] = useState8(Array(OTP_LENGTH).fill(""));
|
|
2610
|
+
const [resendCooldown, setResendCooldown] = useState8(0);
|
|
2483
2611
|
const inputRefs = useRef5([]);
|
|
2484
2612
|
const submittedRef = useRef5(false);
|
|
2485
|
-
|
|
2613
|
+
useEffect11(() => {
|
|
2486
2614
|
inputRefs.current[0]?.focus();
|
|
2487
2615
|
}, []);
|
|
2488
|
-
|
|
2616
|
+
useEffect11(() => {
|
|
2489
2617
|
if (error)
|
|
2490
2618
|
submittedRef.current = false;
|
|
2491
2619
|
}, [error]);
|
|
2492
|
-
|
|
2620
|
+
useEffect11(() => {
|
|
2493
2621
|
if (resendCooldown <= 0)
|
|
2494
2622
|
return;
|
|
2495
2623
|
const timer = setTimeout(() => setResendCooldown((c) => c - 1), 1000);
|
|
2496
2624
|
return () => clearTimeout(timer);
|
|
2497
2625
|
}, [resendCooldown]);
|
|
2498
|
-
const submitCode =
|
|
2626
|
+
const submitCode = useCallback7((code) => {
|
|
2499
2627
|
if (submittedRef.current || isSubmitting)
|
|
2500
2628
|
return;
|
|
2501
2629
|
submittedRef.current = true;
|
|
2502
2630
|
onSubmit(code);
|
|
2503
2631
|
}, [onSubmit, isSubmitting]);
|
|
2504
|
-
const handleDigitChange =
|
|
2632
|
+
const handleDigitChange = useCallback7((index2, value) => {
|
|
2505
2633
|
const digit = value.replace(/\D/g, "").slice(-1);
|
|
2506
2634
|
const newDigits = [...digits];
|
|
2507
2635
|
newDigits[index2] = digit;
|
|
@@ -2515,12 +2643,12 @@ function OtpStep({ email, onSubmit, onResend, onBack, error, isSubmitting }) {
|
|
|
2515
2643
|
submitCode(code);
|
|
2516
2644
|
}
|
|
2517
2645
|
}, [digits, submitCode]);
|
|
2518
|
-
const handleKeyDown =
|
|
2646
|
+
const handleKeyDown = useCallback7((index2, e) => {
|
|
2519
2647
|
if (e.key === "Backspace" && !digits[index2] && index2 > 0) {
|
|
2520
2648
|
inputRefs.current[index2 - 1]?.focus();
|
|
2521
2649
|
}
|
|
2522
2650
|
}, [digits]);
|
|
2523
|
-
const handlePaste =
|
|
2651
|
+
const handlePaste = useCallback7((e) => {
|
|
2524
2652
|
e.preventDefault();
|
|
2525
2653
|
const pasted = e.clipboardData.getData("text").replace(/\D/g, "").slice(0, OTP_LENGTH);
|
|
2526
2654
|
if (pasted.length > 0) {
|
|
@@ -2682,17 +2810,17 @@ function MeetingControls({ onHangUp, showScreenShareToggle = true }) {
|
|
|
2682
2810
|
}
|
|
2683
2811
|
|
|
2684
2812
|
// src/components/MessageList.tsx
|
|
2685
|
-
import { useEffect as
|
|
2813
|
+
import { useEffect as useEffect13, useRef as useRef7 } from "react";
|
|
2686
2814
|
|
|
2687
2815
|
// src/components/ChatInput.tsx
|
|
2688
|
-
import { useEffect as
|
|
2816
|
+
import { useEffect as useEffect12, useRef as useRef6, useState as useState9 } from "react";
|
|
2689
2817
|
import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2690
2818
|
var MAX_INPUT_HEIGHT = 60;
|
|
2691
2819
|
function ChatInput({ sendChatMessage, isSendingChat, autoFocus = false }) {
|
|
2692
|
-
const [inputText, setInputText] =
|
|
2820
|
+
const [inputText, setInputText] = useState9("");
|
|
2693
2821
|
const textareaRef = useRef6(null);
|
|
2694
2822
|
const canSend = inputText.trim().length > 0 && !isSendingChat;
|
|
2695
|
-
|
|
2823
|
+
useEffect12(() => {
|
|
2696
2824
|
if (autoFocus)
|
|
2697
2825
|
textareaRef.current?.focus();
|
|
2698
2826
|
}, [autoFocus]);
|
|
@@ -2839,7 +2967,7 @@ function MessageList({
|
|
|
2839
2967
|
}) {
|
|
2840
2968
|
const scrollRef = useRef7(null);
|
|
2841
2969
|
const lastMessage = messages.length > 0 ? messages[messages.length - 1] : undefined;
|
|
2842
|
-
|
|
2970
|
+
useEffect13(() => {
|
|
2843
2971
|
scrollRef.current?.scrollIntoView({ behavior: "smooth" });
|
|
2844
2972
|
}, [messages.length, lastMessage?.content]);
|
|
2845
2973
|
const showTyping = isStreaming && lastMessage?.role === "assistant" && lastMessage.content === "";
|
|
@@ -2870,50 +2998,205 @@ function MessageList({
|
|
|
2870
2998
|
});
|
|
2871
2999
|
}
|
|
2872
3000
|
|
|
2873
|
-
// src/components/
|
|
3001
|
+
// src/components/ModuleSelector.tsx
|
|
3002
|
+
import { useEffect as useEffect14, useRef as useRef8, useState as useState10 } from "react";
|
|
2874
3003
|
import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
3004
|
+
var AGENT_TYPE_ICONS = {
|
|
3005
|
+
onboarding: UserPlus,
|
|
3006
|
+
demo: Rocket,
|
|
3007
|
+
training: GraduationCap,
|
|
3008
|
+
support: Headset
|
|
3009
|
+
};
|
|
3010
|
+
function getAgentIcon(type) {
|
|
3011
|
+
return AGENT_TYPE_ICONS[type] ?? Bot;
|
|
3012
|
+
}
|
|
3013
|
+
function ModuleSelector() {
|
|
3014
|
+
const {
|
|
3015
|
+
availableModules,
|
|
3016
|
+
isLoadingModules,
|
|
3017
|
+
modulesError,
|
|
3018
|
+
refetchModules,
|
|
3019
|
+
selectModule,
|
|
3020
|
+
isStarting,
|
|
3021
|
+
isDisconnecting,
|
|
3022
|
+
error
|
|
3023
|
+
} = useLiveAgent();
|
|
3024
|
+
const isBusy = isStarting || isDisconnecting;
|
|
3025
|
+
const scrollRef = useRef8(null);
|
|
3026
|
+
const [showScrollHint, setShowScrollHint] = useState10(false);
|
|
3027
|
+
const [isScrolled, setIsScrolled] = useState10(false);
|
|
3028
|
+
useEffect14(() => {
|
|
3029
|
+
const el = scrollRef.current;
|
|
3030
|
+
if (!el)
|
|
3031
|
+
return;
|
|
3032
|
+
function update() {
|
|
3033
|
+
if (!el)
|
|
3034
|
+
return;
|
|
3035
|
+
const overflows = el.scrollHeight > el.clientHeight + 1;
|
|
3036
|
+
const atBottom = el.scrollHeight - el.scrollTop - el.clientHeight < 4;
|
|
3037
|
+
setShowScrollHint(overflows && !atBottom);
|
|
3038
|
+
setIsScrolled(el.scrollTop > 0);
|
|
3039
|
+
}
|
|
3040
|
+
update();
|
|
3041
|
+
el.addEventListener("scroll", update, { passive: true });
|
|
3042
|
+
const observer = new ResizeObserver(update);
|
|
3043
|
+
observer.observe(el);
|
|
3044
|
+
return () => {
|
|
3045
|
+
el.removeEventListener("scroll", update);
|
|
3046
|
+
observer.disconnect();
|
|
3047
|
+
};
|
|
3048
|
+
}, []);
|
|
3049
|
+
if (isLoadingModules && availableModules.length === 0) {
|
|
3050
|
+
return /* @__PURE__ */ jsx14("div", {
|
|
3051
|
+
className: "skippr:flex skippr:flex-1 skippr:items-center skippr:justify-center skippr:p-6",
|
|
3052
|
+
children: /* @__PURE__ */ jsx14(LoadingDots, {
|
|
3053
|
+
label: "Loading modules..."
|
|
3054
|
+
})
|
|
3055
|
+
});
|
|
3056
|
+
}
|
|
3057
|
+
if (modulesError) {
|
|
3058
|
+
return /* @__PURE__ */ jsxs13("div", {
|
|
3059
|
+
className: "skippr:flex skippr:flex-1 skippr:flex-col skippr:items-center skippr:justify-center skippr:gap-3 skippr:p-6 skippr:text-center",
|
|
3060
|
+
children: [
|
|
3061
|
+
/* @__PURE__ */ jsx14("p", {
|
|
3062
|
+
className: "skippr:text-sm skippr:text-foreground",
|
|
3063
|
+
children: "Couldn't load modules."
|
|
3064
|
+
}),
|
|
3065
|
+
/* @__PURE__ */ jsx14("p", {
|
|
3066
|
+
className: "skippr:text-xs skippr:text-muted-foreground",
|
|
3067
|
+
children: modulesError
|
|
3068
|
+
}),
|
|
3069
|
+
/* @__PURE__ */ jsx14("button", {
|
|
3070
|
+
type: "button",
|
|
3071
|
+
onClick: () => void refetchModules(),
|
|
3072
|
+
className: "skippr:cursor-pointer skippr:rounded-lg skippr:bg-foreground skippr:px-3 skippr:py-1.5 skippr:text-xs skippr:font-medium skippr:text-background",
|
|
3073
|
+
children: "Try again"
|
|
3074
|
+
})
|
|
3075
|
+
]
|
|
3076
|
+
});
|
|
3077
|
+
}
|
|
3078
|
+
if (availableModules.length === 0) {
|
|
3079
|
+
return /* @__PURE__ */ jsx14("div", {
|
|
3080
|
+
className: "skippr:flex skippr:flex-1 skippr:items-center skippr:justify-center skippr:p-6 skippr:text-center",
|
|
3081
|
+
children: /* @__PURE__ */ jsx14("p", {
|
|
3082
|
+
className: "skippr:text-sm skippr:text-muted-foreground",
|
|
3083
|
+
children: "No experts available."
|
|
3084
|
+
})
|
|
3085
|
+
});
|
|
3086
|
+
}
|
|
3087
|
+
return /* @__PURE__ */ jsxs13("div", {
|
|
3088
|
+
className: "skippr:relative skippr:flex skippr:min-h-0 skippr:flex-1 skippr:flex-col",
|
|
3089
|
+
children: [
|
|
3090
|
+
/* @__PURE__ */ jsxs13("div", {
|
|
3091
|
+
ref: scrollRef,
|
|
3092
|
+
className: "skippr-no-scrollbar skippr:flex-1 skippr:overflow-y-auto",
|
|
3093
|
+
children: [
|
|
3094
|
+
/* @__PURE__ */ jsx14("h3", {
|
|
3095
|
+
className: `skippr:sticky skippr:top-0 skippr:z-10 skippr:bg-background skippr:px-4 skippr:py-3 skippr:text-xs skippr:font-medium skippr:uppercase skippr:tracking-wider skippr:text-muted-foreground skippr:transition-shadow ${isScrolled ? "skippr:border-b skippr:border-border" : ""}`,
|
|
3096
|
+
children: "Choose an agent"
|
|
3097
|
+
}),
|
|
3098
|
+
/* @__PURE__ */ jsxs13("div", {
|
|
3099
|
+
className: "skippr:px-4 skippr:pt-3 skippr:pb-5",
|
|
3100
|
+
children: [
|
|
3101
|
+
error && /* @__PURE__ */ jsx14("div", {
|
|
3102
|
+
role: "alert",
|
|
3103
|
+
className: "skippr:mb-3 skippr:rounded-lg skippr:border skippr:border-destructive/20 skippr:bg-destructive/10 skippr:px-3 skippr:py-2 skippr:text-xs skippr:text-destructive",
|
|
3104
|
+
children: error
|
|
3105
|
+
}),
|
|
3106
|
+
/* @__PURE__ */ jsx14("div", {
|
|
3107
|
+
className: "skippr:grid skippr:grid-cols-2 skippr:gap-2.5",
|
|
3108
|
+
children: availableModules.map((module, index2) => {
|
|
3109
|
+
const isFeatured = index2 === 0;
|
|
3110
|
+
const isWide = index2 === availableModules.length - 1 && availableModules.length % 2 === 1;
|
|
3111
|
+
const Icon2 = getAgentIcon(module.type);
|
|
3112
|
+
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
|
+
const layout = isWide ? "skippr:items-center skippr:p-3.5 skippr:pb-5" : "skippr:flex-col skippr:items-start skippr:p-3.5";
|
|
3114
|
+
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";
|
|
3115
|
+
const span = isWide ? "skippr:col-span-2" : "";
|
|
3116
|
+
return /* @__PURE__ */ jsxs13("button", {
|
|
3117
|
+
type: "button",
|
|
3118
|
+
disabled: isBusy,
|
|
3119
|
+
onClick: () => selectModule(module.id),
|
|
3120
|
+
className: `${base} ${layout} ${variant} ${span}`,
|
|
3121
|
+
children: [
|
|
3122
|
+
/* @__PURE__ */ jsx14("div", {
|
|
3123
|
+
className: isFeatured ? "skippr:flex skippr:h-9 skippr:w-9 skippr:shrink-0 skippr:items-center skippr:justify-center skippr:rounded-lg skippr:bg-primary-foreground/15" : "skippr:flex skippr:h-9 skippr:w-9 skippr:shrink-0 skippr:items-center skippr:justify-center skippr:rounded-lg skippr:bg-muted",
|
|
3124
|
+
children: /* @__PURE__ */ jsx14(Icon2, {
|
|
3125
|
+
className: isFeatured ? "skippr:h-4 skippr:w-4" : "skippr:h-4 skippr:w-4 skippr:text-muted-foreground"
|
|
3126
|
+
})
|
|
3127
|
+
}),
|
|
3128
|
+
/* @__PURE__ */ jsxs13("div", {
|
|
3129
|
+
className: "skippr:min-w-0 skippr:w-full skippr:space-y-0.5",
|
|
3130
|
+
children: [
|
|
3131
|
+
/* @__PURE__ */ jsx14("p", {
|
|
3132
|
+
className: "skippr:line-clamp-1 skippr:text-sm skippr:font-semibold",
|
|
3133
|
+
children: module.name
|
|
3134
|
+
}),
|
|
3135
|
+
module.description && /* @__PURE__ */ jsx14("p", {
|
|
3136
|
+
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",
|
|
3137
|
+
children: module.description
|
|
3138
|
+
})
|
|
3139
|
+
]
|
|
3140
|
+
})
|
|
3141
|
+
]
|
|
3142
|
+
}, module.id);
|
|
3143
|
+
})
|
|
3144
|
+
})
|
|
3145
|
+
]
|
|
3146
|
+
})
|
|
3147
|
+
]
|
|
3148
|
+
}),
|
|
3149
|
+
showScrollHint && /* @__PURE__ */ jsx14("div", {
|
|
3150
|
+
className: "skippr:pointer-events-none skippr:absolute skippr:bottom-0 skippr:left-0 skippr:right-0 skippr:h-12 skippr:bg-gradient-to-t skippr:from-background skippr:to-transparent"
|
|
3151
|
+
})
|
|
3152
|
+
]
|
|
3153
|
+
});
|
|
3154
|
+
}
|
|
3155
|
+
|
|
3156
|
+
// src/components/SessionAgenda.tsx
|
|
3157
|
+
import { jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
2875
3158
|
function SessionAgenda({ phases, hasStarted }) {
|
|
2876
3159
|
if (phases.length === 0 || !hasStarted) {
|
|
2877
|
-
return /* @__PURE__ */
|
|
3160
|
+
return /* @__PURE__ */ jsx15("div", {
|
|
2878
3161
|
className: "skippr:flex skippr:flex-1 skippr:items-center skippr:justify-center",
|
|
2879
|
-
children: /* @__PURE__ */
|
|
3162
|
+
children: /* @__PURE__ */ jsx15(LoadingDots, {
|
|
2880
3163
|
label: "Waiting for agenda to load..."
|
|
2881
3164
|
})
|
|
2882
3165
|
});
|
|
2883
3166
|
}
|
|
2884
|
-
return /* @__PURE__ */
|
|
3167
|
+
return /* @__PURE__ */ jsx15("div", {
|
|
2885
3168
|
className: "skippr:flex-1 skippr:overflow-y-auto skippr:px-4 skippr:py-4",
|
|
2886
|
-
children: /* @__PURE__ */
|
|
3169
|
+
children: /* @__PURE__ */ jsx15("div", {
|
|
2887
3170
|
className: "skippr:space-y-1",
|
|
2888
3171
|
children: phases.map((phase) => {
|
|
2889
3172
|
const isActive = phase.status === "active";
|
|
2890
3173
|
const isCompleted = phase.status === "completed";
|
|
2891
|
-
return /* @__PURE__ */
|
|
3174
|
+
return /* @__PURE__ */ jsxs14("div", {
|
|
2892
3175
|
className: cn("skippr:flex skippr:items-start skippr:gap-2.5 skippr:rounded-lg skippr:p-2 skippr:transition-colors", isActive && "skippr:bg-primary/10"),
|
|
2893
3176
|
children: [
|
|
2894
|
-
/* @__PURE__ */
|
|
3177
|
+
/* @__PURE__ */ jsx15("div", {
|
|
2895
3178
|
className: "skippr:mt-0.5",
|
|
2896
|
-
children: isCompleted ? /* @__PURE__ */
|
|
3179
|
+
children: isCompleted ? /* @__PURE__ */ jsx15(CircleCheck, {
|
|
2897
3180
|
className: "skippr:size-4 skippr:text-chart-3"
|
|
2898
|
-
}) : isActive ? /* @__PURE__ */
|
|
3181
|
+
}) : isActive ? /* @__PURE__ */ jsx15(Circle, {
|
|
2899
3182
|
className: "skippr:size-4 skippr:fill-primary/30 skippr:text-primary"
|
|
2900
|
-
}) : /* @__PURE__ */
|
|
3183
|
+
}) : /* @__PURE__ */ jsx15(Circle, {
|
|
2901
3184
|
className: "skippr:size-4 skippr:text-muted-foreground/30"
|
|
2902
3185
|
})
|
|
2903
3186
|
}),
|
|
2904
|
-
/* @__PURE__ */
|
|
3187
|
+
/* @__PURE__ */ jsxs14("div", {
|
|
2905
3188
|
className: "skippr:min-w-0 skippr:flex-1",
|
|
2906
3189
|
children: [
|
|
2907
|
-
/* @__PURE__ */
|
|
3190
|
+
/* @__PURE__ */ jsx15("p", {
|
|
2908
3191
|
className: cn("skippr:text-sm", isCompleted && "skippr:text-muted-foreground skippr:line-through", isActive && "skippr:font-medium skippr:text-foreground", phase.status === "pending" && "skippr:text-muted-foreground"),
|
|
2909
3192
|
children: phase.name
|
|
2910
3193
|
}),
|
|
2911
|
-
phase.highlights.length > 0 && /* @__PURE__ */
|
|
3194
|
+
phase.highlights.length > 0 && /* @__PURE__ */ jsx15("ul", {
|
|
2912
3195
|
className: "skippr:mt-1 skippr:space-y-0.5",
|
|
2913
|
-
children: phase.highlights.map((text) => /* @__PURE__ */
|
|
3196
|
+
children: phase.highlights.map((text) => /* @__PURE__ */ jsxs14("li", {
|
|
2914
3197
|
className: cn("skippr:flex skippr:items-center skippr:gap-1.5 skippr:text-[11px] skippr:leading-tight", isCompleted ? "skippr:text-muted-foreground/40 skippr:line-through" : "skippr:text-muted-foreground/70"),
|
|
2915
3198
|
children: [
|
|
2916
|
-
/* @__PURE__ */
|
|
3199
|
+
/* @__PURE__ */ jsx15("span", {
|
|
2917
3200
|
className: "skippr:size-1 skippr:shrink-0 skippr:rounded-full skippr:bg-current"
|
|
2918
3201
|
}),
|
|
2919
3202
|
text
|
|
@@ -2930,12 +3213,12 @@ function SessionAgenda({ phases, hasStarted }) {
|
|
|
2930
3213
|
}
|
|
2931
3214
|
|
|
2932
3215
|
// src/components/SessionWarningBanner.tsx
|
|
2933
|
-
import { jsx as
|
|
3216
|
+
import { jsx as jsx16 } from "react/jsx-runtime";
|
|
2934
3217
|
var SESSION_WARNING_THRESHOLD_SECS = 60;
|
|
2935
3218
|
function SessionWarningBanner({ remaining }) {
|
|
2936
3219
|
if (remaining === null || remaining <= 0 || remaining > SESSION_WARNING_THRESHOLD_SECS)
|
|
2937
3220
|
return null;
|
|
2938
|
-
return /* @__PURE__ */
|
|
3221
|
+
return /* @__PURE__ */ jsx16("div", {
|
|
2939
3222
|
"data-testid": "session-warning-banner",
|
|
2940
3223
|
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",
|
|
2941
3224
|
children: "Session ending soon"
|
|
@@ -2943,24 +3226,30 @@ function SessionWarningBanner({ remaining }) {
|
|
|
2943
3226
|
}
|
|
2944
3227
|
|
|
2945
3228
|
// src/components/StartSessionPrompt.tsx
|
|
2946
|
-
import { jsx as
|
|
3229
|
+
import { jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
2947
3230
|
function StartSessionPrompt({
|
|
2948
3231
|
onStartSession,
|
|
3232
|
+
agentId,
|
|
3233
|
+
agentControls,
|
|
2949
3234
|
isStarting,
|
|
2950
3235
|
error,
|
|
2951
3236
|
label = "Talk to Skippr"
|
|
2952
3237
|
}) {
|
|
2953
|
-
return /* @__PURE__ */
|
|
3238
|
+
return /* @__PURE__ */ jsxs15("div", {
|
|
2954
3239
|
className: "skippr:flex skippr:flex-1 skippr:flex-col skippr:items-center skippr:justify-center skippr:gap-3 skippr:px-4",
|
|
2955
3240
|
children: [
|
|
2956
|
-
/* @__PURE__ */
|
|
3241
|
+
/* @__PURE__ */ jsx17("button", {
|
|
2957
3242
|
type: "button",
|
|
2958
|
-
onClick:
|
|
2959
|
-
|
|
3243
|
+
onClick: () => {
|
|
3244
|
+
if (!agentId)
|
|
3245
|
+
return;
|
|
3246
|
+
onStartSession(agentControls ? { agentId, agentControls } : { agentId });
|
|
3247
|
+
},
|
|
3248
|
+
disabled: isStarting || !agentId,
|
|
2960
3249
|
className: "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",
|
|
2961
3250
|
children: isStarting ? "Starting..." : label
|
|
2962
3251
|
}),
|
|
2963
|
-
error && /* @__PURE__ */
|
|
3252
|
+
error && /* @__PURE__ */ jsx17("p", {
|
|
2964
3253
|
className: "skippr:text-xs skippr:text-destructive",
|
|
2965
3254
|
children: error
|
|
2966
3255
|
})
|
|
@@ -2969,7 +3258,7 @@ function StartSessionPrompt({
|
|
|
2969
3258
|
}
|
|
2970
3259
|
|
|
2971
3260
|
// src/components/Sidebar.tsx
|
|
2972
|
-
import { jsx as
|
|
3261
|
+
import { jsx as jsx18, jsxs as jsxs16, Fragment as Fragment3 } from "react/jsx-runtime";
|
|
2973
3262
|
function Sidebar({
|
|
2974
3263
|
hideControls = false,
|
|
2975
3264
|
hideHeader = false,
|
|
@@ -2993,11 +3282,14 @@ function Sidebar({
|
|
|
2993
3282
|
sidebarTab: activeTab,
|
|
2994
3283
|
setSidebarTab: setActiveTab,
|
|
2995
3284
|
autoFocusChat,
|
|
2996
|
-
captureMode
|
|
3285
|
+
captureMode,
|
|
3286
|
+
hasModuleSelector,
|
|
3287
|
+
agentId,
|
|
3288
|
+
agentControls
|
|
2997
3289
|
} = useLiveAgent();
|
|
2998
3290
|
const isFloating = variant === "floating";
|
|
2999
3291
|
const isSidebar = variant === "sidebar";
|
|
3000
|
-
|
|
3292
|
+
useEffect15(() => {
|
|
3001
3293
|
if (!isSidebar)
|
|
3002
3294
|
return;
|
|
3003
3295
|
const prop = position === "right" ? "marginRight" : "marginLeft";
|
|
@@ -3011,22 +3303,22 @@ function Sidebar({
|
|
|
3011
3303
|
document.body.style.transition = "";
|
|
3012
3304
|
};
|
|
3013
3305
|
}, [isSidebar, isPanelOpen, position]);
|
|
3014
|
-
return /* @__PURE__ */
|
|
3306
|
+
return /* @__PURE__ */ jsxs16("div", {
|
|
3015
3307
|
className: cn("skippr:fixed skippr:z-[9999]", "skippr:bg-card", "skippr:flex skippr:flex-col", "skippr:overflow-hidden", isFloating && "skippr:border skippr:border-border skippr:bottom-[88px] skippr:h-[min(460px,calc(100vh-200px))] skippr:rounded-2xl skippr:shadow-[0_8px_30px_rgba(0,0,0,0.16),0_4px_12px_rgba(0,0,0,0.08)]", isFloating && (position === "right" ? "skippr:right-6" : "skippr:left-6"), isFloating && "skippr:transition-[opacity,transform] skippr:duration-300 skippr:ease-in-out", isFloating && (position === "right" ? "skippr:origin-bottom-right" : "skippr:origin-bottom-left"), isFloating && !isPanelOpen && "skippr:scale-0 skippr:opacity-0 skippr:pointer-events-none", isFloating && isPanelOpen && "skippr:scale-100 skippr:opacity-100", isSidebar && "skippr:top-0 skippr:h-full", isSidebar && "skippr:transition-[width] skippr:duration-300 skippr:ease-in-out", isSidebar && position === "right" && "skippr:right-0 skippr:border-l skippr:border-l-border", isSidebar && position === "left" && "skippr:left-0 skippr:border-r skippr:border-r-border", isSidebar && !isPanelOpen && "skippr:w-0 skippr:border-0"),
|
|
3016
3308
|
style: { width: isPanelOpen ? SIDEBAR_WIDTH : undefined },
|
|
3017
3309
|
children: [
|
|
3018
|
-
!hideHeader && /* @__PURE__ */
|
|
3019
|
-
!isAuthenticated && isValidating ? /* @__PURE__ */
|
|
3310
|
+
!hideHeader && /* @__PURE__ */ jsx18(ChatHeader, {}),
|
|
3311
|
+
!isAuthenticated && isValidating ? /* @__PURE__ */ jsx18("div", {
|
|
3020
3312
|
className: "skippr:flex skippr:flex-1 skippr:items-center skippr:justify-center",
|
|
3021
|
-
children: /* @__PURE__ */
|
|
3313
|
+
children: /* @__PURE__ */ jsx18(LoadingDots, {
|
|
3022
3314
|
label: "Loading..."
|
|
3023
3315
|
})
|
|
3024
|
-
}) : !isAuthenticated ? /* @__PURE__ */
|
|
3316
|
+
}) : !isAuthenticated ? /* @__PURE__ */ jsx18(LoginFlow, {
|
|
3025
3317
|
requestOtp,
|
|
3026
3318
|
verifyOtp,
|
|
3027
3319
|
error: authError,
|
|
3028
3320
|
isSubmitting: isAuthSubmitting
|
|
3029
|
-
}) : /* @__PURE__ */
|
|
3321
|
+
}) : /* @__PURE__ */ jsx18(AuthenticatedContent, {
|
|
3030
3322
|
isConnected,
|
|
3031
3323
|
onStartSession: startSession,
|
|
3032
3324
|
onDisconnect: disconnect,
|
|
@@ -3037,7 +3329,10 @@ function Sidebar({
|
|
|
3037
3329
|
hideControls,
|
|
3038
3330
|
startSessionLabel,
|
|
3039
3331
|
autoFocusChat,
|
|
3040
|
-
showScreenShareToggle: captureMode === "screenshare"
|
|
3332
|
+
showScreenShareToggle: captureMode === "screenshare",
|
|
3333
|
+
hasModuleSelector,
|
|
3334
|
+
agentId,
|
|
3335
|
+
agentControls
|
|
3041
3336
|
})
|
|
3042
3337
|
]
|
|
3043
3338
|
});
|
|
@@ -3053,60 +3348,67 @@ function AuthenticatedContent({
|
|
|
3053
3348
|
hideControls,
|
|
3054
3349
|
startSessionLabel,
|
|
3055
3350
|
autoFocusChat,
|
|
3056
|
-
showScreenShareToggle
|
|
3351
|
+
showScreenShareToggle,
|
|
3352
|
+
hasModuleSelector,
|
|
3353
|
+
agentId,
|
|
3354
|
+
agentControls
|
|
3057
3355
|
}) {
|
|
3058
|
-
|
|
3356
|
+
const showSelectorAsPrompt = hasModuleSelector && !isConnected && !isStarting;
|
|
3357
|
+
const showTabBar = !showSelectorAsPrompt;
|
|
3358
|
+
return /* @__PURE__ */ jsxs16(Fragment3, {
|
|
3059
3359
|
children: [
|
|
3060
|
-
isConnected && /* @__PURE__ */
|
|
3061
|
-
/* @__PURE__ */
|
|
3062
|
-
className: "skippr:flex skippr:gap-2 skippr:border-b skippr:border-border skippr:px-3 skippr:py-2",
|
|
3360
|
+
isConnected && /* @__PURE__ */ jsx18(ConnectedBanner, {}),
|
|
3361
|
+
showTabBar && /* @__PURE__ */ jsxs16("div", {
|
|
3362
|
+
className: "skippr:flex skippr:items-center skippr:gap-2 skippr:border-b skippr:border-border skippr:px-3 skippr:py-2",
|
|
3063
3363
|
children: [
|
|
3064
|
-
/* @__PURE__ */
|
|
3364
|
+
/* @__PURE__ */ jsxs16("button", {
|
|
3065
3365
|
type: "button",
|
|
3066
3366
|
className: cn("skippr:relative skippr:inline-flex skippr:cursor-pointer skippr:items-center skippr:gap-1.5 skippr:rounded-lg skippr:px-3 skippr:py-2 skippr:text-sm skippr:font-medium skippr:transition-all", activeTab === "chat" ? "skippr:text-foreground" : "skippr:text-muted-foreground skippr:hover:text-foreground"),
|
|
3067
3367
|
onClick: () => onTabChange("chat"),
|
|
3068
3368
|
children: [
|
|
3069
|
-
/* @__PURE__ */
|
|
3369
|
+
/* @__PURE__ */ jsx18(MessageCircle, {
|
|
3070
3370
|
className: "skippr:size-3.5"
|
|
3071
3371
|
}),
|
|
3072
3372
|
"Chat",
|
|
3073
|
-
activeTab === "chat" && /* @__PURE__ */
|
|
3373
|
+
activeTab === "chat" && /* @__PURE__ */ jsx18("span", {
|
|
3074
3374
|
className: "skippr:absolute skippr:-bottom-2 skippr:left-3 skippr:right-3 skippr:h-0.5 skippr:rounded-full skippr:bg-foreground"
|
|
3075
3375
|
})
|
|
3076
3376
|
]
|
|
3077
3377
|
}),
|
|
3078
|
-
/* @__PURE__ */
|
|
3378
|
+
/* @__PURE__ */ jsxs16("button", {
|
|
3079
3379
|
type: "button",
|
|
3080
3380
|
className: cn("skippr:relative skippr:inline-flex skippr:cursor-pointer skippr:items-center skippr:gap-1.5 skippr:rounded-lg skippr:px-3 skippr:py-2 skippr:text-sm skippr:font-medium skippr:transition-all", activeTab === "agenda" ? "skippr:text-foreground" : "skippr:text-muted-foreground skippr:hover:text-foreground"),
|
|
3081
3381
|
onClick: () => onTabChange("agenda"),
|
|
3082
3382
|
children: [
|
|
3083
|
-
/* @__PURE__ */
|
|
3383
|
+
/* @__PURE__ */ jsx18(Calendar, {
|
|
3084
3384
|
className: "skippr:size-3.5"
|
|
3085
3385
|
}),
|
|
3086
3386
|
"Agenda",
|
|
3087
|
-
activeTab === "agenda" && /* @__PURE__ */
|
|
3387
|
+
activeTab === "agenda" && /* @__PURE__ */ jsx18("span", {
|
|
3088
3388
|
className: "skippr:absolute skippr:-bottom-2 skippr:left-3 skippr:right-3 skippr:h-0.5 skippr:rounded-full skippr:bg-foreground"
|
|
3089
3389
|
})
|
|
3090
3390
|
]
|
|
3091
3391
|
})
|
|
3092
3392
|
]
|
|
3093
3393
|
}),
|
|
3094
|
-
/* @__PURE__ */
|
|
3394
|
+
/* @__PURE__ */ jsx18("div", {
|
|
3095
3395
|
className: "skippr:flex skippr:min-h-0 skippr:flex-1 skippr:flex-col",
|
|
3096
|
-
children: isConnected || isStarting ? /* @__PURE__ */
|
|
3396
|
+
children: showSelectorAsPrompt ? /* @__PURE__ */ jsx18(ModuleSelector, {}) : isConnected || isStarting ? /* @__PURE__ */ jsx18(ConnectedBody, {
|
|
3097
3397
|
activeTab,
|
|
3098
3398
|
autoFocusChat
|
|
3099
|
-
}) : /* @__PURE__ */
|
|
3399
|
+
}) : /* @__PURE__ */ jsx18("div", {
|
|
3100
3400
|
className: "skippr:flex skippr:min-h-0 skippr:flex-1 skippr:flex-col skippr:animate-skippr-tab-fade",
|
|
3101
|
-
children: /* @__PURE__ */
|
|
3401
|
+
children: /* @__PURE__ */ jsx18(StartSessionPrompt, {
|
|
3102
3402
|
onStartSession,
|
|
3403
|
+
agentId,
|
|
3404
|
+
agentControls,
|
|
3103
3405
|
isStarting,
|
|
3104
3406
|
error,
|
|
3105
3407
|
label: startSessionLabel
|
|
3106
3408
|
})
|
|
3107
3409
|
}, `${activeTab}-empty`)
|
|
3108
3410
|
}),
|
|
3109
|
-
isConnected && !hideControls && /* @__PURE__ */
|
|
3411
|
+
isConnected && !hideControls && /* @__PURE__ */ jsx18(MeetingControls, {
|
|
3110
3412
|
onHangUp: onDisconnect,
|
|
3111
3413
|
showScreenShareToggle
|
|
3112
3414
|
})
|
|
@@ -3115,7 +3417,7 @@ function AuthenticatedContent({
|
|
|
3115
3417
|
}
|
|
3116
3418
|
function ConnectedBanner() {
|
|
3117
3419
|
const remaining = useSessionRemaining();
|
|
3118
|
-
return /* @__PURE__ */
|
|
3420
|
+
return /* @__PURE__ */ jsx18(SessionWarningBanner, {
|
|
3119
3421
|
remaining
|
|
3120
3422
|
});
|
|
3121
3423
|
}
|
|
@@ -3126,17 +3428,17 @@ function ConnectedBody({
|
|
|
3126
3428
|
const { allMessages, agentState, sendChatMessage, isSendingChat } = useCombinedMessages();
|
|
3127
3429
|
const { phases } = usePhaseUpdates();
|
|
3128
3430
|
if (activeTab === "agenda") {
|
|
3129
|
-
return /* @__PURE__ */
|
|
3431
|
+
return /* @__PURE__ */ jsx18("div", {
|
|
3130
3432
|
className: "skippr:min-h-0 skippr:flex-1 skippr:overflow-y-auto skippr:animate-skippr-tab-fade",
|
|
3131
|
-
children: /* @__PURE__ */
|
|
3433
|
+
children: /* @__PURE__ */ jsx18(SessionAgenda, {
|
|
3132
3434
|
phases,
|
|
3133
3435
|
hasStarted: allMessages.length > 0 || agentState === "speaking"
|
|
3134
3436
|
})
|
|
3135
3437
|
}, "agenda");
|
|
3136
3438
|
}
|
|
3137
|
-
return /* @__PURE__ */
|
|
3439
|
+
return /* @__PURE__ */ jsx18("div", {
|
|
3138
3440
|
className: "skippr:flex skippr:min-h-0 skippr:flex-1 skippr:flex-col skippr:animate-skippr-tab-fade",
|
|
3139
|
-
children: /* @__PURE__ */
|
|
3441
|
+
children: /* @__PURE__ */ jsx18(MessageList, {
|
|
3140
3442
|
messages: allMessages,
|
|
3141
3443
|
isStreaming: agentState === "speaking",
|
|
3142
3444
|
sendChatMessage,
|
|
@@ -3147,31 +3449,31 @@ function ConnectedBody({
|
|
|
3147
3449
|
}
|
|
3148
3450
|
|
|
3149
3451
|
// src/components/SidebarTrigger.tsx
|
|
3150
|
-
import { jsx as
|
|
3452
|
+
import { jsx as jsx19 } from "react/jsx-runtime";
|
|
3151
3453
|
function SidebarTrigger() {
|
|
3152
3454
|
const { isPanelOpen, togglePanel, minimizePanel, minimizable, position, isMinimized } = useLiveAgent();
|
|
3153
3455
|
if (isMinimized)
|
|
3154
3456
|
return null;
|
|
3155
3457
|
const handleClick = isPanelOpen && minimizable ? minimizePanel : togglePanel;
|
|
3156
|
-
return /* @__PURE__ */
|
|
3458
|
+
return /* @__PURE__ */ jsx19("button", {
|
|
3157
3459
|
type: "button",
|
|
3158
3460
|
onClick: handleClick,
|
|
3159
3461
|
title: isPanelOpen ? "Close chat" : "Open chat",
|
|
3160
3462
|
"aria-label": isPanelOpen ? "Close chat" : "Open chat",
|
|
3161
3463
|
className: cn("skippr:fixed skippr:bottom-6 skippr:z-[9998]", "skippr:flex skippr:size-12 skippr:items-center skippr:justify-center", "skippr:rounded-[14px] skippr:bg-bubble skippr:text-white", "skippr:shadow-[0_4px_16px_rgba(45,43,61,0.45),0_2px_4px_rgba(0,0,0,0.1)] skippr:transition-all", "skippr:cursor-pointer skippr:hover:brightness-110 skippr:hover:-translate-y-0.5 skippr:active:translate-y-0", position === "right" ? "skippr:right-6" : "skippr:left-6"),
|
|
3162
|
-
children: isPanelOpen ? /* @__PURE__ */
|
|
3464
|
+
children: isPanelOpen ? /* @__PURE__ */ jsx19(ChevronDown, {
|
|
3163
3465
|
className: "skippr:size-5"
|
|
3164
|
-
}) : /* @__PURE__ */
|
|
3466
|
+
}) : /* @__PURE__ */ jsx19(Logo, {
|
|
3165
3467
|
className: "skippr:size-7"
|
|
3166
3468
|
})
|
|
3167
3469
|
});
|
|
3168
3470
|
}
|
|
3169
3471
|
|
|
3170
3472
|
// src/components/LiveAgent.tsx
|
|
3171
|
-
import { jsx as
|
|
3473
|
+
import { jsx as jsx20, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
3172
3474
|
function LiveAgent(props) {
|
|
3173
3475
|
const {
|
|
3174
|
-
agentId,
|
|
3476
|
+
agentId: hostAgentId,
|
|
3175
3477
|
authToken: authTokenProp,
|
|
3176
3478
|
appKey,
|
|
3177
3479
|
userToken,
|
|
@@ -3187,39 +3489,81 @@ function LiveAgent(props) {
|
|
|
3187
3489
|
children
|
|
3188
3490
|
} = props;
|
|
3189
3491
|
const captureMode = props.captureMode ?? "screenshare";
|
|
3190
|
-
let
|
|
3492
|
+
let hostAgentControls;
|
|
3191
3493
|
if ("agentControls" in props && props.agentControls) {
|
|
3192
3494
|
if (captureMode === "auto") {
|
|
3193
|
-
|
|
3495
|
+
hostAgentControls = props.agentControls;
|
|
3194
3496
|
} else {
|
|
3195
3497
|
console.warn('[Skippr] agentControls requires captureMode: "auto"');
|
|
3196
3498
|
}
|
|
3197
3499
|
}
|
|
3198
3500
|
const auth = useAuth({ appKey });
|
|
3199
3501
|
const effectiveAuthToken = authTokenProp || auth.authToken || undefined;
|
|
3502
|
+
const hasModuleSelector = !hostAgentId;
|
|
3503
|
+
const [activeModule, setActiveModule] = useState11(null);
|
|
3504
|
+
const agentId = hasModuleSelector ? activeModule?.id ?? null : hostAgentId;
|
|
3505
|
+
const agentControls = hasModuleSelector ? activeModule?.controls : hostAgentControls;
|
|
3506
|
+
const minimizeOnSessionStart = useCallback8(() => {
|
|
3507
|
+
if (minimizable) {
|
|
3508
|
+
setIsMinimized(true);
|
|
3509
|
+
setIsPanelOpen(false);
|
|
3510
|
+
}
|
|
3511
|
+
}, [minimizable]);
|
|
3512
|
+
const expandOnSessionStartError = useCallback8(() => {
|
|
3513
|
+
setIsMinimized(false);
|
|
3514
|
+
setIsPanelOpen(true);
|
|
3515
|
+
}, []);
|
|
3516
|
+
const minimizeOnSessionDisconnect = useCallback8(() => {
|
|
3517
|
+
if (hasModuleSelector) {
|
|
3518
|
+
setActiveModule(null);
|
|
3519
|
+
}
|
|
3520
|
+
if (minimizable && !hasModuleSelector) {
|
|
3521
|
+
setIsMinimized(true);
|
|
3522
|
+
setIsPanelOpen(false);
|
|
3523
|
+
}
|
|
3524
|
+
}, [minimizable, hasModuleSelector]);
|
|
3200
3525
|
const {
|
|
3201
3526
|
connection,
|
|
3202
3527
|
shouldConnect,
|
|
3203
3528
|
isStarting,
|
|
3529
|
+
isDisconnecting,
|
|
3204
3530
|
error,
|
|
3205
3531
|
errorCode,
|
|
3206
3532
|
startSession,
|
|
3207
3533
|
disconnect,
|
|
3208
|
-
pendingScreenStream
|
|
3534
|
+
pendingScreenStream,
|
|
3535
|
+
bearerToken
|
|
3209
3536
|
} = useSession({
|
|
3210
|
-
agentId,
|
|
3211
3537
|
captureMode,
|
|
3212
|
-
agentControls,
|
|
3213
3538
|
authToken: effectiveAuthToken,
|
|
3214
3539
|
appKey,
|
|
3215
|
-
userToken
|
|
3540
|
+
userToken,
|
|
3541
|
+
onStart: minimizeOnSessionStart,
|
|
3542
|
+
onStartError: expandOnSessionStartError,
|
|
3543
|
+
onDisconnect: minimizeOnSessionDisconnect
|
|
3544
|
+
});
|
|
3545
|
+
const [isPanelOpen, setIsPanelOpen] = useState11(defaultOpen);
|
|
3546
|
+
const [isMinimized, setIsMinimized] = useState11(minimizable && !defaultOpen);
|
|
3547
|
+
const [sidebarTab, setSidebarTab] = useState11("agenda");
|
|
3548
|
+
const {
|
|
3549
|
+
modules: availableModules,
|
|
3550
|
+
isLoading: isLoadingModules,
|
|
3551
|
+
error: modulesError,
|
|
3552
|
+
refetch: refetchModules
|
|
3553
|
+
} = useAvailableModules({
|
|
3554
|
+
enabled: hasModuleSelector && isPanelOpen,
|
|
3555
|
+
bearerToken
|
|
3216
3556
|
});
|
|
3217
|
-
const
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3557
|
+
const selectModule = useCallback8((moduleId) => {
|
|
3558
|
+
const found = availableModules.find((m) => m.id === moduleId);
|
|
3559
|
+
if (!found)
|
|
3560
|
+
return;
|
|
3561
|
+
setActiveModule(found);
|
|
3562
|
+
startSession({ agentId: found.id, agentControls: found.controls });
|
|
3563
|
+
}, [availableModules, startSession]);
|
|
3564
|
+
const [welcomeDismissed, setWelcomeDismissed] = useState11(false);
|
|
3565
|
+
const dismissWelcome = useCallback8(() => setWelcomeDismissed(true), []);
|
|
3566
|
+
const [currentPosition, setCurrentPosition] = useState11(() => {
|
|
3223
3567
|
try {
|
|
3224
3568
|
const saved = localStorage.getItem("skippr_widget_position");
|
|
3225
3569
|
if (saved === "left" || saved === "right")
|
|
@@ -3227,20 +3571,20 @@ function LiveAgent(props) {
|
|
|
3227
3571
|
} catch {}
|
|
3228
3572
|
return position;
|
|
3229
3573
|
});
|
|
3230
|
-
const setPositionWithPersist =
|
|
3574
|
+
const setPositionWithPersist = useCallback8((pos) => {
|
|
3231
3575
|
setCurrentPosition(pos);
|
|
3232
3576
|
try {
|
|
3233
3577
|
localStorage.setItem("skippr_widget_position", pos);
|
|
3234
3578
|
} catch {}
|
|
3235
3579
|
}, []);
|
|
3236
|
-
const openPanel =
|
|
3237
|
-
const closePanel =
|
|
3238
|
-
const togglePanel =
|
|
3239
|
-
const expandPanel =
|
|
3580
|
+
const openPanel = useCallback8(() => setIsPanelOpen(true), []);
|
|
3581
|
+
const closePanel = useCallback8(() => setIsPanelOpen(false), []);
|
|
3582
|
+
const togglePanel = useCallback8(() => setIsPanelOpen((prev) => !prev), []);
|
|
3583
|
+
const expandPanel = useCallback8(() => {
|
|
3240
3584
|
setIsMinimized(false);
|
|
3241
3585
|
setIsPanelOpen(true);
|
|
3242
3586
|
}, []);
|
|
3243
|
-
const minimizePanel =
|
|
3587
|
+
const minimizePanel = useCallback8(() => {
|
|
3244
3588
|
if (!minimizable)
|
|
3245
3589
|
return;
|
|
3246
3590
|
setIsMinimized(true);
|
|
@@ -3248,20 +3592,12 @@ function LiveAgent(props) {
|
|
|
3248
3592
|
}, [minimizable]);
|
|
3249
3593
|
const isConnected = connection !== null;
|
|
3250
3594
|
const isAuthenticated = !!userToken || !!authTokenProp || auth.isAuthenticated;
|
|
3251
|
-
const prevConnectionRef = useRef8(connection);
|
|
3252
|
-
useEffect14(() => {
|
|
3253
|
-
const connectionChanged = prevConnectionRef.current !== connection;
|
|
3254
|
-
prevConnectionRef.current = connection;
|
|
3255
|
-
if (connectionChanged && minimizable) {
|
|
3256
|
-
setIsMinimized(true);
|
|
3257
|
-
setIsPanelOpen(false);
|
|
3258
|
-
}
|
|
3259
|
-
}, [connection, minimizable]);
|
|
3260
3595
|
const ctx = useMemo5(() => ({
|
|
3261
3596
|
connection,
|
|
3262
3597
|
shouldConnect,
|
|
3263
3598
|
isConnected,
|
|
3264
3599
|
isStarting,
|
|
3600
|
+
isDisconnecting,
|
|
3265
3601
|
error,
|
|
3266
3602
|
errorCode,
|
|
3267
3603
|
startSession,
|
|
@@ -3288,12 +3624,21 @@ function LiveAgent(props) {
|
|
|
3288
3624
|
setSidebarTab,
|
|
3289
3625
|
autoFocusChat,
|
|
3290
3626
|
captureMode,
|
|
3291
|
-
|
|
3627
|
+
agentId,
|
|
3628
|
+
agentControls,
|
|
3629
|
+
hasModuleSelector,
|
|
3630
|
+
availableModules,
|
|
3631
|
+
activeModule,
|
|
3632
|
+
isLoadingModules,
|
|
3633
|
+
modulesError,
|
|
3634
|
+
refetchModules,
|
|
3635
|
+
selectModule
|
|
3292
3636
|
}), [
|
|
3293
3637
|
connection,
|
|
3294
3638
|
shouldConnect,
|
|
3295
3639
|
isConnected,
|
|
3296
3640
|
isStarting,
|
|
3641
|
+
isDisconnecting,
|
|
3297
3642
|
error,
|
|
3298
3643
|
errorCode,
|
|
3299
3644
|
startSession,
|
|
@@ -3319,33 +3664,41 @@ function LiveAgent(props) {
|
|
|
3319
3664
|
sidebarTab,
|
|
3320
3665
|
autoFocusChat,
|
|
3321
3666
|
captureMode,
|
|
3322
|
-
|
|
3667
|
+
agentId,
|
|
3668
|
+
agentControls,
|
|
3669
|
+
hasModuleSelector,
|
|
3670
|
+
availableModules,
|
|
3671
|
+
activeModule,
|
|
3672
|
+
isLoadingModules,
|
|
3673
|
+
modulesError,
|
|
3674
|
+
refetchModules,
|
|
3675
|
+
selectModule
|
|
3323
3676
|
]);
|
|
3324
|
-
return /* @__PURE__ */
|
|
3677
|
+
return /* @__PURE__ */ jsx20(LiveAgentContext.Provider, {
|
|
3325
3678
|
value: ctx,
|
|
3326
|
-
children: /* @__PURE__ */
|
|
3679
|
+
children: /* @__PURE__ */ jsxs17(LiveKitRoom, {
|
|
3327
3680
|
serverUrl: connection?.livekitUrl,
|
|
3328
3681
|
token: connection?.token,
|
|
3329
3682
|
connect: shouldConnect,
|
|
3330
3683
|
audio: true,
|
|
3331
3684
|
onDisconnected: disconnect,
|
|
3332
3685
|
children: [
|
|
3333
|
-
connection && /* @__PURE__ */
|
|
3334
|
-
connection && captureMode === "screenshare" && /* @__PURE__ */
|
|
3686
|
+
connection && /* @__PURE__ */ jsx20(RoomAudioRenderer, {}),
|
|
3687
|
+
connection && captureMode === "screenshare" && /* @__PURE__ */ jsx20(AutoStartMedia, {
|
|
3335
3688
|
pendingScreenStream
|
|
3336
3689
|
}),
|
|
3337
|
-
connection && captureMode === "auto" && /* @__PURE__ */
|
|
3338
|
-
connection && captureMode === "auto" && agentControls?.highlight && /* @__PURE__ */
|
|
3339
|
-
/* @__PURE__ */
|
|
3690
|
+
connection && captureMode === "auto" && /* @__PURE__ */ jsx20(DomCapture, {}),
|
|
3691
|
+
connection && captureMode === "auto" && agentControls?.highlight && /* @__PURE__ */ jsx20(HighlightOverlay, {}),
|
|
3692
|
+
/* @__PURE__ */ jsxs17("div", {
|
|
3340
3693
|
id: WIDGET_ROOT_ID,
|
|
3341
3694
|
children: [
|
|
3342
|
-
isMinimized && /* @__PURE__ */
|
|
3695
|
+
isMinimized && /* @__PURE__ */ jsx20(MinimizedBubble, {
|
|
3343
3696
|
welcomeMessage,
|
|
3344
3697
|
welcomeDismissed,
|
|
3345
3698
|
onDismissWelcome: dismissWelcome
|
|
3346
3699
|
}),
|
|
3347
|
-
/* @__PURE__ */
|
|
3348
|
-
/* @__PURE__ */
|
|
3700
|
+
/* @__PURE__ */ jsx20(SidebarTrigger, {}),
|
|
3701
|
+
/* @__PURE__ */ jsx20(Sidebar, {
|
|
3349
3702
|
hideControls,
|
|
3350
3703
|
hideHeader,
|
|
3351
3704
|
startSessionLabel
|