@skippr/live-agent-sdk 0.23.0 → 0.25.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.
Files changed (32) hide show
  1. package/README.md +20 -1
  2. package/dist/esm/lib-exports.js +979 -940
  3. package/dist/esm/logo-icon-cn8fyke6.png +0 -0
  4. package/dist/logo-icon-cn8fyke6.png +0 -0
  5. package/dist/skippr-sdk.css +1 -1
  6. package/dist/skippr-sdk.js +139 -139
  7. package/dist/types/components/AutoStartMedia.d.ts +5 -0
  8. package/dist/types/components/ChatHeader.d.ts +1 -5
  9. package/dist/types/components/ChatInput.d.ts +2 -1
  10. package/dist/types/components/LiveAgent.d.ts +6 -1
  11. package/dist/types/components/LoadingDots.d.ts +5 -0
  12. package/dist/types/components/MessageList.d.ts +2 -1
  13. package/dist/types/components/ObservingBanner.d.ts +1 -0
  14. package/dist/types/components/SessionAgenda.d.ts +1 -3
  15. package/dist/types/components/Sidebar.d.ts +7 -1
  16. package/dist/types/components/StartSessionPrompt.d.ts +8 -0
  17. package/dist/types/components/ui/tooltip.d.ts +3 -1
  18. package/dist/types/context/LiveAgentContext.d.ts +4 -0
  19. package/dist/types/hooks/useAgentVoiceState.d.ts +9 -0
  20. package/dist/types/hooks/useElapsedSeconds.d.ts +1 -0
  21. package/dist/types/hooks/useIsLocalSpeaking.d.ts +1 -0
  22. package/dist/types/hooks/useMediaControls.d.ts +14 -0
  23. package/dist/types/hooks/usePhaseUpdates.d.ts +1 -0
  24. package/dist/types/hooks/useSession.d.ts +1 -0
  25. package/dist/types/hooks/useSessionRemaining.d.ts +1 -0
  26. package/dist/types/lib/assets.d.ts +1 -0
  27. package/dist/types/lib/constants.d.ts +1 -1
  28. package/dist/types/lib-exports.d.ts +5 -0
  29. package/package.json +1 -1
  30. package/dist/types/components/QuickActions.d.ts +0 -7
  31. package/dist/types/components/SettingsView.d.ts +0 -5
  32. package/dist/types/hooks/useQuestionUpdates.d.ts +0 -11
@@ -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,42 @@ 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
+
600
+ // src/assets/logo-icon.png
601
+ var logo_icon_default = "./logo-icon-cn8fyke6.png";
602
+
603
+ // src/lib/assets.ts
604
+ var LOGO_URL = logo_icon_default;
605
+
527
606
  // src/lib/utils.ts
528
607
  import { clsx } from "clsx";
529
608
  import { twMerge } from "tailwind-merge";
@@ -533,13 +612,18 @@ function cn(...inputs) {
533
612
 
534
613
  // src/components/ui/tooltip.tsx
535
614
  import { jsx, jsxs } from "react/jsx-runtime";
536
- function Tooltip({ label, children, position = "top" }) {
615
+ var ALIGN_CLASSES = {
616
+ center: "skippr:left-1/2 skippr:-translate-x-1/2",
617
+ start: "skippr:left-0",
618
+ end: "skippr:right-0"
619
+ };
620
+ function Tooltip({ label, children, position = "top", align = "center" }) {
537
621
  return /* @__PURE__ */ jsxs("span", {
538
622
  className: "skippr:relative skippr:inline-flex skippr:group",
539
623
  children: [
540
624
  children,
541
625
  /* @__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"),
626
+ 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
627
  "aria-hidden": "true",
544
628
  children: label
545
629
  })
@@ -549,140 +633,112 @@ function Tooltip({ label, children, position = "top" }) {
549
633
 
550
634
  // src/components/MinimizedBubble.tsx
551
635
  import { jsx as jsx2, jsxs as jsxs2, Fragment } from "react/jsx-runtime";
552
- function AgentAvatar({ agentState }) {
636
+ 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";
637
+ function AgentBubbleContent({ agentState }) {
553
638
  if (agentState === "speaking") {
554
639
  return /* @__PURE__ */ jsxs2("div", {
555
- className: "skippr:flex skippr:items-end skippr:justify-center skippr:gap-[3px] skippr:h-5",
640
+ className: "skippr:flex skippr:h-5 skippr:items-center skippr:justify-center skippr:gap-[3px]",
556
641
  children: [
557
642
  /* @__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"
643
+ className: "skippr:w-[3px] skippr:h-2 skippr:rounded-sm skippr:bg-white skippr:animate-[skippr-speak_1.1s_ease-in-out_infinite]"
644
+ }),
645
+ /* @__PURE__ */ jsx2("span", {
646
+ 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]"
559
647
  }),
560
648
  /* @__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"
649
+ 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]"
562
650
  }),
563
651
  /* @__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"
652
+ 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]"
565
653
  }),
566
654
  /* @__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"
655
+ 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
656
  })
569
657
  ]
570
658
  });
571
659
  }
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
660
  if (agentState === "thinking") {
578
- return /* @__PURE__ */ jsx2(Bot, {
579
- className: "skippr:relative skippr:z-10 skippr:size-5 skippr:animate-pulse"
661
+ return /* @__PURE__ */ jsxs2("div", {
662
+ className: "skippr:flex skippr:items-center skippr:justify-center skippr:gap-[4px]",
663
+ children: [
664
+ /* @__PURE__ */ jsx2("span", {
665
+ className: "skippr:size-1.5 skippr:rounded-full skippr:bg-white skippr:animate-bounce skippr:[animation-delay:0ms]"
666
+ }),
667
+ /* @__PURE__ */ jsx2("span", {
668
+ className: "skippr:size-1.5 skippr:rounded-full skippr:bg-white skippr:animate-bounce skippr:[animation-delay:150ms]"
669
+ }),
670
+ /* @__PURE__ */ jsx2("span", {
671
+ className: "skippr:size-1.5 skippr:rounded-full skippr:bg-white skippr:animate-bounce skippr:[animation-delay:300ms]"
672
+ })
673
+ ]
580
674
  });
581
675
  }
582
- return /* @__PURE__ */ jsx2(Bot, {
583
- className: "skippr:relative skippr:z-10 skippr:size-5"
676
+ return /* @__PURE__ */ jsx2("img", {
677
+ src: LOGO_URL,
678
+ alt: "Skippr",
679
+ className: "skippr:size-7"
584
680
  });
585
681
  }
586
682
  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";
683
+ const { expandPanel, disconnect, position } = useLiveAgent();
684
+ const { state: agentState } = useAgentVoiceState();
685
+ const { isMuted, toggleMute, isScreenSharing, toggleScreenShare } = useMediaControls();
686
+ const tooltipAlign = position === "right" ? "end" : "start";
612
687
  return /* @__PURE__ */ jsxs2(Fragment, {
613
688
  children: [
614
689
  /* @__PURE__ */ jsx2(Tooltip, {
615
- label: "Open chat & transcript",
616
- children: /* @__PURE__ */ jsxs2("button", {
690
+ label: isMuted ? "Unmute" : "Mute",
691
+ align: tooltipAlign,
692
+ children: /* @__PURE__ */ jsx2("button", {
617
693
  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
- ]
694
+ onClick: toggleMute,
695
+ "aria-label": isMuted ? "Unmute" : "Mute",
696
+ 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"),
697
+ children: isMuted ? /* @__PURE__ */ jsx2(MicOff, {
698
+ className: "skippr:size-5"
699
+ }) : /* @__PURE__ */ jsx2(Mic, {
700
+ className: "skippr:size-5"
701
+ })
636
702
  })
637
703
  }),
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
- })
704
+ /* @__PURE__ */ jsx2(Tooltip, {
705
+ label: isScreenSharing ? "Stop sharing" : "Share screen",
706
+ align: tooltipAlign,
707
+ children: /* @__PURE__ */ jsx2("button", {
708
+ type: "button",
709
+ onClick: toggleScreenShare,
710
+ "aria-label": isScreenSharing ? "Stop sharing screen" : "Share screen",
711
+ 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"),
712
+ children: isScreenSharing ? /* @__PURE__ */ jsx2(MonitorOff, {
713
+ className: "skippr:size-5"
714
+ }) : /* @__PURE__ */ jsx2(Monitor, {
715
+ className: "skippr:size-5"
671
716
  })
672
- ]
673
- }),
674
- /* @__PURE__ */ jsx2("div", {
675
- className: "skippr:mx-1 skippr:h-6 skippr:w-px skippr:bg-border"
717
+ })
676
718
  }),
677
719
  /* @__PURE__ */ jsx2(Tooltip, {
678
720
  label: "End session",
721
+ align: tooltipAlign,
679
722
  children: /* @__PURE__ */ jsx2("button", {
680
723
  type: "button",
681
724
  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",
725
+ "aria-label": "End session",
726
+ className: cn(BUBBLE_BUTTON, "skippr:bg-destructive skippr:text-destructive-foreground skippr:hover:bg-destructive/90"),
684
727
  children: /* @__PURE__ */ jsx2(PhoneOff, {
685
- className: "skippr:size-4"
728
+ className: "skippr:size-5"
729
+ })
730
+ })
731
+ }),
732
+ /* @__PURE__ */ jsx2(Tooltip, {
733
+ label: "Open chat & transcript",
734
+ align: tooltipAlign,
735
+ children: /* @__PURE__ */ jsx2("button", {
736
+ type: "button",
737
+ onClick: expandPanel,
738
+ "aria-label": "Open chat & transcript",
739
+ className: cn(BUBBLE_BUTTON, "skippr:bg-bubble skippr:hover:brightness-110"),
740
+ children: /* @__PURE__ */ jsx2(AgentBubbleContent, {
741
+ agentState
686
742
  })
687
743
  })
688
744
  })
@@ -690,123 +746,27 @@ function ConnectedBubbleContent() {
690
746
  });
691
747
  }
692
748
  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"
749
+ const { expandPanel, position } = useLiveAgent();
750
+ const tooltipAlign = position === "right" ? "end" : "start";
751
+ return /* @__PURE__ */ jsx2(Tooltip, {
752
+ label: "Open Skippr assistant",
753
+ align: tooltipAlign,
754
+ children: /* @__PURE__ */ jsxs2("button", {
755
+ type: "button",
756
+ onClick: expandPanel,
757
+ "aria-label": "Skippr assistant",
758
+ className: cn(BUBBLE_BUTTON, "skippr:relative skippr:bg-bubble skippr:hover:brightness-110"),
759
+ children: [
760
+ /* @__PURE__ */ jsx2("img", {
761
+ src: LOGO_URL,
762
+ alt: "Skippr",
763
+ className: "skippr:relative skippr:z-10 skippr:size-7"
764
+ }),
765
+ /* @__PURE__ */ jsx2("span", {
766
+ className: "skippr:absolute skippr:-inset-[3px] skippr:animate-pulse skippr:rounded-[17px] skippr:border-2 skippr:border-bubble/50"
723
767
  })
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
- ]
768
+ ]
769
+ })
810
770
  });
811
771
  }
812
772
  function WelcomeBubble({
@@ -814,7 +774,7 @@ function WelcomeBubble({
814
774
  position,
815
775
  onDismiss
816
776
  }) {
817
- useEffect3(() => {
777
+ useEffect4(() => {
818
778
  const timer = setTimeout(onDismiss, 5000);
819
779
  return () => clearTimeout(timer);
820
780
  }, [onDismiss]);
@@ -836,31 +796,63 @@ function MinimizedBubble({
836
796
  welcomeDismissed,
837
797
  onDismissWelcome
838
798
  }) {
839
- const { isConnected, position } = useLiveAgent();
799
+ const { isConnected, isStarting, position } = useLiveAgent();
800
+ const inSession = isConnected || isStarting;
840
801
  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"),
802
+ 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
803
  children: [
843
- welcomeMessage && !isConnected && !welcomeDismissed && /* @__PURE__ */ jsx2(WelcomeBubble, {
804
+ welcomeMessage && !inSession && !welcomeDismissed && /* @__PURE__ */ jsx2(WelcomeBubble, {
844
805
  message: welcomeMessage,
845
806
  position,
846
807
  onDismiss: onDismissWelcome
847
808
  }),
848
- isConnected ? /* @__PURE__ */ jsx2(ConnectedBubbleContent, {}) : /* @__PURE__ */ jsx2(IdleBubbleContent, {})
809
+ inSession ? /* @__PURE__ */ jsx2(ConnectedBubbleContent, {}) : /* @__PURE__ */ jsx2(IdleBubbleContent, {})
849
810
  ]
850
811
  });
851
812
  }
852
813
 
814
+ // src/components/ObservingBanner.tsx
815
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
816
+ function ObservingBanner() {
817
+ const { isConnected } = useLiveAgent();
818
+ const { isScreenSharing } = useMediaControls();
819
+ if (!isConnected || !isScreenSharing)
820
+ return null;
821
+ return /* @__PURE__ */ jsx3("div", {
822
+ className: "skippr:fixed skippr:top-0 skippr:left-0 skippr:right-0 skippr:z-[2147483647] skippr:flex skippr:justify-center skippr:pointer-events-none",
823
+ children: /* @__PURE__ */ jsxs3("div", {
824
+ 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",
825
+ children: [
826
+ /* @__PURE__ */ jsxs3("span", {
827
+ className: "skippr:relative skippr:flex skippr:size-1.5",
828
+ children: [
829
+ /* @__PURE__ */ jsx3("span", {
830
+ className: "skippr:absolute skippr:inline-flex skippr:size-full skippr:animate-ping skippr:rounded-full skippr:bg-emerald-400 skippr:opacity-75"
831
+ }),
832
+ /* @__PURE__ */ jsx3("span", {
833
+ className: "skippr:relative skippr:inline-flex skippr:size-1.5 skippr:rounded-full skippr:bg-emerald-400"
834
+ })
835
+ ]
836
+ }),
837
+ /* @__PURE__ */ jsx3(Eye, {
838
+ className: "skippr:size-3.5"
839
+ }),
840
+ /* @__PURE__ */ jsx3("span", {
841
+ children: "Skippr is observing this page"
842
+ })
843
+ ]
844
+ })
845
+ });
846
+ }
847
+
853
848
  // 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";
849
+ import { useEffect as useEffect11 } from "react";
857
850
 
858
851
  // src/hooks/useCombinedMessages.ts
859
- import { useVoiceAssistant as useVoiceAssistant2 } from "@livekit/components-react";
860
852
  import { useMemo as useMemo4 } from "react";
861
853
 
862
854
  // src/hooks/useChatMessages.ts
863
- import { useChat, useLocalParticipant as useLocalParticipant2 } from "@livekit/components-react";
855
+ import { useChat, useLocalParticipant as useLocalParticipant3 } from "@livekit/components-react";
864
856
  import { useMemo as useMemo2 } from "react";
865
857
 
866
858
  // src/lib/filterSystemMessages.ts
@@ -872,7 +864,7 @@ function filterSystemMessages(messages) {
872
864
  // src/hooks/useChatMessages.ts
873
865
  function useChatMessages() {
874
866
  const { chatMessages: rawMessages, send, isSending } = useChat();
875
- const { localParticipant } = useLocalParticipant2();
867
+ const { localParticipant } = useLocalParticipant3();
876
868
  const localIdentity = localParticipant.identity;
877
869
  const chatMessages = useMemo2(() => {
878
870
  const sortedMessages = rawMessages.map((msg) => ({
@@ -888,11 +880,11 @@ function useChatMessages() {
888
880
  }
889
881
 
890
882
  // src/hooks/useStreamingTranscript.ts
891
- import { useLocalParticipant as useLocalParticipant3, useTranscriptions } from "@livekit/components-react";
883
+ import { useLocalParticipant as useLocalParticipant4, useTranscriptions } from "@livekit/components-react";
892
884
  import { useMemo as useMemo3 } from "react";
893
885
  function useStreamingTranscript() {
894
886
  const transcriptions = useTranscriptions();
895
- const { localParticipant } = useLocalParticipant3();
887
+ const { localParticipant } = useLocalParticipant4();
896
888
  const localIdentity = localParticipant.identity;
897
889
  const transcriptMessages = useMemo3(() => filterSystemMessages(transcriptions.filter((stream) => stream.text.trim().length > 0).map((stream) => ({
898
890
  id: stream.streamInfo.id,
@@ -924,7 +916,7 @@ function mergeChatsIntoTranscripts(transcripts, chats) {
924
916
  function useCombinedMessages() {
925
917
  const { transcriptMessages } = useStreamingTranscript();
926
918
  const { chatMessages, sendChatMessage, isSendingChat } = useChatMessages();
927
- const { state: agentState } = useVoiceAssistant2();
919
+ const { state: agentState } = useAgentVoiceState();
928
920
  const allMessages = useMemo4(() => {
929
921
  if (chatMessages.length === 0)
930
922
  return transcriptMessages;
@@ -940,11 +932,11 @@ import { useCallback as useCallback4 } from "react";
940
932
 
941
933
  // src/hooks/useAgentState.ts
942
934
  import { useRemoteParticipants } from "@livekit/components-react";
943
- import { useEffect as useEffect4, useState as useState4 } from "react";
935
+ import { useEffect as useEffect5, useState as useState3 } from "react";
944
936
  function useAgentState(attributeKey, parse, initial) {
945
- const [value, setValue] = useState4(initial);
937
+ const [value, setValue] = useState3(initial);
946
938
  const remoteParticipants = useRemoteParticipants();
947
- useEffect4(() => {
939
+ useEffect5(() => {
948
940
  const agentParticipant = remoteParticipants.find((p) => p.attributes?.[attributeKey]);
949
941
  if (agentParticipant) {
950
942
  const attr = agentParticipant.attributes?.[attributeKey];
@@ -995,96 +987,109 @@ function usePhaseUpdates() {
995
987
  return { phases };
996
988
  }
997
989
 
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;
990
+ // src/hooks/useSessionRemaining.ts
991
+ import { useEffect as useEffect6, useRef as useRef2, useState as useState4 } from "react";
992
+
993
+ // src/lib/format.ts
994
+ function formatTime(seconds) {
995
+ const m = Math.floor(seconds / 60).toString().padStart(2, "0");
996
+ const s = (seconds % 60).toString().padStart(2, "0");
997
+ return `${m}:${s}`;
1008
998
  }
1009
- function useQuestionUpdates() {
1010
- const parse = useCallback5(parseQuestions, []);
1011
- const questions = useAgentState("questions", parse, []);
1012
- return { questions };
999
+ function parseNumber(s) {
1000
+ const n = Number(s);
1001
+ return n > 0 ? n : null;
1002
+ }
1003
+
1004
+ // src/hooks/useSessionRemaining.ts
1005
+ function useSessionRemaining() {
1006
+ const maxCallDuration = useAgentState("maxCallDuration", parseNumber, null);
1007
+ const endTimeRef = useRef2(null);
1008
+ const [remaining, setRemaining] = useState4(null);
1009
+ useEffect6(() => {
1010
+ if (maxCallDuration === null || endTimeRef.current !== null)
1011
+ return;
1012
+ const endTime = Date.now() + maxCallDuration * 1000;
1013
+ endTimeRef.current = endTime;
1014
+ const tick = () => {
1015
+ const secs = Math.ceil((endTime - Date.now()) / 1000);
1016
+ setRemaining(Math.max(secs, 0));
1017
+ };
1018
+ tick();
1019
+ const id = setInterval(tick, 1000);
1020
+ return () => clearInterval(id);
1021
+ }, [maxCallDuration]);
1022
+ return remaining;
1013
1023
  }
1014
1024
 
1015
1025
  // src/lib/constants.ts
1016
- var SIDEBAR_WIDTH = 480;
1026
+ var SIDEBAR_WIDTH = 360;
1017
1027
 
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 = {
1022
- default: "skippr:bg-primary skippr:text-primary-foreground skippr:hover:bg-primary/90",
1023
- destructive: "skippr:bg-destructive skippr:text-white skippr:hover:bg-destructive/90",
1024
- outline: "skippr:border skippr:border-input skippr:bg-background skippr:shadow-xs skippr:hover:bg-accent skippr:hover:text-accent-foreground",
1025
- secondary: "skippr:bg-secondary skippr:text-secondary-foreground skippr:hover:bg-secondary/80",
1026
- ghost: "skippr:hover:bg-accent skippr:hover:text-accent-foreground"
1027
- };
1028
- var sizeClasses = {
1029
- default: "skippr:h-9 skippr:px-4 skippr:py-2",
1030
- xs: "skippr:h-6 skippr:gap-1 skippr:rounded-md skippr:px-2 skippr:text-xs",
1031
- sm: "skippr:h-8 skippr:gap-1.5 skippr:rounded-md skippr:px-3",
1032
- lg: "skippr:h-10 skippr:rounded-md skippr:px-6",
1033
- icon: "skippr:size-9",
1034
- "icon-xs": "skippr:size-6",
1035
- "icon-sm": "skippr:size-8",
1036
- "icon-lg": "skippr:size-10"
1037
- };
1038
- var Button = forwardRef3(({ className, variant = "default", size = "default", ...props }, ref) => {
1039
- return /* @__PURE__ */ jsx3("button", {
1040
- 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
- ref,
1042
- ...props
1043
- });
1044
- });
1045
- Button.displayName = "Button";
1028
+ // src/hooks/useElapsedSeconds.ts
1029
+ import { useEffect as useEffect7, useState as useState5 } from "react";
1030
+ function useElapsedSeconds(isRunning) {
1031
+ const [elapsed, setElapsed] = useState5(0);
1032
+ useEffect7(() => {
1033
+ if (!isRunning) {
1034
+ setElapsed(0);
1035
+ return;
1036
+ }
1037
+ const startedAt = Date.now();
1038
+ setElapsed(0);
1039
+ const id = setInterval(() => {
1040
+ setElapsed(Math.floor((Date.now() - startedAt) / 1000));
1041
+ }, 1000);
1042
+ return () => clearInterval(id);
1043
+ }, [isRunning]);
1044
+ return elapsed;
1045
+ }
1046
1046
 
1047
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",
1048
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
1049
+ function ChatHeader() {
1050
+ const { isConnected, minimizePanel, minimizable } = useLiveAgent();
1051
+ const elapsed = useElapsedSeconds(isConnected);
1052
+ return /* @__PURE__ */ jsxs4("header", {
1053
+ 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",
1053
1054
  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
- })
1055
+ /* @__PURE__ */ jsx4("p", {
1056
+ className: "skippr:text-sm skippr:font-semibold skippr:text-primary-foreground",
1057
+ children: "Skippr"
1059
1058
  }),
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",
1059
+ /* @__PURE__ */ jsxs4("div", {
1060
+ className: "skippr:flex skippr:items-center skippr:gap-2",
1069
1061
  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
- })
1062
+ isConnected && /* @__PURE__ */ jsxs4("div", {
1063
+ 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",
1064
+ children: [
1065
+ /* @__PURE__ */ jsxs4("span", {
1066
+ className: "skippr:relative skippr:flex skippr:size-1.5",
1067
+ children: [
1068
+ /* @__PURE__ */ jsx4("span", {
1069
+ 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"
1070
+ }),
1071
+ /* @__PURE__ */ jsx4("span", {
1072
+ className: "skippr:relative skippr:inline-flex skippr:size-1.5 skippr:rounded-full skippr:bg-red-400"
1073
+ })
1074
+ ]
1075
+ }),
1076
+ /* @__PURE__ */ jsx4("span", {
1077
+ className: "skippr:text-[10px] skippr:font-medium skippr:text-primary-foreground",
1078
+ children: "REC"
1079
+ }),
1080
+ /* @__PURE__ */ jsx4("span", {
1081
+ className: "skippr:text-[10px] skippr:font-mono skippr:text-primary-foreground",
1082
+ children: formatTime(elapsed)
1083
+ })
1084
+ ]
1079
1085
  }),
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"
1086
+ minimizable && /* @__PURE__ */ jsx4("button", {
1087
+ type: "button",
1088
+ onClick: minimizePanel,
1089
+ "aria-label": "Minimize",
1090
+ 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",
1091
+ children: /* @__PURE__ */ jsx4(Minimize2, {
1092
+ className: "skippr:size-3.5"
1088
1093
  })
1089
1094
  })
1090
1095
  ]
@@ -1093,30 +1098,89 @@ function ChatHeader({ onOpenSettings }) {
1093
1098
  });
1094
1099
  }
1095
1100
 
1101
+ // src/components/LoadingDots.tsx
1102
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1103
+ function LoadingDots({ label }) {
1104
+ return /* @__PURE__ */ jsxs5("div", {
1105
+ className: "skippr:flex skippr:items-center skippr:gap-2 skippr:py-4",
1106
+ children: [
1107
+ /* @__PURE__ */ jsxs5("div", {
1108
+ className: "skippr:flex skippr:gap-1",
1109
+ children: [
1110
+ /* @__PURE__ */ jsx5("span", {
1111
+ className: "skippr:size-1.5 skippr:rounded-full skippr:bg-muted-foreground/40 skippr:animate-bounce skippr:[animation-delay:0ms]"
1112
+ }),
1113
+ /* @__PURE__ */ jsx5("span", {
1114
+ className: "skippr:size-1.5 skippr:rounded-full skippr:bg-muted-foreground/40 skippr:animate-bounce skippr:[animation-delay:150ms]"
1115
+ }),
1116
+ /* @__PURE__ */ jsx5("span", {
1117
+ className: "skippr:size-1.5 skippr:rounded-full skippr:bg-muted-foreground/40 skippr:animate-bounce skippr:[animation-delay:300ms]"
1118
+ })
1119
+ ]
1120
+ }),
1121
+ /* @__PURE__ */ jsx5("p", {
1122
+ className: "skippr:text-xs skippr:text-muted-foreground",
1123
+ children: label
1124
+ })
1125
+ ]
1126
+ });
1127
+ }
1128
+
1129
+ // src/components/LoginFlow.tsx
1130
+ import { useCallback as useCallback5, useEffect as useEffect8, useRef as useRef3, useState as useState6 } from "react";
1131
+
1132
+ // src/components/ui/button.tsx
1133
+ import { forwardRef as forwardRef3 } from "react";
1134
+ import { jsx as jsx6 } from "react/jsx-runtime";
1135
+ var variantClasses = {
1136
+ default: "skippr:bg-primary skippr:text-primary-foreground skippr:hover:bg-primary/90",
1137
+ destructive: "skippr:bg-destructive skippr:text-white skippr:hover:bg-destructive/90",
1138
+ outline: "skippr:border skippr:border-input skippr:bg-background skippr:shadow-xs skippr:hover:bg-accent skippr:hover:text-accent-foreground",
1139
+ secondary: "skippr:bg-secondary skippr:text-secondary-foreground skippr:hover:bg-secondary/80",
1140
+ ghost: "skippr:hover:bg-accent skippr:hover:text-accent-foreground"
1141
+ };
1142
+ var sizeClasses = {
1143
+ default: "skippr:h-9 skippr:px-4 skippr:py-2",
1144
+ xs: "skippr:h-6 skippr:gap-1 skippr:rounded-md skippr:px-2 skippr:text-xs",
1145
+ sm: "skippr:h-8 skippr:gap-1.5 skippr:rounded-md skippr:px-3",
1146
+ lg: "skippr:h-10 skippr:rounded-md skippr:px-6",
1147
+ icon: "skippr:size-9",
1148
+ "icon-xs": "skippr:size-6",
1149
+ "icon-sm": "skippr:size-8",
1150
+ "icon-lg": "skippr:size-10"
1151
+ };
1152
+ var Button = forwardRef3(({ className, variant = "default", size = "default", ...props }, ref) => {
1153
+ return /* @__PURE__ */ jsx6("button", {
1154
+ 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),
1155
+ ref,
1156
+ ...props
1157
+ });
1158
+ });
1159
+ Button.displayName = "Button";
1160
+
1096
1161
  // 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";
1162
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
1099
1163
  var OTP_LENGTH = 6;
1100
1164
  var DIGIT_KEYS = ["d0", "d1", "d2", "d3", "d4", "d5"];
1101
1165
  function LoginFlow({ requestOtp, verifyOtp, error, isSubmitting }) {
1102
- const [step, setStep] = useState5("email");
1103
- const [email, setEmail] = useState5("");
1104
- const handleRequestOtp = useCallback6(async (emailValue) => {
1166
+ const [step, setStep] = useState6("email");
1167
+ const [email, setEmail] = useState6("");
1168
+ const handleRequestOtp = useCallback5(async (emailValue) => {
1105
1169
  const success = await requestOtp(emailValue);
1106
1170
  if (success)
1107
1171
  setStep("otp");
1108
1172
  }, [requestOtp]);
1109
- const handleVerifyOtp = useCallback6(async (code) => {
1173
+ const handleVerifyOtp = useCallback5(async (code) => {
1110
1174
  await verifyOtp(email, code);
1111
1175
  }, [verifyOtp, email]);
1112
- const handleBack = useCallback6(() => {
1176
+ const handleBack = useCallback5(() => {
1113
1177
  setStep("email");
1114
1178
  }, []);
1115
- const handleResend = useCallback6(async () => {
1179
+ const handleResend = useCallback5(async () => {
1116
1180
  await requestOtp(email);
1117
1181
  }, [requestOtp, email]);
1118
1182
  if (step === "otp") {
1119
- return /* @__PURE__ */ jsx5(OtpStep, {
1183
+ return /* @__PURE__ */ jsx7(OtpStep, {
1120
1184
  email,
1121
1185
  onSubmit: handleVerifyOtp,
1122
1186
  onResend: handleResend,
@@ -1125,7 +1189,7 @@ function LoginFlow({ requestOtp, verifyOtp, error, isSubmitting }) {
1125
1189
  isSubmitting
1126
1190
  });
1127
1191
  }
1128
- return /* @__PURE__ */ jsx5(EmailStep, {
1192
+ return /* @__PURE__ */ jsx7(EmailStep, {
1129
1193
  email,
1130
1194
  onEmailChange: setEmail,
1131
1195
  onSubmit: handleRequestOtp,
@@ -1139,30 +1203,30 @@ function EmailStep({ email, onEmailChange, onSubmit, error, isSubmitting }) {
1139
1203
  if (email.trim())
1140
1204
  onSubmit(email.trim());
1141
1205
  }
1142
- return /* @__PURE__ */ jsxs4("div", {
1206
+ return /* @__PURE__ */ jsxs6("div", {
1143
1207
  className: "skippr:flex skippr:flex-1 skippr:flex-col skippr:px-4 skippr:py-4",
1144
1208
  children: [
1145
- /* @__PURE__ */ jsxs4("div", {
1209
+ /* @__PURE__ */ jsxs6("div", {
1146
1210
  className: "skippr:mb-4 skippr:text-center",
1147
1211
  children: [
1148
- /* @__PURE__ */ jsx5(Mail, {
1212
+ /* @__PURE__ */ jsx7(Mail, {
1149
1213
  className: "skippr:mx-auto skippr:mb-2 skippr:size-6 skippr:text-primary"
1150
1214
  }),
1151
- /* @__PURE__ */ jsx5("p", {
1215
+ /* @__PURE__ */ jsx7("p", {
1152
1216
  className: "skippr:text-sm skippr:font-medium skippr:text-foreground",
1153
1217
  children: "Sign in to continue"
1154
1218
  }),
1155
- /* @__PURE__ */ jsx5("p", {
1219
+ /* @__PURE__ */ jsx7("p", {
1156
1220
  className: "skippr:mt-1 skippr:text-xs skippr:text-muted-foreground",
1157
1221
  children: "Your email will be used to identify you across sessions"
1158
1222
  })
1159
1223
  ]
1160
1224
  }),
1161
- /* @__PURE__ */ jsxs4("form", {
1225
+ /* @__PURE__ */ jsxs6("form", {
1162
1226
  onSubmit: handleSubmit,
1163
1227
  className: "skippr:flex skippr:flex-col skippr:gap-3",
1164
1228
  children: [
1165
- /* @__PURE__ */ jsx5("input", {
1229
+ /* @__PURE__ */ jsx7("input", {
1166
1230
  type: "email",
1167
1231
  placeholder: "you@example.com",
1168
1232
  value: email,
@@ -1171,15 +1235,15 @@ function EmailStep({ email, onEmailChange, onSubmit, error, isSubmitting }) {
1171
1235
  required: true,
1172
1236
  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
1237
  }),
1174
- /* @__PURE__ */ jsx5(Button, {
1238
+ /* @__PURE__ */ jsx7(Button, {
1175
1239
  type: "submit",
1176
1240
  disabled: isSubmitting || !email.trim(),
1177
1241
  className: "skippr:w-full",
1178
- children: isSubmitting ? /* @__PURE__ */ jsx5(LoaderCircle, {
1242
+ children: isSubmitting ? /* @__PURE__ */ jsx7(LoaderCircle, {
1179
1243
  className: "skippr:size-4 skippr:animate-spin"
1180
1244
  }) : "Continue"
1181
1245
  }),
1182
- error && /* @__PURE__ */ jsx5("p", {
1246
+ error && /* @__PURE__ */ jsx7("p", {
1183
1247
  className: "skippr:text-xs skippr:text-center skippr:text-destructive",
1184
1248
  children: error
1185
1249
  })
@@ -1189,30 +1253,30 @@ function EmailStep({ email, onEmailChange, onSubmit, error, isSubmitting }) {
1189
1253
  });
1190
1254
  }
1191
1255
  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(() => {
1256
+ const [digits, setDigits] = useState6(Array(OTP_LENGTH).fill(""));
1257
+ const [resendCooldown, setResendCooldown] = useState6(0);
1258
+ const inputRefs = useRef3([]);
1259
+ const submittedRef = useRef3(false);
1260
+ useEffect8(() => {
1197
1261
  inputRefs.current[0]?.focus();
1198
1262
  }, []);
1199
- useEffect5(() => {
1263
+ useEffect8(() => {
1200
1264
  if (error)
1201
1265
  submittedRef.current = false;
1202
1266
  }, [error]);
1203
- useEffect5(() => {
1267
+ useEffect8(() => {
1204
1268
  if (resendCooldown <= 0)
1205
1269
  return;
1206
1270
  const timer = setTimeout(() => setResendCooldown((c) => c - 1), 1000);
1207
1271
  return () => clearTimeout(timer);
1208
1272
  }, [resendCooldown]);
1209
- const submitCode = useCallback6((code) => {
1273
+ const submitCode = useCallback5((code) => {
1210
1274
  if (submittedRef.current || isSubmitting)
1211
1275
  return;
1212
1276
  submittedRef.current = true;
1213
1277
  onSubmit(code);
1214
1278
  }, [onSubmit, isSubmitting]);
1215
- const handleDigitChange = useCallback6((index2, value) => {
1279
+ const handleDigitChange = useCallback5((index2, value) => {
1216
1280
  const digit = value.replace(/\D/g, "").slice(-1);
1217
1281
  const newDigits = [...digits];
1218
1282
  newDigits[index2] = digit;
@@ -1226,12 +1290,12 @@ function OtpStep({ email, onSubmit, onResend, onBack, error, isSubmitting }) {
1226
1290
  submitCode(code);
1227
1291
  }
1228
1292
  }, [digits, submitCode]);
1229
- const handleKeyDown = useCallback6((index2, e) => {
1293
+ const handleKeyDown = useCallback5((index2, e) => {
1230
1294
  if (e.key === "Backspace" && !digits[index2] && index2 > 0) {
1231
1295
  inputRefs.current[index2 - 1]?.focus();
1232
1296
  }
1233
1297
  }, [digits]);
1234
- const handlePaste = useCallback6((e) => {
1298
+ const handlePaste = useCallback5((e) => {
1235
1299
  e.preventDefault();
1236
1300
  const pasted = e.clipboardData.getData("text").replace(/\D/g, "").slice(0, OTP_LENGTH);
1237
1301
  if (pasted.length > 0) {
@@ -1259,22 +1323,22 @@ function OtpStep({ email, onSubmit, onResend, onBack, error, isSubmitting }) {
1259
1323
  submittedRef.current = false;
1260
1324
  inputRefs.current[0]?.focus();
1261
1325
  }
1262
- return /* @__PURE__ */ jsxs4("div", {
1326
+ return /* @__PURE__ */ jsxs6("div", {
1263
1327
  className: "skippr:flex skippr:flex-1 skippr:flex-col skippr:px-4 skippr:py-4",
1264
1328
  children: [
1265
- /* @__PURE__ */ jsxs4("div", {
1329
+ /* @__PURE__ */ jsxs6("div", {
1266
1330
  className: "skippr:mb-4 skippr:text-center",
1267
1331
  children: [
1268
- /* @__PURE__ */ jsx5("p", {
1332
+ /* @__PURE__ */ jsx7("p", {
1269
1333
  className: "skippr:text-sm skippr:font-medium skippr:text-foreground",
1270
1334
  children: "Enter verification code"
1271
1335
  }),
1272
- /* @__PURE__ */ jsxs4("p", {
1336
+ /* @__PURE__ */ jsxs6("p", {
1273
1337
  className: "skippr:mt-1 skippr:text-xs skippr:text-muted-foreground",
1274
1338
  children: [
1275
1339
  "We sent a 6-digit code to",
1276
1340
  " ",
1277
- /* @__PURE__ */ jsx5("span", {
1341
+ /* @__PURE__ */ jsx7("span", {
1278
1342
  className: "skippr:font-medium skippr:text-foreground",
1279
1343
  children: email
1280
1344
  })
@@ -1282,13 +1346,13 @@ function OtpStep({ email, onSubmit, onResend, onBack, error, isSubmitting }) {
1282
1346
  })
1283
1347
  ]
1284
1348
  }),
1285
- /* @__PURE__ */ jsxs4("form", {
1349
+ /* @__PURE__ */ jsxs6("form", {
1286
1350
  onSubmit: handleSubmit,
1287
1351
  className: "skippr:flex skippr:flex-col skippr:gap-3",
1288
1352
  children: [
1289
- /* @__PURE__ */ jsx5("div", {
1353
+ /* @__PURE__ */ jsx7("div", {
1290
1354
  className: "skippr:flex skippr:justify-center skippr:gap-1.5",
1291
- children: digits.map((digit, index2) => /* @__PURE__ */ jsx5("input", {
1355
+ children: digits.map((digit, index2) => /* @__PURE__ */ jsx7("input", {
1292
1356
  ref: (el) => {
1293
1357
  inputRefs.current[index2] = el;
1294
1358
  },
@@ -1303,29 +1367,29 @@ function OtpStep({ email, onSubmit, onResend, onBack, error, isSubmitting }) {
1303
1367
  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
1368
  }, DIGIT_KEYS[index2]))
1305
1369
  }),
1306
- error && /* @__PURE__ */ jsx5("p", {
1370
+ error && /* @__PURE__ */ jsx7("p", {
1307
1371
  className: "skippr:text-xs skippr:text-center skippr:text-destructive",
1308
1372
  children: error
1309
1373
  }),
1310
- /* @__PURE__ */ jsx5(Button, {
1374
+ /* @__PURE__ */ jsx7(Button, {
1311
1375
  type: "submit",
1312
1376
  disabled: isSubmitting || digits.join("").length !== OTP_LENGTH,
1313
1377
  className: "skippr:w-full",
1314
- children: isSubmitting ? /* @__PURE__ */ jsx5(LoaderCircle, {
1378
+ children: isSubmitting ? /* @__PURE__ */ jsx7(LoaderCircle, {
1315
1379
  className: "skippr:size-4 skippr:animate-spin"
1316
1380
  }) : "Verify"
1317
1381
  }),
1318
- /* @__PURE__ */ jsxs4("div", {
1382
+ /* @__PURE__ */ jsxs6("div", {
1319
1383
  className: "skippr:flex skippr:items-center skippr:justify-between skippr:text-xs",
1320
1384
  children: [
1321
- /* @__PURE__ */ jsx5("button", {
1385
+ /* @__PURE__ */ jsx7("button", {
1322
1386
  type: "button",
1323
1387
  onClick: onBack,
1324
1388
  disabled: isSubmitting,
1325
1389
  className: "skippr:text-muted-foreground hover:skippr:text-foreground skippr:transition-colors disabled:skippr:opacity-50",
1326
1390
  children: "Change email"
1327
1391
  }),
1328
- /* @__PURE__ */ jsx5("button", {
1392
+ /* @__PURE__ */ jsx7("button", {
1329
1393
  type: "button",
1330
1394
  onClick: handleResend,
1331
1395
  disabled: isSubmitting || resendCooldown > 0,
@@ -1341,476 +1405,351 @@ function OtpStep({ email, onSubmit, onResend, onBack, error, isSubmitting }) {
1341
1405
  }
1342
1406
 
1343
1407
  // 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";
1408
+ import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
1409
+ var CONTROL_BUTTON = "skippr:flex skippr:size-11 skippr:cursor-pointer skippr:items-center skippr:justify-center skippr:rounded-full skippr:transition-colors";
1374
1410
  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", {
1411
+ const { isMuted, toggleMute, isScreenSharing, toggleScreenShare } = useMediaControls();
1412
+ return /* @__PURE__ */ jsxs7("div", {
1413
+ className: "skippr:shrink-0 skippr:border-t skippr:border-border skippr:bg-background skippr:px-4 skippr:py-4",
1415
1414
  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",
1415
+ /* @__PURE__ */ jsxs7("div", {
1416
+ className: "skippr:flex skippr:items-center skippr:justify-center skippr:gap-3",
1421
1417
  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
- ]
1418
+ /* @__PURE__ */ jsx8("button", {
1419
+ type: "button",
1420
+ onClick: toggleMute,
1421
+ "aria-label": isMuted ? "Unmute" : "Mute",
1422
+ 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"),
1423
+ children: isMuted ? /* @__PURE__ */ jsx8(MicOff, {
1424
+ className: "skippr:size-5"
1425
+ }) : /* @__PURE__ */ jsx8(Mic, {
1426
+ className: "skippr:size-5"
1427
+ })
1454
1428
  }),
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)
1429
+ /* @__PURE__ */ jsx8("button", {
1430
+ type: "button",
1431
+ onClick: toggleScreenShare,
1432
+ "aria-label": isScreenSharing ? "Stop sharing screen" : "Share screen",
1433
+ 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"),
1434
+ children: isScreenSharing ? /* @__PURE__ */ jsx8(MonitorOff, {
1435
+ className: "skippr:size-5"
1436
+ }) : /* @__PURE__ */ jsx8(Monitor, {
1437
+ className: "skippr:size-5"
1438
+ })
1458
1439
  }),
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
- })
1440
+ /* @__PURE__ */ jsx8("button", {
1441
+ type: "button",
1442
+ onClick: onHangUp,
1443
+ "aria-label": "End session",
1444
+ className: cn(CONTROL_BUTTON, "skippr:bg-destructive skippr:text-destructive-foreground skippr:hover:bg-destructive/90"),
1445
+ children: /* @__PURE__ */ jsx8(PhoneOff, {
1446
+ className: "skippr:size-5"
1469
1447
  })
1470
1448
  })
1471
1449
  ]
1450
+ }),
1451
+ /* @__PURE__ */ jsx8("p", {
1452
+ className: "skippr:mt-3 skippr:text-center skippr:text-[10px] skippr:text-muted-foreground",
1453
+ children: "Powered by Skippr"
1472
1454
  })
1473
1455
  ]
1474
1456
  });
1475
1457
  }
1476
1458
 
1477
1459
  // src/components/MessageList.tsx
1478
- import { useEffect as useEffect7, useRef as useRef4 } from "react";
1460
+ import { useEffect as useEffect10, useRef as useRef5 } from "react";
1479
1461
 
1480
1462
  // 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 }) {
1463
+ import { useEffect as useEffect9, useRef as useRef4, useState as useState7 } from "react";
1464
+ import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
1465
+ var MAX_INPUT_HEIGHT = 60;
1466
+ function ChatInput({ sendChatMessage, isSendingChat, autoFocus = false }) {
1484
1467
  const [inputText, setInputText] = useState7("");
1468
+ const textareaRef = useRef4(null);
1485
1469
  const canSend = inputText.trim().length > 0 && !isSendingChat;
1470
+ useEffect9(() => {
1471
+ if (autoFocus)
1472
+ textareaRef.current?.focus();
1473
+ }, [autoFocus]);
1474
+ function resizeTextarea() {
1475
+ const el = textareaRef.current;
1476
+ if (!el)
1477
+ return;
1478
+ el.style.height = "auto";
1479
+ el.style.height = `${Math.min(el.scrollHeight, MAX_INPUT_HEIGHT)}px`;
1480
+ }
1481
+ function handleChange(e) {
1482
+ setInputText(e.target.value);
1483
+ resizeTextarea();
1484
+ }
1486
1485
  function handleSubmit(e) {
1487
1486
  e.preventDefault();
1488
1487
  const text = inputText.trim();
1489
1488
  if (!text || isSendingChat)
1490
1489
  return;
1491
1490
  setInputText("");
1491
+ resizeTextarea();
1492
1492
  sendChatMessage(text).catch(() => setInputText(text));
1493
+ textareaRef.current?.focus();
1493
1494
  }
1494
- return /* @__PURE__ */ jsxs6("form", {
1495
+ function handleKeyDown(e) {
1496
+ if (e.key === "Enter" && !e.shiftKey) {
1497
+ e.preventDefault();
1498
+ handleSubmit(e);
1499
+ }
1500
+ }
1501
+ return /* @__PURE__ */ jsx9("form", {
1495
1502
  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"
1503
+ className: "skippr:border-t skippr:border-border skippr:p-3",
1504
+ children: /* @__PURE__ */ jsxs8("div", {
1505
+ 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",
1506
+ children: [
1507
+ /* @__PURE__ */ jsx9("textarea", {
1508
+ ref: textareaRef,
1509
+ rows: 1,
1510
+ value: inputText,
1511
+ onChange: handleChange,
1512
+ onKeyDown: handleKeyDown,
1513
+ placeholder: "Type a message...",
1514
+ 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",
1515
+ style: { maxHeight: `${MAX_INPUT_HEIGHT}px` }
1516
+ }),
1517
+ /* @__PURE__ */ jsx9("button", {
1518
+ type: "submit",
1519
+ disabled: !canSend,
1520
+ "aria-label": "Send message",
1521
+ 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"),
1522
+ children: /* @__PURE__ */ jsx9(Send, {
1523
+ className: "skippr:size-3.5"
1524
+ })
1513
1525
  })
1514
- })
1515
- ]
1526
+ ]
1527
+ })
1516
1528
  });
1517
1529
  }
1518
1530
 
1519
1531
  // src/components/ChatMessage.tsx
1520
- import { jsx as jsx9 } from "react/jsx-runtime";
1532
+ import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
1533
+ function formatTimestamp(ts) {
1534
+ return new Date(ts).toLocaleTimeString("en-US", {
1535
+ hour: "numeric",
1536
+ minute: "2-digit",
1537
+ hour12: true
1538
+ });
1539
+ }
1521
1540
  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
- })
1541
+ const isAgent = message.role === "assistant";
1542
+ return /* @__PURE__ */ jsxs9("div", {
1543
+ className: cn("skippr:flex skippr:gap-2", isAgent ? "skippr:items-start" : "skippr:justify-end"),
1544
+ children: [
1545
+ isAgent && /* @__PURE__ */ jsx10("div", {
1546
+ 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",
1547
+ children: /* @__PURE__ */ jsx10(Sparkles, {
1548
+ className: "skippr:size-3.5 skippr:text-primary-foreground"
1549
+ })
1550
+ }),
1551
+ /* @__PURE__ */ jsxs9("div", {
1552
+ className: cn("skippr:flex skippr:max-w-[80%] skippr:flex-col", isAgent ? "skippr:items-start" : "skippr:items-end"),
1553
+ children: [
1554
+ /* @__PURE__ */ jsx10("div", {
1555
+ 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"),
1556
+ children: message.content
1557
+ }),
1558
+ message.timestamp && /* @__PURE__ */ jsx10("span", {
1559
+ className: "skippr:mt-1 skippr:px-1 skippr:text-[10px] skippr:text-muted-foreground/60",
1560
+ children: formatTimestamp(message.timestamp)
1561
+ })
1562
+ ]
1563
+ })
1564
+ ]
1529
1565
  });
1530
1566
  }
1531
1567
 
1532
1568
  // src/components/TypingIndicator.tsx
1533
- import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
1569
+ import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
1534
1570
  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]"
1571
+ return /* @__PURE__ */ jsxs10("div", {
1572
+ className: "skippr:flex skippr:items-start skippr:gap-2 skippr:animate-skippr-tab-fade",
1573
+ children: [
1574
+ /* @__PURE__ */ jsx11("div", {
1575
+ 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",
1576
+ children: /* @__PURE__ */ jsx11(Sparkles, {
1577
+ className: "skippr:size-3.5 skippr:text-primary-foreground"
1548
1578
  })
1549
- ]
1550
- })
1579
+ }),
1580
+ /* @__PURE__ */ jsxs10("div", {
1581
+ 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",
1582
+ children: [
1583
+ /* @__PURE__ */ jsx11("span", {
1584
+ children: "Agent is analyzing your screen"
1585
+ }),
1586
+ /* @__PURE__ */ jsxs10("span", {
1587
+ className: "skippr:inline-flex skippr:items-center skippr:gap-[2px]",
1588
+ children: [
1589
+ /* @__PURE__ */ jsx11("span", {
1590
+ className: "skippr:size-1 skippr:rounded-full skippr:bg-primary skippr:animate-skippr-thinking-dot skippr:[animation-delay:0ms]"
1591
+ }),
1592
+ /* @__PURE__ */ jsx11("span", {
1593
+ className: "skippr:size-1 skippr:rounded-full skippr:bg-primary skippr:animate-skippr-thinking-dot skippr:[animation-delay:200ms]"
1594
+ }),
1595
+ /* @__PURE__ */ jsx11("span", {
1596
+ className: "skippr:size-1 skippr:rounded-full skippr:bg-primary skippr:animate-skippr-thinking-dot skippr:[animation-delay:400ms]"
1597
+ })
1598
+ ]
1599
+ })
1600
+ ]
1601
+ })
1602
+ ]
1551
1603
  });
1552
1604
  }
1553
1605
 
1554
1606
  // src/components/MessageList.tsx
1555
- import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
1607
+ import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
1556
1608
  function MessageList({
1557
1609
  messages,
1558
1610
  isStreaming,
1559
1611
  sendChatMessage,
1560
- isSendingChat
1612
+ isSendingChat,
1613
+ autoFocus = false
1561
1614
  }) {
1562
- const scrollRef = useRef4(null);
1615
+ const scrollRef = useRef5(null);
1563
1616
  const lastMessage = messages.length > 0 ? messages[messages.length - 1] : undefined;
1564
- useEffect7(() => {
1617
+ useEffect10(() => {
1565
1618
  scrollRef.current?.scrollIntoView({ behavior: "smooth" });
1566
1619
  }, [messages.length, lastMessage?.content]);
1567
1620
  const showTyping = isStreaming && lastMessage?.role === "assistant" && lastMessage.content === "";
1568
- return /* @__PURE__ */ jsxs8("div", {
1621
+ return /* @__PURE__ */ jsxs11("div", {
1569
1622
  className: "skippr:flex skippr:min-h-0 skippr:flex-1 skippr:flex-col",
1570
1623
  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,
1624
+ /* @__PURE__ */ jsxs11("div", {
1625
+ className: "skippr:min-h-0 skippr:flex-1 skippr:space-y-4 skippr:overflow-y-auto skippr:p-4",
1638
1626
  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"
1627
+ messages.length === 0 && !showTyping && /* @__PURE__ */ jsx12(LoadingDots, {
1628
+ label: "Waiting for conversation to begin..."
1643
1629
  }),
1644
- isStarting ? "Starting..." : "Start Session"
1630
+ messages.map((message) => /* @__PURE__ */ jsx12(ChatMessage, {
1631
+ message
1632
+ }, message.id)),
1633
+ showTyping && /* @__PURE__ */ jsx12(TypingIndicator, {}),
1634
+ /* @__PURE__ */ jsx12("div", {
1635
+ ref: scrollRef
1636
+ })
1645
1637
  ]
1646
1638
  }),
1647
- error && /* @__PURE__ */ jsx12("p", {
1648
- className: "skippr:text-xs skippr:text-destructive",
1649
- children: error
1639
+ /* @__PURE__ */ jsx12(ChatInput, {
1640
+ sendChatMessage,
1641
+ isSendingChat,
1642
+ autoFocus
1650
1643
  })
1651
1644
  ]
1652
1645
  });
1653
1646
  }
1654
1647
 
1655
1648
  // src/components/SessionAgenda.tsx
1656
- import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
1657
- function SessionAgenda({ phases, questions = [] }) {
1649
+ import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
1650
+ function SessionAgenda({ phases }) {
1658
1651
  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
1652
  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
1653
+ className: "skippr:flex skippr:flex-1 skippr:items-center skippr:justify-center",
1654
+ children: /* @__PURE__ */ jsx13(LoadingDots, {
1655
+ label: "Waiting for agenda to load..."
1724
1656
  })
1725
1657
  });
1726
1658
  }
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",
1659
+ return /* @__PURE__ */ jsx13("div", {
1660
+ className: "skippr:flex-1 skippr:overflow-y-auto skippr:px-4 skippr:py-4",
1661
+ children: /* @__PURE__ */ jsx13("div", {
1662
+ className: "skippr:space-y-1",
1663
+ children: phases.map((phase) => {
1664
+ const isActive = phase.status === "active";
1665
+ const isCompleted = phase.status === "completed";
1666
+ return /* @__PURE__ */ jsxs12("div", {
1667
+ 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
1668
  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"
1669
+ /* @__PURE__ */ jsx13("div", {
1670
+ className: "skippr:mt-0.5",
1671
+ children: isCompleted ? /* @__PURE__ */ jsx13(CircleCheck, {
1672
+ className: "skippr:size-4 skippr:text-chart-3"
1673
+ }) : isActive ? /* @__PURE__ */ jsx13(Circle, {
1674
+ className: "skippr:size-4 skippr:fill-primary/30 skippr:text-primary"
1675
+ }) : /* @__PURE__ */ jsx13(Circle, {
1676
+ className: "skippr:size-4 skippr:text-muted-foreground/30"
1677
+ })
1770
1678
  }),
1771
- /* @__PURE__ */ jsxs11("div", {
1772
- className: "skippr:flex skippr:gap-2",
1679
+ /* @__PURE__ */ jsxs12("div", {
1680
+ className: "skippr:min-w-0 skippr:flex-1",
1773
1681
  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
- ]
1682
+ /* @__PURE__ */ jsx13("p", {
1683
+ 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"),
1684
+ children: phase.name
1787
1685
  }),
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
- ]
1686
+ phase.highlights.length > 0 && /* @__PURE__ */ jsx13("ul", {
1687
+ className: "skippr:mt-1 skippr:space-y-0.5",
1688
+ children: phase.highlights.map((text) => /* @__PURE__ */ jsxs12("li", {
1689
+ 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"),
1690
+ children: [
1691
+ /* @__PURE__ */ jsx13("span", {
1692
+ className: "skippr:size-1 skippr:shrink-0 skippr:rounded-full skippr:bg-current"
1693
+ }),
1694
+ text
1695
+ ]
1696
+ }, text))
1801
1697
  })
1802
1698
  ]
1803
1699
  })
1804
1700
  ]
1805
- })
1701
+ }, phase.name);
1702
+ })
1703
+ })
1704
+ });
1705
+ }
1706
+
1707
+ // src/components/SessionWarningBanner.tsx
1708
+ import { jsx as jsx14 } from "react/jsx-runtime";
1709
+ var SESSION_WARNING_THRESHOLD_SECS = 60;
1710
+ function SessionWarningBanner({ remaining }) {
1711
+ if (remaining === null || remaining <= 0 || remaining > SESSION_WARNING_THRESHOLD_SECS)
1712
+ return null;
1713
+ return /* @__PURE__ */ jsx14("div", {
1714
+ "data-testid": "session-warning-banner",
1715
+ 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",
1716
+ children: "Session ending soon"
1717
+ });
1718
+ }
1719
+
1720
+ // src/components/StartSessionPrompt.tsx
1721
+ import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
1722
+ function StartSessionPrompt({
1723
+ onStartSession,
1724
+ isStarting,
1725
+ error,
1726
+ label = "Talk to Skippr"
1727
+ }) {
1728
+ return /* @__PURE__ */ jsxs13("div", {
1729
+ className: "skippr:flex skippr:flex-1 skippr:flex-col skippr:items-center skippr:justify-center skippr:gap-3 skippr:px-4",
1730
+ children: [
1731
+ /* @__PURE__ */ jsx15("button", {
1732
+ type: "button",
1733
+ onClick: onStartSession,
1734
+ disabled: isStarting,
1735
+ 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",
1736
+ children: isStarting ? "Starting..." : label
1737
+ }),
1738
+ error && /* @__PURE__ */ jsx15("p", {
1739
+ className: "skippr:text-xs skippr:text-destructive",
1740
+ children: error
1806
1741
  })
1807
1742
  ]
1808
1743
  });
1809
1744
  }
1810
1745
 
1811
1746
  // src/components/Sidebar.tsx
1812
- import { jsx as jsx15, jsxs as jsxs12, Fragment as Fragment2 } from "react/jsx-runtime";
1813
- function Sidebar() {
1747
+ import { jsx as jsx16, jsxs as jsxs14, Fragment as Fragment2 } from "react/jsx-runtime";
1748
+ function Sidebar({
1749
+ hideControls = false,
1750
+ hideHeader = false,
1751
+ startSessionLabel = "Talk to Skippr"
1752
+ }) {
1814
1753
  const {
1815
1754
  variant,
1816
1755
  isConnected,
@@ -1825,12 +1764,14 @@ function Sidebar() {
1825
1764
  authError,
1826
1765
  requestOtp,
1827
1766
  verifyOtp,
1828
- isAuthSubmitting
1767
+ isAuthSubmitting,
1768
+ sidebarTab: activeTab,
1769
+ setSidebarTab: setActiveTab,
1770
+ autoFocusChat
1829
1771
  } = useLiveAgent();
1830
- const [view, setView] = useState8("main");
1831
1772
  const isFloating = variant === "floating";
1832
1773
  const isSidebar = variant === "sidebar";
1833
- useEffect8(() => {
1774
+ useEffect11(() => {
1834
1775
  if (!isSidebar)
1835
1776
  return;
1836
1777
  const prop = position === "right" ? "marginRight" : "marginLeft";
@@ -1844,105 +1785,162 @@ function Sidebar() {
1844
1785
  document.body.style.transition = "";
1845
1786
  };
1846
1787
  }, [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"),
1788
+ return /* @__PURE__ */ jsxs14("div", {
1789
+ 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
1790
  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
1791
+ children: [
1792
+ !hideHeader && /* @__PURE__ */ jsx16(ChatHeader, {}),
1793
+ !isAuthenticated && isValidating ? /* @__PURE__ */ jsx16("div", {
1794
+ className: "skippr:flex skippr:flex-1 skippr:items-center skippr:justify-center",
1795
+ children: /* @__PURE__ */ jsx16(LoadingDots, {
1796
+ label: "Loading..."
1874
1797
  })
1875
- ]
1876
- })
1798
+ }) : !isAuthenticated ? /* @__PURE__ */ jsx16(LoginFlow, {
1799
+ requestOtp,
1800
+ verifyOtp,
1801
+ error: authError,
1802
+ isSubmitting: isAuthSubmitting
1803
+ }) : /* @__PURE__ */ jsx16(AuthenticatedContent, {
1804
+ isConnected,
1805
+ onStartSession: startSession,
1806
+ onDisconnect: disconnect,
1807
+ isStarting,
1808
+ error,
1809
+ activeTab,
1810
+ onTabChange: setActiveTab,
1811
+ hideControls,
1812
+ startSessionLabel,
1813
+ autoFocusChat
1814
+ })
1815
+ ]
1877
1816
  });
1878
1817
  }
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, {
1818
+ function AuthenticatedContent({
1819
+ isConnected,
1820
+ onStartSession,
1821
+ onDisconnect,
1822
+ isStarting,
1823
+ error,
1824
+ activeTab,
1825
+ onTabChange,
1826
+ hideControls,
1827
+ startSessionLabel,
1828
+ autoFocusChat
1829
+ }) {
1830
+ return /* @__PURE__ */ jsxs14(Fragment2, {
1896
1831
  children: [
1897
- /* @__PURE__ */ jsx15(MeetingControls, {
1898
- onHangUp: onDisconnect
1899
- }),
1900
- /* @__PURE__ */ jsxs12("div", {
1901
- className: "skippr:flex skippr:min-h-0 skippr:flex-1",
1832
+ isConnected && /* @__PURE__ */ jsx16(ConnectedBanner, {}),
1833
+ /* @__PURE__ */ jsxs14("div", {
1834
+ className: "skippr:flex skippr:gap-2 skippr:border-b skippr:border-border skippr:px-3 skippr:py-2",
1902
1835
  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
- })
1836
+ /* @__PURE__ */ jsxs14("button", {
1837
+ type: "button",
1838
+ 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"),
1839
+ onClick: () => onTabChange("chat"),
1840
+ children: [
1841
+ /* @__PURE__ */ jsx16(MessageCircle, {
1842
+ className: "skippr:size-3.5"
1843
+ }),
1844
+ "Chat",
1845
+ activeTab === "chat" && /* @__PURE__ */ jsx16("span", {
1846
+ className: "skippr:absolute skippr:-bottom-2 skippr:left-3 skippr:right-3 skippr:h-0.5 skippr:rounded-full skippr:bg-foreground"
1847
+ })
1848
+ ]
1909
1849
  }),
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
- })
1850
+ /* @__PURE__ */ jsxs14("button", {
1851
+ type: "button",
1852
+ 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"),
1853
+ onClick: () => onTabChange("agenda"),
1854
+ children: [
1855
+ /* @__PURE__ */ jsx16(Calendar, {
1856
+ className: "skippr:size-3.5"
1857
+ }),
1858
+ "Agenda",
1859
+ activeTab === "agenda" && /* @__PURE__ */ jsx16("span", {
1860
+ className: "skippr:absolute skippr:-bottom-2 skippr:left-3 skippr:right-3 skippr:h-0.5 skippr:rounded-full skippr:bg-foreground"
1861
+ })
1862
+ ]
1918
1863
  })
1919
1864
  ]
1865
+ }),
1866
+ /* @__PURE__ */ jsx16("div", {
1867
+ className: "skippr:flex skippr:min-h-0 skippr:flex-1 skippr:flex-col",
1868
+ children: isConnected ? /* @__PURE__ */ jsx16(ConnectedBody, {
1869
+ activeTab,
1870
+ autoFocusChat
1871
+ }) : /* @__PURE__ */ jsx16("div", {
1872
+ className: "skippr:flex skippr:min-h-0 skippr:flex-1 skippr:flex-col skippr:animate-skippr-tab-fade",
1873
+ children: /* @__PURE__ */ jsx16(StartSessionPrompt, {
1874
+ onStartSession,
1875
+ isStarting,
1876
+ error,
1877
+ label: startSessionLabel
1878
+ })
1879
+ }, `${activeTab}-empty`)
1880
+ }),
1881
+ isConnected && !hideControls && /* @__PURE__ */ jsx16(MeetingControls, {
1882
+ onHangUp: onDisconnect
1920
1883
  })
1921
1884
  ]
1922
1885
  });
1923
1886
  }
1887
+ function ConnectedBanner() {
1888
+ const remaining = useSessionRemaining();
1889
+ return /* @__PURE__ */ jsx16(SessionWarningBanner, {
1890
+ remaining
1891
+ });
1892
+ }
1893
+ function ConnectedBody({
1894
+ activeTab,
1895
+ autoFocusChat
1896
+ }) {
1897
+ const { allMessages, agentState, sendChatMessage, isSendingChat } = useCombinedMessages();
1898
+ const { phases } = usePhaseUpdates();
1899
+ if (activeTab === "agenda") {
1900
+ return /* @__PURE__ */ jsx16("div", {
1901
+ className: "skippr:min-h-0 skippr:flex-1 skippr:overflow-y-auto skippr:animate-skippr-tab-fade",
1902
+ children: /* @__PURE__ */ jsx16(SessionAgenda, {
1903
+ phases
1904
+ })
1905
+ }, "agenda");
1906
+ }
1907
+ return /* @__PURE__ */ jsx16("div", {
1908
+ className: "skippr:flex skippr:min-h-0 skippr:flex-1 skippr:flex-col skippr:animate-skippr-tab-fade",
1909
+ children: /* @__PURE__ */ jsx16(MessageList, {
1910
+ messages: allMessages,
1911
+ isStreaming: agentState === "speaking",
1912
+ sendChatMessage,
1913
+ isSendingChat,
1914
+ autoFocus: autoFocusChat
1915
+ })
1916
+ }, "chat");
1917
+ }
1924
1918
 
1925
1919
  // src/components/SidebarTrigger.tsx
1926
- import { jsx as jsx16 } from "react/jsx-runtime";
1920
+ import { jsx as jsx17 } from "react/jsx-runtime";
1927
1921
  function SidebarTrigger() {
1928
- const { isPanelOpen, togglePanel, position, isMinimized } = useLiveAgent();
1922
+ const { isPanelOpen, togglePanel, minimizePanel, minimizable, position, isMinimized } = useLiveAgent();
1929
1923
  if (isMinimized)
1930
1924
  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"
1925
+ const handleClick = isPanelOpen && minimizable ? minimizePanel : togglePanel;
1926
+ return /* @__PURE__ */ jsx17("button", {
1927
+ type: "button",
1928
+ onClick: handleClick,
1929
+ title: isPanelOpen ? "Close chat" : "Open chat",
1930
+ "aria-label": isPanelOpen ? "Close chat" : "Open chat",
1931
+ 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"),
1932
+ children: isPanelOpen ? /* @__PURE__ */ jsx17(ChevronDown, {
1933
+ className: "skippr:size-5"
1934
+ }) : /* @__PURE__ */ jsx17("img", {
1935
+ src: LOGO_URL,
1936
+ alt: "Skippr",
1937
+ className: "skippr:size-7"
1940
1938
  })
1941
1939
  });
1942
1940
  }
1943
1941
 
1944
1942
  // src/components/LiveAgent.tsx
1945
- import { jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
1943
+ import { jsx as jsx18, jsxs as jsxs15 } from "react/jsx-runtime";
1946
1944
  function LiveAgent({
1947
1945
  agentId,
1948
1946
  authToken: authTokenProp,
@@ -1953,21 +1951,36 @@ function LiveAgent({
1953
1951
  minimizable = true,
1954
1952
  defaultOpen = false,
1955
1953
  welcomeMessage,
1954
+ hideControls = false,
1955
+ hideHeader = false,
1956
+ startSessionLabel = "Talk to Skippr",
1957
+ autoFocusChat = true,
1958
+ showObservingBanner = true,
1956
1959
  children
1957
1960
  }) {
1958
1961
  const auth = useAuth({ appKey });
1959
1962
  const effectiveAuthToken = authTokenProp || auth.authToken || undefined;
1960
- const { connection, shouldConnect, isStarting, error, errorCode, startSession, disconnect } = useSession({
1963
+ const {
1964
+ connection,
1965
+ shouldConnect,
1966
+ isStarting,
1967
+ error,
1968
+ errorCode,
1969
+ startSession,
1970
+ disconnect,
1971
+ pendingScreenStream
1972
+ } = useSession({
1961
1973
  agentId,
1962
1974
  authToken: effectiveAuthToken,
1963
1975
  appKey,
1964
1976
  userToken
1965
1977
  });
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(() => {
1978
+ const [isPanelOpen, setIsPanelOpen] = useState8(defaultOpen);
1979
+ const [isMinimized, setIsMinimized] = useState8(minimizable && !defaultOpen);
1980
+ const [sidebarTab, setSidebarTab] = useState8("agenda");
1981
+ const [welcomeDismissed, setWelcomeDismissed] = useState8(false);
1982
+ const dismissWelcome = useCallback6(() => setWelcomeDismissed(true), []);
1983
+ const [currentPosition, setCurrentPosition] = useState8(() => {
1971
1984
  try {
1972
1985
  const saved = localStorage.getItem("skippr_widget_position");
1973
1986
  if (saved === "left" || saved === "right")
@@ -1975,20 +1988,20 @@ function LiveAgent({
1975
1988
  } catch {}
1976
1989
  return position;
1977
1990
  });
1978
- const setPositionWithPersist = useCallback8((pos) => {
1991
+ const setPositionWithPersist = useCallback6((pos) => {
1979
1992
  setCurrentPosition(pos);
1980
1993
  try {
1981
1994
  localStorage.setItem("skippr_widget_position", pos);
1982
1995
  } catch {}
1983
1996
  }, []);
1984
- const openPanel = useCallback8(() => setIsPanelOpen(true), []);
1985
- const closePanel = useCallback8(() => setIsPanelOpen(false), []);
1986
- const togglePanel = useCallback8(() => setIsPanelOpen((prev) => !prev), []);
1987
- const expandPanel = useCallback8(() => {
1997
+ const openPanel = useCallback6(() => setIsPanelOpen(true), []);
1998
+ const closePanel = useCallback6(() => setIsPanelOpen(false), []);
1999
+ const togglePanel = useCallback6(() => setIsPanelOpen((prev) => !prev), []);
2000
+ const expandPanel = useCallback6(() => {
1988
2001
  setIsMinimized(false);
1989
2002
  setIsPanelOpen(true);
1990
2003
  }, []);
1991
- const minimizePanel = useCallback8(() => {
2004
+ const minimizePanel = useCallback6(() => {
1992
2005
  if (!minimizable)
1993
2006
  return;
1994
2007
  setIsMinimized(true);
@@ -1996,8 +2009,8 @@ function LiveAgent({
1996
2009
  }, [minimizable]);
1997
2010
  const isConnected = connection !== null;
1998
2011
  const isAuthenticated = !!userToken || !!authTokenProp || auth.isAuthenticated;
1999
- const prevConnectionRef = useRef5(connection);
2000
- useEffect9(() => {
2012
+ const prevConnectionRef = useRef6(connection);
2013
+ useEffect12(() => {
2001
2014
  const connectionChanged = prevConnectionRef.current !== connection;
2002
2015
  prevConnectionRef.current = connection;
2003
2016
  if (connectionChanged && minimizable) {
@@ -2031,7 +2044,10 @@ function LiveAgent({
2031
2044
  requestOtp: auth.requestOtp,
2032
2045
  verifyOtp: auth.verifyOtp,
2033
2046
  logoutAuth: auth.logout,
2034
- isAuthSubmitting: auth.isSubmitting
2047
+ isAuthSubmitting: auth.isSubmitting,
2048
+ sidebarTab,
2049
+ setSidebarTab,
2050
+ autoFocusChat
2035
2051
  }), [
2036
2052
  connection,
2037
2053
  shouldConnect,
@@ -2058,31 +2074,54 @@ function LiveAgent({
2058
2074
  auth.requestOtp,
2059
2075
  auth.verifyOtp,
2060
2076
  auth.logout,
2061
- auth.isSubmitting
2077
+ auth.isSubmitting,
2078
+ sidebarTab,
2079
+ autoFocusChat
2062
2080
  ]);
2063
- return /* @__PURE__ */ jsx17(LiveAgentContext.Provider, {
2081
+ return /* @__PURE__ */ jsx18(LiveAgentContext.Provider, {
2064
2082
  value: ctx,
2065
- children: /* @__PURE__ */ jsxs13(LiveKitRoom, {
2083
+ children: /* @__PURE__ */ jsxs15(LiveKitRoom, {
2066
2084
  serverUrl: connection?.livekitUrl,
2067
2085
  token: connection?.token,
2068
2086
  connect: shouldConnect,
2069
2087
  audio: true,
2070
2088
  onDisconnected: disconnect,
2071
2089
  children: [
2072
- connection && /* @__PURE__ */ jsx17(RoomAudioRenderer, {}),
2073
- isMinimized && /* @__PURE__ */ jsx17(MinimizedBubble, {
2090
+ connection && /* @__PURE__ */ jsx18(RoomAudioRenderer, {}),
2091
+ showObservingBanner && /* @__PURE__ */ jsx18(ObservingBanner, {}),
2092
+ connection && /* @__PURE__ */ jsx18(AutoStartMedia, {
2093
+ pendingScreenStream
2094
+ }),
2095
+ isMinimized && /* @__PURE__ */ jsx18(MinimizedBubble, {
2074
2096
  welcomeMessage,
2075
2097
  welcomeDismissed,
2076
2098
  onDismissWelcome: dismissWelcome
2077
2099
  }),
2078
- /* @__PURE__ */ jsx17(SidebarTrigger, {}),
2079
- /* @__PURE__ */ jsx17(Sidebar, {}),
2100
+ /* @__PURE__ */ jsx18(SidebarTrigger, {}),
2101
+ /* @__PURE__ */ jsx18(Sidebar, {
2102
+ hideControls,
2103
+ hideHeader,
2104
+ startSessionLabel
2105
+ }),
2080
2106
  children
2081
2107
  ]
2082
2108
  })
2083
2109
  });
2084
2110
  }
2111
+ // src/hooks/useIsLocalSpeaking.ts
2112
+ import { useIsSpeaking, useLocalParticipant as useLocalParticipant5 } from "@livekit/components-react";
2113
+ function useIsLocalSpeaking() {
2114
+ const { localParticipant } = useLocalParticipant5();
2115
+ return useIsSpeaking(localParticipant);
2116
+ }
2085
2117
  export {
2118
+ useMediaControls,
2086
2119
  useLiveAgent,
2120
+ useIsLocalSpeaking,
2121
+ useElapsedSeconds,
2122
+ useAgentVoiceState,
2123
+ parseNumber,
2124
+ formatTime,
2125
+ SCREEN_SHARE_OPTIONS,
2087
2126
  LiveAgent
2088
2127
  };