@skippr/live-agent-sdk 0.24.0 → 0.26.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.
@@ -1,6 +1,6 @@
1
1
  // src/components/LiveAgent.tsx
2
2
  import { LiveKitRoom, RoomAudioRenderer } from "@livekit/components-react";
3
- import { useCallback as useCallback8, useEffect as useEffect9, useMemo as useMemo5, useRef as useRef5, useState as useState9 } from "react";
3
+ import { useCallback as useCallback6, useEffect as useEffect12, useMemo as useMemo5, useRef as useRef6, useState as useState8 } from "react";
4
4
 
5
5
  // src/context/LiveAgentContext.tsx
6
6
  import { createContext } from "react";
@@ -173,6 +173,7 @@ function useSession({ agentId, authToken, appKey, userToken }) {
173
173
  const [errorCode, setErrorCode] = useState2(null);
174
174
  const [sessionId, setSessionId] = useState2(null);
175
175
  const [bearerToken, setBearerToken] = useState2(authToken ?? null);
176
+ const [pendingScreenStream, setPendingScreenStream] = useState2(null);
176
177
  useEffect2(() => {
177
178
  let stale = false;
178
179
  if (authToken) {
@@ -197,10 +198,18 @@ function useSession({ agentId, authToken, appKey, userToken }) {
197
198
  setError("No auth token available");
198
199
  return;
199
200
  }
200
- const headers = { Authorization: `Bearer ${bearerToken}` };
201
201
  setIsStarting(true);
202
202
  setError("");
203
203
  setErrorCode(null);
204
+ let screenStream = null;
205
+ try {
206
+ screenStream = await navigator.mediaDevices.getDisplayMedia({
207
+ video: { displaySurface: "browser" }
208
+ });
209
+ } catch {
210
+ screenStream = null;
211
+ }
212
+ const headers = { Authorization: `Bearer ${bearerToken}` };
204
213
  try {
205
214
  const createResp = await fetch(`${API_URL2}/v1/sessions`, {
206
215
  method: "POST",
@@ -230,8 +239,13 @@ function useSession({ agentId, authToken, appKey, userToken }) {
230
239
  livekitUrl: conn.livekitUrl,
231
240
  token: conn.token
232
241
  });
242
+ setPendingScreenStream(screenStream);
233
243
  setShouldConnect(true);
234
244
  } catch (e) {
245
+ if (screenStream) {
246
+ for (const track of screenStream.getTracks())
247
+ track.stop();
248
+ }
235
249
  setError(e instanceof Error ? e.message : "Failed to start session");
236
250
  } finally {
237
251
  setIsStarting(false);
@@ -248,17 +262,57 @@ function useSession({ agentId, authToken, appKey, userToken }) {
248
262
  });
249
263
  } catch {}
250
264
  }
265
+ if (pendingScreenStream) {
266
+ for (const track of pendingScreenStream.getTracks())
267
+ track.stop();
268
+ }
251
269
  setError("");
252
270
  setShouldConnect(false);
253
271
  setConnection(null);
254
272
  setSessionId(null);
255
- }, [sessionId, bearerToken]);
256
- return { connection, shouldConnect, isStarting, error, errorCode, startSession, disconnect };
273
+ setPendingScreenStream(null);
274
+ }, [sessionId, bearerToken, pendingScreenStream]);
275
+ return {
276
+ connection,
277
+ shouldConnect,
278
+ isStarting,
279
+ error,
280
+ errorCode,
281
+ startSession,
282
+ disconnect,
283
+ pendingScreenStream
284
+ };
257
285
  }
258
286
 
259
- // src/components/MinimizedBubble.tsx
260
- import { useLocalParticipant, useVoiceAssistant } from "@livekit/components-react";
261
- import { ScreenSharePresets } from "livekit-client";
287
+ // src/components/AutoStartMedia.tsx
288
+ import { useConnectionState, useLocalParticipant } from "@livekit/components-react";
289
+ import { ConnectionState, Track } from "livekit-client";
290
+ import { useEffect as useEffect3, useRef } from "react";
291
+ function AutoStartMedia({ pendingScreenStream }) {
292
+ const { localParticipant } = useLocalParticipant();
293
+ const connectionState = useConnectionState();
294
+ const didStartRef = useRef(false);
295
+ useEffect3(() => {
296
+ if (didStartRef.current)
297
+ return;
298
+ if (connectionState !== ConnectionState.Connected)
299
+ return;
300
+ didStartRef.current = true;
301
+ localParticipant.setMicrophoneEnabled(true).catch((error) => console.error("Failed to enable microphone:", error));
302
+ if (pendingScreenStream) {
303
+ const videoTrack = pendingScreenStream.getVideoTracks()[0];
304
+ if (videoTrack) {
305
+ videoTrack.contentHint = "detail";
306
+ localParticipant.publishTrack(videoTrack, { source: Track.Source.ScreenShare }).catch((error) => {
307
+ console.error("Failed to publish screen share track:", error);
308
+ for (const track of pendingScreenStream.getTracks())
309
+ track.stop();
310
+ });
311
+ }
312
+ }
313
+ }, [connectionState, localParticipant, pendingScreenStream]);
314
+ return null;
315
+ }
262
316
  // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/createLucideIcon.js
263
317
  import { forwardRef as forwardRef2, createElement as createElement3 } from "react";
264
318
 
@@ -350,70 +404,63 @@ var createLucideIcon = (iconName, iconNode) => {
350
404
  return Component;
351
405
  };
352
406
 
407
+ // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/circle-check.js
408
+ var __iconNode = [
409
+ ["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
410
+ ["path", { d: "m9 12 2 2 4-4", key: "dzmm74" }]
411
+ ];
412
+ var CircleCheck = createLucideIcon("circle-check", __iconNode);
353
413
  // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/loader-circle.js
354
- var __iconNode = [["path", { d: "M21 12a9 9 0 1 1-6.219-8.56", key: "13zald" }]];
355
- var LoaderCircle = createLucideIcon("loader-circle", __iconNode);
356
- // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/message-circle-question-mark.js
357
- var __iconNode2 = [
414
+ var __iconNode2 = [["path", { d: "M21 12a9 9 0 1 1-6.219-8.56", key: "13zald" }]];
415
+ var LoaderCircle = createLucideIcon("loader-circle", __iconNode2);
416
+ // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/sparkles.js
417
+ var __iconNode3 = [
358
418
  [
359
419
  "path",
360
420
  {
361
- d: "M2.992 16.342a2 2 0 0 1 .094 1.167l-1.065 3.29a1 1 0 0 0 1.236 1.168l3.413-.998a2 2 0 0 1 1.099.092 10 10 0 1 0-4.777-4.719",
362
- key: "1sd12s"
421
+ d: "M11.017 2.814a1 1 0 0 1 1.966 0l1.051 5.558a2 2 0 0 0 1.594 1.594l5.558 1.051a1 1 0 0 1 0 1.966l-5.558 1.051a2 2 0 0 0-1.594 1.594l-1.051 5.558a1 1 0 0 1-1.966 0l-1.051-5.558a2 2 0 0 0-1.594-1.594l-5.558-1.051a1 1 0 0 1 0-1.966l5.558-1.051a2 2 0 0 0 1.594-1.594z",
422
+ key: "1s2grr"
363
423
  }
364
424
  ],
365
- ["path", { d: "M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3", key: "1u773s" }],
366
- ["path", { d: "M12 17h.01", key: "p32p05" }]
425
+ ["path", { d: "M20 2v4", key: "1rf3ol" }],
426
+ ["path", { d: "M22 4h-4", key: "gwowj6" }],
427
+ ["circle", { cx: "4", cy: "20", r: "2", key: "6kqj1y" }]
367
428
  ];
368
- var MessageCircleQuestionMark = createLucideIcon("message-circle-question-mark", __iconNode2);
369
- // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/panel-left.js
370
- var __iconNode3 = [
371
- ["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", key: "afitv7" }],
372
- ["path", { d: "M9 3v18", key: "fh3hqa" }]
373
- ];
374
- var PanelLeft = createLucideIcon("panel-left", __iconNode3);
375
- // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/send-horizontal.js
429
+ var Sparkles = createLucideIcon("sparkles", __iconNode3);
430
+ // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/calendar.js
376
431
  var __iconNode4 = [
432
+ ["path", { d: "M8 2v4", key: "1cmpym" }],
433
+ ["path", { d: "M16 2v4", key: "4m81vk" }],
434
+ ["rect", { width: "18", height: "18", x: "3", y: "4", rx: "2", key: "1hopcy" }],
435
+ ["path", { d: "M3 10h18", key: "8toen8" }]
436
+ ];
437
+ var Calendar = createLucideIcon("calendar", __iconNode4);
438
+ // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/chevron-down.js
439
+ var __iconNode5 = [["path", { d: "m6 9 6 6 6-6", key: "qrunsl" }]];
440
+ var ChevronDown = createLucideIcon("chevron-down", __iconNode5);
441
+ // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/circle.js
442
+ var __iconNode6 = [["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }]];
443
+ var Circle = createLucideIcon("circle", __iconNode6);
444
+ // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/eye.js
445
+ var __iconNode7 = [
377
446
  [
378
447
  "path",
379
448
  {
380
- d: "M3.714 3.048a.498.498 0 0 0-.683.627l2.843 7.627a2 2 0 0 1 0 1.396l-2.842 7.627a.498.498 0 0 0 .682.627l18-8.5a.5.5 0 0 0 0-.904z",
381
- key: "117uat"
449
+ d: "M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0",
450
+ key: "1nclc0"
382
451
  }
383
452
  ],
384
- ["path", { d: "M6 12h16", key: "s4cdu5" }]
385
- ];
386
- var SendHorizontal = createLucideIcon("send-horizontal", __iconNode4);
387
- // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/arrow-left.js
388
- var __iconNode5 = [
389
- ["path", { d: "m12 19-7-7 7-7", key: "1l729n" }],
390
- ["path", { d: "M19 12H5", key: "x3x0zl" }]
391
- ];
392
- var ArrowLeft = createLucideIcon("arrow-left", __iconNode5);
393
- // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/bot.js
394
- var __iconNode6 = [
395
- ["path", { d: "M12 8V4H8", key: "hb8ula" }],
396
- ["rect", { width: "16", height: "12", x: "4", y: "8", rx: "2", key: "enze0r" }],
397
- ["path", { d: "M2 14h2", key: "vft8re" }],
398
- ["path", { d: "M20 14h2", key: "4cs60a" }],
399
- ["path", { d: "M15 13v2", key: "1xurst" }],
400
- ["path", { d: "M9 13v2", key: "rq6x2g" }]
453
+ ["circle", { cx: "12", cy: "12", r: "3", key: "1v7zrd" }]
401
454
  ];
402
- var Bot = createLucideIcon("bot", __iconNode6);
403
- // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/check.js
404
- var __iconNode7 = [["path", { d: "M20 6 9 17l-5-5", key: "1gmf2c" }]];
405
- var Check = createLucideIcon("check", __iconNode7);
406
- // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/circle.js
407
- var __iconNode8 = [["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }]];
408
- var Circle = createLucideIcon("circle", __iconNode8);
455
+ var Eye = createLucideIcon("eye", __iconNode7);
409
456
  // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/mail.js
410
- var __iconNode9 = [
457
+ var __iconNode8 = [
411
458
  ["path", { d: "m22 7-8.991 5.727a2 2 0 0 1-2.009 0L2 7", key: "132q7q" }],
412
459
  ["rect", { x: "2", y: "4", width: "20", height: "16", rx: "2", key: "izxlao" }]
413
460
  ];
414
- var Mail = createLucideIcon("mail", __iconNode9);
461
+ var Mail = createLucideIcon("mail", __iconNode8);
415
462
  // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/message-circle.js
416
- var __iconNode10 = [
463
+ var __iconNode9 = [
417
464
  [
418
465
  "path",
419
466
  {
@@ -422,20 +469,9 @@ var __iconNode10 = [
422
469
  }
423
470
  ]
424
471
  ];
425
- var MessageCircle = createLucideIcon("message-circle", __iconNode10);
426
- // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/message-square.js
427
- var __iconNode11 = [
428
- [
429
- "path",
430
- {
431
- d: "M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z",
432
- key: "18887p"
433
- }
434
- ]
435
- ];
436
- var MessageSquare = createLucideIcon("message-square", __iconNode11);
472
+ var MessageCircle = createLucideIcon("message-circle", __iconNode9);
437
473
  // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/mic-off.js
438
- var __iconNode12 = [
474
+ var __iconNode10 = [
439
475
  ["path", { d: "M12 19v3", key: "npa21l" }],
440
476
  ["path", { d: "M15 9.34V5a3 3 0 0 0-5.68-1.33", key: "1gzdoj" }],
441
477
  ["path", { d: "M16.95 16.95A7 7 0 0 1 5 12v-2", key: "cqa7eg" }],
@@ -443,38 +479,40 @@ var __iconNode12 = [
443
479
  ["path", { d: "m2 2 20 20", key: "1ooewy" }],
444
480
  ["path", { d: "M9 9v3a3 3 0 0 0 5.12 2.12", key: "r2i35w" }]
445
481
  ];
446
- var MicOff = createLucideIcon("mic-off", __iconNode12);
482
+ var MicOff = createLucideIcon("mic-off", __iconNode10);
447
483
  // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/mic.js
448
- var __iconNode13 = [
484
+ var __iconNode11 = [
449
485
  ["path", { d: "M12 19v3", key: "npa21l" }],
450
486
  ["path", { d: "M19 10v2a7 7 0 0 1-14 0v-2", key: "1vc78b" }],
451
487
  ["rect", { x: "9", y: "2", width: "6", height: "13", rx: "3", key: "s6n7sd" }]
452
488
  ];
453
- var Mic = createLucideIcon("mic", __iconNode13);
489
+ var Mic = createLucideIcon("mic", __iconNode11);
490
+ // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/minimize-2.js
491
+ var __iconNode12 = [
492
+ ["path", { d: "m14 10 7-7", key: "oa77jy" }],
493
+ ["path", { d: "M20 10h-6V4", key: "mjg0md" }],
494
+ ["path", { d: "m3 21 7-7", key: "tjx5ai" }],
495
+ ["path", { d: "M4 14h6v6", key: "rmj7iw" }]
496
+ ];
497
+ var Minimize2 = createLucideIcon("minimize-2", __iconNode12);
454
498
  // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/monitor-off.js
455
- var __iconNode14 = [
499
+ var __iconNode13 = [
456
500
  ["path", { d: "M12 17v4", key: "1riwvh" }],
457
501
  ["path", { d: "M17 17H4a2 2 0 0 1-2-2V5a2 2 0 0 1 1.184-1.826", key: "cv7jms" }],
458
502
  ["path", { d: "m2 2 20 20", key: "1ooewy" }],
459
503
  ["path", { d: "M8 21h8", key: "1ev6f3" }],
460
504
  ["path", { d: "M8.656 3H20a2 2 0 0 1 2 2v10a2 2 0 0 1-.293 1.042", key: "z8ni2w" }]
461
505
  ];
462
- var MonitorOff = createLucideIcon("monitor-off", __iconNode14);
506
+ var MonitorOff = createLucideIcon("monitor-off", __iconNode13);
463
507
  // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/monitor.js
464
- var __iconNode15 = [
508
+ var __iconNode14 = [
465
509
  ["rect", { width: "20", height: "14", x: "2", y: "3", rx: "2", key: "48i651" }],
466
510
  ["line", { x1: "8", x2: "16", y1: "21", y2: "21", key: "1svkeh" }],
467
511
  ["line", { x1: "12", x2: "12", y1: "17", y2: "21", key: "vw1qmm" }]
468
512
  ];
469
- var Monitor = createLucideIcon("monitor", __iconNode15);
470
- // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/panel-right.js
471
- var __iconNode16 = [
472
- ["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", key: "afitv7" }],
473
- ["path", { d: "M15 3v18", key: "14nvp0" }]
474
- ];
475
- var PanelRight = createLucideIcon("panel-right", __iconNode16);
513
+ var Monitor = createLucideIcon("monitor", __iconNode14);
476
514
  // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/phone-off.js
477
- var __iconNode17 = [
515
+ var __iconNode15 = [
478
516
  [
479
517
  "path",
480
518
  {
@@ -491,27 +529,32 @@ var __iconNode17 = [
491
529
  }
492
530
  ]
493
531
  ];
494
- var PhoneOff = createLucideIcon("phone-off", __iconNode17);
495
- // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/settings.js
496
- var __iconNode18 = [
532
+ var PhoneOff = createLucideIcon("phone-off", __iconNode15);
533
+ // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/send.js
534
+ var __iconNode16 = [
497
535
  [
498
536
  "path",
499
537
  {
500
- d: "M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915",
501
- key: "1i5ecw"
538
+ 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",
539
+ key: "1ffxy3"
502
540
  }
503
541
  ],
504
- ["circle", { cx: "12", cy: "12", r: "3", key: "1v7zrd" }]
505
- ];
506
- var Settings = createLucideIcon("settings", __iconNode18);
507
- // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/x.js
508
- var __iconNode19 = [
509
- ["path", { d: "M18 6 6 18", key: "1bl5f8" }],
510
- ["path", { d: "m6 6 12 12", key: "d8bk6v" }]
542
+ ["path", { d: "m21.854 2.147-10.94 10.939", key: "12cjpa" }]
511
543
  ];
512
- var X = createLucideIcon("x", __iconNode19);
544
+ var Send = createLucideIcon("send", __iconNode16);
513
545
  // src/components/MinimizedBubble.tsx
514
- import { useCallback as useCallback3, useEffect as useEffect3, useRef, useState as useState3 } from "react";
546
+ import { useEffect as useEffect4 } from "react";
547
+
548
+ // src/hooks/useAgentVoiceState.ts
549
+ import { useVoiceAssistant } from "@livekit/components-react";
550
+ function useAgentVoiceState() {
551
+ const { state } = useVoiceAssistant();
552
+ return {
553
+ state,
554
+ isSpeaking: state === "speaking",
555
+ isListening: state === "listening"
556
+ };
557
+ }
515
558
 
516
559
  // src/hooks/useLiveAgent.ts
517
560
  import { use } from "react";
@@ -524,6 +567,36 @@ function useLiveAgent() {
524
567
  return publicValue;
525
568
  }
526
569
 
570
+ // src/hooks/useMediaControls.ts
571
+ import { useLocalParticipant as useLocalParticipant2 } from "@livekit/components-react";
572
+ import { ScreenSharePresets } from "livekit-client";
573
+ import { useCallback as useCallback3 } from "react";
574
+ var SCREEN_SHARE_OPTIONS = {
575
+ video: { displaySurface: "browser" },
576
+ resolution: ScreenSharePresets.h720fps30.resolution,
577
+ contentHint: "detail"
578
+ };
579
+ function useMediaControls() {
580
+ const { localParticipant } = useLocalParticipant2();
581
+ const isMuted = !localParticipant.isMicrophoneEnabled;
582
+ const isScreenSharing = localParticipant.isScreenShareEnabled;
583
+ const toggleMute = useCallback3(async () => {
584
+ try {
585
+ await localParticipant.setMicrophoneEnabled(isMuted);
586
+ } catch (error) {
587
+ console.error("Failed to toggle microphone:", error);
588
+ }
589
+ }, [localParticipant, isMuted]);
590
+ const toggleScreenShare = useCallback3(async () => {
591
+ try {
592
+ await localParticipant.setScreenShareEnabled(!isScreenSharing, SCREEN_SHARE_OPTIONS);
593
+ } catch (error) {
594
+ console.error("Failed to toggle screen share:", error);
595
+ }
596
+ }, [localParticipant, isScreenSharing]);
597
+ return { isMuted, toggleMute, isScreenSharing, toggleScreenShare };
598
+ }
599
+
527
600
  // src/lib/utils.ts
528
601
  import { clsx } from "clsx";
529
602
  import { twMerge } from "tailwind-merge";
@@ -531,15 +604,102 @@ function cn(...inputs) {
531
604
  return twMerge(clsx(inputs));
532
605
  }
533
606
 
534
- // src/components/ui/tooltip.tsx
607
+ // src/components/Logo.tsx
608
+ import { useId } from "react";
535
609
  import { jsx, jsxs } from "react/jsx-runtime";
536
- function Tooltip({ label, children, position = "top" }) {
537
- return /* @__PURE__ */ jsxs("span", {
610
+ function Logo({ className }) {
611
+ const reactId = useId().replace(/:/g, "");
612
+ const clipId = `skippr-logo-clip-${reactId}`;
613
+ const gradientId = `skippr-logo-gradient-${reactId}`;
614
+ return /* @__PURE__ */ jsxs("svg", {
615
+ width: "1em",
616
+ height: "1em",
617
+ viewBox: "0 0 30 30",
618
+ fill: "none",
619
+ xmlns: "http://www.w3.org/2000/svg",
620
+ role: "img",
621
+ "aria-label": "Skippr",
622
+ className,
623
+ children: [
624
+ /* @__PURE__ */ jsxs("g", {
625
+ clipPath: `url(#${clipId})`,
626
+ children: [
627
+ /* @__PURE__ */ jsx("path", {
628
+ d: "M0 10C0 4.47715 4.47715 0 10 0H20C25.5228 0 30 4.47715 30 10V20C30 25.5228 25.5228 30 20 30H10C4.47715 30 0 25.5228 0 20V10Z",
629
+ fill: "#2D2D3F"
630
+ }),
631
+ /* @__PURE__ */ jsx("rect", {
632
+ x: "7.83325",
633
+ y: "14.9404",
634
+ width: "12.4083",
635
+ height: "4.72833",
636
+ rx: "2.36417",
637
+ transform: "rotate(-45 7.83325 14.9404)",
638
+ fill: "#52FFF9"
639
+ }),
640
+ /* @__PURE__ */ jsx("path", {
641
+ d: "M18.8975 12.5928C20.2728 12.5928 21.3877 13.647 21.3877 14.9474C21.3877 16.2479 20.2728 17.3021 18.8975 17.3021L11.4269 17.3021C10.0516 17.3021 8.93665 16.2479 8.93665 14.9474C8.93665 13.647 10.0516 12.5928 11.4269 12.5928L18.8975 12.5928Z",
642
+ fill: `url(#${gradientId})`
643
+ }),
644
+ /* @__PURE__ */ jsx("rect", {
645
+ x: "10.1665",
646
+ y: "20.4404",
647
+ width: "12.4083",
648
+ height: "4.72833",
649
+ rx: "2.36417",
650
+ transform: "rotate(-45 10.1665 20.4404)",
651
+ fill: "white"
652
+ })
653
+ ]
654
+ }),
655
+ /* @__PURE__ */ jsxs("defs", {
656
+ children: [
657
+ /* @__PURE__ */ jsxs("linearGradient", {
658
+ id: gradientId,
659
+ x1: "18.9237",
660
+ y1: "16.9997",
661
+ x2: "11.4129",
662
+ y2: "14.1904",
663
+ gradientUnits: "userSpaceOnUse",
664
+ children: [
665
+ /* @__PURE__ */ jsx("stop", {
666
+ offset: "0.473958",
667
+ stopColor: "white"
668
+ }),
669
+ /* @__PURE__ */ jsx("stop", {
670
+ offset: "1",
671
+ stopColor: "#52FFF9"
672
+ })
673
+ ]
674
+ }),
675
+ /* @__PURE__ */ jsx("clipPath", {
676
+ id: clipId,
677
+ children: /* @__PURE__ */ jsx("rect", {
678
+ width: "30",
679
+ height: "30",
680
+ fill: "white"
681
+ })
682
+ })
683
+ ]
684
+ })
685
+ ]
686
+ });
687
+ }
688
+
689
+ // src/components/ui/tooltip.tsx
690
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
691
+ var ALIGN_CLASSES = {
692
+ center: "skippr:left-1/2 skippr:-translate-x-1/2",
693
+ start: "skippr:left-0",
694
+ end: "skippr:right-0"
695
+ };
696
+ function Tooltip({ label, children, position = "top", align = "center" }) {
697
+ return /* @__PURE__ */ jsxs2("span", {
538
698
  className: "skippr:relative skippr:inline-flex skippr:group",
539
699
  children: [
540
700
  children,
541
- /* @__PURE__ */ jsx("span", {
542
- className: cn("skippr:pointer-events-none skippr:absolute skippr:left-1/2 skippr:-translate-x-1/2 skippr:z-10", "skippr:whitespace-nowrap skippr:rounded-md skippr:bg-foreground skippr:px-2 skippr:py-1", "skippr:text-[11px] skippr:text-background skippr:font-medium", "skippr:opacity-0 skippr:scale-95 skippr:transition-all skippr:duration-150", "skippr:group-hover:opacity-100 skippr:group-hover:scale-100", "skippr:group-focus-within:opacity-100 skippr:group-focus-within:scale-100", position === "top" && "skippr:bottom-full skippr:mb-1.5", position === "bottom" && "skippr:top-full skippr:mt-1.5"),
701
+ /* @__PURE__ */ jsx2("span", {
702
+ className: cn("skippr:pointer-events-none skippr:absolute skippr:z-10", "skippr:whitespace-nowrap skippr:rounded-md skippr:bg-foreground skippr:px-2 skippr:py-1", "skippr:text-[11px] skippr:text-background skippr:font-medium", "skippr:opacity-0 skippr:scale-95 skippr:transition-all skippr:duration-150", "skippr:group-hover:opacity-100 skippr:group-hover:scale-100", "skippr:group-focus-within:opacity-100 skippr:group-focus-within:scale-100", ALIGN_CLASSES[align], position === "top" && "skippr:bottom-full skippr:mb-1.5", position === "bottom" && "skippr:top-full skippr:mt-1.5"),
543
703
  "aria-hidden": "true",
544
704
  children: label
545
705
  })
@@ -548,141 +708,111 @@ function Tooltip({ label, children, position = "top" }) {
548
708
  }
549
709
 
550
710
  // src/components/MinimizedBubble.tsx
551
- import { jsx as jsx2, jsxs as jsxs2, Fragment } from "react/jsx-runtime";
552
- function AgentAvatar({ agentState }) {
711
+ import { jsx as jsx3, jsxs as jsxs3, Fragment } from "react/jsx-runtime";
712
+ var BUBBLE_BUTTON = "skippr:flex skippr:size-12 skippr:cursor-pointer skippr:items-center skippr:justify-center skippr:rounded-[14px] skippr:shadow-[0_4px_16px_rgba(45,43,61,0.45),0_2px_4px_rgba(0,0,0,0.1)] skippr:transition-all skippr:hover:-translate-y-0.5 skippr:active:translate-y-0";
713
+ function AgentBubbleContent({ agentState }) {
553
714
  if (agentState === "speaking") {
554
- return /* @__PURE__ */ jsxs2("div", {
555
- className: "skippr:flex skippr:items-end skippr:justify-center skippr:gap-[3px] skippr:h-5",
715
+ return /* @__PURE__ */ jsxs3("div", {
716
+ className: "skippr:flex skippr:h-5 skippr:items-center skippr:justify-center skippr:gap-[3px]",
556
717
  children: [
557
- /* @__PURE__ */ jsx2("span", {
558
- className: "skippr:w-[3px] skippr:h-full skippr:rounded-full skippr:bg-primary-foreground skippr:origin-bottom skippr:animate-skippr-bar-1"
718
+ /* @__PURE__ */ jsx3("span", {
719
+ className: "skippr:w-[3px] skippr:h-2 skippr:rounded-sm skippr:bg-white skippr:animate-[skippr-speak_1.1s_ease-in-out_infinite]"
559
720
  }),
560
- /* @__PURE__ */ jsx2("span", {
561
- className: "skippr:w-[3px] skippr:h-full skippr:rounded-full skippr:bg-primary-foreground skippr:origin-bottom skippr:animate-skippr-bar-2"
721
+ /* @__PURE__ */ jsx3("span", {
722
+ className: "skippr:w-[3px] skippr:h-3.5 skippr:rounded-sm skippr:bg-white skippr:animate-[skippr-speak_1.1s_ease-in-out_0.15s_infinite]"
562
723
  }),
563
- /* @__PURE__ */ jsx2("span", {
564
- className: "skippr:w-[3px] skippr:h-full skippr:rounded-full skippr:bg-primary-foreground skippr:origin-bottom skippr:animate-skippr-bar-3"
724
+ /* @__PURE__ */ jsx3("span", {
725
+ className: "skippr:w-[3px] skippr:h-2.5 skippr:rounded-sm skippr:bg-white skippr:animate-[skippr-speak_1.1s_ease-in-out_0.3s_infinite]"
565
726
  }),
566
- /* @__PURE__ */ jsx2("span", {
567
- className: "skippr:w-[3px] skippr:h-full skippr:rounded-full skippr:bg-primary-foreground skippr:origin-bottom skippr:animate-skippr-bar-4"
727
+ /* @__PURE__ */ jsx3("span", {
728
+ className: "skippr:w-[3px] skippr:h-4 skippr:rounded-sm skippr:bg-white skippr:animate-[skippr-speak_1.1s_ease-in-out_0.45s_infinite]"
729
+ }),
730
+ /* @__PURE__ */ jsx3("span", {
731
+ className: "skippr:w-[3px] skippr:h-1.5 skippr:rounded-sm skippr:bg-white skippr:animate-[skippr-speak_1.1s_ease-in-out_0.6s_infinite]"
568
732
  })
569
733
  ]
570
734
  });
571
735
  }
572
- if (agentState === "listening") {
573
- return /* @__PURE__ */ jsx2(Bot, {
574
- className: "skippr:relative skippr:z-10 skippr:size-5 skippr:animate-skippr-breathe"
575
- });
576
- }
577
736
  if (agentState === "thinking") {
578
- return /* @__PURE__ */ jsx2(Bot, {
579
- className: "skippr:relative skippr:z-10 skippr:size-5 skippr:animate-pulse"
737
+ return /* @__PURE__ */ jsxs3("div", {
738
+ className: "skippr:flex skippr:items-center skippr:justify-center skippr:gap-[4px]",
739
+ children: [
740
+ /* @__PURE__ */ jsx3("span", {
741
+ className: "skippr:size-1.5 skippr:rounded-full skippr:bg-white skippr:animate-bounce skippr:[animation-delay:0ms]"
742
+ }),
743
+ /* @__PURE__ */ jsx3("span", {
744
+ className: "skippr:size-1.5 skippr:rounded-full skippr:bg-white skippr:animate-bounce skippr:[animation-delay:150ms]"
745
+ }),
746
+ /* @__PURE__ */ jsx3("span", {
747
+ className: "skippr:size-1.5 skippr:rounded-full skippr:bg-white skippr:animate-bounce skippr:[animation-delay:300ms]"
748
+ })
749
+ ]
580
750
  });
581
751
  }
582
- return /* @__PURE__ */ jsx2(Bot, {
583
- className: "skippr:relative skippr:z-10 skippr:size-5"
752
+ return /* @__PURE__ */ jsx3(Logo, {
753
+ className: "skippr:size-7"
584
754
  });
585
755
  }
586
756
  function ConnectedBubbleContent() {
587
- const { expandPanel, disconnect } = useLiveAgent();
588
- const { state: agentState } = useVoiceAssistant();
589
- const { localParticipant } = useLocalParticipant();
590
- const isMuted = !localParticipant.isMicrophoneEnabled;
591
- const isScreenSharing = localParticipant.isScreenShareEnabled;
592
- const toggleMute = useCallback3(async () => {
593
- try {
594
- await localParticipant.setMicrophoneEnabled(isMuted);
595
- } catch (e) {
596
- console.error("Failed to toggle microphone:", e);
597
- }
598
- }, [localParticipant, isMuted]);
599
- const toggleScreenShare = useCallback3(async () => {
600
- try {
601
- await localParticipant.setScreenShareEnabled(!isScreenSharing, {
602
- video: { displaySurface: "browser" },
603
- resolution: ScreenSharePresets.h720fps30.resolution,
604
- contentHint: "detail"
605
- });
606
- } catch (e) {
607
- console.error("Failed to toggle screen share:", e);
608
- }
609
- }, [localParticipant, isScreenSharing]);
610
- const isSpeaking = agentState === "speaking";
611
- const isListening = agentState === "listening";
612
- return /* @__PURE__ */ jsxs2(Fragment, {
757
+ const { expandPanel, disconnect, position } = useLiveAgent();
758
+ const { state: agentState } = useAgentVoiceState();
759
+ const { isMuted, toggleMute, isScreenSharing, toggleScreenShare } = useMediaControls();
760
+ const tooltipAlign = position === "right" ? "end" : "start";
761
+ return /* @__PURE__ */ jsxs3(Fragment, {
613
762
  children: [
614
- /* @__PURE__ */ jsx2(Tooltip, {
615
- label: "Open chat & transcript",
616
- children: /* @__PURE__ */ jsxs2("button", {
763
+ /* @__PURE__ */ jsx3(Tooltip, {
764
+ label: isMuted ? "Unmute" : "Mute",
765
+ align: tooltipAlign,
766
+ children: /* @__PURE__ */ jsx3("button", {
617
767
  type: "button",
618
- onClick: expandPanel,
619
- className: cn("skippr:relative skippr:size-11 skippr:rounded-full", "skippr:bg-primary skippr:text-primary-foreground", "skippr:flex skippr:items-center skippr:justify-center", "skippr:cursor-pointer skippr:transition-all skippr:duration-300", "skippr:hover:brightness-110", isListening && "skippr:animate-skippr-pulse-ring"),
620
- "aria-label": "Open chat & transcript",
621
- children: [
622
- isSpeaking && /* @__PURE__ */ jsxs2(Fragment, {
623
- children: [
624
- /* @__PURE__ */ jsx2("span", {
625
- className: "skippr:absolute skippr:inset-0 skippr:rounded-full skippr:bg-primary skippr:animate-skippr-speak-ripple"
626
- }),
627
- /* @__PURE__ */ jsx2("span", {
628
- className: "skippr:absolute skippr:inset-0 skippr:rounded-full skippr:bg-primary skippr:animate-skippr-speak-ripple-delayed"
629
- })
630
- ]
631
- }),
632
- /* @__PURE__ */ jsx2(AgentAvatar, {
633
- agentState
634
- })
635
- ]
768
+ onClick: toggleMute,
769
+ "aria-label": isMuted ? "Unmute" : "Mute",
770
+ className: cn(BUBBLE_BUTTON, isMuted ? "skippr:bg-destructive skippr:text-destructive-foreground skippr:hover:bg-destructive/90" : "skippr:bg-white skippr:text-foreground skippr:hover:bg-muted"),
771
+ children: isMuted ? /* @__PURE__ */ jsx3(MicOff, {
772
+ className: "skippr:size-5"
773
+ }) : /* @__PURE__ */ jsx3(Mic, {
774
+ className: "skippr:size-5"
775
+ })
636
776
  })
637
777
  }),
638
- /* @__PURE__ */ jsx2("div", {
639
- className: "skippr:mx-1 skippr:h-6 skippr:w-px skippr:bg-border"
640
- }),
641
- /* @__PURE__ */ jsxs2("div", {
642
- className: "skippr:flex skippr:items-center skippr:gap-1",
643
- children: [
644
- /* @__PURE__ */ jsx2(Tooltip, {
645
- label: isMuted ? "Unmute" : "Mute",
646
- children: /* @__PURE__ */ jsx2("button", {
647
- type: "button",
648
- onClick: toggleMute,
649
- className: cn("skippr:size-9 skippr:rounded-full skippr:flex skippr:items-center skippr:justify-center", "skippr:transition-colors skippr:cursor-pointer", isMuted ? "skippr:bg-destructive skippr:text-white" : "skippr:text-foreground skippr:hover:bg-accent"),
650
- "aria-label": isMuted ? "Unmute" : "Mute",
651
- children: isMuted ? /* @__PURE__ */ jsx2(MicOff, {
652
- className: "skippr:size-4"
653
- }) : /* @__PURE__ */ jsx2(Mic, {
654
- className: "skippr:size-4"
655
- })
656
- })
657
- }),
658
- /* @__PURE__ */ jsx2(Tooltip, {
659
- label: isScreenSharing ? "Stop sharing" : "Share screen",
660
- children: /* @__PURE__ */ jsx2("button", {
661
- type: "button",
662
- onClick: toggleScreenShare,
663
- className: cn("skippr:size-9 skippr:rounded-full skippr:flex skippr:items-center skippr:justify-center", "skippr:transition-colors skippr:cursor-pointer", isScreenSharing ? "skippr:text-foreground skippr:hover:bg-accent" : "skippr:bg-destructive skippr:text-white"),
664
- "aria-label": isScreenSharing ? "Stop sharing" : "Share screen",
665
- children: isScreenSharing ? /* @__PURE__ */ jsx2(MonitorOff, {
666
- className: "skippr:size-4"
667
- }) : /* @__PURE__ */ jsx2(Monitor, {
668
- className: "skippr:size-4"
669
- })
670
- })
778
+ /* @__PURE__ */ jsx3(Tooltip, {
779
+ label: isScreenSharing ? "Stop sharing" : "Share screen",
780
+ align: tooltipAlign,
781
+ children: /* @__PURE__ */ jsx3("button", {
782
+ type: "button",
783
+ onClick: toggleScreenShare,
784
+ "aria-label": isScreenSharing ? "Stop sharing screen" : "Share screen",
785
+ className: cn(BUBBLE_BUTTON, isScreenSharing ? "skippr:bg-bubble skippr:text-white skippr:hover:brightness-110" : "skippr:bg-white skippr:text-foreground skippr:hover:bg-muted"),
786
+ children: isScreenSharing ? /* @__PURE__ */ jsx3(MonitorOff, {
787
+ className: "skippr:size-5"
788
+ }) : /* @__PURE__ */ jsx3(Monitor, {
789
+ className: "skippr:size-5"
671
790
  })
672
- ]
673
- }),
674
- /* @__PURE__ */ jsx2("div", {
675
- className: "skippr:mx-1 skippr:h-6 skippr:w-px skippr:bg-border"
791
+ })
676
792
  }),
677
- /* @__PURE__ */ jsx2(Tooltip, {
793
+ /* @__PURE__ */ jsx3(Tooltip, {
678
794
  label: "End session",
679
- children: /* @__PURE__ */ jsx2("button", {
795
+ align: tooltipAlign,
796
+ children: /* @__PURE__ */ jsx3("button", {
680
797
  type: "button",
681
798
  onClick: () => disconnect(),
682
- className: "skippr:size-9 skippr:rounded-full skippr:flex skippr:items-center skippr:justify-center skippr:bg-destructive skippr:text-white skippr:cursor-pointer skippr:transition-colors skippr:hover:bg-destructive/90",
683
- "aria-label": "Hang up",
684
- children: /* @__PURE__ */ jsx2(PhoneOff, {
685
- className: "skippr:size-4"
799
+ "aria-label": "End session",
800
+ className: cn(BUBBLE_BUTTON, "skippr:bg-destructive skippr:text-destructive-foreground skippr:hover:bg-destructive/90"),
801
+ children: /* @__PURE__ */ jsx3(PhoneOff, {
802
+ className: "skippr:size-5"
803
+ })
804
+ })
805
+ }),
806
+ /* @__PURE__ */ jsx3(Tooltip, {
807
+ label: "Open chat & transcript",
808
+ align: tooltipAlign,
809
+ children: /* @__PURE__ */ jsx3("button", {
810
+ type: "button",
811
+ onClick: expandPanel,
812
+ "aria-label": "Open chat & transcript",
813
+ className: cn(BUBBLE_BUTTON, "skippr:bg-bubble skippr:hover:brightness-110"),
814
+ children: /* @__PURE__ */ jsx3(AgentBubbleContent, {
815
+ agentState
686
816
  })
687
817
  })
688
818
  })
@@ -690,123 +820,25 @@ function ConnectedBubbleContent() {
690
820
  });
691
821
  }
692
822
  function IdleBubbleContent() {
693
- const { expandPanel, startSession, isStarting, isAuthenticated } = useLiveAgent();
694
- const [showCapabilities, setShowCapabilities] = useState3(true);
695
- const fadeTimer = useRef(null);
696
- useEffect3(() => {
697
- fadeTimer.current = setTimeout(() => setShowCapabilities(false), 5000);
698
- return () => {
699
- if (fadeTimer.current)
700
- clearTimeout(fadeTimer.current);
701
- };
702
- }, []);
703
- const handleStart = useCallback3(() => {
704
- if (!isAuthenticated) {
705
- expandPanel();
706
- return;
707
- }
708
- startSession();
709
- }, [isAuthenticated, expandPanel, startSession]);
710
- return /* @__PURE__ */ jsxs2("div", {
711
- className: "skippr:flex skippr:items-center skippr:gap-0",
712
- onMouseEnter: () => !isStarting && setShowCapabilities(true),
713
- onMouseLeave: () => !isStarting && setShowCapabilities(false),
714
- children: [
715
- /* @__PURE__ */ jsx2("button", {
716
- type: "button",
717
- onClick: handleStart,
718
- disabled: isStarting,
719
- className: cn("skippr:relative skippr:size-11 skippr:rounded-full", "skippr:bg-primary skippr:text-primary-foreground", "skippr:flex skippr:items-center skippr:justify-center", "skippr:cursor-pointer skippr:transition-all skippr:duration-300", "skippr:hover:brightness-110", isStarting && "skippr:animate-skippr-pulse-ring skippr:cursor-not-allowed"),
720
- "aria-label": "AI Agent",
721
- children: /* @__PURE__ */ jsx2(Bot, {
722
- className: "skippr:relative skippr:z-10 skippr:size-5"
823
+ const { expandPanel, position } = useLiveAgent();
824
+ const tooltipAlign = position === "right" ? "end" : "start";
825
+ return /* @__PURE__ */ jsx3(Tooltip, {
826
+ label: "Open Skippr assistant",
827
+ align: tooltipAlign,
828
+ children: /* @__PURE__ */ jsxs3("button", {
829
+ type: "button",
830
+ onClick: expandPanel,
831
+ "aria-label": "Skippr assistant",
832
+ className: cn(BUBBLE_BUTTON, "skippr:relative skippr:bg-bubble skippr:hover:brightness-110"),
833
+ children: [
834
+ /* @__PURE__ */ jsx3(Logo, {
835
+ className: "skippr:relative skippr:z-10 skippr:size-7"
836
+ }),
837
+ /* @__PURE__ */ jsx3("span", {
838
+ className: "skippr:absolute skippr:-inset-[3px] skippr:animate-pulse skippr:rounded-[17px] skippr:border-2 skippr:border-bubble/50"
723
839
  })
724
- }),
725
- isStarting ? /* @__PURE__ */ jsxs2("div", {
726
- className: "skippr:flex skippr:items-center skippr:gap-2 skippr:ml-2 skippr:mr-1 skippr:whitespace-nowrap skippr:text-xs skippr:text-muted-foreground",
727
- children: [
728
- /* @__PURE__ */ jsx2("span", {
729
- className: "skippr:inline-block skippr:size-1.5 skippr:rounded-full skippr:bg-primary skippr:animate-pulse"
730
- }),
731
- "Setting up voice & screen..."
732
- ]
733
- }) : /* @__PURE__ */ jsxs2(Fragment, {
734
- children: [
735
- /* @__PURE__ */ jsx2("div", {
736
- className: cn("skippr:overflow-hidden skippr:transition-all skippr:duration-500 skippr:ease-in-out", showCapabilities ? "skippr:max-w-64 skippr:opacity-100 skippr:ml-2 skippr:mr-1" : "skippr:max-w-0 skippr:opacity-0 skippr:ml-0 skippr:mr-0"),
737
- "aria-hidden": !showCapabilities,
738
- children: /* @__PURE__ */ jsxs2("button", {
739
- type: "button",
740
- onClick: handleStart,
741
- tabIndex: showCapabilities ? 0 : -1,
742
- className: "skippr:flex skippr:items-center skippr:gap-3 skippr:whitespace-nowrap skippr:text-xs skippr:text-muted-foreground skippr:cursor-pointer skippr:hover:text-foreground skippr:transition-colors",
743
- children: [
744
- /* @__PURE__ */ jsxs2("span", {
745
- className: "skippr:flex skippr:items-center skippr:gap-1",
746
- children: [
747
- /* @__PURE__ */ jsx2(Mic, {
748
- className: "skippr:size-3"
749
- }),
750
- "Talk"
751
- ]
752
- }),
753
- /* @__PURE__ */ jsx2("span", {
754
- className: "skippr:text-border",
755
- children: "·"
756
- }),
757
- /* @__PURE__ */ jsxs2("span", {
758
- className: "skippr:flex skippr:items-center skippr:gap-1",
759
- children: [
760
- /* @__PURE__ */ jsx2(Monitor, {
761
- className: "skippr:size-3"
762
- }),
763
- "Screen"
764
- ]
765
- }),
766
- /* @__PURE__ */ jsx2("span", {
767
- className: "skippr:text-border",
768
- children: "·"
769
- }),
770
- /* @__PURE__ */ jsxs2("span", {
771
- className: "skippr:flex skippr:items-center skippr:gap-1",
772
- children: [
773
- /* @__PURE__ */ jsx2(MessageSquare, {
774
- className: "skippr:size-3"
775
- }),
776
- "Chat"
777
- ]
778
- })
779
- ]
780
- })
781
- }),
782
- /* @__PURE__ */ jsx2("div", {
783
- className: cn("skippr:overflow-hidden skippr:transition-all skippr:duration-500 skippr:ease-in-out", showCapabilities ? "skippr:max-w-0 skippr:opacity-0" : "skippr:max-w-12 skippr:opacity-100"),
784
- "aria-hidden": showCapabilities,
785
- children: /* @__PURE__ */ jsxs2("div", {
786
- className: "skippr:flex skippr:items-center",
787
- children: [
788
- /* @__PURE__ */ jsx2("div", {
789
- className: "skippr:mx-1 skippr:h-6 skippr:w-px skippr:bg-border"
790
- }),
791
- /* @__PURE__ */ jsx2(Tooltip, {
792
- label: "Start session",
793
- children: /* @__PURE__ */ jsx2("button", {
794
- type: "button",
795
- onClick: handleStart,
796
- tabIndex: showCapabilities ? -1 : 0,
797
- className: "skippr:size-9 skippr:rounded-full skippr:flex skippr:items-center skippr:justify-center skippr:transition-colors skippr:cursor-pointer skippr:bg-primary skippr:text-primary-foreground skippr:hover:brightness-110",
798
- "aria-label": "Start session",
799
- children: /* @__PURE__ */ jsx2(Monitor, {
800
- className: "skippr:size-4"
801
- })
802
- })
803
- })
804
- ]
805
- })
806
- })
807
- ]
808
- })
809
- ]
840
+ ]
841
+ })
810
842
  });
811
843
  }
812
844
  function WelcomeBubble({
@@ -814,18 +846,18 @@ function WelcomeBubble({
814
846
  position,
815
847
  onDismiss
816
848
  }) {
817
- useEffect3(() => {
849
+ useEffect4(() => {
818
850
  const timer = setTimeout(onDismiss, 5000);
819
851
  return () => clearTimeout(timer);
820
852
  }, [onDismiss]);
821
- return /* @__PURE__ */ jsxs2("button", {
853
+ return /* @__PURE__ */ jsxs3("button", {
822
854
  type: "button",
823
855
  className: cn("skippr:absolute skippr:bottom-full skippr:mb-3", "skippr:max-w-64 skippr:rounded-xl skippr:bg-card skippr:shadow-lg", "skippr:border skippr:border-border skippr:px-4 skippr:py-3", "skippr:text-sm skippr:text-foreground skippr:leading-relaxed skippr:text-left", "skippr:animate-[skippr-fade-in_0.3s_ease-out]", "skippr:cursor-pointer", position === "right" ? "skippr:right-0" : "skippr:left-0"),
824
856
  onClick: onDismiss,
825
857
  "aria-label": "Dismiss",
826
858
  children: [
827
859
  message,
828
- /* @__PURE__ */ jsx2("span", {
860
+ /* @__PURE__ */ jsx3("span", {
829
861
  className: cn("skippr:absolute skippr:top-full skippr:size-2.5", "skippr:border-l skippr:border-t skippr:border-border skippr:bg-card", "skippr:rotate-[225deg]", position === "right" ? "skippr:right-5" : "skippr:left-5", "skippr:-mt-[5px]")
830
862
  })
831
863
  ]
@@ -836,31 +868,63 @@ function MinimizedBubble({
836
868
  welcomeDismissed,
837
869
  onDismissWelcome
838
870
  }) {
839
- const { isConnected, position } = useLiveAgent();
840
- return /* @__PURE__ */ jsxs2("div", {
841
- className: cn("skippr:fixed skippr:bottom-6 skippr:z-[9999]", "skippr:flex skippr:items-center skippr:gap-0", "skippr:rounded-full skippr:bg-card skippr:shadow-2xl", "skippr:border skippr:border-border", "skippr:p-1.5", "skippr:transition-all skippr:duration-300 skippr:ease-in-out", position === "right" ? "skippr:right-6" : "skippr:left-6"),
871
+ const { isConnected, isStarting, position } = useLiveAgent();
872
+ const inSession = isConnected || isStarting;
873
+ return /* @__PURE__ */ jsxs3("div", {
874
+ 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"),
842
875
  children: [
843
- welcomeMessage && !isConnected && !welcomeDismissed && /* @__PURE__ */ jsx2(WelcomeBubble, {
876
+ welcomeMessage && !inSession && !welcomeDismissed && /* @__PURE__ */ jsx3(WelcomeBubble, {
844
877
  message: welcomeMessage,
845
878
  position,
846
879
  onDismiss: onDismissWelcome
847
880
  }),
848
- isConnected ? /* @__PURE__ */ jsx2(ConnectedBubbleContent, {}) : /* @__PURE__ */ jsx2(IdleBubbleContent, {})
881
+ inSession ? /* @__PURE__ */ jsx3(ConnectedBubbleContent, {}) : /* @__PURE__ */ jsx3(IdleBubbleContent, {})
849
882
  ]
850
883
  });
851
884
  }
852
885
 
886
+ // src/components/ObservingBanner.tsx
887
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
888
+ function ObservingBanner() {
889
+ const { isConnected } = useLiveAgent();
890
+ const { isScreenSharing } = useMediaControls();
891
+ if (!isConnected || !isScreenSharing)
892
+ return null;
893
+ return /* @__PURE__ */ jsx4("div", {
894
+ className: "skippr:fixed skippr:top-0 skippr:left-0 skippr:right-0 skippr:z-[2147483647] skippr:flex skippr:justify-center skippr:pointer-events-none",
895
+ children: /* @__PURE__ */ jsxs4("div", {
896
+ className: "skippr:pointer-events-auto skippr:flex skippr:items-center skippr:gap-2 skippr:bg-indigo-500/95 skippr:backdrop-blur-sm skippr:text-white skippr:text-xs skippr:font-medium skippr:px-4 skippr:py-1.5 skippr:rounded-b-lg skippr:shadow-lg",
897
+ children: [
898
+ /* @__PURE__ */ jsxs4("span", {
899
+ className: "skippr:relative skippr:flex skippr:size-1.5",
900
+ children: [
901
+ /* @__PURE__ */ jsx4("span", {
902
+ className: "skippr:absolute skippr:inline-flex skippr:size-full skippr:animate-ping skippr:rounded-full skippr:bg-emerald-400 skippr:opacity-75"
903
+ }),
904
+ /* @__PURE__ */ jsx4("span", {
905
+ className: "skippr:relative skippr:inline-flex skippr:size-1.5 skippr:rounded-full skippr:bg-emerald-400"
906
+ })
907
+ ]
908
+ }),
909
+ /* @__PURE__ */ jsx4(Eye, {
910
+ className: "skippr:size-3.5"
911
+ }),
912
+ /* @__PURE__ */ jsx4("span", {
913
+ children: "Skippr is observing this page"
914
+ })
915
+ ]
916
+ })
917
+ });
918
+ }
919
+
853
920
  // src/components/Sidebar.tsx
854
- import { useConnectionState } from "@livekit/components-react";
855
- import { ConnectionState } from "livekit-client";
856
- import { useEffect as useEffect8, useState as useState8 } from "react";
921
+ import { useEffect as useEffect11 } from "react";
857
922
 
858
923
  // src/hooks/useCombinedMessages.ts
859
- import { useVoiceAssistant as useVoiceAssistant2 } from "@livekit/components-react";
860
924
  import { useMemo as useMemo4 } from "react";
861
925
 
862
926
  // src/hooks/useChatMessages.ts
863
- import { useChat, useLocalParticipant as useLocalParticipant2 } from "@livekit/components-react";
927
+ import { useChat, useLocalParticipant as useLocalParticipant3 } from "@livekit/components-react";
864
928
  import { useMemo as useMemo2 } from "react";
865
929
 
866
930
  // src/lib/filterSystemMessages.ts
@@ -872,7 +936,7 @@ function filterSystemMessages(messages) {
872
936
  // src/hooks/useChatMessages.ts
873
937
  function useChatMessages() {
874
938
  const { chatMessages: rawMessages, send, isSending } = useChat();
875
- const { localParticipant } = useLocalParticipant2();
939
+ const { localParticipant } = useLocalParticipant3();
876
940
  const localIdentity = localParticipant.identity;
877
941
  const chatMessages = useMemo2(() => {
878
942
  const sortedMessages = rawMessages.map((msg) => ({
@@ -888,11 +952,11 @@ function useChatMessages() {
888
952
  }
889
953
 
890
954
  // src/hooks/useStreamingTranscript.ts
891
- import { useLocalParticipant as useLocalParticipant3, useTranscriptions } from "@livekit/components-react";
955
+ import { useLocalParticipant as useLocalParticipant4, useTranscriptions } from "@livekit/components-react";
892
956
  import { useMemo as useMemo3 } from "react";
893
957
  function useStreamingTranscript() {
894
958
  const transcriptions = useTranscriptions();
895
- const { localParticipant } = useLocalParticipant3();
959
+ const { localParticipant } = useLocalParticipant4();
896
960
  const localIdentity = localParticipant.identity;
897
961
  const transcriptMessages = useMemo3(() => filterSystemMessages(transcriptions.filter((stream) => stream.text.trim().length > 0).map((stream) => ({
898
962
  id: stream.streamInfo.id,
@@ -924,7 +988,7 @@ function mergeChatsIntoTranscripts(transcripts, chats) {
924
988
  function useCombinedMessages() {
925
989
  const { transcriptMessages } = useStreamingTranscript();
926
990
  const { chatMessages, sendChatMessage, isSendingChat } = useChatMessages();
927
- const { state: agentState } = useVoiceAssistant2();
991
+ const { state: agentState } = useAgentVoiceState();
928
992
  const allMessages = useMemo4(() => {
929
993
  if (chatMessages.length === 0)
930
994
  return transcriptMessages;
@@ -940,11 +1004,11 @@ import { useCallback as useCallback4 } from "react";
940
1004
 
941
1005
  // src/hooks/useAgentState.ts
942
1006
  import { useRemoteParticipants } from "@livekit/components-react";
943
- import { useEffect as useEffect4, useState as useState4 } from "react";
1007
+ import { useEffect as useEffect5, useState as useState3 } from "react";
944
1008
  function useAgentState(attributeKey, parse, initial) {
945
- const [value, setValue] = useState4(initial);
1009
+ const [value, setValue] = useState3(initial);
946
1010
  const remoteParticipants = useRemoteParticipants();
947
- useEffect4(() => {
1011
+ useEffect5(() => {
948
1012
  const agentParticipant = remoteParticipants.find((p) => p.attributes?.[attributeKey]);
949
1013
  if (agentParticipant) {
950
1014
  const attr = agentParticipant.attributes?.[attributeKey];
@@ -995,30 +1059,152 @@ function usePhaseUpdates() {
995
1059
  return { phases };
996
1060
  }
997
1061
 
998
- // src/hooks/useQuestionUpdates.ts
999
- import { useCallback as useCallback5 } from "react";
1000
- function parseQuestions(json) {
1001
- try {
1002
- const data = JSON.parse(json);
1003
- if (data.type === "question_update" && Array.isArray(data.questions)) {
1004
- return data.questions;
1005
- }
1006
- } catch {}
1007
- return null;
1062
+ // src/hooks/useSessionRemaining.ts
1063
+ import { useEffect as useEffect6, useRef as useRef2, useState as useState4 } from "react";
1064
+
1065
+ // src/lib/format.ts
1066
+ function formatTime(seconds) {
1067
+ const m = Math.floor(seconds / 60).toString().padStart(2, "0");
1068
+ const s = (seconds % 60).toString().padStart(2, "0");
1069
+ return `${m}:${s}`;
1070
+ }
1071
+ function parseNumber(s) {
1072
+ const n = Number(s);
1073
+ return n > 0 ? n : null;
1008
1074
  }
1009
- function useQuestionUpdates() {
1010
- const parse = useCallback5(parseQuestions, []);
1011
- const questions = useAgentState("questions", parse, []);
1012
- return { questions };
1075
+
1076
+ // src/hooks/useSessionRemaining.ts
1077
+ function useSessionRemaining() {
1078
+ const maxCallDuration = useAgentState("maxCallDuration", parseNumber, null);
1079
+ const endTimeRef = useRef2(null);
1080
+ const [remaining, setRemaining] = useState4(null);
1081
+ useEffect6(() => {
1082
+ if (maxCallDuration === null || endTimeRef.current !== null)
1083
+ return;
1084
+ const endTime = Date.now() + maxCallDuration * 1000;
1085
+ endTimeRef.current = endTime;
1086
+ const tick = () => {
1087
+ const secs = Math.ceil((endTime - Date.now()) / 1000);
1088
+ setRemaining(Math.max(secs, 0));
1089
+ };
1090
+ tick();
1091
+ const id = setInterval(tick, 1000);
1092
+ return () => clearInterval(id);
1093
+ }, [maxCallDuration]);
1094
+ return remaining;
1013
1095
  }
1014
1096
 
1015
1097
  // src/lib/constants.ts
1016
- var SIDEBAR_WIDTH = 480;
1098
+ var SIDEBAR_WIDTH = 360;
1017
1099
 
1018
- // src/components/ui/button.tsx
1019
- import { forwardRef as forwardRef3 } from "react";
1020
- import { jsx as jsx3 } from "react/jsx-runtime";
1021
- var variantClasses = {
1100
+ // src/hooks/useElapsedSeconds.ts
1101
+ import { useEffect as useEffect7, useState as useState5 } from "react";
1102
+ function useElapsedSeconds(isRunning) {
1103
+ const [elapsed, setElapsed] = useState5(0);
1104
+ useEffect7(() => {
1105
+ if (!isRunning) {
1106
+ setElapsed(0);
1107
+ return;
1108
+ }
1109
+ const startedAt = Date.now();
1110
+ setElapsed(0);
1111
+ const id = setInterval(() => {
1112
+ setElapsed(Math.floor((Date.now() - startedAt) / 1000));
1113
+ }, 1000);
1114
+ return () => clearInterval(id);
1115
+ }, [isRunning]);
1116
+ return elapsed;
1117
+ }
1118
+
1119
+ // src/components/ChatHeader.tsx
1120
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1121
+ function ChatHeader() {
1122
+ const { isConnected, minimizePanel, minimizable } = useLiveAgent();
1123
+ const elapsed = useElapsedSeconds(isConnected);
1124
+ return /* @__PURE__ */ jsxs5("header", {
1125
+ className: "skippr:sticky skippr:top-0 skippr:z-10 skippr:flex skippr:shrink-0 skippr:items-center skippr:justify-between skippr:border-b skippr:border-border skippr:bg-primary skippr:px-4 skippr:py-3",
1126
+ children: [
1127
+ /* @__PURE__ */ jsx5("p", {
1128
+ className: "skippr:text-sm skippr:font-semibold skippr:text-primary-foreground",
1129
+ children: "Skippr"
1130
+ }),
1131
+ /* @__PURE__ */ jsxs5("div", {
1132
+ className: "skippr:flex skippr:items-center skippr:gap-2",
1133
+ children: [
1134
+ isConnected && /* @__PURE__ */ jsxs5("div", {
1135
+ className: "skippr:flex skippr:items-center skippr:gap-1.5 skippr:rounded-full skippr:bg-primary-foreground/20 skippr:px-2.5 skippr:py-1",
1136
+ children: [
1137
+ /* @__PURE__ */ jsxs5("span", {
1138
+ className: "skippr:relative skippr:flex skippr:size-1.5",
1139
+ children: [
1140
+ /* @__PURE__ */ jsx5("span", {
1141
+ className: "skippr:absolute skippr:inline-flex skippr:h-full skippr:w-full skippr:animate-ping skippr:rounded-full skippr:bg-red-400 skippr:opacity-75"
1142
+ }),
1143
+ /* @__PURE__ */ jsx5("span", {
1144
+ className: "skippr:relative skippr:inline-flex skippr:size-1.5 skippr:rounded-full skippr:bg-red-400"
1145
+ })
1146
+ ]
1147
+ }),
1148
+ /* @__PURE__ */ jsx5("span", {
1149
+ className: "skippr:text-[10px] skippr:font-medium skippr:text-primary-foreground",
1150
+ children: "REC"
1151
+ }),
1152
+ /* @__PURE__ */ jsx5("span", {
1153
+ className: "skippr:text-[10px] skippr:font-mono skippr:text-primary-foreground",
1154
+ children: formatTime(elapsed)
1155
+ })
1156
+ ]
1157
+ }),
1158
+ minimizable && /* @__PURE__ */ jsx5("button", {
1159
+ type: "button",
1160
+ onClick: minimizePanel,
1161
+ "aria-label": "Minimize",
1162
+ className: "skippr:flex skippr:size-6 skippr:cursor-pointer skippr:items-center skippr:justify-center skippr:rounded-md skippr:text-primary-foreground/70 skippr:transition-colors skippr:hover:bg-primary-foreground/10 skippr:hover:text-primary-foreground",
1163
+ children: /* @__PURE__ */ jsx5(Minimize2, {
1164
+ className: "skippr:size-3.5"
1165
+ })
1166
+ })
1167
+ ]
1168
+ })
1169
+ ]
1170
+ });
1171
+ }
1172
+
1173
+ // src/components/LoadingDots.tsx
1174
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
1175
+ function LoadingDots({ label }) {
1176
+ return /* @__PURE__ */ jsxs6("div", {
1177
+ className: "skippr:flex skippr:items-center skippr:gap-2 skippr:py-4",
1178
+ children: [
1179
+ /* @__PURE__ */ jsxs6("div", {
1180
+ className: "skippr:flex skippr:gap-1",
1181
+ children: [
1182
+ /* @__PURE__ */ jsx6("span", {
1183
+ className: "skippr:size-1.5 skippr:rounded-full skippr:bg-muted-foreground/40 skippr:animate-bounce skippr:[animation-delay:0ms]"
1184
+ }),
1185
+ /* @__PURE__ */ jsx6("span", {
1186
+ className: "skippr:size-1.5 skippr:rounded-full skippr:bg-muted-foreground/40 skippr:animate-bounce skippr:[animation-delay:150ms]"
1187
+ }),
1188
+ /* @__PURE__ */ jsx6("span", {
1189
+ className: "skippr:size-1.5 skippr:rounded-full skippr:bg-muted-foreground/40 skippr:animate-bounce skippr:[animation-delay:300ms]"
1190
+ })
1191
+ ]
1192
+ }),
1193
+ /* @__PURE__ */ jsx6("p", {
1194
+ className: "skippr:text-xs skippr:text-muted-foreground",
1195
+ children: label
1196
+ })
1197
+ ]
1198
+ });
1199
+ }
1200
+
1201
+ // src/components/LoginFlow.tsx
1202
+ import { useCallback as useCallback5, useEffect as useEffect8, useRef as useRef3, useState as useState6 } from "react";
1203
+
1204
+ // src/components/ui/button.tsx
1205
+ import { forwardRef as forwardRef3 } from "react";
1206
+ import { jsx as jsx7 } from "react/jsx-runtime";
1207
+ var variantClasses = {
1022
1208
  default: "skippr:bg-primary skippr:text-primary-foreground skippr:hover:bg-primary/90",
1023
1209
  destructive: "skippr:bg-destructive skippr:text-white skippr:hover:bg-destructive/90",
1024
1210
  outline: "skippr:border skippr:border-input skippr:bg-background skippr:shadow-xs skippr:hover:bg-accent skippr:hover:text-accent-foreground",
@@ -1036,7 +1222,7 @@ var sizeClasses = {
1036
1222
  "icon-lg": "skippr:size-10"
1037
1223
  };
1038
1224
  var Button = forwardRef3(({ className, variant = "default", size = "default", ...props }, ref) => {
1039
- return /* @__PURE__ */ jsx3("button", {
1225
+ return /* @__PURE__ */ jsx7("button", {
1040
1226
  className: cn("skippr:inline-flex skippr:items-center skippr:justify-center skippr:gap-2 skippr:whitespace-nowrap skippr:rounded-md skippr:text-sm skippr:font-medium skippr:ring-offset-background skippr:transition-all skippr:cursor-pointer skippr:focus-visible:outline-none skippr:focus-visible:ring-2 skippr:focus-visible:ring-ring skippr:focus-visible:ring-offset-2 skippr:disabled:pointer-events-none skippr:disabled:opacity-50 skippr:shrink-0 skippr:[&_svg]:pointer-events-none skippr:[&_svg:not([class*='size-'])]:size-4 skippr:[&_svg]:shrink-0", variantClasses[variant], sizeClasses[size], className),
1041
1227
  ref,
1042
1228
  ...props
@@ -1044,79 +1230,29 @@ var Button = forwardRef3(({ className, variant = "default", size = "default", ..
1044
1230
  });
1045
1231
  Button.displayName = "Button";
1046
1232
 
1047
- // src/components/ChatHeader.tsx
1048
- import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
1049
- function ChatHeader({ onOpenSettings }) {
1050
- const { closePanel, minimizePanel, minimizable } = useLiveAgent();
1051
- return /* @__PURE__ */ jsxs3("div", {
1052
- className: "skippr:flex skippr:items-center skippr:gap-3 skippr:bg-primary skippr:px-4 skippr:py-3 skippr:text-primary-foreground",
1053
- children: [
1054
- /* @__PURE__ */ jsx4("div", {
1055
- className: "skippr:flex skippr:size-6 skippr:items-center skippr:justify-center skippr:rounded-full skippr:bg-primary-foreground/20",
1056
- children: /* @__PURE__ */ jsx4(Bot, {
1057
- className: "skippr:size-3.5 skippr:text-primary-foreground"
1058
- })
1059
- }),
1060
- /* @__PURE__ */ jsx4("div", {
1061
- className: "skippr:flex-1",
1062
- children: /* @__PURE__ */ jsx4("p", {
1063
- className: "skippr:text-sm skippr:font-semibold skippr:leading-none",
1064
- children: "AI Agent"
1065
- })
1066
- }),
1067
- /* @__PURE__ */ jsxs3("div", {
1068
- className: "skippr:flex skippr:items-center skippr:gap-1",
1069
- children: [
1070
- /* @__PURE__ */ jsx4(Button, {
1071
- variant: "ghost",
1072
- size: "icon-xs",
1073
- onClick: onOpenSettings,
1074
- className: "skippr:text-primary-foreground skippr:hover:bg-primary-foreground/20 skippr:hover:text-primary-foreground",
1075
- "aria-label": "Settings",
1076
- children: /* @__PURE__ */ jsx4(Settings, {
1077
- className: "skippr:size-4"
1078
- })
1079
- }),
1080
- /* @__PURE__ */ jsx4(Button, {
1081
- variant: "ghost",
1082
- size: "icon-xs",
1083
- onClick: minimizable ? minimizePanel : closePanel,
1084
- className: "skippr:text-primary-foreground skippr:hover:bg-primary-foreground/20 skippr:hover:text-primary-foreground",
1085
- "aria-label": "Close",
1086
- children: /* @__PURE__ */ jsx4(X, {
1087
- className: "skippr:size-4"
1088
- })
1089
- })
1090
- ]
1091
- })
1092
- ]
1093
- });
1094
- }
1095
-
1096
1233
  // src/components/LoginFlow.tsx
1097
- import { useCallback as useCallback6, useEffect as useEffect5, useRef as useRef2, useState as useState5 } from "react";
1098
- import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
1234
+ import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
1099
1235
  var OTP_LENGTH = 6;
1100
1236
  var DIGIT_KEYS = ["d0", "d1", "d2", "d3", "d4", "d5"];
1101
1237
  function LoginFlow({ requestOtp, verifyOtp, error, isSubmitting }) {
1102
- const [step, setStep] = useState5("email");
1103
- const [email, setEmail] = useState5("");
1104
- const handleRequestOtp = useCallback6(async (emailValue) => {
1238
+ const [step, setStep] = useState6("email");
1239
+ const [email, setEmail] = useState6("");
1240
+ const handleRequestOtp = useCallback5(async (emailValue) => {
1105
1241
  const success = await requestOtp(emailValue);
1106
1242
  if (success)
1107
1243
  setStep("otp");
1108
1244
  }, [requestOtp]);
1109
- const handleVerifyOtp = useCallback6(async (code) => {
1245
+ const handleVerifyOtp = useCallback5(async (code) => {
1110
1246
  await verifyOtp(email, code);
1111
1247
  }, [verifyOtp, email]);
1112
- const handleBack = useCallback6(() => {
1248
+ const handleBack = useCallback5(() => {
1113
1249
  setStep("email");
1114
1250
  }, []);
1115
- const handleResend = useCallback6(async () => {
1251
+ const handleResend = useCallback5(async () => {
1116
1252
  await requestOtp(email);
1117
1253
  }, [requestOtp, email]);
1118
1254
  if (step === "otp") {
1119
- return /* @__PURE__ */ jsx5(OtpStep, {
1255
+ return /* @__PURE__ */ jsx8(OtpStep, {
1120
1256
  email,
1121
1257
  onSubmit: handleVerifyOtp,
1122
1258
  onResend: handleResend,
@@ -1125,7 +1261,7 @@ function LoginFlow({ requestOtp, verifyOtp, error, isSubmitting }) {
1125
1261
  isSubmitting
1126
1262
  });
1127
1263
  }
1128
- return /* @__PURE__ */ jsx5(EmailStep, {
1264
+ return /* @__PURE__ */ jsx8(EmailStep, {
1129
1265
  email,
1130
1266
  onEmailChange: setEmail,
1131
1267
  onSubmit: handleRequestOtp,
@@ -1139,30 +1275,30 @@ function EmailStep({ email, onEmailChange, onSubmit, error, isSubmitting }) {
1139
1275
  if (email.trim())
1140
1276
  onSubmit(email.trim());
1141
1277
  }
1142
- return /* @__PURE__ */ jsxs4("div", {
1278
+ return /* @__PURE__ */ jsxs7("div", {
1143
1279
  className: "skippr:flex skippr:flex-1 skippr:flex-col skippr:px-4 skippr:py-4",
1144
1280
  children: [
1145
- /* @__PURE__ */ jsxs4("div", {
1281
+ /* @__PURE__ */ jsxs7("div", {
1146
1282
  className: "skippr:mb-4 skippr:text-center",
1147
1283
  children: [
1148
- /* @__PURE__ */ jsx5(Mail, {
1284
+ /* @__PURE__ */ jsx8(Mail, {
1149
1285
  className: "skippr:mx-auto skippr:mb-2 skippr:size-6 skippr:text-primary"
1150
1286
  }),
1151
- /* @__PURE__ */ jsx5("p", {
1287
+ /* @__PURE__ */ jsx8("p", {
1152
1288
  className: "skippr:text-sm skippr:font-medium skippr:text-foreground",
1153
1289
  children: "Sign in to continue"
1154
1290
  }),
1155
- /* @__PURE__ */ jsx5("p", {
1291
+ /* @__PURE__ */ jsx8("p", {
1156
1292
  className: "skippr:mt-1 skippr:text-xs skippr:text-muted-foreground",
1157
1293
  children: "Your email will be used to identify you across sessions"
1158
1294
  })
1159
1295
  ]
1160
1296
  }),
1161
- /* @__PURE__ */ jsxs4("form", {
1297
+ /* @__PURE__ */ jsxs7("form", {
1162
1298
  onSubmit: handleSubmit,
1163
1299
  className: "skippr:flex skippr:flex-col skippr:gap-3",
1164
1300
  children: [
1165
- /* @__PURE__ */ jsx5("input", {
1301
+ /* @__PURE__ */ jsx8("input", {
1166
1302
  type: "email",
1167
1303
  placeholder: "you@example.com",
1168
1304
  value: email,
@@ -1171,15 +1307,15 @@ function EmailStep({ email, onEmailChange, onSubmit, error, isSubmitting }) {
1171
1307
  required: true,
1172
1308
  className: "skippr:w-full skippr:rounded-md skippr:border skippr:border-border skippr:bg-background skippr:px-3 skippr:py-2 skippr:text-sm skippr:text-foreground skippr:placeholder-muted-foreground skippr:outline-none focus:skippr:ring-2 focus:skippr:ring-primary/30 focus:skippr:border-primary disabled:skippr:opacity-50"
1173
1309
  }),
1174
- /* @__PURE__ */ jsx5(Button, {
1310
+ /* @__PURE__ */ jsx8(Button, {
1175
1311
  type: "submit",
1176
1312
  disabled: isSubmitting || !email.trim(),
1177
1313
  className: "skippr:w-full",
1178
- children: isSubmitting ? /* @__PURE__ */ jsx5(LoaderCircle, {
1314
+ children: isSubmitting ? /* @__PURE__ */ jsx8(LoaderCircle, {
1179
1315
  className: "skippr:size-4 skippr:animate-spin"
1180
1316
  }) : "Continue"
1181
1317
  }),
1182
- error && /* @__PURE__ */ jsx5("p", {
1318
+ error && /* @__PURE__ */ jsx8("p", {
1183
1319
  className: "skippr:text-xs skippr:text-center skippr:text-destructive",
1184
1320
  children: error
1185
1321
  })
@@ -1189,30 +1325,30 @@ function EmailStep({ email, onEmailChange, onSubmit, error, isSubmitting }) {
1189
1325
  });
1190
1326
  }
1191
1327
  function OtpStep({ email, onSubmit, onResend, onBack, error, isSubmitting }) {
1192
- const [digits, setDigits] = useState5(Array(OTP_LENGTH).fill(""));
1193
- const [resendCooldown, setResendCooldown] = useState5(0);
1194
- const inputRefs = useRef2([]);
1195
- const submittedRef = useRef2(false);
1196
- useEffect5(() => {
1328
+ const [digits, setDigits] = useState6(Array(OTP_LENGTH).fill(""));
1329
+ const [resendCooldown, setResendCooldown] = useState6(0);
1330
+ const inputRefs = useRef3([]);
1331
+ const submittedRef = useRef3(false);
1332
+ useEffect8(() => {
1197
1333
  inputRefs.current[0]?.focus();
1198
1334
  }, []);
1199
- useEffect5(() => {
1335
+ useEffect8(() => {
1200
1336
  if (error)
1201
1337
  submittedRef.current = false;
1202
1338
  }, [error]);
1203
- useEffect5(() => {
1339
+ useEffect8(() => {
1204
1340
  if (resendCooldown <= 0)
1205
1341
  return;
1206
1342
  const timer = setTimeout(() => setResendCooldown((c) => c - 1), 1000);
1207
1343
  return () => clearTimeout(timer);
1208
1344
  }, [resendCooldown]);
1209
- const submitCode = useCallback6((code) => {
1345
+ const submitCode = useCallback5((code) => {
1210
1346
  if (submittedRef.current || isSubmitting)
1211
1347
  return;
1212
1348
  submittedRef.current = true;
1213
1349
  onSubmit(code);
1214
1350
  }, [onSubmit, isSubmitting]);
1215
- const handleDigitChange = useCallback6((index2, value) => {
1351
+ const handleDigitChange = useCallback5((index2, value) => {
1216
1352
  const digit = value.replace(/\D/g, "").slice(-1);
1217
1353
  const newDigits = [...digits];
1218
1354
  newDigits[index2] = digit;
@@ -1226,12 +1362,12 @@ function OtpStep({ email, onSubmit, onResend, onBack, error, isSubmitting }) {
1226
1362
  submitCode(code);
1227
1363
  }
1228
1364
  }, [digits, submitCode]);
1229
- const handleKeyDown = useCallback6((index2, e) => {
1365
+ const handleKeyDown = useCallback5((index2, e) => {
1230
1366
  if (e.key === "Backspace" && !digits[index2] && index2 > 0) {
1231
1367
  inputRefs.current[index2 - 1]?.focus();
1232
1368
  }
1233
1369
  }, [digits]);
1234
- const handlePaste = useCallback6((e) => {
1370
+ const handlePaste = useCallback5((e) => {
1235
1371
  e.preventDefault();
1236
1372
  const pasted = e.clipboardData.getData("text").replace(/\D/g, "").slice(0, OTP_LENGTH);
1237
1373
  if (pasted.length > 0) {
@@ -1259,22 +1395,22 @@ function OtpStep({ email, onSubmit, onResend, onBack, error, isSubmitting }) {
1259
1395
  submittedRef.current = false;
1260
1396
  inputRefs.current[0]?.focus();
1261
1397
  }
1262
- return /* @__PURE__ */ jsxs4("div", {
1398
+ return /* @__PURE__ */ jsxs7("div", {
1263
1399
  className: "skippr:flex skippr:flex-1 skippr:flex-col skippr:px-4 skippr:py-4",
1264
1400
  children: [
1265
- /* @__PURE__ */ jsxs4("div", {
1401
+ /* @__PURE__ */ jsxs7("div", {
1266
1402
  className: "skippr:mb-4 skippr:text-center",
1267
1403
  children: [
1268
- /* @__PURE__ */ jsx5("p", {
1404
+ /* @__PURE__ */ jsx8("p", {
1269
1405
  className: "skippr:text-sm skippr:font-medium skippr:text-foreground",
1270
1406
  children: "Enter verification code"
1271
1407
  }),
1272
- /* @__PURE__ */ jsxs4("p", {
1408
+ /* @__PURE__ */ jsxs7("p", {
1273
1409
  className: "skippr:mt-1 skippr:text-xs skippr:text-muted-foreground",
1274
1410
  children: [
1275
1411
  "We sent a 6-digit code to",
1276
1412
  " ",
1277
- /* @__PURE__ */ jsx5("span", {
1413
+ /* @__PURE__ */ jsx8("span", {
1278
1414
  className: "skippr:font-medium skippr:text-foreground",
1279
1415
  children: email
1280
1416
  })
@@ -1282,13 +1418,13 @@ function OtpStep({ email, onSubmit, onResend, onBack, error, isSubmitting }) {
1282
1418
  })
1283
1419
  ]
1284
1420
  }),
1285
- /* @__PURE__ */ jsxs4("form", {
1421
+ /* @__PURE__ */ jsxs7("form", {
1286
1422
  onSubmit: handleSubmit,
1287
1423
  className: "skippr:flex skippr:flex-col skippr:gap-3",
1288
1424
  children: [
1289
- /* @__PURE__ */ jsx5("div", {
1425
+ /* @__PURE__ */ jsx8("div", {
1290
1426
  className: "skippr:flex skippr:justify-center skippr:gap-1.5",
1291
- children: digits.map((digit, index2) => /* @__PURE__ */ jsx5("input", {
1427
+ children: digits.map((digit, index2) => /* @__PURE__ */ jsx8("input", {
1292
1428
  ref: (el) => {
1293
1429
  inputRefs.current[index2] = el;
1294
1430
  },
@@ -1303,29 +1439,29 @@ function OtpStep({ email, onSubmit, onResend, onBack, error, isSubmitting }) {
1303
1439
  className: "skippr:h-10 skippr:w-10 skippr:rounded-md skippr:border skippr:border-border skippr:bg-background skippr:text-center skippr:text-sm skippr:font-semibold skippr:text-foreground skippr:outline-none focus:skippr:ring-2 focus:skippr:ring-primary/30 focus:skippr:border-primary disabled:skippr:opacity-50"
1304
1440
  }, DIGIT_KEYS[index2]))
1305
1441
  }),
1306
- error && /* @__PURE__ */ jsx5("p", {
1442
+ error && /* @__PURE__ */ jsx8("p", {
1307
1443
  className: "skippr:text-xs skippr:text-center skippr:text-destructive",
1308
1444
  children: error
1309
1445
  }),
1310
- /* @__PURE__ */ jsx5(Button, {
1446
+ /* @__PURE__ */ jsx8(Button, {
1311
1447
  type: "submit",
1312
1448
  disabled: isSubmitting || digits.join("").length !== OTP_LENGTH,
1313
1449
  className: "skippr:w-full",
1314
- children: isSubmitting ? /* @__PURE__ */ jsx5(LoaderCircle, {
1450
+ children: isSubmitting ? /* @__PURE__ */ jsx8(LoaderCircle, {
1315
1451
  className: "skippr:size-4 skippr:animate-spin"
1316
1452
  }) : "Verify"
1317
1453
  }),
1318
- /* @__PURE__ */ jsxs4("div", {
1454
+ /* @__PURE__ */ jsxs7("div", {
1319
1455
  className: "skippr:flex skippr:items-center skippr:justify-between skippr:text-xs",
1320
1456
  children: [
1321
- /* @__PURE__ */ jsx5("button", {
1457
+ /* @__PURE__ */ jsx8("button", {
1322
1458
  type: "button",
1323
1459
  onClick: onBack,
1324
1460
  disabled: isSubmitting,
1325
1461
  className: "skippr:text-muted-foreground hover:skippr:text-foreground skippr:transition-colors disabled:skippr:opacity-50",
1326
1462
  children: "Change email"
1327
1463
  }),
1328
- /* @__PURE__ */ jsx5("button", {
1464
+ /* @__PURE__ */ jsx8("button", {
1329
1465
  type: "button",
1330
1466
  onClick: handleResend,
1331
1467
  disabled: isSubmitting || resendCooldown > 0,
@@ -1341,476 +1477,351 @@ function OtpStep({ email, onSubmit, onResend, onBack, error, isSubmitting }) {
1341
1477
  }
1342
1478
 
1343
1479
  // src/components/MeetingControls.tsx
1344
- import { useLocalParticipant as useLocalParticipant4 } from "@livekit/components-react";
1345
- import { ScreenSharePresets as ScreenSharePresets2 } from "livekit-client";
1346
- import { useCallback as useCallback7, useEffect as useEffect6, useRef as useRef3, useState as useState6 } from "react";
1347
-
1348
- // src/lib/format.ts
1349
- function formatTime(seconds) {
1350
- const m = Math.floor(seconds / 60).toString().padStart(2, "0");
1351
- const s = (seconds % 60).toString().padStart(2, "0");
1352
- return `${m}:${s}`;
1353
- }
1354
- function parseNumber(s) {
1355
- const n = Number(s);
1356
- return n > 0 ? n : null;
1357
- }
1358
-
1359
- // src/components/SessionWarningBanner.tsx
1360
- import { jsx as jsx6 } from "react/jsx-runtime";
1361
- var SESSION_WARNING_THRESHOLD_SECS = 60;
1362
- function SessionWarningBanner({ remaining }) {
1363
- if (remaining === null || remaining <= 0 || remaining > SESSION_WARNING_THRESHOLD_SECS)
1364
- return null;
1365
- return /* @__PURE__ */ jsx6("div", {
1366
- "data-testid": "session-warning-banner",
1367
- 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",
1368
- children: "Session ending soon"
1369
- });
1370
- }
1371
-
1372
- // src/components/MeetingControls.tsx
1373
- import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
1480
+ import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
1481
+ var CONTROL_BUTTON = "skippr:flex skippr:size-11 skippr:cursor-pointer skippr:items-center skippr:justify-center skippr:rounded-full skippr:transition-colors";
1374
1482
  function MeetingControls({ onHangUp }) {
1375
- const maxCallDuration = useAgentState("maxCallDuration", parseNumber, null);
1376
- const { localParticipant } = useLocalParticipant4();
1377
- const isMuted = !localParticipant.isMicrophoneEnabled;
1378
- const isScreenSharing = localParticipant.isScreenShareEnabled;
1379
- const endTimeRef = useRef3(null);
1380
- const [remaining, setRemaining] = useState6(null);
1381
- useEffect6(() => {
1382
- if (maxCallDuration === null || endTimeRef.current !== null)
1383
- return;
1384
- endTimeRef.current = Date.now() + maxCallDuration * 1000;
1385
- const tick = () => {
1386
- const secs = Math.ceil((endTimeRef.current - Date.now()) / 1000);
1387
- setRemaining(Math.max(secs, 0));
1388
- };
1389
- tick();
1390
- const id = setInterval(tick, 1000);
1391
- return () => clearInterval(id);
1392
- }, [maxCallDuration]);
1393
- const toggleMute = useCallback7(async () => {
1394
- try {
1395
- await localParticipant.setMicrophoneEnabled(isMuted);
1396
- } catch (e) {
1397
- console.error("Failed to toggle microphone:", e);
1398
- }
1399
- }, [localParticipant, isMuted]);
1400
- const toggleScreenShare = useCallback7(async () => {
1401
- try {
1402
- await localParticipant.setScreenShareEnabled(!isScreenSharing, {
1403
- video: { displaySurface: "browser" },
1404
- resolution: ScreenSharePresets2.h720fps30.resolution,
1405
- contentHint: "detail"
1406
- });
1407
- } catch (e) {
1408
- console.error("Failed to toggle screen share:", e);
1409
- }
1410
- }, [localParticipant, isScreenSharing]);
1411
- useEffect6(() => {
1412
- toggleMute().then(() => toggleScreenShare());
1413
- }, []);
1414
- return /* @__PURE__ */ jsxs5("div", {
1483
+ const { isMuted, toggleMute, isScreenSharing, toggleScreenShare } = useMediaControls();
1484
+ return /* @__PURE__ */ jsxs8("div", {
1485
+ className: "skippr:shrink-0 skippr:border-t skippr:border-border skippr:bg-background skippr:px-4 skippr:py-4",
1415
1486
  children: [
1416
- /* @__PURE__ */ jsx7(SessionWarningBanner, {
1417
- remaining
1418
- }),
1419
- /* @__PURE__ */ jsxs5("div", {
1420
- className: "skippr:flex skippr:items-center skippr:justify-between skippr:border-b skippr:px-4 skippr:py-3",
1487
+ /* @__PURE__ */ jsxs8("div", {
1488
+ className: "skippr:flex skippr:items-center skippr:justify-center skippr:gap-3",
1421
1489
  children: [
1422
- /* @__PURE__ */ jsxs5("div", {
1423
- className: "skippr:flex skippr:items-center skippr:gap-2",
1424
- children: [
1425
- /* @__PURE__ */ jsx7(Tooltip, {
1426
- label: isMuted ? "Unmute" : "Mute",
1427
- children: /* @__PURE__ */ jsx7(Button, {
1428
- size: "icon-sm",
1429
- variant: isMuted ? "destructive" : "outline",
1430
- onClick: toggleMute,
1431
- "aria-label": isMuted ? "Unmute" : "Mute",
1432
- children: isMuted ? /* @__PURE__ */ jsx7(MicOff, {
1433
- className: "skippr:size-4"
1434
- }) : /* @__PURE__ */ jsx7(Mic, {
1435
- className: "skippr:size-4"
1436
- })
1437
- })
1438
- }),
1439
- /* @__PURE__ */ jsx7(Tooltip, {
1440
- label: isScreenSharing ? "Stop sharing" : "Share screen",
1441
- children: /* @__PURE__ */ jsx7(Button, {
1442
- size: "icon-sm",
1443
- variant: isScreenSharing ? "outline" : "destructive",
1444
- onClick: toggleScreenShare,
1445
- "aria-label": isScreenSharing ? "Stop sharing" : "Share screen",
1446
- children: isScreenSharing ? /* @__PURE__ */ jsx7(MonitorOff, {
1447
- className: "skippr:size-4"
1448
- }) : /* @__PURE__ */ jsx7(Monitor, {
1449
- className: "skippr:size-4"
1450
- })
1451
- })
1452
- })
1453
- ]
1490
+ /* @__PURE__ */ jsx9("button", {
1491
+ type: "button",
1492
+ onClick: toggleMute,
1493
+ "aria-label": isMuted ? "Unmute" : "Mute",
1494
+ className: cn(CONTROL_BUTTON, isMuted ? "skippr:bg-destructive/15 skippr:text-destructive skippr:hover:bg-destructive/25" : "skippr:bg-muted skippr:text-foreground skippr:hover:bg-muted/80"),
1495
+ children: isMuted ? /* @__PURE__ */ jsx9(MicOff, {
1496
+ className: "skippr:size-5"
1497
+ }) : /* @__PURE__ */ jsx9(Mic, {
1498
+ className: "skippr:size-5"
1499
+ })
1454
1500
  }),
1455
- remaining !== null && /* @__PURE__ */ jsx7("span", {
1456
- className: cn("skippr:text-sm skippr:font-medium skippr:tabular-nums", remaining <= SESSION_WARNING_THRESHOLD_SECS ? "skippr:text-red-600 skippr:animate-pulse" : "skippr:text-muted-foreground"),
1457
- children: formatTime(remaining)
1501
+ /* @__PURE__ */ jsx9("button", {
1502
+ type: "button",
1503
+ onClick: toggleScreenShare,
1504
+ "aria-label": isScreenSharing ? "Stop sharing screen" : "Share screen",
1505
+ className: cn(CONTROL_BUTTON, isScreenSharing ? "skippr:bg-bubble skippr:text-white skippr:hover:brightness-110" : "skippr:bg-muted skippr:text-foreground skippr:hover:bg-muted/80"),
1506
+ children: isScreenSharing ? /* @__PURE__ */ jsx9(MonitorOff, {
1507
+ className: "skippr:size-5"
1508
+ }) : /* @__PURE__ */ jsx9(Monitor, {
1509
+ className: "skippr:size-5"
1510
+ })
1458
1511
  }),
1459
- /* @__PURE__ */ jsx7(Tooltip, {
1460
- label: "End session",
1461
- children: /* @__PURE__ */ jsx7(Button, {
1462
- size: "icon-sm",
1463
- variant: "destructive",
1464
- onClick: onHangUp,
1465
- "aria-label": "Hang up",
1466
- children: /* @__PURE__ */ jsx7(PhoneOff, {
1467
- className: "skippr:size-4"
1468
- })
1512
+ /* @__PURE__ */ jsx9("button", {
1513
+ type: "button",
1514
+ onClick: onHangUp,
1515
+ "aria-label": "End session",
1516
+ className: cn(CONTROL_BUTTON, "skippr:bg-destructive skippr:text-destructive-foreground skippr:hover:bg-destructive/90"),
1517
+ children: /* @__PURE__ */ jsx9(PhoneOff, {
1518
+ className: "skippr:size-5"
1469
1519
  })
1470
1520
  })
1471
1521
  ]
1522
+ }),
1523
+ /* @__PURE__ */ jsx9("p", {
1524
+ className: "skippr:mt-3 skippr:text-center skippr:text-[10px] skippr:text-muted-foreground",
1525
+ children: "Powered by Skippr"
1472
1526
  })
1473
1527
  ]
1474
1528
  });
1475
1529
  }
1476
1530
 
1477
1531
  // src/components/MessageList.tsx
1478
- import { useEffect as useEffect7, useRef as useRef4 } from "react";
1532
+ import { useEffect as useEffect10, useRef as useRef5 } from "react";
1479
1533
 
1480
1534
  // src/components/ChatInput.tsx
1481
- import { useState as useState7 } from "react";
1482
- import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
1483
- function ChatInput({ sendChatMessage, isSendingChat }) {
1535
+ import { useEffect as useEffect9, useRef as useRef4, useState as useState7 } from "react";
1536
+ import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
1537
+ var MAX_INPUT_HEIGHT = 60;
1538
+ function ChatInput({ sendChatMessage, isSendingChat, autoFocus = false }) {
1484
1539
  const [inputText, setInputText] = useState7("");
1540
+ const textareaRef = useRef4(null);
1485
1541
  const canSend = inputText.trim().length > 0 && !isSendingChat;
1542
+ useEffect9(() => {
1543
+ if (autoFocus)
1544
+ textareaRef.current?.focus();
1545
+ }, [autoFocus]);
1546
+ function resizeTextarea() {
1547
+ const el = textareaRef.current;
1548
+ if (!el)
1549
+ return;
1550
+ el.style.height = "auto";
1551
+ el.style.height = `${Math.min(el.scrollHeight, MAX_INPUT_HEIGHT)}px`;
1552
+ }
1553
+ function handleChange(e) {
1554
+ setInputText(e.target.value);
1555
+ resizeTextarea();
1556
+ }
1486
1557
  function handleSubmit(e) {
1487
1558
  e.preventDefault();
1488
1559
  const text = inputText.trim();
1489
1560
  if (!text || isSendingChat)
1490
1561
  return;
1491
1562
  setInputText("");
1563
+ resizeTextarea();
1492
1564
  sendChatMessage(text).catch(() => setInputText(text));
1565
+ textareaRef.current?.focus();
1566
+ }
1567
+ function handleKeyDown(e) {
1568
+ if (e.key === "Enter" && !e.shiftKey) {
1569
+ e.preventDefault();
1570
+ handleSubmit(e);
1571
+ }
1493
1572
  }
1494
- return /* @__PURE__ */ jsxs6("form", {
1573
+ return /* @__PURE__ */ jsx10("form", {
1495
1574
  onSubmit: handleSubmit,
1496
- className: "skippr:flex skippr:items-center skippr:gap-2 skippr:border-t skippr:border-border skippr:px-3 skippr:py-2",
1497
- children: [
1498
- /* @__PURE__ */ jsx8("input", {
1499
- type: "text",
1500
- value: inputText,
1501
- onChange: (e) => setInputText(e.target.value),
1502
- placeholder: "Type a message...",
1503
- className: cn("skippr:flex-1 skippr:rounded-lg skippr:border skippr:border-border skippr:bg-background", "skippr:px-3 skippr:py-2 skippr:text-sm skippr:text-foreground", "skippr:placeholder:text-muted-foreground skippr:outline-none", "skippr:focus:ring-1 skippr:focus:ring-ring"),
1504
- disabled: isSendingChat
1505
- }),
1506
- /* @__PURE__ */ jsx8("button", {
1507
- type: "submit",
1508
- disabled: !canSend,
1509
- "aria-label": "Send message",
1510
- className: cn("skippr:flex skippr:size-9 skippr:shrink-0 skippr:items-center skippr:justify-center", "skippr:rounded-lg skippr:bg-primary skippr:text-primary-foreground", "skippr:transition-opacity", !canSend && "skippr:opacity-50 skippr:cursor-not-allowed"),
1511
- children: /* @__PURE__ */ jsx8(SendHorizontal, {
1512
- className: "skippr:size-4"
1575
+ className: "skippr:border-t skippr:border-border skippr:p-3",
1576
+ children: /* @__PURE__ */ jsxs9("div", {
1577
+ className: "skippr:flex skippr:items-center skippr:gap-2 skippr:rounded-xl skippr:bg-background skippr:ring-1 skippr:ring-foreground/10 skippr:px-3 skippr:py-2",
1578
+ children: [
1579
+ /* @__PURE__ */ jsx10("textarea", {
1580
+ ref: textareaRef,
1581
+ rows: 1,
1582
+ value: inputText,
1583
+ onChange: handleChange,
1584
+ onKeyDown: handleKeyDown,
1585
+ placeholder: "Type a message...",
1586
+ className: "skippr:flex-1 skippr:resize-none skippr:overflow-y-auto skippr:bg-transparent skippr:text-sm skippr:leading-5 skippr:text-foreground skippr:placeholder:text-muted-foreground skippr:outline-none",
1587
+ style: { maxHeight: `${MAX_INPUT_HEIGHT}px` }
1588
+ }),
1589
+ /* @__PURE__ */ jsx10("button", {
1590
+ type: "submit",
1591
+ disabled: !canSend,
1592
+ "aria-label": "Send message",
1593
+ className: cn("skippr:flex skippr:size-8 skippr:shrink-0 skippr:items-center skippr:justify-center skippr:rounded-lg skippr:transition-colors", canSend ? "skippr:bg-primary skippr:text-primary-foreground skippr:hover:bg-primary/90" : "skippr:bg-muted-foreground/20 skippr:text-muted-foreground/60"),
1594
+ children: /* @__PURE__ */ jsx10(Send, {
1595
+ className: "skippr:size-3.5"
1596
+ })
1513
1597
  })
1514
- })
1515
- ]
1598
+ ]
1599
+ })
1516
1600
  });
1517
1601
  }
1518
1602
 
1519
1603
  // src/components/ChatMessage.tsx
1520
- import { jsx as jsx9 } from "react/jsx-runtime";
1604
+ import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
1605
+ function formatTimestamp(ts) {
1606
+ return new Date(ts).toLocaleTimeString("en-US", {
1607
+ hour: "numeric",
1608
+ minute: "2-digit",
1609
+ hour12: true
1610
+ });
1611
+ }
1521
1612
  function ChatMessage({ message }) {
1522
- const isUser = message.role === "user";
1523
- return /* @__PURE__ */ jsx9("div", {
1524
- className: cn("skippr:flex skippr:w-full skippr:px-4 skippr:py-1", isUser ? "skippr:justify-end" : "skippr:justify-start"),
1525
- children: /* @__PURE__ */ jsx9("div", {
1526
- className: cn("skippr:max-w-[85%] skippr:whitespace-pre-wrap skippr:rounded-2xl skippr:px-4 skippr:py-2.5 skippr:text-sm skippr:leading-relaxed", isUser ? "skippr:rounded-br-sm skippr:bg-primary skippr:text-primary-foreground" : "skippr:rounded-bl-sm skippr:bg-muted skippr:text-foreground"),
1527
- children: message.content
1528
- })
1613
+ const isAgent = message.role === "assistant";
1614
+ return /* @__PURE__ */ jsxs10("div", {
1615
+ className: cn("skippr:flex skippr:gap-2", isAgent ? "skippr:items-start" : "skippr:justify-end"),
1616
+ children: [
1617
+ isAgent && /* @__PURE__ */ jsx11("div", {
1618
+ className: "skippr:mt-0.5 skippr:flex skippr:size-7 skippr:shrink-0 skippr:items-center skippr:justify-center skippr:rounded-md skippr:bg-primary",
1619
+ children: /* @__PURE__ */ jsx11(Sparkles, {
1620
+ className: "skippr:size-3.5 skippr:text-primary-foreground"
1621
+ })
1622
+ }),
1623
+ /* @__PURE__ */ jsxs10("div", {
1624
+ className: cn("skippr:flex skippr:max-w-[80%] skippr:flex-col", isAgent ? "skippr:items-start" : "skippr:items-end"),
1625
+ children: [
1626
+ /* @__PURE__ */ jsx11("div", {
1627
+ className: cn("skippr:rounded-2xl skippr:px-4 skippr:py-2.5 skippr:text-sm skippr:leading-relaxed", isAgent ? "skippr:border skippr:border-border skippr:bg-card skippr:text-foreground" : "skippr:bg-primary skippr:text-primary-foreground"),
1628
+ children: message.content
1629
+ }),
1630
+ message.timestamp && /* @__PURE__ */ jsx11("span", {
1631
+ className: "skippr:mt-1 skippr:px-1 skippr:text-[10px] skippr:text-muted-foreground/60",
1632
+ children: formatTimestamp(message.timestamp)
1633
+ })
1634
+ ]
1635
+ })
1636
+ ]
1529
1637
  });
1530
1638
  }
1531
1639
 
1532
1640
  // src/components/TypingIndicator.tsx
1533
- import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
1641
+ import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
1534
1642
  function TypingIndicator() {
1535
- return /* @__PURE__ */ jsx10("div", {
1536
- className: "skippr:flex skippr:items-center skippr:gap-1 skippr:px-4 skippr:py-3",
1537
- children: /* @__PURE__ */ jsxs7("div", {
1538
- className: "skippr:flex skippr:items-center skippr:gap-1 skippr:rounded-2xl skippr:rounded-bl-sm skippr:bg-muted skippr:px-4 skippr:py-2.5",
1539
- children: [
1540
- /* @__PURE__ */ jsx10("span", {
1541
- className: "skippr:size-1.5 skippr:animate-bounce skippr:rounded-full skippr:bg-muted-foreground/60 skippr:[animation-delay:0ms]"
1542
- }),
1543
- /* @__PURE__ */ jsx10("span", {
1544
- className: "skippr:size-1.5 skippr:animate-bounce skippr:rounded-full skippr:bg-muted-foreground/60 skippr:[animation-delay:150ms]"
1545
- }),
1546
- /* @__PURE__ */ jsx10("span", {
1547
- className: "skippr:size-1.5 skippr:animate-bounce skippr:rounded-full skippr:bg-muted-foreground/60 skippr:[animation-delay:300ms]"
1643
+ return /* @__PURE__ */ jsxs11("div", {
1644
+ className: "skippr:flex skippr:items-start skippr:gap-2 skippr:animate-skippr-tab-fade",
1645
+ children: [
1646
+ /* @__PURE__ */ jsx12("div", {
1647
+ className: "skippr:mt-0.5 skippr:flex skippr:size-7 skippr:shrink-0 skippr:items-center skippr:justify-center skippr:rounded-md skippr:bg-primary",
1648
+ children: /* @__PURE__ */ jsx12(Sparkles, {
1649
+ className: "skippr:size-3.5 skippr:text-primary-foreground"
1548
1650
  })
1549
- ]
1550
- })
1651
+ }),
1652
+ /* @__PURE__ */ jsxs11("div", {
1653
+ className: "skippr:inline-flex skippr:items-center skippr:gap-1 skippr:rounded-2xl skippr:border skippr:border-primary/20 skippr:bg-primary/10 skippr:px-4 skippr:py-2 skippr:text-xs skippr:text-primary",
1654
+ children: [
1655
+ /* @__PURE__ */ jsx12("span", {
1656
+ children: "Agent is analyzing your screen"
1657
+ }),
1658
+ /* @__PURE__ */ jsxs11("span", {
1659
+ className: "skippr:inline-flex skippr:items-center skippr:gap-[2px]",
1660
+ children: [
1661
+ /* @__PURE__ */ jsx12("span", {
1662
+ className: "skippr:size-1 skippr:rounded-full skippr:bg-primary skippr:animate-skippr-thinking-dot skippr:[animation-delay:0ms]"
1663
+ }),
1664
+ /* @__PURE__ */ jsx12("span", {
1665
+ className: "skippr:size-1 skippr:rounded-full skippr:bg-primary skippr:animate-skippr-thinking-dot skippr:[animation-delay:200ms]"
1666
+ }),
1667
+ /* @__PURE__ */ jsx12("span", {
1668
+ className: "skippr:size-1 skippr:rounded-full skippr:bg-primary skippr:animate-skippr-thinking-dot skippr:[animation-delay:400ms]"
1669
+ })
1670
+ ]
1671
+ })
1672
+ ]
1673
+ })
1674
+ ]
1551
1675
  });
1552
1676
  }
1553
1677
 
1554
1678
  // src/components/MessageList.tsx
1555
- import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
1679
+ import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
1556
1680
  function MessageList({
1557
1681
  messages,
1558
1682
  isStreaming,
1559
1683
  sendChatMessage,
1560
- isSendingChat
1684
+ isSendingChat,
1685
+ autoFocus = false
1561
1686
  }) {
1562
- const scrollRef = useRef4(null);
1687
+ const scrollRef = useRef5(null);
1563
1688
  const lastMessage = messages.length > 0 ? messages[messages.length - 1] : undefined;
1564
- useEffect7(() => {
1689
+ useEffect10(() => {
1565
1690
  scrollRef.current?.scrollIntoView({ behavior: "smooth" });
1566
1691
  }, [messages.length, lastMessage?.content]);
1567
1692
  const showTyping = isStreaming && lastMessage?.role === "assistant" && lastMessage.content === "";
1568
- return /* @__PURE__ */ jsxs8("div", {
1693
+ return /* @__PURE__ */ jsxs12("div", {
1569
1694
  className: "skippr:flex skippr:min-h-0 skippr:flex-1 skippr:flex-col",
1570
1695
  children: [
1571
- /* @__PURE__ */ jsx11("div", {
1572
- className: "skippr:min-h-0 skippr:flex-1 skippr:overflow-y-auto",
1573
- children: /* @__PURE__ */ jsxs8("div", {
1574
- className: "skippr:flex skippr:flex-col skippr:gap-1 skippr:py-3",
1575
- children: [
1576
- messages.length === 0 && !showTyping && /* @__PURE__ */ jsxs8("div", {
1577
- className: "skippr:flex skippr:flex-col skippr:items-center skippr:justify-center skippr:gap-3 skippr:py-10 skippr:text-center skippr:px-4",
1578
- children: [
1579
- /* @__PURE__ */ jsxs8("div", {
1580
- className: "skippr:flex skippr:items-end skippr:justify-center skippr:gap-[3px] skippr:h-5",
1581
- children: [
1582
- /* @__PURE__ */ jsx11("span", {
1583
- className: "skippr:w-[3px] skippr:rounded-full skippr:bg-primary/40 skippr:animate-[skippr-wave_1.2s_ease-in-out_infinite] skippr:h-2"
1584
- }),
1585
- /* @__PURE__ */ jsx11("span", {
1586
- className: "skippr:w-[3px] skippr:rounded-full skippr:bg-primary/40 skippr:animate-[skippr-wave_1.2s_ease-in-out_0.15s_infinite] skippr:h-3"
1587
- }),
1588
- /* @__PURE__ */ jsx11("span", {
1589
- className: "skippr:w-[3px] skippr:rounded-full skippr:bg-primary/40 skippr:animate-[skippr-wave_1.2s_ease-in-out_0.3s_infinite] skippr:h-4"
1590
- }),
1591
- /* @__PURE__ */ jsx11("span", {
1592
- className: "skippr:w-[3px] skippr:rounded-full skippr:bg-primary/40 skippr:animate-[skippr-wave_1.2s_ease-in-out_0.45s_infinite] skippr:h-3"
1593
- }),
1594
- /* @__PURE__ */ jsx11("span", {
1595
- className: "skippr:w-[3px] skippr:rounded-full skippr:bg-primary/40 skippr:animate-[skippr-wave_1.2s_ease-in-out_0.6s_infinite] skippr:h-2"
1596
- })
1597
- ]
1598
- }),
1599
- /* @__PURE__ */ jsx11("p", {
1600
- className: "skippr:text-xs skippr:text-muted-foreground",
1601
- children: "Agent is getting ready..."
1602
- })
1603
- ]
1604
- }),
1605
- messages.map((message) => /* @__PURE__ */ jsx11(ChatMessage, {
1606
- message
1607
- }, message.id)),
1608
- showTyping && /* @__PURE__ */ jsx11(TypingIndicator, {}),
1609
- /* @__PURE__ */ jsx11("div", {
1610
- ref: scrollRef
1611
- })
1612
- ]
1613
- })
1614
- }),
1615
- /* @__PURE__ */ jsx11(ChatInput, {
1616
- sendChatMessage,
1617
- isSendingChat
1618
- })
1619
- ]
1620
- });
1621
- }
1622
-
1623
- // src/components/QuickActions.tsx
1624
- import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
1625
- function QuickActions({ onStartSession, isStarting, error }) {
1626
- return /* @__PURE__ */ jsxs9("div", {
1627
- className: "skippr:flex skippr:flex-1 skippr:flex-col skippr:items-center skippr:gap-6 skippr:overflow-y-auto skippr:px-4 skippr:py-4",
1628
- children: [
1629
- /* @__PURE__ */ jsx12("p", {
1630
- className: "skippr:mb-1 skippr:text-sm skippr:text-muted-foreground",
1631
- children: "How can I help you today?"
1632
- }),
1633
- /* @__PURE__ */ jsxs9(Button, {
1634
- variant: "outline",
1635
- className: "skippr:h-auto skippr:flex-col skippr:gap-1.5 skippr:whitespace-normal skippr:py-3 skippr:text-xs",
1636
- onClick: onStartSession,
1637
- disabled: isStarting,
1696
+ /* @__PURE__ */ jsxs12("div", {
1697
+ className: "skippr:min-h-0 skippr:flex-1 skippr:space-y-4 skippr:overflow-y-auto skippr:p-4",
1638
1698
  children: [
1639
- isStarting ? /* @__PURE__ */ jsx12(LoaderCircle, {
1640
- className: "skippr:size-4 skippr:animate-spin skippr:text-primary"
1641
- }) : /* @__PURE__ */ jsx12(MessageCircleQuestionMark, {
1642
- className: "skippr:size-4 skippr:text-primary"
1699
+ messages.length === 0 && !showTyping && /* @__PURE__ */ jsx13(LoadingDots, {
1700
+ label: "Waiting for conversation to begin..."
1643
1701
  }),
1644
- isStarting ? "Starting..." : "Start Session"
1702
+ messages.map((message) => /* @__PURE__ */ jsx13(ChatMessage, {
1703
+ message
1704
+ }, message.id)),
1705
+ showTyping && /* @__PURE__ */ jsx13(TypingIndicator, {}),
1706
+ /* @__PURE__ */ jsx13("div", {
1707
+ ref: scrollRef
1708
+ })
1645
1709
  ]
1646
1710
  }),
1647
- error && /* @__PURE__ */ jsx12("p", {
1648
- className: "skippr:text-xs skippr:text-destructive",
1649
- children: error
1711
+ /* @__PURE__ */ jsx13(ChatInput, {
1712
+ sendChatMessage,
1713
+ isSendingChat,
1714
+ autoFocus
1650
1715
  })
1651
1716
  ]
1652
1717
  });
1653
1718
  }
1654
1719
 
1655
1720
  // src/components/SessionAgenda.tsx
1656
- import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
1657
- function SessionAgenda({ phases, questions = [] }) {
1721
+ import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
1722
+ function SessionAgenda({ phases }) {
1658
1723
  if (phases.length === 0) {
1659
- return /* @__PURE__ */ jsxs10("div", {
1660
- className: "skippr:flex skippr:flex-col skippr:gap-3 skippr:p-4",
1661
- children: [
1662
- /* @__PURE__ */ jsx13("h3", {
1663
- className: "skippr:text-sm skippr:font-semibold",
1664
- children: "Agenda"
1665
- }),
1666
- /* @__PURE__ */ jsx13("p", {
1667
- className: "skippr:text-xs skippr:text-muted-foreground",
1668
- children: "Waiting for session..."
1669
- })
1670
- ]
1671
- });
1672
- }
1673
- return /* @__PURE__ */ jsxs10("div", {
1674
- className: "skippr:flex skippr:flex-col skippr:gap-3 skippr:p-4",
1675
- children: [
1676
- /* @__PURE__ */ jsx13("h3", {
1677
- className: "skippr:text-sm skippr:font-semibold",
1678
- children: "Agenda"
1679
- }),
1680
- /* @__PURE__ */ jsx13("ul", {
1681
- className: "skippr:flex skippr:flex-col skippr:gap-2",
1682
- children: phases.map((phase) => {
1683
- const phaseQuestions = questions.filter((q) => q.phaseName === phase.name);
1684
- const answeredCount = phaseQuestions.filter((q) => q.status === "answered").length;
1685
- const totalCount = phaseQuestions.length;
1686
- return /* @__PURE__ */ jsxs10("li", {
1687
- className: "skippr:flex skippr:flex-col skippr:gap-0.5",
1688
- children: [
1689
- /* @__PURE__ */ jsxs10("div", {
1690
- className: "skippr:flex skippr:items-center skippr:gap-2 skippr:text-sm",
1691
- children: [
1692
- /* @__PURE__ */ jsx13(PhaseIcon, {
1693
- status: phase.status
1694
- }),
1695
- /* @__PURE__ */ jsx13("span", {
1696
- className: cn(phase.status === "completed" && "skippr:text-muted-foreground skippr:line-through", phase.status === "active" && "skippr:font-medium skippr:text-primary"),
1697
- children: phase.name
1698
- })
1699
- ]
1700
- }),
1701
- totalCount > 0 && /* @__PURE__ */ jsxs10("span", {
1702
- className: "skippr:ml-6 skippr:text-xs skippr:text-muted-foreground",
1703
- children: [
1704
- answeredCount,
1705
- "/",
1706
- totalCount,
1707
- " answered"
1708
- ]
1709
- })
1710
- ]
1711
- }, phase.name);
1712
- })
1713
- })
1714
- ]
1715
- });
1716
- }
1717
- function PhaseIcon({ status }) {
1718
- if (status === "completed") {
1719
- return /* @__PURE__ */ jsx13("div", {
1720
- className: "skippr:flex skippr:size-4 skippr:shrink-0 skippr:items-center skippr:justify-center skippr:rounded-full skippr:bg-primary",
1721
- children: /* @__PURE__ */ jsx13(Check, {
1722
- className: "skippr:size-2.5 skippr:text-primary-foreground",
1723
- strokeWidth: 3
1724
+ return /* @__PURE__ */ jsx14("div", {
1725
+ className: "skippr:flex skippr:flex-1 skippr:items-center skippr:justify-center",
1726
+ children: /* @__PURE__ */ jsx14(LoadingDots, {
1727
+ label: "Waiting for agenda to load..."
1724
1728
  })
1725
1729
  });
1726
1730
  }
1727
- if (status === "active") {
1728
- return /* @__PURE__ */ jsx13(LoaderCircle, {
1729
- className: "skippr:size-4 skippr:shrink-0 skippr:text-primary skippr:animate-spin"
1730
- });
1731
- }
1732
- return /* @__PURE__ */ jsx13(Circle, {
1733
- className: "skippr:size-4 skippr:shrink-0 skippr:text-muted-foreground"
1734
- });
1735
- }
1736
-
1737
- // src/components/SettingsView.tsx
1738
- import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
1739
- function SettingsView({ onBack }) {
1740
- const { position, setPosition } = useLiveAgent();
1741
- return /* @__PURE__ */ jsxs11("div", {
1742
- className: "skippr:flex skippr:flex-1 skippr:flex-col",
1743
- children: [
1744
- /* @__PURE__ */ jsxs11("div", {
1745
- className: "skippr:flex skippr:items-center skippr:gap-2 skippr:border-b skippr:border-border skippr:px-4 skippr:py-3",
1746
- children: [
1747
- /* @__PURE__ */ jsx14(Button, {
1748
- variant: "ghost",
1749
- size: "icon-xs",
1750
- onClick: onBack,
1751
- "aria-label": "Back",
1752
- children: /* @__PURE__ */ jsx14(ArrowLeft, {
1753
- className: "skippr:size-4"
1754
- })
1755
- }),
1756
- /* @__PURE__ */ jsx14("p", {
1757
- className: "skippr:text-sm skippr:font-semibold",
1758
- children: "Settings"
1759
- })
1760
- ]
1761
- }),
1762
- /* @__PURE__ */ jsx14("div", {
1763
- className: "skippr:flex-1 skippr:overflow-y-auto skippr:p-4",
1764
- children: /* @__PURE__ */ jsxs11("div", {
1765
- className: "skippr:mb-4",
1731
+ return /* @__PURE__ */ jsx14("div", {
1732
+ className: "skippr:flex-1 skippr:overflow-y-auto skippr:px-4 skippr:py-4",
1733
+ children: /* @__PURE__ */ jsx14("div", {
1734
+ className: "skippr:space-y-1",
1735
+ children: phases.map((phase) => {
1736
+ const isActive = phase.status === "active";
1737
+ const isCompleted = phase.status === "completed";
1738
+ return /* @__PURE__ */ jsxs13("div", {
1739
+ 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"),
1766
1740
  children: [
1767
- /* @__PURE__ */ jsx14("p", {
1768
- className: "skippr:mb-2 skippr:text-xs skippr:font-medium skippr:uppercase skippr:tracking-wide skippr:text-muted-foreground",
1769
- children: "Widget Position"
1741
+ /* @__PURE__ */ jsx14("div", {
1742
+ className: "skippr:mt-0.5",
1743
+ children: isCompleted ? /* @__PURE__ */ jsx14(CircleCheck, {
1744
+ className: "skippr:size-4 skippr:text-chart-3"
1745
+ }) : isActive ? /* @__PURE__ */ jsx14(Circle, {
1746
+ className: "skippr:size-4 skippr:fill-primary/30 skippr:text-primary"
1747
+ }) : /* @__PURE__ */ jsx14(Circle, {
1748
+ className: "skippr:size-4 skippr:text-muted-foreground/30"
1749
+ })
1770
1750
  }),
1771
- /* @__PURE__ */ jsxs11("div", {
1772
- className: "skippr:flex skippr:gap-2",
1751
+ /* @__PURE__ */ jsxs13("div", {
1752
+ className: "skippr:min-w-0 skippr:flex-1",
1773
1753
  children: [
1774
- /* @__PURE__ */ jsxs11("button", {
1775
- type: "button",
1776
- onClick: () => setPosition("left"),
1777
- className: cn("skippr:flex skippr:flex-1 skippr:flex-col skippr:items-center skippr:gap-2 skippr:rounded-lg skippr:border skippr:p-3 skippr:cursor-pointer skippr:transition-colors", position === "left" ? "skippr:border-primary skippr:bg-primary/5 skippr:text-primary" : "skippr:border-border skippr:text-muted-foreground skippr:hover:border-primary/40"),
1778
- children: [
1779
- /* @__PURE__ */ jsx14(PanelLeft, {
1780
- className: "skippr:size-5"
1781
- }),
1782
- /* @__PURE__ */ jsx14("span", {
1783
- className: "skippr:text-xs skippr:font-medium",
1784
- children: "Left"
1785
- })
1786
- ]
1754
+ /* @__PURE__ */ jsx14("p", {
1755
+ 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"),
1756
+ children: phase.name
1787
1757
  }),
1788
- /* @__PURE__ */ jsxs11("button", {
1789
- type: "button",
1790
- onClick: () => setPosition("right"),
1791
- className: cn("skippr:flex skippr:flex-1 skippr:flex-col skippr:items-center skippr:gap-2 skippr:rounded-lg skippr:border skippr:p-3 skippr:cursor-pointer skippr:transition-colors", position === "right" ? "skippr:border-primary skippr:bg-primary/5 skippr:text-primary" : "skippr:border-border skippr:text-muted-foreground skippr:hover:border-primary/40"),
1792
- children: [
1793
- /* @__PURE__ */ jsx14(PanelRight, {
1794
- className: "skippr:size-5"
1795
- }),
1796
- /* @__PURE__ */ jsx14("span", {
1797
- className: "skippr:text-xs skippr:font-medium",
1798
- children: "Right"
1799
- })
1800
- ]
1758
+ phase.highlights.length > 0 && /* @__PURE__ */ jsx14("ul", {
1759
+ className: "skippr:mt-1 skippr:space-y-0.5",
1760
+ children: phase.highlights.map((text) => /* @__PURE__ */ jsxs13("li", {
1761
+ 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"),
1762
+ children: [
1763
+ /* @__PURE__ */ jsx14("span", {
1764
+ className: "skippr:size-1 skippr:shrink-0 skippr:rounded-full skippr:bg-current"
1765
+ }),
1766
+ text
1767
+ ]
1768
+ }, text))
1801
1769
  })
1802
1770
  ]
1803
1771
  })
1804
1772
  ]
1805
- })
1773
+ }, phase.name);
1774
+ })
1775
+ })
1776
+ });
1777
+ }
1778
+
1779
+ // src/components/SessionWarningBanner.tsx
1780
+ import { jsx as jsx15 } from "react/jsx-runtime";
1781
+ var SESSION_WARNING_THRESHOLD_SECS = 60;
1782
+ function SessionWarningBanner({ remaining }) {
1783
+ if (remaining === null || remaining <= 0 || remaining > SESSION_WARNING_THRESHOLD_SECS)
1784
+ return null;
1785
+ return /* @__PURE__ */ jsx15("div", {
1786
+ "data-testid": "session-warning-banner",
1787
+ 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",
1788
+ children: "Session ending soon"
1789
+ });
1790
+ }
1791
+
1792
+ // src/components/StartSessionPrompt.tsx
1793
+ import { jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
1794
+ function StartSessionPrompt({
1795
+ onStartSession,
1796
+ isStarting,
1797
+ error,
1798
+ label = "Talk to Skippr"
1799
+ }) {
1800
+ return /* @__PURE__ */ jsxs14("div", {
1801
+ className: "skippr:flex skippr:flex-1 skippr:flex-col skippr:items-center skippr:justify-center skippr:gap-3 skippr:px-4",
1802
+ children: [
1803
+ /* @__PURE__ */ jsx16("button", {
1804
+ type: "button",
1805
+ onClick: onStartSession,
1806
+ disabled: isStarting,
1807
+ 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",
1808
+ children: isStarting ? "Starting..." : label
1809
+ }),
1810
+ error && /* @__PURE__ */ jsx16("p", {
1811
+ className: "skippr:text-xs skippr:text-destructive",
1812
+ children: error
1806
1813
  })
1807
1814
  ]
1808
1815
  });
1809
1816
  }
1810
1817
 
1811
1818
  // src/components/Sidebar.tsx
1812
- import { jsx as jsx15, jsxs as jsxs12, Fragment as Fragment2 } from "react/jsx-runtime";
1813
- function Sidebar() {
1819
+ import { jsx as jsx17, jsxs as jsxs15, Fragment as Fragment2 } from "react/jsx-runtime";
1820
+ function Sidebar({
1821
+ hideControls = false,
1822
+ hideHeader = false,
1823
+ startSessionLabel = "Talk to Skippr"
1824
+ }) {
1814
1825
  const {
1815
1826
  variant,
1816
1827
  isConnected,
@@ -1825,12 +1836,14 @@ function Sidebar() {
1825
1836
  authError,
1826
1837
  requestOtp,
1827
1838
  verifyOtp,
1828
- isAuthSubmitting
1839
+ isAuthSubmitting,
1840
+ sidebarTab: activeTab,
1841
+ setSidebarTab: setActiveTab,
1842
+ autoFocusChat
1829
1843
  } = useLiveAgent();
1830
- const [view, setView] = useState8("main");
1831
1844
  const isFloating = variant === "floating";
1832
1845
  const isSidebar = variant === "sidebar";
1833
- useEffect8(() => {
1846
+ useEffect11(() => {
1834
1847
  if (!isSidebar)
1835
1848
  return;
1836
1849
  const prop = position === "right" ? "marginRight" : "marginLeft";
@@ -1844,105 +1857,160 @@ function Sidebar() {
1844
1857
  document.body.style.transition = "";
1845
1858
  };
1846
1859
  }, [isSidebar, isPanelOpen, position]);
1847
- return /* @__PURE__ */ jsx15("div", {
1848
- className: cn("skippr:fixed skippr:z-[9999]", "skippr:bg-background skippr:border skippr:border-border", "skippr:flex skippr:flex-col", "skippr:overflow-hidden", isFloating && "skippr:bottom-4 skippr:min-h-[28rem] skippr:max-h-[calc(100vh-6rem)] skippr:rounded-2xl skippr:shadow-2xl", isFloating && (position === "right" ? "skippr:right-4" : "skippr:left-4"), 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"),
1860
+ return /* @__PURE__ */ jsxs15("div", {
1861
+ 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-[calc(100vh-112px)] 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"),
1849
1862
  style: { width: isPanelOpen ? SIDEBAR_WIDTH : undefined },
1850
- children: view === "settings" ? /* @__PURE__ */ jsx15(SettingsView, {
1851
- onBack: () => setView("main")
1852
- }) : /* @__PURE__ */ jsxs12(Fragment2, {
1853
- children: [
1854
- /* @__PURE__ */ jsx15(ChatHeader, {
1855
- onOpenSettings: () => setView("settings")
1856
- }),
1857
- isConnected ? /* @__PURE__ */ jsx15(ConnectedContent, {
1858
- onDisconnect: disconnect
1859
- }) : isValidating ? /* @__PURE__ */ jsx15("div", {
1860
- className: "skippr:flex skippr:flex-1 skippr:items-center skippr:justify-center",
1861
- children: /* @__PURE__ */ jsx15("p", {
1862
- className: "skippr:text-sm skippr:text-muted-foreground",
1863
- children: "Loading..."
1864
- })
1865
- }) : isAuthenticated ? /* @__PURE__ */ jsx15(QuickActions, {
1866
- onStartSession: startSession,
1867
- isStarting,
1868
- error
1869
- }) : /* @__PURE__ */ jsx15(LoginFlow, {
1870
- requestOtp,
1871
- verifyOtp,
1872
- error: authError,
1873
- isSubmitting: isAuthSubmitting
1863
+ children: [
1864
+ !hideHeader && /* @__PURE__ */ jsx17(ChatHeader, {}),
1865
+ !isAuthenticated && isValidating ? /* @__PURE__ */ jsx17("div", {
1866
+ className: "skippr:flex skippr:flex-1 skippr:items-center skippr:justify-center",
1867
+ children: /* @__PURE__ */ jsx17(LoadingDots, {
1868
+ label: "Loading..."
1874
1869
  })
1875
- ]
1876
- })
1870
+ }) : !isAuthenticated ? /* @__PURE__ */ jsx17(LoginFlow, {
1871
+ requestOtp,
1872
+ verifyOtp,
1873
+ error: authError,
1874
+ isSubmitting: isAuthSubmitting
1875
+ }) : /* @__PURE__ */ jsx17(AuthenticatedContent, {
1876
+ isConnected,
1877
+ onStartSession: startSession,
1878
+ onDisconnect: disconnect,
1879
+ isStarting,
1880
+ error,
1881
+ activeTab,
1882
+ onTabChange: setActiveTab,
1883
+ hideControls,
1884
+ startSessionLabel,
1885
+ autoFocusChat
1886
+ })
1887
+ ]
1877
1888
  });
1878
1889
  }
1879
- function ConnectedContent({ onDisconnect }) {
1880
- const connectionState = useConnectionState();
1881
- const isConnected = connectionState === ConnectionState.Connected;
1882
- const { allMessages, agentState, sendChatMessage, isSendingChat } = useCombinedMessages();
1883
- const { phases } = usePhaseUpdates();
1884
- const { questions } = useQuestionUpdates();
1885
- if (!isConnected) {
1886
- return /* @__PURE__ */ jsx15("div", {
1887
- className: "skippr:flex skippr:flex-1 skippr:items-center skippr:justify-center",
1888
- children: /* @__PURE__ */ jsx15("p", {
1889
- className: "skippr:text-sm skippr:text-muted-foreground",
1890
- children: "Connecting..."
1891
- })
1892
- });
1893
- }
1894
- const isAgentSpeaking = agentState === "speaking";
1895
- return /* @__PURE__ */ jsxs12(Fragment2, {
1890
+ function AuthenticatedContent({
1891
+ isConnected,
1892
+ onStartSession,
1893
+ onDisconnect,
1894
+ isStarting,
1895
+ error,
1896
+ activeTab,
1897
+ onTabChange,
1898
+ hideControls,
1899
+ startSessionLabel,
1900
+ autoFocusChat
1901
+ }) {
1902
+ return /* @__PURE__ */ jsxs15(Fragment2, {
1896
1903
  children: [
1897
- /* @__PURE__ */ jsx15(MeetingControls, {
1898
- onHangUp: onDisconnect
1899
- }),
1900
- /* @__PURE__ */ jsxs12("div", {
1901
- className: "skippr:flex skippr:min-h-0 skippr:flex-1",
1904
+ isConnected && /* @__PURE__ */ jsx17(ConnectedBanner, {}),
1905
+ /* @__PURE__ */ jsxs15("div", {
1906
+ className: "skippr:flex skippr:gap-2 skippr:border-b skippr:border-border skippr:px-3 skippr:py-2",
1902
1907
  children: [
1903
- /* @__PURE__ */ jsx15("div", {
1904
- className: "skippr:w-[180px] skippr:shrink-0 skippr:overflow-y-auto skippr:border-r",
1905
- children: /* @__PURE__ */ jsx15(SessionAgenda, {
1906
- phases,
1907
- questions
1908
- })
1908
+ /* @__PURE__ */ jsxs15("button", {
1909
+ type: "button",
1910
+ 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"),
1911
+ onClick: () => onTabChange("chat"),
1912
+ children: [
1913
+ /* @__PURE__ */ jsx17(MessageCircle, {
1914
+ className: "skippr:size-3.5"
1915
+ }),
1916
+ "Chat",
1917
+ activeTab === "chat" && /* @__PURE__ */ jsx17("span", {
1918
+ className: "skippr:absolute skippr:-bottom-2 skippr:left-3 skippr:right-3 skippr:h-0.5 skippr:rounded-full skippr:bg-foreground"
1919
+ })
1920
+ ]
1909
1921
  }),
1910
- /* @__PURE__ */ jsx15("div", {
1911
- className: "skippr:flex skippr:min-w-0 skippr:flex-1 skippr:flex-col",
1912
- children: /* @__PURE__ */ jsx15(MessageList, {
1913
- messages: allMessages,
1914
- isStreaming: isAgentSpeaking,
1915
- sendChatMessage,
1916
- isSendingChat
1917
- })
1922
+ /* @__PURE__ */ jsxs15("button", {
1923
+ type: "button",
1924
+ 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"),
1925
+ onClick: () => onTabChange("agenda"),
1926
+ children: [
1927
+ /* @__PURE__ */ jsx17(Calendar, {
1928
+ className: "skippr:size-3.5"
1929
+ }),
1930
+ "Agenda",
1931
+ activeTab === "agenda" && /* @__PURE__ */ jsx17("span", {
1932
+ className: "skippr:absolute skippr:-bottom-2 skippr:left-3 skippr:right-3 skippr:h-0.5 skippr:rounded-full skippr:bg-foreground"
1933
+ })
1934
+ ]
1918
1935
  })
1919
1936
  ]
1937
+ }),
1938
+ /* @__PURE__ */ jsx17("div", {
1939
+ className: "skippr:flex skippr:min-h-0 skippr:flex-1 skippr:flex-col",
1940
+ children: isConnected ? /* @__PURE__ */ jsx17(ConnectedBody, {
1941
+ activeTab,
1942
+ autoFocusChat
1943
+ }) : /* @__PURE__ */ jsx17("div", {
1944
+ className: "skippr:flex skippr:min-h-0 skippr:flex-1 skippr:flex-col skippr:animate-skippr-tab-fade",
1945
+ children: /* @__PURE__ */ jsx17(StartSessionPrompt, {
1946
+ onStartSession,
1947
+ isStarting,
1948
+ error,
1949
+ label: startSessionLabel
1950
+ })
1951
+ }, `${activeTab}-empty`)
1952
+ }),
1953
+ isConnected && !hideControls && /* @__PURE__ */ jsx17(MeetingControls, {
1954
+ onHangUp: onDisconnect
1920
1955
  })
1921
1956
  ]
1922
1957
  });
1923
1958
  }
1959
+ function ConnectedBanner() {
1960
+ const remaining = useSessionRemaining();
1961
+ return /* @__PURE__ */ jsx17(SessionWarningBanner, {
1962
+ remaining
1963
+ });
1964
+ }
1965
+ function ConnectedBody({
1966
+ activeTab,
1967
+ autoFocusChat
1968
+ }) {
1969
+ const { allMessages, agentState, sendChatMessage, isSendingChat } = useCombinedMessages();
1970
+ const { phases } = usePhaseUpdates();
1971
+ if (activeTab === "agenda") {
1972
+ return /* @__PURE__ */ jsx17("div", {
1973
+ className: "skippr:min-h-0 skippr:flex-1 skippr:overflow-y-auto skippr:animate-skippr-tab-fade",
1974
+ children: /* @__PURE__ */ jsx17(SessionAgenda, {
1975
+ phases
1976
+ })
1977
+ }, "agenda");
1978
+ }
1979
+ return /* @__PURE__ */ jsx17("div", {
1980
+ className: "skippr:flex skippr:min-h-0 skippr:flex-1 skippr:flex-col skippr:animate-skippr-tab-fade",
1981
+ children: /* @__PURE__ */ jsx17(MessageList, {
1982
+ messages: allMessages,
1983
+ isStreaming: agentState === "speaking",
1984
+ sendChatMessage,
1985
+ isSendingChat,
1986
+ autoFocus: autoFocusChat
1987
+ })
1988
+ }, "chat");
1989
+ }
1924
1990
 
1925
1991
  // src/components/SidebarTrigger.tsx
1926
- import { jsx as jsx16 } from "react/jsx-runtime";
1992
+ import { jsx as jsx18 } from "react/jsx-runtime";
1927
1993
  function SidebarTrigger() {
1928
- const { isPanelOpen, togglePanel, position, isMinimized } = useLiveAgent();
1994
+ const { isPanelOpen, togglePanel, minimizePanel, minimizable, position, isMinimized } = useLiveAgent();
1929
1995
  if (isMinimized)
1930
1996
  return null;
1931
- return /* @__PURE__ */ jsx16(Button, {
1932
- size: "icon-lg",
1933
- onClick: togglePanel,
1934
- className: cn("skippr:fixed skippr:bottom-6 skippr:z-[9998] skippr:size-14 skippr:rounded-full skippr:shadow-lg skippr:transition-all skippr:duration-300", position === "right" ? "skippr:right-6" : "skippr:left-6"),
1935
- title: isPanelOpen ? "Close chat" : "Chat with us",
1936
- children: isPanelOpen ? /* @__PURE__ */ jsx16(X, {
1937
- className: "skippr:size-6"
1938
- }) : /* @__PURE__ */ jsx16(MessageCircle, {
1939
- className: "skippr:size-6"
1997
+ const handleClick = isPanelOpen && minimizable ? minimizePanel : togglePanel;
1998
+ return /* @__PURE__ */ jsx18("button", {
1999
+ type: "button",
2000
+ onClick: handleClick,
2001
+ title: isPanelOpen ? "Close chat" : "Open chat",
2002
+ "aria-label": isPanelOpen ? "Close chat" : "Open chat",
2003
+ 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"),
2004
+ children: isPanelOpen ? /* @__PURE__ */ jsx18(ChevronDown, {
2005
+ className: "skippr:size-5"
2006
+ }) : /* @__PURE__ */ jsx18(Logo, {
2007
+ className: "skippr:size-7"
1940
2008
  })
1941
2009
  });
1942
2010
  }
1943
2011
 
1944
2012
  // src/components/LiveAgent.tsx
1945
- import { jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
2013
+ import { jsx as jsx19, jsxs as jsxs16 } from "react/jsx-runtime";
1946
2014
  function LiveAgent({
1947
2015
  agentId,
1948
2016
  authToken: authTokenProp,
@@ -1953,21 +2021,36 @@ function LiveAgent({
1953
2021
  minimizable = true,
1954
2022
  defaultOpen = false,
1955
2023
  welcomeMessage,
2024
+ hideControls = false,
2025
+ hideHeader = false,
2026
+ startSessionLabel = "Talk to Skippr",
2027
+ autoFocusChat = true,
2028
+ showObservingBanner = true,
1956
2029
  children
1957
2030
  }) {
1958
2031
  const auth = useAuth({ appKey });
1959
2032
  const effectiveAuthToken = authTokenProp || auth.authToken || undefined;
1960
- const { connection, shouldConnect, isStarting, error, errorCode, startSession, disconnect } = useSession({
2033
+ const {
2034
+ connection,
2035
+ shouldConnect,
2036
+ isStarting,
2037
+ error,
2038
+ errorCode,
2039
+ startSession,
2040
+ disconnect,
2041
+ pendingScreenStream
2042
+ } = useSession({
1961
2043
  agentId,
1962
2044
  authToken: effectiveAuthToken,
1963
2045
  appKey,
1964
2046
  userToken
1965
2047
  });
1966
- const [isPanelOpen, setIsPanelOpen] = useState9(defaultOpen);
1967
- const [isMinimized, setIsMinimized] = useState9(minimizable && !defaultOpen);
1968
- const [welcomeDismissed, setWelcomeDismissed] = useState9(false);
1969
- const dismissWelcome = useCallback8(() => setWelcomeDismissed(true), []);
1970
- const [currentPosition, setCurrentPosition] = useState9(() => {
2048
+ const [isPanelOpen, setIsPanelOpen] = useState8(defaultOpen);
2049
+ const [isMinimized, setIsMinimized] = useState8(minimizable && !defaultOpen);
2050
+ const [sidebarTab, setSidebarTab] = useState8("agenda");
2051
+ const [welcomeDismissed, setWelcomeDismissed] = useState8(false);
2052
+ const dismissWelcome = useCallback6(() => setWelcomeDismissed(true), []);
2053
+ const [currentPosition, setCurrentPosition] = useState8(() => {
1971
2054
  try {
1972
2055
  const saved = localStorage.getItem("skippr_widget_position");
1973
2056
  if (saved === "left" || saved === "right")
@@ -1975,20 +2058,20 @@ function LiveAgent({
1975
2058
  } catch {}
1976
2059
  return position;
1977
2060
  });
1978
- const setPositionWithPersist = useCallback8((pos) => {
2061
+ const setPositionWithPersist = useCallback6((pos) => {
1979
2062
  setCurrentPosition(pos);
1980
2063
  try {
1981
2064
  localStorage.setItem("skippr_widget_position", pos);
1982
2065
  } catch {}
1983
2066
  }, []);
1984
- const openPanel = useCallback8(() => setIsPanelOpen(true), []);
1985
- const closePanel = useCallback8(() => setIsPanelOpen(false), []);
1986
- const togglePanel = useCallback8(() => setIsPanelOpen((prev) => !prev), []);
1987
- const expandPanel = useCallback8(() => {
2067
+ const openPanel = useCallback6(() => setIsPanelOpen(true), []);
2068
+ const closePanel = useCallback6(() => setIsPanelOpen(false), []);
2069
+ const togglePanel = useCallback6(() => setIsPanelOpen((prev) => !prev), []);
2070
+ const expandPanel = useCallback6(() => {
1988
2071
  setIsMinimized(false);
1989
2072
  setIsPanelOpen(true);
1990
2073
  }, []);
1991
- const minimizePanel = useCallback8(() => {
2074
+ const minimizePanel = useCallback6(() => {
1992
2075
  if (!minimizable)
1993
2076
  return;
1994
2077
  setIsMinimized(true);
@@ -1996,8 +2079,8 @@ function LiveAgent({
1996
2079
  }, [minimizable]);
1997
2080
  const isConnected = connection !== null;
1998
2081
  const isAuthenticated = !!userToken || !!authTokenProp || auth.isAuthenticated;
1999
- const prevConnectionRef = useRef5(connection);
2000
- useEffect9(() => {
2082
+ const prevConnectionRef = useRef6(connection);
2083
+ useEffect12(() => {
2001
2084
  const connectionChanged = prevConnectionRef.current !== connection;
2002
2085
  prevConnectionRef.current = connection;
2003
2086
  if (connectionChanged && minimizable) {
@@ -2031,7 +2114,10 @@ function LiveAgent({
2031
2114
  requestOtp: auth.requestOtp,
2032
2115
  verifyOtp: auth.verifyOtp,
2033
2116
  logoutAuth: auth.logout,
2034
- isAuthSubmitting: auth.isSubmitting
2117
+ isAuthSubmitting: auth.isSubmitting,
2118
+ sidebarTab,
2119
+ setSidebarTab,
2120
+ autoFocusChat
2035
2121
  }), [
2036
2122
  connection,
2037
2123
  shouldConnect,
@@ -2058,31 +2144,54 @@ function LiveAgent({
2058
2144
  auth.requestOtp,
2059
2145
  auth.verifyOtp,
2060
2146
  auth.logout,
2061
- auth.isSubmitting
2147
+ auth.isSubmitting,
2148
+ sidebarTab,
2149
+ autoFocusChat
2062
2150
  ]);
2063
- return /* @__PURE__ */ jsx17(LiveAgentContext.Provider, {
2151
+ return /* @__PURE__ */ jsx19(LiveAgentContext.Provider, {
2064
2152
  value: ctx,
2065
- children: /* @__PURE__ */ jsxs13(LiveKitRoom, {
2153
+ children: /* @__PURE__ */ jsxs16(LiveKitRoom, {
2066
2154
  serverUrl: connection?.livekitUrl,
2067
2155
  token: connection?.token,
2068
2156
  connect: shouldConnect,
2069
2157
  audio: true,
2070
2158
  onDisconnected: disconnect,
2071
2159
  children: [
2072
- connection && /* @__PURE__ */ jsx17(RoomAudioRenderer, {}),
2073
- isMinimized && /* @__PURE__ */ jsx17(MinimizedBubble, {
2160
+ connection && /* @__PURE__ */ jsx19(RoomAudioRenderer, {}),
2161
+ showObservingBanner && /* @__PURE__ */ jsx19(ObservingBanner, {}),
2162
+ connection && /* @__PURE__ */ jsx19(AutoStartMedia, {
2163
+ pendingScreenStream
2164
+ }),
2165
+ isMinimized && /* @__PURE__ */ jsx19(MinimizedBubble, {
2074
2166
  welcomeMessage,
2075
2167
  welcomeDismissed,
2076
2168
  onDismissWelcome: dismissWelcome
2077
2169
  }),
2078
- /* @__PURE__ */ jsx17(SidebarTrigger, {}),
2079
- /* @__PURE__ */ jsx17(Sidebar, {}),
2170
+ /* @__PURE__ */ jsx19(SidebarTrigger, {}),
2171
+ /* @__PURE__ */ jsx19(Sidebar, {
2172
+ hideControls,
2173
+ hideHeader,
2174
+ startSessionLabel
2175
+ }),
2080
2176
  children
2081
2177
  ]
2082
2178
  })
2083
2179
  });
2084
2180
  }
2181
+ // src/hooks/useIsLocalSpeaking.ts
2182
+ import { useIsSpeaking, useLocalParticipant as useLocalParticipant5 } from "@livekit/components-react";
2183
+ function useIsLocalSpeaking() {
2184
+ const { localParticipant } = useLocalParticipant5();
2185
+ return useIsSpeaking(localParticipant);
2186
+ }
2085
2187
  export {
2188
+ useMediaControls,
2086
2189
  useLiveAgent,
2190
+ useIsLocalSpeaking,
2191
+ useElapsedSeconds,
2192
+ useAgentVoiceState,
2193
+ parseNumber,
2194
+ formatTime,
2195
+ SCREEN_SHARE_OPTIONS,
2087
2196
  LiveAgent
2088
2197
  };