@skippr/live-agent-sdk 0.15.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  // src/components/LiveAgent.tsx
2
2
  import { LiveKitRoom, RoomAudioRenderer } from "@livekit/components-react";
3
- import { useCallback as useCallback7, useMemo as useMemo4, useState as useState7 } from "react";
3
+ import { useCallback as useCallback8, useEffect as useEffect8, useMemo as useMemo4, useState as useState8 } from "react";
4
4
 
5
5
  // src/context/LiveAgentContext.tsx
6
6
  import { createContext } from "react";
@@ -145,17 +145,21 @@ function useAuth({ appKey }) {
145
145
  }
146
146
 
147
147
  // src/hooks/useSession.ts
148
- import { useCallback as useCallback2, useState as useState2 } from "react";
148
+ import { useCallback as useCallback2, useEffect as useEffect2, useState as useState2 } from "react";
149
149
  var API_URL2 = "https://skipprapi-production.up.railway.app";
150
- function resolveAuthHeaders(authToken, appKey, userToken) {
151
- const headers = {};
152
- if (authToken)
153
- headers.Authorization = `Bearer ${authToken}`;
154
- if (appKey)
155
- headers["X-App-Key"] = appKey;
156
- if (userToken)
157
- headers["X-User-Token"] = userToken;
158
- return headers;
150
+ async function exchangeForBearerToken(appKey, userToken) {
151
+ const resp = await fetch(`${API_URL2}/v1/auth/token-exchange`, {
152
+ method: "POST",
153
+ headers: {
154
+ "X-App-Key": appKey,
155
+ "X-User-Token": userToken
156
+ }
157
+ });
158
+ if (!resp.ok) {
159
+ throw new Error(`Token exchange failed: ${resp.status}`);
160
+ }
161
+ const { token } = await resp.json();
162
+ return token;
159
163
  }
160
164
  function useSession({ agentId, authToken, appKey, userToken }) {
161
165
  const [connection, setConnection] = useState2(null);
@@ -163,32 +167,53 @@ function useSession({ agentId, authToken, appKey, userToken }) {
163
167
  const [isStarting, setIsStarting] = useState2(false);
164
168
  const [error, setError] = useState2("");
165
169
  const [sessionId, setSessionId] = useState2(null);
166
- const [sessionToken, setSessionToken] = useState2(null);
170
+ const [bearerToken, setBearerToken] = useState2(authToken ?? null);
171
+ useEffect2(() => {
172
+ let stale = false;
173
+ if (authToken) {
174
+ setBearerToken(authToken);
175
+ return;
176
+ }
177
+ if (appKey && userToken) {
178
+ exchangeForBearerToken(appKey, userToken).then((token) => {
179
+ if (!stale)
180
+ setBearerToken(token);
181
+ }).catch((e) => {
182
+ if (!stale)
183
+ setError(e instanceof Error ? e.message : "Token exchange failed");
184
+ });
185
+ }
186
+ return () => {
187
+ stale = true;
188
+ };
189
+ }, [authToken, appKey, userToken]);
167
190
  const startSession = useCallback2(async () => {
191
+ if (!bearerToken) {
192
+ setError("No auth token available");
193
+ return;
194
+ }
195
+ const headers = { Authorization: `Bearer ${bearerToken}` };
168
196
  setIsStarting(true);
169
197
  setError("");
170
198
  try {
171
- const authHeaders = resolveAuthHeaders(authToken, appKey, userToken);
172
199
  const createResp = await fetch(`${API_URL2}/v1/sessions`, {
173
200
  method: "POST",
174
- headers: { "Content-Type": "application/json", ...authHeaders },
201
+ headers: { "Content-Type": "application/json", ...headers },
175
202
  body: JSON.stringify({ agentId })
176
203
  });
177
204
  if (!createResp.ok) {
178
205
  throw new Error(`Failed to create session: ${createResp.status}`);
179
206
  }
180
207
  const { session } = await createResp.json();
181
- const sessionHeaders = { "X-Session-Token": session.sessionToken };
182
208
  const startResp = await fetch(`${API_URL2}/v1/sessions/${session.id}/start`, {
183
209
  method: "POST",
184
- headers: sessionHeaders
210
+ headers
185
211
  });
186
212
  if (!startResp.ok) {
187
213
  throw new Error(`Failed to start session: ${startResp.status}`);
188
214
  }
189
215
  const { connection: conn } = await startResp.json();
190
216
  setSessionId(session.id);
191
- setSessionToken(session.sessionToken);
192
217
  setConnection({
193
218
  livekitUrl: conn.livekitUrl,
194
219
  token: conn.token
@@ -199,14 +224,13 @@ function useSession({ agentId, authToken, appKey, userToken }) {
199
224
  } finally {
200
225
  setIsStarting(false);
201
226
  }
202
- }, [agentId, authToken, appKey, userToken]);
227
+ }, [agentId, bearerToken]);
203
228
  const disconnect = useCallback2(async () => {
204
- if (sessionId && sessionToken) {
205
- const sessionHeaders = { "X-Session-Token": sessionToken };
229
+ if (sessionId && bearerToken) {
206
230
  try {
207
231
  await fetch(`${API_URL2}/v1/sessions/${sessionId}/complete`, {
208
232
  method: "POST",
209
- headers: { "Content-Type": "application/json", ...sessionHeaders },
233
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${bearerToken}` },
210
234
  body: JSON.stringify({})
211
235
  });
212
236
  } catch {}
@@ -215,193 +239,13 @@ function useSession({ agentId, authToken, appKey, userToken }) {
215
239
  setShouldConnect(false);
216
240
  setConnection(null);
217
241
  setSessionId(null);
218
- setSessionToken(null);
219
- }, [sessionId, sessionToken]);
242
+ }, [sessionId, bearerToken]);
220
243
  return { connection, shouldConnect, isStarting, error, startSession, disconnect };
221
244
  }
222
245
 
223
- // src/components/Sidebar.tsx
224
- import { useConnectionState } from "@livekit/components-react";
225
- import { ConnectionState } from "livekit-client";
226
- import { useEffect as useEffect6 } from "react";
227
-
228
- // src/hooks/useCombinedMessages.ts
229
- import { useVoiceAssistant } from "@livekit/components-react";
230
- import { useMemo as useMemo3 } from "react";
231
-
232
- // src/hooks/useChatMessages.ts
233
- import { useChat, useLocalParticipant } from "@livekit/components-react";
234
- import { useMemo } from "react";
235
-
236
- // src/lib/filterSystemMessages.ts
237
- var SYSTEM_MESSAGE_PATTERN = /^\[\w+\]$/;
238
- function filterSystemMessages(messages) {
239
- return messages.filter((m) => !SYSTEM_MESSAGE_PATTERN.test(m.content.trim()));
240
- }
241
-
242
- // src/hooks/useChatMessages.ts
243
- function useChatMessages() {
244
- const { chatMessages: rawMessages, send, isSending } = useChat();
245
- const { localParticipant } = useLocalParticipant();
246
- const localIdentity = localParticipant.identity;
247
- const chatMessages = useMemo(() => {
248
- const sortedMessages = rawMessages.map((msg) => ({
249
- id: msg.id,
250
- role: msg.from?.identity === localIdentity ? "user" : "assistant",
251
- content: msg.message,
252
- source: "chat",
253
- timestamp: msg.timestamp
254
- })).sort((a, b) => (a.timestamp ?? 0) - (b.timestamp ?? 0));
255
- return filterSystemMessages(sortedMessages);
256
- }, [rawMessages, localIdentity]);
257
- return { chatMessages, sendChatMessage: send, isSendingChat: isSending };
258
- }
259
-
260
- // src/hooks/useStreamingTranscript.ts
261
- import { useLocalParticipant as useLocalParticipant2, useTranscriptions } from "@livekit/components-react";
262
- import { useMemo as useMemo2 } from "react";
263
- function useStreamingTranscript() {
264
- const transcriptions = useTranscriptions();
265
- const { localParticipant } = useLocalParticipant2();
266
- const localIdentity = localParticipant.identity;
267
- const transcriptMessages = useMemo2(() => filterSystemMessages(transcriptions.filter((stream) => stream.text.trim().length > 0).map((stream) => ({
268
- id: stream.streamInfo.id,
269
- role: stream.participantInfo.identity === localIdentity ? "user" : "assistant",
270
- content: stream.text,
271
- source: "voice-transcript",
272
- timestamp: stream.streamInfo.timestamp
273
- }))), [transcriptions, localIdentity]);
274
- return { transcriptMessages };
275
- }
276
-
277
- // src/hooks/useCombinedMessages.ts
278
- function mergeChatsIntoTranscripts(transcripts, chats) {
279
- const merged = [];
280
- let chatIndex = 0;
281
- for (const transcript of transcripts) {
282
- while (chatIndex < chats.length && (chats[chatIndex].timestamp ?? 0) <= (transcript.timestamp ?? 0)) {
283
- merged.push(chats[chatIndex]);
284
- chatIndex++;
285
- }
286
- merged.push(transcript);
287
- }
288
- while (chatIndex < chats.length) {
289
- merged.push(chats[chatIndex]);
290
- chatIndex++;
291
- }
292
- return merged;
293
- }
294
- function useCombinedMessages() {
295
- const { transcriptMessages } = useStreamingTranscript();
296
- const { chatMessages, sendChatMessage, isSendingChat } = useChatMessages();
297
- const { state: agentState } = useVoiceAssistant();
298
- const allMessages = useMemo3(() => {
299
- if (chatMessages.length === 0)
300
- return transcriptMessages;
301
- if (transcriptMessages.length === 0)
302
- return chatMessages;
303
- return mergeChatsIntoTranscripts(transcriptMessages, chatMessages);
304
- }, [transcriptMessages, chatMessages]);
305
- return { allMessages, agentState, sendChatMessage, isSendingChat };
306
- }
307
-
308
- // src/hooks/useLiveAgent.ts
309
- import { use } from "react";
310
- function useLiveAgent() {
311
- const ctx = use(LiveAgentContext);
312
- if (!ctx) {
313
- throw new Error("useLiveAgent must be used within a <LiveAgent> provider");
314
- }
315
- const { connection, shouldConnect, ...publicValue } = ctx;
316
- return publicValue;
317
- }
318
-
319
- // src/hooks/usePhaseUpdates.ts
320
- import { useCallback as useCallback3 } from "react";
321
-
322
- // src/hooks/useAgentState.ts
323
- import { useRemoteParticipants } from "@livekit/components-react";
324
- import { useEffect as useEffect2, useState as useState3 } from "react";
325
- function useAgentState(attributeKey, parse, initial) {
326
- const [value, setValue] = useState3(initial);
327
- const remoteParticipants = useRemoteParticipants();
328
- useEffect2(() => {
329
- const agentParticipant = remoteParticipants.find((p) => p.attributes?.[attributeKey]);
330
- if (agentParticipant) {
331
- const attr = agentParticipant.attributes?.[attributeKey];
332
- if (attr) {
333
- const parsed = parse(attr);
334
- if (parsed)
335
- setValue(parsed);
336
- }
337
- }
338
- const handlers = new Map;
339
- for (const p of remoteParticipants) {
340
- const handler = (changedAttributes) => {
341
- if (changedAttributes[attributeKey]) {
342
- const parsed = parse(changedAttributes[attributeKey]);
343
- if (parsed)
344
- setValue(parsed);
345
- } else if (p.attributes?.[attributeKey]) {
346
- const parsed = parse(p.attributes[attributeKey]);
347
- if (parsed)
348
- setValue(parsed);
349
- }
350
- };
351
- handlers.set(p, handler);
352
- p.on("attributesChanged", handler);
353
- }
354
- return () => {
355
- for (const [p, handler] of handlers) {
356
- p.off("attributesChanged", handler);
357
- }
358
- };
359
- }, [remoteParticipants, attributeKey, parse]);
360
- return value;
361
- }
362
-
363
- // src/hooks/usePhaseUpdates.ts
364
- function parsePhases(json) {
365
- try {
366
- const data = JSON.parse(json);
367
- if (data.type === "phase_update" && Array.isArray(data.phases)) {
368
- return data.phases;
369
- }
370
- } catch {}
371
- return null;
372
- }
373
- function usePhaseUpdates() {
374
- const parse = useCallback3(parsePhases, []);
375
- const phases = useAgentState("phases", parse, []);
376
- return { phases };
377
- }
378
-
379
- // src/hooks/useQuestionUpdates.ts
380
- import { useCallback as useCallback4 } from "react";
381
- function parseQuestions(json) {
382
- try {
383
- const data = JSON.parse(json);
384
- if (data.type === "question_update" && Array.isArray(data.questions)) {
385
- return data.questions;
386
- }
387
- } catch {}
388
- return null;
389
- }
390
- function useQuestionUpdates() {
391
- const parse = useCallback4(parseQuestions, []);
392
- const questions = useAgentState("questions", parse, []);
393
- return { questions };
394
- }
395
-
396
- // src/lib/constants.ts
397
- var SIDEBAR_WIDTH = 600;
398
-
399
- // src/lib/utils.ts
400
- import { clsx } from "clsx";
401
- import { twMerge } from "tailwind-merge";
402
- function cn(...inputs) {
403
- return twMerge(clsx(inputs));
404
- }
246
+ // src/components/MinimizedBubble.tsx
247
+ import { useLocalParticipant, useVoiceAssistant } from "@livekit/components-react";
248
+ import { ScreenSharePresets } from "livekit-client";
405
249
  // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/createLucideIcon.js
406
250
  import { forwardRef as forwardRef2, createElement as createElement2 } from "react";
407
251
 
@@ -501,112 +345,471 @@ var __iconNode2 = [
501
345
  ["path", { d: "M12 17h.01", key: "p32p05" }]
502
346
  ];
503
347
  var MessageCircleQuestionMark = createLucideIcon("message-circle-question-mark", __iconNode2);
504
- // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/send-horizontal.js
348
+ // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/panel-left.js
505
349
  var __iconNode3 = [
350
+ ["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", key: "afitv7" }],
351
+ ["path", { d: "M9 3v18", key: "fh3hqa" }]
352
+ ];
353
+ var PanelLeft = createLucideIcon("panel-left", __iconNode3);
354
+ // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/send-horizontal.js
355
+ var __iconNode4 = [
506
356
  [
507
357
  "path",
508
358
  {
509
359
  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",
510
360
  key: "117uat"
511
361
  }
512
- ],
513
- ["path", { d: "M6 12h16", key: "s4cdu5" }]
514
- ];
515
- var SendHorizontal = createLucideIcon("send-horizontal", __iconNode3);
516
- // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/bot.js
517
- var __iconNode4 = [
518
- ["path", { d: "M12 8V4H8", key: "hb8ula" }],
519
- ["rect", { width: "16", height: "12", x: "4", y: "8", rx: "2", key: "enze0r" }],
520
- ["path", { d: "M2 14h2", key: "vft8re" }],
521
- ["path", { d: "M20 14h2", key: "4cs60a" }],
522
- ["path", { d: "M15 13v2", key: "1xurst" }],
523
- ["path", { d: "M9 13v2", key: "rq6x2g" }]
524
- ];
525
- var Bot = createLucideIcon("bot", __iconNode4);
526
- // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/check.js
527
- var __iconNode5 = [["path", { d: "M20 6 9 17l-5-5", key: "1gmf2c" }]];
528
- var Check = createLucideIcon("check", __iconNode5);
529
- // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/circle.js
530
- var __iconNode6 = [["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }]];
531
- var Circle = createLucideIcon("circle", __iconNode6);
532
- // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/mail.js
533
- var __iconNode7 = [
534
- ["path", { d: "m22 7-8.991 5.727a2 2 0 0 1-2.009 0L2 7", key: "132q7q" }],
535
- ["rect", { x: "2", y: "4", width: "20", height: "16", rx: "2", key: "izxlao" }]
536
- ];
537
- var Mail = createLucideIcon("mail", __iconNode7);
538
- // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/message-circle.js
539
- var __iconNode8 = [
540
- [
541
- "path",
542
- {
543
- 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",
544
- key: "1sd12s"
362
+ ],
363
+ ["path", { d: "M6 12h16", key: "s4cdu5" }]
364
+ ];
365
+ var SendHorizontal = createLucideIcon("send-horizontal", __iconNode4);
366
+ // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/arrow-left.js
367
+ var __iconNode5 = [
368
+ ["path", { d: "m12 19-7-7 7-7", key: "1l729n" }],
369
+ ["path", { d: "M19 12H5", key: "x3x0zl" }]
370
+ ];
371
+ var ArrowLeft = createLucideIcon("arrow-left", __iconNode5);
372
+ // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/bot.js
373
+ var __iconNode6 = [
374
+ ["path", { d: "M12 8V4H8", key: "hb8ula" }],
375
+ ["rect", { width: "16", height: "12", x: "4", y: "8", rx: "2", key: "enze0r" }],
376
+ ["path", { d: "M2 14h2", key: "vft8re" }],
377
+ ["path", { d: "M20 14h2", key: "4cs60a" }],
378
+ ["path", { d: "M15 13v2", key: "1xurst" }],
379
+ ["path", { d: "M9 13v2", key: "rq6x2g" }]
380
+ ];
381
+ var Bot = createLucideIcon("bot", __iconNode6);
382
+ // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/check.js
383
+ var __iconNode7 = [["path", { d: "M20 6 9 17l-5-5", key: "1gmf2c" }]];
384
+ var Check = createLucideIcon("check", __iconNode7);
385
+ // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/circle.js
386
+ var __iconNode8 = [["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }]];
387
+ var Circle = createLucideIcon("circle", __iconNode8);
388
+ // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/mail.js
389
+ var __iconNode9 = [
390
+ ["path", { d: "m22 7-8.991 5.727a2 2 0 0 1-2.009 0L2 7", key: "132q7q" }],
391
+ ["rect", { x: "2", y: "4", width: "20", height: "16", rx: "2", key: "izxlao" }]
392
+ ];
393
+ var Mail = createLucideIcon("mail", __iconNode9);
394
+ // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/message-circle.js
395
+ var __iconNode10 = [
396
+ [
397
+ "path",
398
+ {
399
+ 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",
400
+ key: "1sd12s"
401
+ }
402
+ ]
403
+ ];
404
+ var MessageCircle = createLucideIcon("message-circle", __iconNode10);
405
+ // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/mic-off.js
406
+ var __iconNode11 = [
407
+ ["path", { d: "M12 19v3", key: "npa21l" }],
408
+ ["path", { d: "M15 9.34V5a3 3 0 0 0-5.68-1.33", key: "1gzdoj" }],
409
+ ["path", { d: "M16.95 16.95A7 7 0 0 1 5 12v-2", key: "cqa7eg" }],
410
+ ["path", { d: "M18.89 13.23A7 7 0 0 0 19 12v-2", key: "16hl24" }],
411
+ ["path", { d: "m2 2 20 20", key: "1ooewy" }],
412
+ ["path", { d: "M9 9v3a3 3 0 0 0 5.12 2.12", key: "r2i35w" }]
413
+ ];
414
+ var MicOff = createLucideIcon("mic-off", __iconNode11);
415
+ // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/mic.js
416
+ var __iconNode12 = [
417
+ ["path", { d: "M12 19v3", key: "npa21l" }],
418
+ ["path", { d: "M19 10v2a7 7 0 0 1-14 0v-2", key: "1vc78b" }],
419
+ ["rect", { x: "9", y: "2", width: "6", height: "13", rx: "3", key: "s6n7sd" }]
420
+ ];
421
+ var Mic = createLucideIcon("mic", __iconNode12);
422
+ // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/minimize-2.js
423
+ var __iconNode13 = [
424
+ ["path", { d: "m14 10 7-7", key: "oa77jy" }],
425
+ ["path", { d: "M20 10h-6V4", key: "mjg0md" }],
426
+ ["path", { d: "m3 21 7-7", key: "tjx5ai" }],
427
+ ["path", { d: "M4 14h6v6", key: "rmj7iw" }]
428
+ ];
429
+ var Minimize2 = createLucideIcon("minimize-2", __iconNode13);
430
+ // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/monitor-off.js
431
+ var __iconNode14 = [
432
+ ["path", { d: "M12 17v4", key: "1riwvh" }],
433
+ ["path", { d: "M17 17H4a2 2 0 0 1-2-2V5a2 2 0 0 1 1.184-1.826", key: "cv7jms" }],
434
+ ["path", { d: "m2 2 20 20", key: "1ooewy" }],
435
+ ["path", { d: "M8 21h8", key: "1ev6f3" }],
436
+ ["path", { d: "M8.656 3H20a2 2 0 0 1 2 2v10a2 2 0 0 1-.293 1.042", key: "z8ni2w" }]
437
+ ];
438
+ var MonitorOff = createLucideIcon("monitor-off", __iconNode14);
439
+ // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/monitor.js
440
+ var __iconNode15 = [
441
+ ["rect", { width: "20", height: "14", x: "2", y: "3", rx: "2", key: "48i651" }],
442
+ ["line", { x1: "8", x2: "16", y1: "21", y2: "21", key: "1svkeh" }],
443
+ ["line", { x1: "12", x2: "12", y1: "17", y2: "21", key: "vw1qmm" }]
444
+ ];
445
+ var Monitor = createLucideIcon("monitor", __iconNode15);
446
+ // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/panel-right.js
447
+ var __iconNode16 = [
448
+ ["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", key: "afitv7" }],
449
+ ["path", { d: "M15 3v18", key: "14nvp0" }]
450
+ ];
451
+ var PanelRight = createLucideIcon("panel-right", __iconNode16);
452
+ // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/phone-off.js
453
+ var __iconNode17 = [
454
+ [
455
+ "path",
456
+ {
457
+ d: "M10.1 13.9a14 14 0 0 0 3.732 2.668 1 1 0 0 0 1.213-.303l.355-.465A2 2 0 0 1 17 15h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2 18 18 0 0 1-12.728-5.272",
458
+ key: "1wngk7"
459
+ }
460
+ ],
461
+ ["path", { d: "M22 2 2 22", key: "y4kqgn" }],
462
+ [
463
+ "path",
464
+ {
465
+ d: "M4.76 13.582A18 18 0 0 1 2 4a2 2 0 0 1 2-2h3a2 2 0 0 1 2 2v3a2 2 0 0 1-.8 1.6l-.468.351a1 1 0 0 0-.292 1.233 14 14 0 0 0 .244.473",
466
+ key: "10hv5p"
467
+ }
468
+ ]
469
+ ];
470
+ var PhoneOff = createLucideIcon("phone-off", __iconNode17);
471
+ // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/settings.js
472
+ var __iconNode18 = [
473
+ [
474
+ "path",
475
+ {
476
+ 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",
477
+ key: "1i5ecw"
478
+ }
479
+ ],
480
+ ["circle", { cx: "12", cy: "12", r: "3", key: "1v7zrd" }]
481
+ ];
482
+ var Settings = createLucideIcon("settings", __iconNode18);
483
+ // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/x.js
484
+ var __iconNode19 = [
485
+ ["path", { d: "M18 6 6 18", key: "1bl5f8" }],
486
+ ["path", { d: "m6 6 12 12", key: "d8bk6v" }]
487
+ ];
488
+ var X = createLucideIcon("x", __iconNode19);
489
+ // src/components/MinimizedBubble.tsx
490
+ import { useCallback as useCallback3 } from "react";
491
+
492
+ // src/hooks/useLiveAgent.ts
493
+ import { use } from "react";
494
+ function useLiveAgent() {
495
+ const ctx = use(LiveAgentContext);
496
+ if (!ctx) {
497
+ throw new Error("useLiveAgent must be used within a <LiveAgent> provider");
498
+ }
499
+ const { connection, shouldConnect, ...publicValue } = ctx;
500
+ return publicValue;
501
+ }
502
+
503
+ // src/lib/utils.ts
504
+ import { clsx } from "clsx";
505
+ import { twMerge } from "tailwind-merge";
506
+ function cn(...inputs) {
507
+ return twMerge(clsx(inputs));
508
+ }
509
+
510
+ // src/components/MinimizedBubble.tsx
511
+ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
512
+ function AgentAvatar({ agentState }) {
513
+ if (agentState === "speaking") {
514
+ return /* @__PURE__ */ jsxs("div", {
515
+ className: "skippr:flex skippr:items-end skippr:justify-center skippr:gap-[3px] skippr:h-5",
516
+ children: [
517
+ /* @__PURE__ */ jsx("span", {
518
+ className: "skippr:w-[3px] skippr:h-full skippr:rounded-full skippr:bg-primary-foreground skippr:origin-bottom skippr:animate-skippr-bar-1"
519
+ }),
520
+ /* @__PURE__ */ jsx("span", {
521
+ className: "skippr:w-[3px] skippr:h-full skippr:rounded-full skippr:bg-primary-foreground skippr:origin-bottom skippr:animate-skippr-bar-2"
522
+ }),
523
+ /* @__PURE__ */ jsx("span", {
524
+ className: "skippr:w-[3px] skippr:h-full skippr:rounded-full skippr:bg-primary-foreground skippr:origin-bottom skippr:animate-skippr-bar-3"
525
+ }),
526
+ /* @__PURE__ */ jsx("span", {
527
+ className: "skippr:w-[3px] skippr:h-full skippr:rounded-full skippr:bg-primary-foreground skippr:origin-bottom skippr:animate-skippr-bar-4"
528
+ })
529
+ ]
530
+ });
531
+ }
532
+ if (agentState === "listening") {
533
+ return /* @__PURE__ */ jsx(Bot, {
534
+ className: "skippr:relative skippr:z-10 skippr:size-5 skippr:animate-skippr-breathe"
535
+ });
536
+ }
537
+ if (agentState === "thinking") {
538
+ return /* @__PURE__ */ jsx(Bot, {
539
+ className: "skippr:relative skippr:z-10 skippr:size-5 skippr:animate-pulse"
540
+ });
541
+ }
542
+ return /* @__PURE__ */ jsx(Bot, {
543
+ className: "skippr:relative skippr:z-10 skippr:size-5"
544
+ });
545
+ }
546
+ function MinimizedBubble() {
547
+ const { expandPanel, disconnect, position } = useLiveAgent();
548
+ const { state: agentState } = useVoiceAssistant();
549
+ const { localParticipant } = useLocalParticipant();
550
+ const isMuted = !localParticipant.isMicrophoneEnabled;
551
+ const isScreenSharing = localParticipant.isScreenShareEnabled;
552
+ const toggleMute = useCallback3(async () => {
553
+ try {
554
+ await localParticipant.setMicrophoneEnabled(isMuted);
555
+ } catch (e) {
556
+ console.error("Failed to toggle microphone:", e);
557
+ }
558
+ }, [localParticipant, isMuted]);
559
+ const toggleScreenShare = useCallback3(async () => {
560
+ try {
561
+ await localParticipant.setScreenShareEnabled(!isScreenSharing, {
562
+ video: { displaySurface: "browser" },
563
+ resolution: ScreenSharePresets.h720fps30.resolution,
564
+ contentHint: "detail"
565
+ });
566
+ } catch (e) {
567
+ console.error("Failed to toggle screen share:", e);
568
+ }
569
+ }, [localParticipant, isScreenSharing]);
570
+ const handleHangUp = useCallback3(async () => {
571
+ await disconnect();
572
+ }, [disconnect]);
573
+ const isSpeaking = agentState === "speaking";
574
+ const isListening = agentState === "listening";
575
+ return /* @__PURE__ */ jsxs("div", {
576
+ 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", position === "right" ? "skippr:right-6" : "skippr:left-6"),
577
+ children: [
578
+ /* @__PURE__ */ jsxs("button", {
579
+ type: "button",
580
+ onClick: expandPanel,
581
+ 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"),
582
+ "aria-label": "Expand meeting panel",
583
+ children: [
584
+ isSpeaking && /* @__PURE__ */ jsxs(Fragment, {
585
+ children: [
586
+ /* @__PURE__ */ jsx("span", {
587
+ className: "skippr:absolute skippr:inset-0 skippr:rounded-full skippr:bg-primary skippr:animate-skippr-speak-ripple"
588
+ }),
589
+ /* @__PURE__ */ jsx("span", {
590
+ className: "skippr:absolute skippr:inset-0 skippr:rounded-full skippr:bg-primary skippr:animate-skippr-speak-ripple-delayed"
591
+ })
592
+ ]
593
+ }),
594
+ /* @__PURE__ */ jsx(AgentAvatar, {
595
+ agentState
596
+ })
597
+ ]
598
+ }),
599
+ /* @__PURE__ */ jsx("div", {
600
+ className: "skippr:mx-1 skippr:h-6 skippr:w-px skippr:bg-border"
601
+ }),
602
+ /* @__PURE__ */ jsxs("div", {
603
+ className: "skippr:flex skippr:items-center skippr:gap-1",
604
+ children: [
605
+ /* @__PURE__ */ jsx("button", {
606
+ type: "button",
607
+ onClick: toggleMute,
608
+ 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"),
609
+ "aria-label": isMuted ? "Unmute" : "Mute",
610
+ children: isMuted ? /* @__PURE__ */ jsx(MicOff, {
611
+ className: "skippr:size-4"
612
+ }) : /* @__PURE__ */ jsx(Mic, {
613
+ className: "skippr:size-4"
614
+ })
615
+ }),
616
+ /* @__PURE__ */ jsx("button", {
617
+ type: "button",
618
+ onClick: toggleScreenShare,
619
+ 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"),
620
+ "aria-label": isScreenSharing ? "Stop sharing" : "Share screen",
621
+ children: isScreenSharing ? /* @__PURE__ */ jsx(MonitorOff, {
622
+ className: "skippr:size-4"
623
+ }) : /* @__PURE__ */ jsx(Monitor, {
624
+ className: "skippr:size-4"
625
+ })
626
+ })
627
+ ]
628
+ }),
629
+ /* @__PURE__ */ jsx("div", {
630
+ className: "skippr:mx-1 skippr:h-6 skippr:w-px skippr:bg-border"
631
+ }),
632
+ /* @__PURE__ */ jsx("button", {
633
+ type: "button",
634
+ onClick: handleHangUp,
635
+ 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",
636
+ "aria-label": "Hang up",
637
+ children: /* @__PURE__ */ jsx(PhoneOff, {
638
+ className: "skippr:size-4"
639
+ })
640
+ })
641
+ ]
642
+ });
643
+ }
644
+
645
+ // src/components/Sidebar.tsx
646
+ import { useConnectionState } from "@livekit/components-react";
647
+ import { ConnectionState } from "livekit-client";
648
+ import { useEffect as useEffect7, useState as useState7 } from "react";
649
+
650
+ // src/hooks/useCombinedMessages.ts
651
+ import { useVoiceAssistant as useVoiceAssistant2 } from "@livekit/components-react";
652
+ import { useMemo as useMemo3 } from "react";
653
+
654
+ // src/hooks/useChatMessages.ts
655
+ import { useChat, useLocalParticipant as useLocalParticipant2 } from "@livekit/components-react";
656
+ import { useMemo } from "react";
657
+
658
+ // src/lib/filterSystemMessages.ts
659
+ var SYSTEM_MESSAGE_PATTERN = /^\[\w+\]$/;
660
+ function filterSystemMessages(messages) {
661
+ return messages.filter((m) => !SYSTEM_MESSAGE_PATTERN.test(m.content.trim()));
662
+ }
663
+
664
+ // src/hooks/useChatMessages.ts
665
+ function useChatMessages() {
666
+ const { chatMessages: rawMessages, send, isSending } = useChat();
667
+ const { localParticipant } = useLocalParticipant2();
668
+ const localIdentity = localParticipant.identity;
669
+ const chatMessages = useMemo(() => {
670
+ const sortedMessages = rawMessages.map((msg) => ({
671
+ id: msg.id,
672
+ role: msg.from?.identity === localIdentity ? "user" : "assistant",
673
+ content: msg.message,
674
+ source: "chat",
675
+ timestamp: msg.timestamp
676
+ })).sort((a, b) => (a.timestamp ?? 0) - (b.timestamp ?? 0));
677
+ return filterSystemMessages(sortedMessages);
678
+ }, [rawMessages, localIdentity]);
679
+ return { chatMessages, sendChatMessage: send, isSendingChat: isSending };
680
+ }
681
+
682
+ // src/hooks/useStreamingTranscript.ts
683
+ import { useLocalParticipant as useLocalParticipant3, useTranscriptions } from "@livekit/components-react";
684
+ import { useMemo as useMemo2 } from "react";
685
+ function useStreamingTranscript() {
686
+ const transcriptions = useTranscriptions();
687
+ const { localParticipant } = useLocalParticipant3();
688
+ const localIdentity = localParticipant.identity;
689
+ const transcriptMessages = useMemo2(() => filterSystemMessages(transcriptions.filter((stream) => stream.text.trim().length > 0).map((stream) => ({
690
+ id: stream.streamInfo.id,
691
+ role: stream.participantInfo.identity === localIdentity ? "user" : "assistant",
692
+ content: stream.text,
693
+ source: "voice-transcript",
694
+ timestamp: stream.streamInfo.timestamp
695
+ }))), [transcriptions, localIdentity]);
696
+ return { transcriptMessages };
697
+ }
698
+
699
+ // src/hooks/useCombinedMessages.ts
700
+ function mergeChatsIntoTranscripts(transcripts, chats) {
701
+ const merged = [];
702
+ let chatIndex = 0;
703
+ for (const transcript of transcripts) {
704
+ while (chatIndex < chats.length && (chats[chatIndex].timestamp ?? 0) <= (transcript.timestamp ?? 0)) {
705
+ merged.push(chats[chatIndex]);
706
+ chatIndex++;
707
+ }
708
+ merged.push(transcript);
709
+ }
710
+ while (chatIndex < chats.length) {
711
+ merged.push(chats[chatIndex]);
712
+ chatIndex++;
713
+ }
714
+ return merged;
715
+ }
716
+ function useCombinedMessages() {
717
+ const { transcriptMessages } = useStreamingTranscript();
718
+ const { chatMessages, sendChatMessage, isSendingChat } = useChatMessages();
719
+ const { state: agentState } = useVoiceAssistant2();
720
+ const allMessages = useMemo3(() => {
721
+ if (chatMessages.length === 0)
722
+ return transcriptMessages;
723
+ if (transcriptMessages.length === 0)
724
+ return chatMessages;
725
+ return mergeChatsIntoTranscripts(transcriptMessages, chatMessages);
726
+ }, [transcriptMessages, chatMessages]);
727
+ return { allMessages, agentState, sendChatMessage, isSendingChat };
728
+ }
729
+
730
+ // src/hooks/usePhaseUpdates.ts
731
+ import { useCallback as useCallback4 } from "react";
732
+
733
+ // src/hooks/useAgentState.ts
734
+ import { useRemoteParticipants } from "@livekit/components-react";
735
+ import { useEffect as useEffect3, useState as useState3 } from "react";
736
+ function useAgentState(attributeKey, parse, initial) {
737
+ const [value, setValue] = useState3(initial);
738
+ const remoteParticipants = useRemoteParticipants();
739
+ useEffect3(() => {
740
+ const agentParticipant = remoteParticipants.find((p) => p.attributes?.[attributeKey]);
741
+ if (agentParticipant) {
742
+ const attr = agentParticipant.attributes?.[attributeKey];
743
+ if (attr) {
744
+ const parsed = parse(attr);
745
+ if (parsed)
746
+ setValue(parsed);
747
+ }
748
+ }
749
+ const handlers = new Map;
750
+ for (const p of remoteParticipants) {
751
+ const handler = (changedAttributes) => {
752
+ if (changedAttributes[attributeKey]) {
753
+ const parsed = parse(changedAttributes[attributeKey]);
754
+ if (parsed)
755
+ setValue(parsed);
756
+ } else if (p.attributes?.[attributeKey]) {
757
+ const parsed = parse(p.attributes[attributeKey]);
758
+ if (parsed)
759
+ setValue(parsed);
760
+ }
761
+ };
762
+ handlers.set(p, handler);
763
+ p.on("attributesChanged", handler);
545
764
  }
546
- ]
547
- ];
548
- var MessageCircle = createLucideIcon("message-circle", __iconNode8);
549
- // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/mic-off.js
550
- var __iconNode9 = [
551
- ["path", { d: "M12 19v3", key: "npa21l" }],
552
- ["path", { d: "M15 9.34V5a3 3 0 0 0-5.68-1.33", key: "1gzdoj" }],
553
- ["path", { d: "M16.95 16.95A7 7 0 0 1 5 12v-2", key: "cqa7eg" }],
554
- ["path", { d: "M18.89 13.23A7 7 0 0 0 19 12v-2", key: "16hl24" }],
555
- ["path", { d: "m2 2 20 20", key: "1ooewy" }],
556
- ["path", { d: "M9 9v3a3 3 0 0 0 5.12 2.12", key: "r2i35w" }]
557
- ];
558
- var MicOff = createLucideIcon("mic-off", __iconNode9);
559
- // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/mic.js
560
- var __iconNode10 = [
561
- ["path", { d: "M12 19v3", key: "npa21l" }],
562
- ["path", { d: "M19 10v2a7 7 0 0 1-14 0v-2", key: "1vc78b" }],
563
- ["rect", { x: "9", y: "2", width: "6", height: "13", rx: "3", key: "s6n7sd" }]
564
- ];
565
- var Mic = createLucideIcon("mic", __iconNode10);
566
- // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/monitor-off.js
567
- var __iconNode11 = [
568
- ["path", { d: "M12 17v4", key: "1riwvh" }],
569
- ["path", { d: "M17 17H4a2 2 0 0 1-2-2V5a2 2 0 0 1 1.184-1.826", key: "cv7jms" }],
570
- ["path", { d: "m2 2 20 20", key: "1ooewy" }],
571
- ["path", { d: "M8 21h8", key: "1ev6f3" }],
572
- ["path", { d: "M8.656 3H20a2 2 0 0 1 2 2v10a2 2 0 0 1-.293 1.042", key: "z8ni2w" }]
573
- ];
574
- var MonitorOff = createLucideIcon("monitor-off", __iconNode11);
575
- // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/monitor.js
576
- var __iconNode12 = [
577
- ["rect", { width: "20", height: "14", x: "2", y: "3", rx: "2", key: "48i651" }],
578
- ["line", { x1: "8", x2: "16", y1: "21", y2: "21", key: "1svkeh" }],
579
- ["line", { x1: "12", x2: "12", y1: "17", y2: "21", key: "vw1qmm" }]
580
- ];
581
- var Monitor = createLucideIcon("monitor", __iconNode12);
582
- // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/phone-off.js
583
- var __iconNode13 = [
584
- [
585
- "path",
586
- {
587
- d: "M10.1 13.9a14 14 0 0 0 3.732 2.668 1 1 0 0 0 1.213-.303l.355-.465A2 2 0 0 1 17 15h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2 18 18 0 0 1-12.728-5.272",
588
- key: "1wngk7"
765
+ return () => {
766
+ for (const [p, handler] of handlers) {
767
+ p.off("attributesChanged", handler);
768
+ }
769
+ };
770
+ }, [remoteParticipants, attributeKey, parse]);
771
+ return value;
772
+ }
773
+
774
+ // src/hooks/usePhaseUpdates.ts
775
+ function parsePhases(json) {
776
+ try {
777
+ const data = JSON.parse(json);
778
+ if (data.type === "phase_update" && Array.isArray(data.phases)) {
779
+ return data.phases;
589
780
  }
590
- ],
591
- ["path", { d: "M22 2 2 22", key: "y4kqgn" }],
592
- [
593
- "path",
594
- {
595
- d: "M4.76 13.582A18 18 0 0 1 2 4a2 2 0 0 1 2-2h3a2 2 0 0 1 2 2v3a2 2 0 0 1-.8 1.6l-.468.351a1 1 0 0 0-.292 1.233 14 14 0 0 0 .244.473",
596
- key: "10hv5p"
781
+ } catch {}
782
+ return null;
783
+ }
784
+ function usePhaseUpdates() {
785
+ const parse = useCallback4(parsePhases, []);
786
+ const phases = useAgentState("phases", parse, []);
787
+ return { phases };
788
+ }
789
+
790
+ // src/hooks/useQuestionUpdates.ts
791
+ import { useCallback as useCallback5 } from "react";
792
+ function parseQuestions(json) {
793
+ try {
794
+ const data = JSON.parse(json);
795
+ if (data.type === "question_update" && Array.isArray(data.questions)) {
796
+ return data.questions;
597
797
  }
598
- ]
599
- ];
600
- var PhoneOff = createLucideIcon("phone-off", __iconNode13);
601
- // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/x.js
602
- var __iconNode14 = [
603
- ["path", { d: "M18 6 6 18", key: "1bl5f8" }],
604
- ["path", { d: "m6 6 12 12", key: "d8bk6v" }]
605
- ];
606
- var X = createLucideIcon("x", __iconNode14);
798
+ } catch {}
799
+ return null;
800
+ }
801
+ function useQuestionUpdates() {
802
+ const parse = useCallback5(parseQuestions, []);
803
+ const questions = useAgentState("questions", parse, []);
804
+ return { questions };
805
+ }
806
+
807
+ // src/lib/constants.ts
808
+ var SIDEBAR_WIDTH = 480;
809
+
607
810
  // src/components/ui/button.tsx
608
811
  import { forwardRef as forwardRef3 } from "react";
609
- import { jsx } from "react/jsx-runtime";
812
+ import { jsx as jsx2 } from "react/jsx-runtime";
610
813
  var variantClasses = {
611
814
  default: "skippr:bg-primary skippr:text-primary-foreground skippr:hover:bg-primary/90",
612
815
  destructive: "skippr:bg-destructive skippr:text-white skippr:hover:bg-destructive/90",
@@ -625,7 +828,7 @@ var sizeClasses = {
625
828
  "icon-lg": "skippr:size-10"
626
829
  };
627
830
  var Button = forwardRef3(({ className, variant = "default", size = "default", ...props }, ref) => {
628
- return /* @__PURE__ */ jsx("button", {
831
+ return /* @__PURE__ */ jsx2("button", {
629
832
  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: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),
630
833
  ref,
631
834
  ...props
@@ -634,61 +837,88 @@ var Button = forwardRef3(({ className, variant = "default", size = "default", ..
634
837
  Button.displayName = "Button";
635
838
 
636
839
  // src/components/ChatHeader.tsx
637
- import { jsx as jsx2, jsxs } from "react/jsx-runtime";
638
- function ChatHeader({ onClose }) {
639
- return /* @__PURE__ */ jsxs("div", {
840
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
841
+ function ChatHeader({ onOpenSettings }) {
842
+ const { closePanel, minimizePanel, minimizable, isConnected } = useLiveAgent();
843
+ return /* @__PURE__ */ jsxs2("div", {
640
844
  className: "skippr:flex skippr:items-center skippr:gap-3 skippr:bg-primary skippr:px-4 skippr:py-3 skippr:text-primary-foreground",
641
845
  children: [
642
- /* @__PURE__ */ jsx2("div", {
846
+ /* @__PURE__ */ jsx3("div", {
643
847
  className: "skippr:flex skippr:size-6 skippr:items-center skippr:justify-center skippr:rounded-full skippr:bg-primary-foreground/20",
644
- children: /* @__PURE__ */ jsx2(Bot, {
848
+ children: /* @__PURE__ */ jsx3(Bot, {
645
849
  className: "skippr:size-3.5 skippr:text-primary-foreground"
646
850
  })
647
851
  }),
648
- /* @__PURE__ */ jsx2("div", {
852
+ /* @__PURE__ */ jsx3("div", {
649
853
  className: "skippr:flex-1",
650
- children: /* @__PURE__ */ jsx2("p", {
854
+ children: /* @__PURE__ */ jsx3("p", {
651
855
  className: "skippr:text-sm skippr:font-semibold skippr:leading-none",
652
856
  children: "AI Agent"
653
857
  })
654
858
  }),
655
- /* @__PURE__ */ jsx2(Button, {
656
- variant: "ghost",
657
- size: "icon-xs",
658
- onClick: onClose,
659
- className: "skippr:text-primary-foreground skippr:hover:bg-primary-foreground/20 skippr:hover:text-primary-foreground",
660
- children: /* @__PURE__ */ jsx2(X, {
661
- className: "skippr:size-4"
662
- })
859
+ /* @__PURE__ */ jsxs2("div", {
860
+ className: "skippr:flex skippr:items-center skippr:gap-1",
861
+ children: [
862
+ /* @__PURE__ */ jsx3(Button, {
863
+ variant: "ghost",
864
+ size: "icon-xs",
865
+ onClick: onOpenSettings,
866
+ className: "skippr:text-primary-foreground skippr:hover:bg-primary-foreground/20 skippr:hover:text-primary-foreground",
867
+ "aria-label": "Settings",
868
+ children: /* @__PURE__ */ jsx3(Settings, {
869
+ className: "skippr:size-4"
870
+ })
871
+ }),
872
+ minimizable && isConnected && /* @__PURE__ */ jsx3(Button, {
873
+ variant: "ghost",
874
+ size: "icon-xs",
875
+ onClick: minimizePanel,
876
+ className: "skippr:text-primary-foreground skippr:hover:bg-primary-foreground/20 skippr:hover:text-primary-foreground",
877
+ "aria-label": "Minimize",
878
+ children: /* @__PURE__ */ jsx3(Minimize2, {
879
+ className: "skippr:size-4"
880
+ })
881
+ }),
882
+ /* @__PURE__ */ jsx3(Button, {
883
+ variant: "ghost",
884
+ size: "icon-xs",
885
+ onClick: closePanel,
886
+ className: "skippr:text-primary-foreground skippr:hover:bg-primary-foreground/20 skippr:hover:text-primary-foreground",
887
+ "aria-label": "Close",
888
+ children: /* @__PURE__ */ jsx3(X, {
889
+ className: "skippr:size-4"
890
+ })
891
+ })
892
+ ]
663
893
  })
664
894
  ]
665
895
  });
666
896
  }
667
897
 
668
898
  // src/components/LoginFlow.tsx
669
- import { useCallback as useCallback5, useEffect as useEffect3, useRef, useState as useState4 } from "react";
670
- import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
899
+ import { useCallback as useCallback6, useEffect as useEffect4, useRef, useState as useState4 } from "react";
900
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
671
901
  var OTP_LENGTH = 6;
672
902
  var DIGIT_KEYS = ["d0", "d1", "d2", "d3", "d4", "d5"];
673
903
  function LoginFlow({ requestOtp, verifyOtp, error, isSubmitting }) {
674
904
  const [step, setStep] = useState4("email");
675
905
  const [email, setEmail] = useState4("");
676
- const handleRequestOtp = useCallback5(async (emailValue) => {
906
+ const handleRequestOtp = useCallback6(async (emailValue) => {
677
907
  const success = await requestOtp(emailValue);
678
908
  if (success)
679
909
  setStep("otp");
680
910
  }, [requestOtp]);
681
- const handleVerifyOtp = useCallback5(async (code) => {
911
+ const handleVerifyOtp = useCallback6(async (code) => {
682
912
  await verifyOtp(email, code);
683
913
  }, [verifyOtp, email]);
684
- const handleBack = useCallback5(() => {
914
+ const handleBack = useCallback6(() => {
685
915
  setStep("email");
686
916
  }, []);
687
- const handleResend = useCallback5(async () => {
917
+ const handleResend = useCallback6(async () => {
688
918
  await requestOtp(email);
689
919
  }, [requestOtp, email]);
690
920
  if (step === "otp") {
691
- return /* @__PURE__ */ jsx3(OtpStep, {
921
+ return /* @__PURE__ */ jsx4(OtpStep, {
692
922
  email,
693
923
  onSubmit: handleVerifyOtp,
694
924
  onResend: handleResend,
@@ -697,7 +927,7 @@ function LoginFlow({ requestOtp, verifyOtp, error, isSubmitting }) {
697
927
  isSubmitting
698
928
  });
699
929
  }
700
- return /* @__PURE__ */ jsx3(EmailStep, {
930
+ return /* @__PURE__ */ jsx4(EmailStep, {
701
931
  email,
702
932
  onEmailChange: setEmail,
703
933
  onSubmit: handleRequestOtp,
@@ -711,30 +941,30 @@ function EmailStep({ email, onEmailChange, onSubmit, error, isSubmitting }) {
711
941
  if (email.trim())
712
942
  onSubmit(email.trim());
713
943
  }
714
- return /* @__PURE__ */ jsxs2("div", {
944
+ return /* @__PURE__ */ jsxs3("div", {
715
945
  className: "skippr:flex skippr:flex-1 skippr:flex-col skippr:px-4 skippr:py-4",
716
946
  children: [
717
- /* @__PURE__ */ jsxs2("div", {
947
+ /* @__PURE__ */ jsxs3("div", {
718
948
  className: "skippr:mb-4 skippr:text-center",
719
949
  children: [
720
- /* @__PURE__ */ jsx3(Mail, {
950
+ /* @__PURE__ */ jsx4(Mail, {
721
951
  className: "skippr:mx-auto skippr:mb-2 skippr:size-6 skippr:text-primary"
722
952
  }),
723
- /* @__PURE__ */ jsx3("p", {
953
+ /* @__PURE__ */ jsx4("p", {
724
954
  className: "skippr:text-sm skippr:font-medium skippr:text-foreground",
725
955
  children: "Sign in to continue"
726
956
  }),
727
- /* @__PURE__ */ jsx3("p", {
957
+ /* @__PURE__ */ jsx4("p", {
728
958
  className: "skippr:mt-1 skippr:text-xs skippr:text-muted-foreground",
729
959
  children: "Your email will be used to identify you across sessions"
730
960
  })
731
961
  ]
732
962
  }),
733
- /* @__PURE__ */ jsxs2("form", {
963
+ /* @__PURE__ */ jsxs3("form", {
734
964
  onSubmit: handleSubmit,
735
965
  className: "skippr:flex skippr:flex-col skippr:gap-3",
736
966
  children: [
737
- /* @__PURE__ */ jsx3("input", {
967
+ /* @__PURE__ */ jsx4("input", {
738
968
  type: "email",
739
969
  placeholder: "you@example.com",
740
970
  value: email,
@@ -743,15 +973,15 @@ function EmailStep({ email, onEmailChange, onSubmit, error, isSubmitting }) {
743
973
  required: true,
744
974
  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"
745
975
  }),
746
- /* @__PURE__ */ jsx3(Button, {
976
+ /* @__PURE__ */ jsx4(Button, {
747
977
  type: "submit",
748
978
  disabled: isSubmitting || !email.trim(),
749
979
  className: "skippr:w-full",
750
- children: isSubmitting ? /* @__PURE__ */ jsx3(LoaderCircle, {
980
+ children: isSubmitting ? /* @__PURE__ */ jsx4(LoaderCircle, {
751
981
  className: "skippr:size-4 skippr:animate-spin"
752
982
  }) : "Continue"
753
983
  }),
754
- error && /* @__PURE__ */ jsx3("p", {
984
+ error && /* @__PURE__ */ jsx4("p", {
755
985
  className: "skippr:text-xs skippr:text-center skippr:text-destructive",
756
986
  children: error
757
987
  })
@@ -765,26 +995,26 @@ function OtpStep({ email, onSubmit, onResend, onBack, error, isSubmitting }) {
765
995
  const [resendCooldown, setResendCooldown] = useState4(0);
766
996
  const inputRefs = useRef([]);
767
997
  const submittedRef = useRef(false);
768
- useEffect3(() => {
998
+ useEffect4(() => {
769
999
  inputRefs.current[0]?.focus();
770
1000
  }, []);
771
- useEffect3(() => {
1001
+ useEffect4(() => {
772
1002
  if (error)
773
1003
  submittedRef.current = false;
774
1004
  }, [error]);
775
- useEffect3(() => {
1005
+ useEffect4(() => {
776
1006
  if (resendCooldown <= 0)
777
1007
  return;
778
1008
  const timer = setTimeout(() => setResendCooldown((c) => c - 1), 1000);
779
1009
  return () => clearTimeout(timer);
780
1010
  }, [resendCooldown]);
781
- const submitCode = useCallback5((code) => {
1011
+ const submitCode = useCallback6((code) => {
782
1012
  if (submittedRef.current || isSubmitting)
783
1013
  return;
784
1014
  submittedRef.current = true;
785
1015
  onSubmit(code);
786
1016
  }, [onSubmit, isSubmitting]);
787
- const handleDigitChange = useCallback5((index2, value) => {
1017
+ const handleDigitChange = useCallback6((index2, value) => {
788
1018
  const digit = value.replace(/\D/g, "").slice(-1);
789
1019
  const newDigits = [...digits];
790
1020
  newDigits[index2] = digit;
@@ -798,12 +1028,12 @@ function OtpStep({ email, onSubmit, onResend, onBack, error, isSubmitting }) {
798
1028
  submitCode(code);
799
1029
  }
800
1030
  }, [digits, submitCode]);
801
- const handleKeyDown = useCallback5((index2, e) => {
1031
+ const handleKeyDown = useCallback6((index2, e) => {
802
1032
  if (e.key === "Backspace" && !digits[index2] && index2 > 0) {
803
1033
  inputRefs.current[index2 - 1]?.focus();
804
1034
  }
805
1035
  }, [digits]);
806
- const handlePaste = useCallback5((e) => {
1036
+ const handlePaste = useCallback6((e) => {
807
1037
  e.preventDefault();
808
1038
  const pasted = e.clipboardData.getData("text").replace(/\D/g, "").slice(0, OTP_LENGTH);
809
1039
  if (pasted.length > 0) {
@@ -831,22 +1061,22 @@ function OtpStep({ email, onSubmit, onResend, onBack, error, isSubmitting }) {
831
1061
  submittedRef.current = false;
832
1062
  inputRefs.current[0]?.focus();
833
1063
  }
834
- return /* @__PURE__ */ jsxs2("div", {
1064
+ return /* @__PURE__ */ jsxs3("div", {
835
1065
  className: "skippr:flex skippr:flex-1 skippr:flex-col skippr:px-4 skippr:py-4",
836
1066
  children: [
837
- /* @__PURE__ */ jsxs2("div", {
1067
+ /* @__PURE__ */ jsxs3("div", {
838
1068
  className: "skippr:mb-4 skippr:text-center",
839
1069
  children: [
840
- /* @__PURE__ */ jsx3("p", {
1070
+ /* @__PURE__ */ jsx4("p", {
841
1071
  className: "skippr:text-sm skippr:font-medium skippr:text-foreground",
842
1072
  children: "Enter verification code"
843
1073
  }),
844
- /* @__PURE__ */ jsxs2("p", {
1074
+ /* @__PURE__ */ jsxs3("p", {
845
1075
  className: "skippr:mt-1 skippr:text-xs skippr:text-muted-foreground",
846
1076
  children: [
847
1077
  "We sent a 6-digit code to",
848
1078
  " ",
849
- /* @__PURE__ */ jsx3("span", {
1079
+ /* @__PURE__ */ jsx4("span", {
850
1080
  className: "skippr:font-medium skippr:text-foreground",
851
1081
  children: email
852
1082
  })
@@ -854,13 +1084,13 @@ function OtpStep({ email, onSubmit, onResend, onBack, error, isSubmitting }) {
854
1084
  })
855
1085
  ]
856
1086
  }),
857
- /* @__PURE__ */ jsxs2("form", {
1087
+ /* @__PURE__ */ jsxs3("form", {
858
1088
  onSubmit: handleSubmit,
859
1089
  className: "skippr:flex skippr:flex-col skippr:gap-3",
860
1090
  children: [
861
- /* @__PURE__ */ jsx3("div", {
1091
+ /* @__PURE__ */ jsx4("div", {
862
1092
  className: "skippr:flex skippr:justify-center skippr:gap-1.5",
863
- children: digits.map((digit, index2) => /* @__PURE__ */ jsx3("input", {
1093
+ children: digits.map((digit, index2) => /* @__PURE__ */ jsx4("input", {
864
1094
  ref: (el) => {
865
1095
  inputRefs.current[index2] = el;
866
1096
  },
@@ -875,29 +1105,29 @@ function OtpStep({ email, onSubmit, onResend, onBack, error, isSubmitting }) {
875
1105
  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"
876
1106
  }, DIGIT_KEYS[index2]))
877
1107
  }),
878
- error && /* @__PURE__ */ jsx3("p", {
1108
+ error && /* @__PURE__ */ jsx4("p", {
879
1109
  className: "skippr:text-xs skippr:text-center skippr:text-destructive",
880
1110
  children: error
881
1111
  }),
882
- /* @__PURE__ */ jsx3(Button, {
1112
+ /* @__PURE__ */ jsx4(Button, {
883
1113
  type: "submit",
884
1114
  disabled: isSubmitting || digits.join("").length !== OTP_LENGTH,
885
1115
  className: "skippr:w-full",
886
- children: isSubmitting ? /* @__PURE__ */ jsx3(LoaderCircle, {
1116
+ children: isSubmitting ? /* @__PURE__ */ jsx4(LoaderCircle, {
887
1117
  className: "skippr:size-4 skippr:animate-spin"
888
1118
  }) : "Verify"
889
1119
  }),
890
- /* @__PURE__ */ jsxs2("div", {
1120
+ /* @__PURE__ */ jsxs3("div", {
891
1121
  className: "skippr:flex skippr:items-center skippr:justify-between skippr:text-xs",
892
1122
  children: [
893
- /* @__PURE__ */ jsx3("button", {
1123
+ /* @__PURE__ */ jsx4("button", {
894
1124
  type: "button",
895
1125
  onClick: onBack,
896
1126
  disabled: isSubmitting,
897
1127
  className: "skippr:text-muted-foreground hover:skippr:text-foreground skippr:transition-colors disabled:skippr:opacity-50",
898
1128
  children: "Change email"
899
1129
  }),
900
- /* @__PURE__ */ jsx3("button", {
1130
+ /* @__PURE__ */ jsx4("button", {
901
1131
  type: "button",
902
1132
  onClick: handleResend,
903
1133
  disabled: isSubmitting || resendCooldown > 0,
@@ -913,9 +1143,9 @@ function OtpStep({ email, onSubmit, onResend, onBack, error, isSubmitting }) {
913
1143
  }
914
1144
 
915
1145
  // src/components/MeetingControls.tsx
916
- import { useLocalParticipant as useLocalParticipant3 } from "@livekit/components-react";
917
- import { ScreenSharePresets } from "livekit-client";
918
- import { useCallback as useCallback6, useEffect as useEffect4, useRef as useRef2, useState as useState5 } from "react";
1146
+ import { useLocalParticipant as useLocalParticipant4 } from "@livekit/components-react";
1147
+ import { ScreenSharePresets as ScreenSharePresets2 } from "livekit-client";
1148
+ import { useCallback as useCallback7, useEffect as useEffect5, useRef as useRef2, useState as useState5 } from "react";
919
1149
 
920
1150
  // src/lib/format.ts
921
1151
  function formatTime(seconds) {
@@ -929,12 +1159,12 @@ function parseNumber(s) {
929
1159
  }
930
1160
 
931
1161
  // src/components/SessionWarningBanner.tsx
932
- import { jsx as jsx4 } from "react/jsx-runtime";
1162
+ import { jsx as jsx5 } from "react/jsx-runtime";
933
1163
  var SESSION_WARNING_THRESHOLD_SECS = 60;
934
1164
  function SessionWarningBanner({ remaining }) {
935
1165
  if (remaining === null || remaining <= 0 || remaining > SESSION_WARNING_THRESHOLD_SECS)
936
1166
  return null;
937
- return /* @__PURE__ */ jsx4("div", {
1167
+ return /* @__PURE__ */ jsx5("div", {
938
1168
  "data-testid": "session-warning-banner",
939
1169
  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",
940
1170
  children: "Session ending soon"
@@ -942,15 +1172,15 @@ function SessionWarningBanner({ remaining }) {
942
1172
  }
943
1173
 
944
1174
  // src/components/MeetingControls.tsx
945
- import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
1175
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
946
1176
  function MeetingControls({ onHangUp }) {
947
1177
  const maxCallDuration = useAgentState("maxCallDuration", parseNumber, null);
948
- const { localParticipant } = useLocalParticipant3();
1178
+ const { localParticipant } = useLocalParticipant4();
949
1179
  const isMuted = !localParticipant.isMicrophoneEnabled;
950
1180
  const isScreenSharing = localParticipant.isScreenShareEnabled;
951
1181
  const endTimeRef = useRef2(null);
952
1182
  const [remaining, setRemaining] = useState5(null);
953
- useEffect4(() => {
1183
+ useEffect5(() => {
954
1184
  if (maxCallDuration === null || endTimeRef.current !== null)
955
1185
  return;
956
1186
  endTimeRef.current = Date.now() + maxCallDuration * 1000;
@@ -962,72 +1192,72 @@ function MeetingControls({ onHangUp }) {
962
1192
  const id = setInterval(tick, 1000);
963
1193
  return () => clearInterval(id);
964
1194
  }, [maxCallDuration]);
965
- const toggleMute = useCallback6(async () => {
1195
+ const toggleMute = useCallback7(async () => {
966
1196
  try {
967
1197
  await localParticipant.setMicrophoneEnabled(isMuted);
968
1198
  } catch (e) {
969
1199
  console.error("Failed to toggle microphone:", e);
970
1200
  }
971
1201
  }, [localParticipant, isMuted]);
972
- const toggleScreenShare = useCallback6(async () => {
1202
+ const toggleScreenShare = useCallback7(async () => {
973
1203
  try {
974
1204
  await localParticipant.setScreenShareEnabled(!isScreenSharing, {
975
1205
  video: { displaySurface: "browser" },
976
- resolution: ScreenSharePresets.h720fps30.resolution,
1206
+ resolution: ScreenSharePresets2.h720fps30.resolution,
977
1207
  contentHint: "detail"
978
1208
  });
979
1209
  } catch (e) {
980
1210
  console.error("Failed to toggle screen share:", e);
981
1211
  }
982
1212
  }, [localParticipant, isScreenSharing]);
983
- useEffect4(() => {
1213
+ useEffect5(() => {
984
1214
  toggleMute().then(() => toggleScreenShare());
985
1215
  }, []);
986
- return /* @__PURE__ */ jsxs3("div", {
1216
+ return /* @__PURE__ */ jsxs4("div", {
987
1217
  children: [
988
- /* @__PURE__ */ jsx5(SessionWarningBanner, {
1218
+ /* @__PURE__ */ jsx6(SessionWarningBanner, {
989
1219
  remaining
990
1220
  }),
991
- /* @__PURE__ */ jsxs3("div", {
1221
+ /* @__PURE__ */ jsxs4("div", {
992
1222
  className: "skippr:flex skippr:items-center skippr:justify-between skippr:border-b skippr:px-4 skippr:py-3",
993
1223
  children: [
994
- /* @__PURE__ */ jsxs3("div", {
1224
+ /* @__PURE__ */ jsxs4("div", {
995
1225
  className: "skippr:flex skippr:items-center skippr:gap-2",
996
1226
  children: [
997
- /* @__PURE__ */ jsx5(Button, {
1227
+ /* @__PURE__ */ jsx6(Button, {
998
1228
  size: "icon-sm",
999
1229
  variant: isMuted ? "destructive" : "outline",
1000
1230
  onClick: toggleMute,
1001
1231
  "aria-label": isMuted ? "Unmute" : "Mute",
1002
- children: isMuted ? /* @__PURE__ */ jsx5(MicOff, {
1232
+ children: isMuted ? /* @__PURE__ */ jsx6(MicOff, {
1003
1233
  className: "skippr:size-4"
1004
- }) : /* @__PURE__ */ jsx5(Mic, {
1234
+ }) : /* @__PURE__ */ jsx6(Mic, {
1005
1235
  className: "skippr:size-4"
1006
1236
  })
1007
1237
  }),
1008
- /* @__PURE__ */ jsx5(Button, {
1238
+ /* @__PURE__ */ jsx6(Button, {
1009
1239
  size: "icon-sm",
1010
- variant: isScreenSharing ? "default" : "outline",
1240
+ variant: isScreenSharing ? "outline" : "destructive",
1011
1241
  onClick: toggleScreenShare,
1012
1242
  "aria-label": isScreenSharing ? "Stop sharing" : "Share screen",
1013
- children: isScreenSharing ? /* @__PURE__ */ jsx5(MonitorOff, {
1243
+ children: isScreenSharing ? /* @__PURE__ */ jsx6(MonitorOff, {
1014
1244
  className: "skippr:size-4"
1015
- }) : /* @__PURE__ */ jsx5(Monitor, {
1245
+ }) : /* @__PURE__ */ jsx6(Monitor, {
1016
1246
  className: "skippr:size-4"
1017
1247
  })
1018
1248
  })
1019
1249
  ]
1020
1250
  }),
1021
- remaining !== null && /* @__PURE__ */ jsx5("span", {
1251
+ remaining !== null && /* @__PURE__ */ jsx6("span", {
1022
1252
  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"),
1023
1253
  children: formatTime(remaining)
1024
1254
  }),
1025
- /* @__PURE__ */ jsx5(Button, {
1255
+ /* @__PURE__ */ jsx6(Button, {
1026
1256
  size: "icon-sm",
1027
1257
  variant: "destructive",
1028
1258
  onClick: onHangUp,
1029
1259
  "aria-label": "Hang up",
1030
- children: /* @__PURE__ */ jsx5(PhoneOff, {
1260
+ children: /* @__PURE__ */ jsx6(PhoneOff, {
1031
1261
  className: "skippr:size-4"
1032
1262
  })
1033
1263
  })
@@ -1038,11 +1268,11 @@ function MeetingControls({ onHangUp }) {
1038
1268
  }
1039
1269
 
1040
1270
  // src/components/MessageList.tsx
1041
- import { useEffect as useEffect5, useRef as useRef3 } from "react";
1271
+ import { useEffect as useEffect6, useRef as useRef3 } from "react";
1042
1272
 
1043
1273
  // src/components/ChatInput.tsx
1044
1274
  import { useState as useState6 } from "react";
1045
- import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
1275
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
1046
1276
  function ChatInput({ sendChatMessage, isSendingChat }) {
1047
1277
  const [inputText, setInputText] = useState6("");
1048
1278
  const canSend = inputText.trim().length > 0 && !isSendingChat;
@@ -1054,11 +1284,11 @@ function ChatInput({ sendChatMessage, isSendingChat }) {
1054
1284
  setInputText("");
1055
1285
  sendChatMessage(text).catch(() => setInputText(text));
1056
1286
  }
1057
- return /* @__PURE__ */ jsxs4("form", {
1287
+ return /* @__PURE__ */ jsxs5("form", {
1058
1288
  onSubmit: handleSubmit,
1059
1289
  className: "skippr:flex skippr:items-center skippr:gap-2 skippr:border-t skippr:border-border skippr:px-3 skippr:py-2",
1060
1290
  children: [
1061
- /* @__PURE__ */ jsx6("input", {
1291
+ /* @__PURE__ */ jsx7("input", {
1062
1292
  type: "text",
1063
1293
  value: inputText,
1064
1294
  onChange: (e) => setInputText(e.target.value),
@@ -1066,12 +1296,12 @@ function ChatInput({ sendChatMessage, isSendingChat }) {
1066
1296
  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"),
1067
1297
  disabled: isSendingChat
1068
1298
  }),
1069
- /* @__PURE__ */ jsx6("button", {
1299
+ /* @__PURE__ */ jsx7("button", {
1070
1300
  type: "submit",
1071
1301
  disabled: !canSend,
1072
1302
  "aria-label": "Send message",
1073
1303
  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"),
1074
- children: /* @__PURE__ */ jsx6(SendHorizontal, {
1304
+ children: /* @__PURE__ */ jsx7(SendHorizontal, {
1075
1305
  className: "skippr:size-4"
1076
1306
  })
1077
1307
  })
@@ -1080,12 +1310,12 @@ function ChatInput({ sendChatMessage, isSendingChat }) {
1080
1310
  }
1081
1311
 
1082
1312
  // src/components/ChatMessage.tsx
1083
- import { jsx as jsx7 } from "react/jsx-runtime";
1313
+ import { jsx as jsx8 } from "react/jsx-runtime";
1084
1314
  function ChatMessage({ message }) {
1085
1315
  const isUser = message.role === "user";
1086
- return /* @__PURE__ */ jsx7("div", {
1316
+ return /* @__PURE__ */ jsx8("div", {
1087
1317
  className: cn("skippr:flex skippr:w-full skippr:px-4 skippr:py-1", isUser ? "skippr:justify-end" : "skippr:justify-start"),
1088
- children: /* @__PURE__ */ jsx7("div", {
1318
+ children: /* @__PURE__ */ jsx8("div", {
1089
1319
  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"),
1090
1320
  children: message.content
1091
1321
  })
@@ -1093,20 +1323,20 @@ function ChatMessage({ message }) {
1093
1323
  }
1094
1324
 
1095
1325
  // src/components/TypingIndicator.tsx
1096
- import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
1326
+ import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
1097
1327
  function TypingIndicator() {
1098
- return /* @__PURE__ */ jsx8("div", {
1328
+ return /* @__PURE__ */ jsx9("div", {
1099
1329
  className: "skippr:flex skippr:items-center skippr:gap-1 skippr:px-4 skippr:py-3",
1100
- children: /* @__PURE__ */ jsxs5("div", {
1330
+ children: /* @__PURE__ */ jsxs6("div", {
1101
1331
  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",
1102
1332
  children: [
1103
- /* @__PURE__ */ jsx8("span", {
1333
+ /* @__PURE__ */ jsx9("span", {
1104
1334
  className: "skippr:size-1.5 skippr:animate-bounce skippr:rounded-full skippr:bg-muted-foreground/60 skippr:[animation-delay:0ms]"
1105
1335
  }),
1106
- /* @__PURE__ */ jsx8("span", {
1336
+ /* @__PURE__ */ jsx9("span", {
1107
1337
  className: "skippr:size-1.5 skippr:animate-bounce skippr:rounded-full skippr:bg-muted-foreground/60 skippr:[animation-delay:150ms]"
1108
1338
  }),
1109
- /* @__PURE__ */ jsx8("span", {
1339
+ /* @__PURE__ */ jsx9("span", {
1110
1340
  className: "skippr:size-1.5 skippr:animate-bounce skippr:rounded-full skippr:bg-muted-foreground/60 skippr:[animation-delay:300ms]"
1111
1341
  })
1112
1342
  ]
@@ -1115,7 +1345,7 @@ function TypingIndicator() {
1115
1345
  }
1116
1346
 
1117
1347
  // src/components/MessageList.tsx
1118
- import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
1348
+ import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
1119
1349
  function MessageList({
1120
1350
  messages,
1121
1351
  isStreaming,
@@ -1124,29 +1354,29 @@ function MessageList({
1124
1354
  }) {
1125
1355
  const scrollRef = useRef3(null);
1126
1356
  const lastMessage = messages.length > 0 ? messages[messages.length - 1] : undefined;
1127
- useEffect5(() => {
1357
+ useEffect6(() => {
1128
1358
  scrollRef.current?.scrollIntoView({ behavior: "smooth" });
1129
1359
  }, [messages.length, lastMessage?.content]);
1130
1360
  const showTyping = isStreaming && lastMessage?.role === "assistant" && lastMessage.content === "";
1131
- return /* @__PURE__ */ jsxs6("div", {
1361
+ return /* @__PURE__ */ jsxs7("div", {
1132
1362
  className: "skippr:flex skippr:min-h-0 skippr:flex-1 skippr:flex-col",
1133
1363
  children: [
1134
- /* @__PURE__ */ jsx9("div", {
1364
+ /* @__PURE__ */ jsx10("div", {
1135
1365
  className: "skippr:min-h-0 skippr:flex-1 skippr:overflow-y-auto",
1136
- children: /* @__PURE__ */ jsxs6("div", {
1366
+ children: /* @__PURE__ */ jsxs7("div", {
1137
1367
  className: "skippr:flex skippr:flex-col skippr:gap-1 skippr:py-3",
1138
1368
  children: [
1139
- messages.map((message) => /* @__PURE__ */ jsx9(ChatMessage, {
1369
+ messages.map((message) => /* @__PURE__ */ jsx10(ChatMessage, {
1140
1370
  message
1141
1371
  }, message.id)),
1142
- showTyping && /* @__PURE__ */ jsx9(TypingIndicator, {}),
1143
- /* @__PURE__ */ jsx9("div", {
1372
+ showTyping && /* @__PURE__ */ jsx10(TypingIndicator, {}),
1373
+ /* @__PURE__ */ jsx10("div", {
1144
1374
  ref: scrollRef
1145
1375
  })
1146
1376
  ]
1147
1377
  })
1148
1378
  }),
1149
- /* @__PURE__ */ jsx9(ChatInput, {
1379
+ /* @__PURE__ */ jsx10(ChatInput, {
1150
1380
  sendChatMessage,
1151
1381
  isSendingChat
1152
1382
  })
@@ -1155,30 +1385,30 @@ function MessageList({
1155
1385
  }
1156
1386
 
1157
1387
  // src/components/QuickActions.tsx
1158
- import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
1388
+ import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
1159
1389
  function QuickActions({ onStartSession, isStarting, error }) {
1160
- return /* @__PURE__ */ jsxs7("div", {
1390
+ return /* @__PURE__ */ jsxs8("div", {
1161
1391
  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",
1162
1392
  children: [
1163
- /* @__PURE__ */ jsx10("p", {
1393
+ /* @__PURE__ */ jsx11("p", {
1164
1394
  className: "skippr:mb-1 skippr:text-sm skippr:text-muted-foreground",
1165
1395
  children: "How can I help you today?"
1166
1396
  }),
1167
- /* @__PURE__ */ jsxs7(Button, {
1397
+ /* @__PURE__ */ jsxs8(Button, {
1168
1398
  variant: "outline",
1169
1399
  className: "skippr:h-auto skippr:flex-col skippr:gap-1.5 skippr:whitespace-normal skippr:py-3 skippr:text-xs",
1170
1400
  onClick: onStartSession,
1171
1401
  disabled: isStarting,
1172
1402
  children: [
1173
- isStarting ? /* @__PURE__ */ jsx10(LoaderCircle, {
1403
+ isStarting ? /* @__PURE__ */ jsx11(LoaderCircle, {
1174
1404
  className: "skippr:size-4 skippr:animate-spin skippr:text-primary"
1175
- }) : /* @__PURE__ */ jsx10(MessageCircleQuestionMark, {
1405
+ }) : /* @__PURE__ */ jsx11(MessageCircleQuestionMark, {
1176
1406
  className: "skippr:size-4 skippr:text-primary"
1177
1407
  }),
1178
1408
  isStarting ? "Starting..." : "Start Session"
1179
1409
  ]
1180
1410
  }),
1181
- error && /* @__PURE__ */ jsx10("p", {
1411
+ error && /* @__PURE__ */ jsx11("p", {
1182
1412
  className: "skippr:text-xs skippr:text-destructive",
1183
1413
  children: error
1184
1414
  })
@@ -1187,52 +1417,52 @@ function QuickActions({ onStartSession, isStarting, error }) {
1187
1417
  }
1188
1418
 
1189
1419
  // src/components/SessionAgenda.tsx
1190
- import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
1420
+ import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
1191
1421
  function SessionAgenda({ phases, questions = [] }) {
1192
1422
  if (phases.length === 0) {
1193
- return /* @__PURE__ */ jsxs8("div", {
1423
+ return /* @__PURE__ */ jsxs9("div", {
1194
1424
  className: "skippr:flex skippr:flex-col skippr:gap-3 skippr:p-4",
1195
1425
  children: [
1196
- /* @__PURE__ */ jsx11("h3", {
1426
+ /* @__PURE__ */ jsx12("h3", {
1197
1427
  className: "skippr:text-sm skippr:font-semibold",
1198
1428
  children: "Agenda"
1199
1429
  }),
1200
- /* @__PURE__ */ jsx11("p", {
1430
+ /* @__PURE__ */ jsx12("p", {
1201
1431
  className: "skippr:text-xs skippr:text-muted-foreground",
1202
1432
  children: "Waiting for session..."
1203
1433
  })
1204
1434
  ]
1205
1435
  });
1206
1436
  }
1207
- return /* @__PURE__ */ jsxs8("div", {
1437
+ return /* @__PURE__ */ jsxs9("div", {
1208
1438
  className: "skippr:flex skippr:flex-col skippr:gap-3 skippr:p-4",
1209
1439
  children: [
1210
- /* @__PURE__ */ jsx11("h3", {
1440
+ /* @__PURE__ */ jsx12("h3", {
1211
1441
  className: "skippr:text-sm skippr:font-semibold",
1212
1442
  children: "Agenda"
1213
1443
  }),
1214
- /* @__PURE__ */ jsx11("ul", {
1444
+ /* @__PURE__ */ jsx12("ul", {
1215
1445
  className: "skippr:flex skippr:flex-col skippr:gap-2",
1216
1446
  children: phases.map((phase) => {
1217
1447
  const phaseQuestions = questions.filter((q) => q.phaseName === phase.name);
1218
1448
  const answeredCount = phaseQuestions.filter((q) => q.status === "answered").length;
1219
1449
  const totalCount = phaseQuestions.length;
1220
- return /* @__PURE__ */ jsxs8("li", {
1450
+ return /* @__PURE__ */ jsxs9("li", {
1221
1451
  className: "skippr:flex skippr:flex-col skippr:gap-0.5",
1222
1452
  children: [
1223
- /* @__PURE__ */ jsxs8("div", {
1453
+ /* @__PURE__ */ jsxs9("div", {
1224
1454
  className: "skippr:flex skippr:items-center skippr:gap-2 skippr:text-sm",
1225
1455
  children: [
1226
- /* @__PURE__ */ jsx11(PhaseIcon, {
1456
+ /* @__PURE__ */ jsx12(PhaseIcon, {
1227
1457
  status: phase.status
1228
1458
  }),
1229
- /* @__PURE__ */ jsx11("span", {
1459
+ /* @__PURE__ */ jsx12("span", {
1230
1460
  className: cn(phase.status === "completed" && "skippr:text-muted-foreground skippr:line-through", phase.status === "active" && "skippr:font-medium skippr:text-primary"),
1231
1461
  children: phase.name
1232
1462
  })
1233
1463
  ]
1234
1464
  }),
1235
- totalCount > 0 && /* @__PURE__ */ jsxs8("span", {
1465
+ totalCount > 0 && /* @__PURE__ */ jsxs9("span", {
1236
1466
  className: "skippr:ml-6 skippr:text-xs skippr:text-muted-foreground",
1237
1467
  children: [
1238
1468
  answeredCount,
@@ -1250,35 +1480,110 @@ function SessionAgenda({ phases, questions = [] }) {
1250
1480
  }
1251
1481
  function PhaseIcon({ status }) {
1252
1482
  if (status === "completed") {
1253
- return /* @__PURE__ */ jsx11("div", {
1483
+ return /* @__PURE__ */ jsx12("div", {
1254
1484
  className: "skippr:flex skippr:size-4 skippr:shrink-0 skippr:items-center skippr:justify-center skippr:rounded-full skippr:bg-primary",
1255
- children: /* @__PURE__ */ jsx11(Check, {
1485
+ children: /* @__PURE__ */ jsx12(Check, {
1256
1486
  className: "skippr:size-2.5 skippr:text-primary-foreground",
1257
1487
  strokeWidth: 3
1258
1488
  })
1259
1489
  });
1260
1490
  }
1261
1491
  if (status === "active") {
1262
- return /* @__PURE__ */ jsx11(LoaderCircle, {
1492
+ return /* @__PURE__ */ jsx12(LoaderCircle, {
1263
1493
  className: "skippr:size-4 skippr:shrink-0 skippr:text-primary skippr:animate-spin"
1264
1494
  });
1265
1495
  }
1266
- return /* @__PURE__ */ jsx11(Circle, {
1496
+ return /* @__PURE__ */ jsx12(Circle, {
1267
1497
  className: "skippr:size-4 skippr:shrink-0 skippr:text-muted-foreground"
1268
1498
  });
1269
1499
  }
1270
1500
 
1501
+ // src/components/SettingsView.tsx
1502
+ import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
1503
+ function SettingsView({ onBack }) {
1504
+ const { position, setPosition } = useLiveAgent();
1505
+ return /* @__PURE__ */ jsxs10("div", {
1506
+ className: "skippr:flex skippr:flex-1 skippr:flex-col",
1507
+ children: [
1508
+ /* @__PURE__ */ jsxs10("div", {
1509
+ className: "skippr:flex skippr:items-center skippr:gap-2 skippr:border-b skippr:border-border skippr:px-4 skippr:py-3",
1510
+ children: [
1511
+ /* @__PURE__ */ jsx13(Button, {
1512
+ variant: "ghost",
1513
+ size: "icon-xs",
1514
+ onClick: onBack,
1515
+ "aria-label": "Back",
1516
+ children: /* @__PURE__ */ jsx13(ArrowLeft, {
1517
+ className: "skippr:size-4"
1518
+ })
1519
+ }),
1520
+ /* @__PURE__ */ jsx13("p", {
1521
+ className: "skippr:text-sm skippr:font-semibold",
1522
+ children: "Settings"
1523
+ })
1524
+ ]
1525
+ }),
1526
+ /* @__PURE__ */ jsx13("div", {
1527
+ className: "skippr:flex-1 skippr:overflow-y-auto skippr:p-4",
1528
+ children: /* @__PURE__ */ jsxs10("div", {
1529
+ className: "skippr:mb-4",
1530
+ children: [
1531
+ /* @__PURE__ */ jsx13("p", {
1532
+ className: "skippr:mb-2 skippr:text-xs skippr:font-medium skippr:uppercase skippr:tracking-wide skippr:text-muted-foreground",
1533
+ children: "Widget Position"
1534
+ }),
1535
+ /* @__PURE__ */ jsxs10("div", {
1536
+ className: "skippr:flex skippr:gap-2",
1537
+ children: [
1538
+ /* @__PURE__ */ jsxs10("button", {
1539
+ type: "button",
1540
+ onClick: () => setPosition("left"),
1541
+ 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"),
1542
+ children: [
1543
+ /* @__PURE__ */ jsx13(PanelLeft, {
1544
+ className: "skippr:size-5"
1545
+ }),
1546
+ /* @__PURE__ */ jsx13("span", {
1547
+ className: "skippr:text-xs skippr:font-medium",
1548
+ children: "Left"
1549
+ })
1550
+ ]
1551
+ }),
1552
+ /* @__PURE__ */ jsxs10("button", {
1553
+ type: "button",
1554
+ onClick: () => setPosition("right"),
1555
+ 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"),
1556
+ children: [
1557
+ /* @__PURE__ */ jsx13(PanelRight, {
1558
+ className: "skippr:size-5"
1559
+ }),
1560
+ /* @__PURE__ */ jsx13("span", {
1561
+ className: "skippr:text-xs skippr:font-medium",
1562
+ children: "Right"
1563
+ })
1564
+ ]
1565
+ })
1566
+ ]
1567
+ })
1568
+ ]
1569
+ })
1570
+ })
1571
+ ]
1572
+ });
1573
+ }
1574
+
1271
1575
  // src/components/Sidebar.tsx
1272
- import { jsx as jsx12, jsxs as jsxs9, Fragment } from "react/jsx-runtime";
1576
+ import { jsx as jsx14, jsxs as jsxs11, Fragment as Fragment2 } from "react/jsx-runtime";
1273
1577
  function Sidebar() {
1274
1578
  const {
1579
+ variant,
1275
1580
  isConnected,
1276
1581
  isStarting,
1277
1582
  error,
1278
1583
  startSession,
1279
1584
  disconnect,
1280
1585
  isPanelOpen,
1281
- closePanel,
1586
+ position,
1282
1587
  isAuthenticated,
1283
1588
  isValidating,
1284
1589
  authError,
@@ -1286,42 +1591,53 @@ function Sidebar() {
1286
1591
  verifyOtp,
1287
1592
  isAuthSubmitting
1288
1593
  } = useLiveAgent();
1289
- useEffect6(() => {
1290
- document.body.style.transition = "margin-right 300ms ease-in-out";
1291
- document.body.style.marginRight = isPanelOpen ? `${SIDEBAR_WIDTH}px` : "0px";
1292
- }, [isPanelOpen]);
1293
- useEffect6(() => {
1594
+ const [view, setView] = useState7("main");
1595
+ const isFloating = variant === "floating";
1596
+ const isSidebar = variant === "sidebar";
1597
+ useEffect7(() => {
1598
+ if (!isSidebar)
1599
+ return;
1600
+ const prop = position === "right" ? "marginRight" : "marginLeft";
1601
+ const opposite = position === "right" ? "marginLeft" : "marginRight";
1602
+ document.body.style.transition = "margin 300ms ease-in-out";
1603
+ document.body.style[opposite] = "";
1604
+ document.body.style[prop] = isPanelOpen ? `${SIDEBAR_WIDTH}px` : "";
1294
1605
  return () => {
1295
1606
  document.body.style.marginRight = "";
1607
+ document.body.style.marginLeft = "";
1296
1608
  document.body.style.transition = "";
1297
1609
  };
1298
- }, []);
1299
- return /* @__PURE__ */ jsxs9("div", {
1300
- className: cn("skippr:fixed skippr:top-0 skippr:right-0 skippr:h-full skippr:z-[9999]", "skippr:bg-background skippr:border-l skippr:border-border", "skippr:flex skippr:flex-col", "skippr:transition-all skippr:duration-300 skippr:ease-in-out skippr:overflow-hidden", !isPanelOpen && "skippr:w-0 skippr:border-l-0"),
1610
+ }, [isSidebar, isPanelOpen, position]);
1611
+ return /* @__PURE__ */ jsx14("div", {
1612
+ className: cn("skippr:fixed skippr:z-[9999]", "skippr:bg-background skippr:border skippr:border-border", "skippr:flex skippr:flex-col", "skippr:transition-all skippr:duration-300 skippr:ease-in-out 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 && !isPanelOpen && "skippr:w-0 skippr:h-0 skippr:border-0", isSidebar && "skippr:top-0 skippr:h-full", 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"),
1301
1613
  style: { width: isPanelOpen ? SIDEBAR_WIDTH : undefined },
1302
- children: [
1303
- /* @__PURE__ */ jsx12(ChatHeader, {
1304
- onClose: closePanel
1305
- }),
1306
- isConnected ? /* @__PURE__ */ jsx12(ConnectedContent, {
1307
- onDisconnect: disconnect
1308
- }) : isValidating ? /* @__PURE__ */ jsx12("div", {
1309
- className: "skippr:flex skippr:flex-1 skippr:items-center skippr:justify-center",
1310
- children: /* @__PURE__ */ jsx12("p", {
1311
- className: "skippr:text-sm skippr:text-muted-foreground",
1312
- children: "Loading..."
1614
+ children: view === "settings" ? /* @__PURE__ */ jsx14(SettingsView, {
1615
+ onBack: () => setView("main")
1616
+ }) : /* @__PURE__ */ jsxs11(Fragment2, {
1617
+ children: [
1618
+ /* @__PURE__ */ jsx14(ChatHeader, {
1619
+ onOpenSettings: () => setView("settings")
1620
+ }),
1621
+ isConnected ? /* @__PURE__ */ jsx14(ConnectedContent, {
1622
+ onDisconnect: disconnect
1623
+ }) : isValidating ? /* @__PURE__ */ jsx14("div", {
1624
+ className: "skippr:flex skippr:flex-1 skippr:items-center skippr:justify-center",
1625
+ children: /* @__PURE__ */ jsx14("p", {
1626
+ className: "skippr:text-sm skippr:text-muted-foreground",
1627
+ children: "Loading..."
1628
+ })
1629
+ }) : isAuthenticated ? /* @__PURE__ */ jsx14(QuickActions, {
1630
+ onStartSession: startSession,
1631
+ isStarting,
1632
+ error
1633
+ }) : /* @__PURE__ */ jsx14(LoginFlow, {
1634
+ requestOtp,
1635
+ verifyOtp,
1636
+ error: authError,
1637
+ isSubmitting: isAuthSubmitting
1313
1638
  })
1314
- }) : isAuthenticated ? /* @__PURE__ */ jsx12(QuickActions, {
1315
- onStartSession: startSession,
1316
- isStarting,
1317
- error
1318
- }) : /* @__PURE__ */ jsx12(LoginFlow, {
1319
- requestOtp,
1320
- verifyOtp,
1321
- error: authError,
1322
- isSubmitting: isAuthSubmitting
1323
- })
1324
- ]
1639
+ ]
1640
+ })
1325
1641
  });
1326
1642
  }
1327
1643
  function ConnectedContent({ onDisconnect }) {
@@ -1331,33 +1647,33 @@ function ConnectedContent({ onDisconnect }) {
1331
1647
  const { phases } = usePhaseUpdates();
1332
1648
  const { questions } = useQuestionUpdates();
1333
1649
  if (!isConnected) {
1334
- return /* @__PURE__ */ jsx12("div", {
1650
+ return /* @__PURE__ */ jsx14("div", {
1335
1651
  className: "skippr:flex skippr:flex-1 skippr:items-center skippr:justify-center",
1336
- children: /* @__PURE__ */ jsx12("p", {
1652
+ children: /* @__PURE__ */ jsx14("p", {
1337
1653
  className: "skippr:text-sm skippr:text-muted-foreground",
1338
1654
  children: "Connecting..."
1339
1655
  })
1340
1656
  });
1341
1657
  }
1342
1658
  const isAgentSpeaking = agentState === "speaking";
1343
- return /* @__PURE__ */ jsxs9(Fragment, {
1659
+ return /* @__PURE__ */ jsxs11(Fragment2, {
1344
1660
  children: [
1345
- /* @__PURE__ */ jsx12(MeetingControls, {
1661
+ /* @__PURE__ */ jsx14(MeetingControls, {
1346
1662
  onHangUp: onDisconnect
1347
1663
  }),
1348
- /* @__PURE__ */ jsxs9("div", {
1664
+ /* @__PURE__ */ jsxs11("div", {
1349
1665
  className: "skippr:flex skippr:min-h-0 skippr:flex-1",
1350
1666
  children: [
1351
- /* @__PURE__ */ jsx12("div", {
1352
- className: "skippr:w-[260px] skippr:shrink-0 skippr:overflow-y-auto skippr:border-r",
1353
- children: /* @__PURE__ */ jsx12(SessionAgenda, {
1667
+ /* @__PURE__ */ jsx14("div", {
1668
+ className: "skippr:w-[180px] skippr:shrink-0 skippr:overflow-y-auto skippr:border-r",
1669
+ children: /* @__PURE__ */ jsx14(SessionAgenda, {
1354
1670
  phases,
1355
1671
  questions
1356
1672
  })
1357
1673
  }),
1358
- /* @__PURE__ */ jsx12("div", {
1674
+ /* @__PURE__ */ jsx14("div", {
1359
1675
  className: "skippr:flex skippr:min-w-0 skippr:flex-1 skippr:flex-col",
1360
- children: /* @__PURE__ */ jsx12(MessageList, {
1676
+ children: /* @__PURE__ */ jsx14(MessageList, {
1361
1677
  messages: allMessages,
1362
1678
  isStreaming: isAgentSpeaking,
1363
1679
  sendChatMessage,
@@ -1371,32 +1687,34 @@ function ConnectedContent({ onDisconnect }) {
1371
1687
  }
1372
1688
 
1373
1689
  // src/components/SidebarTrigger.tsx
1374
- import { jsx as jsx13 } from "react/jsx-runtime";
1375
- var TRIGGER_GAP = 16;
1376
- var TRIGGER_DEFAULT_RIGHT = 24;
1690
+ import { jsx as jsx15 } from "react/jsx-runtime";
1377
1691
  function SidebarTrigger() {
1378
- const { isPanelOpen, togglePanel } = useLiveAgent();
1379
- return /* @__PURE__ */ jsx13(Button, {
1692
+ const { isPanelOpen, togglePanel, position, isMinimized, isConnected } = useLiveAgent();
1693
+ if (isMinimized && isConnected)
1694
+ return null;
1695
+ return /* @__PURE__ */ jsx15(Button, {
1380
1696
  size: "icon-lg",
1381
1697
  onClick: togglePanel,
1382
- className: "skippr:fixed skippr:bottom-6 skippr:z-[9998] skippr:size-14 skippr:rounded-full skippr:shadow-lg skippr:transition-all skippr:duration-300",
1383
- style: { right: isPanelOpen ? SIDEBAR_WIDTH + TRIGGER_GAP : TRIGGER_DEFAULT_RIGHT },
1698
+ 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"),
1384
1699
  title: isPanelOpen ? "Close chat" : "Chat with us",
1385
- children: isPanelOpen ? /* @__PURE__ */ jsx13(X, {
1700
+ children: isPanelOpen ? /* @__PURE__ */ jsx15(X, {
1386
1701
  className: "skippr:size-6"
1387
- }) : /* @__PURE__ */ jsx13(MessageCircle, {
1702
+ }) : /* @__PURE__ */ jsx15(MessageCircle, {
1388
1703
  className: "skippr:size-6"
1389
1704
  })
1390
1705
  });
1391
1706
  }
1392
1707
 
1393
1708
  // src/components/LiveAgent.tsx
1394
- import { jsx as jsx14, jsxs as jsxs10, Fragment as Fragment2 } from "react/jsx-runtime";
1709
+ import { jsx as jsx16, jsxs as jsxs12, Fragment as Fragment3 } from "react/jsx-runtime";
1395
1710
  function LiveAgent({
1396
1711
  agentId,
1397
1712
  authToken: authTokenProp,
1398
1713
  appKey,
1399
1714
  userToken,
1715
+ position = "right",
1716
+ variant = "floating",
1717
+ minimizable = true,
1400
1718
  defaultOpen = false,
1401
1719
  children
1402
1720
  }) {
@@ -1408,12 +1726,45 @@ function LiveAgent({
1408
1726
  appKey,
1409
1727
  userToken
1410
1728
  });
1411
- const [isPanelOpen, setIsPanelOpen] = useState7(defaultOpen);
1412
- const openPanel = useCallback7(() => setIsPanelOpen(true), []);
1413
- const closePanel = useCallback7(() => setIsPanelOpen(false), []);
1414
- const togglePanel = useCallback7(() => setIsPanelOpen((prev) => !prev), []);
1729
+ const [isPanelOpen, setIsPanelOpen] = useState8(defaultOpen);
1730
+ const [isMinimized, setIsMinimized] = useState8(false);
1731
+ const [currentPosition, setCurrentPosition] = useState8(() => {
1732
+ try {
1733
+ const saved = localStorage.getItem("skippr_widget_position");
1734
+ if (saved === "left" || saved === "right")
1735
+ return saved;
1736
+ } catch {}
1737
+ return position;
1738
+ });
1739
+ const setPositionWithPersist = useCallback8((pos) => {
1740
+ setCurrentPosition(pos);
1741
+ try {
1742
+ localStorage.setItem("skippr_widget_position", pos);
1743
+ } catch {}
1744
+ }, []);
1745
+ const openPanel = useCallback8(() => setIsPanelOpen(true), []);
1746
+ const closePanel = useCallback8(() => setIsPanelOpen(false), []);
1747
+ const togglePanel = useCallback8(() => setIsPanelOpen((prev) => !prev), []);
1748
+ const expandPanel = useCallback8(() => {
1749
+ setIsMinimized(false);
1750
+ setIsPanelOpen(true);
1751
+ }, []);
1752
+ const minimizePanel = useCallback8(() => {
1753
+ if (!minimizable)
1754
+ return;
1755
+ setIsMinimized(true);
1756
+ setIsPanelOpen(false);
1757
+ }, [minimizable]);
1415
1758
  const isConnected = connection !== null;
1416
1759
  const isAuthenticated = !!userToken || !!authTokenProp || auth.isAuthenticated;
1760
+ useEffect8(() => {
1761
+ if (connection && minimizable) {
1762
+ setIsMinimized(true);
1763
+ setIsPanelOpen(false);
1764
+ } else if (!connection) {
1765
+ setIsMinimized(false);
1766
+ }
1767
+ }, [connection, minimizable]);
1417
1768
  const ctx = useMemo4(() => ({
1418
1769
  connection,
1419
1770
  shouldConnect,
@@ -1426,6 +1777,13 @@ function LiveAgent({
1426
1777
  openPanel,
1427
1778
  closePanel,
1428
1779
  togglePanel,
1780
+ variant,
1781
+ position: currentPosition,
1782
+ setPosition: setPositionWithPersist,
1783
+ minimizable,
1784
+ isMinimized,
1785
+ expandPanel,
1786
+ minimizePanel,
1429
1787
  isAuthenticated,
1430
1788
  isValidating: auth.isValidating,
1431
1789
  authError: auth.error,
@@ -1445,6 +1803,13 @@ function LiveAgent({
1445
1803
  openPanel,
1446
1804
  closePanel,
1447
1805
  togglePanel,
1806
+ variant,
1807
+ currentPosition,
1808
+ setPositionWithPersist,
1809
+ minimizable,
1810
+ isMinimized,
1811
+ expandPanel,
1812
+ minimizePanel,
1448
1813
  isAuthenticated,
1449
1814
  auth.isValidating,
1450
1815
  auth.error,
@@ -1453,17 +1818,18 @@ function LiveAgent({
1453
1818
  auth.logout,
1454
1819
  auth.isSubmitting
1455
1820
  ]);
1456
- const widgetContent = /* @__PURE__ */ jsxs10(Fragment2, {
1821
+ const widgetContent = /* @__PURE__ */ jsxs12(Fragment3, {
1457
1822
  children: [
1458
- connection && /* @__PURE__ */ jsx14(RoomAudioRenderer, {}),
1459
- /* @__PURE__ */ jsx14(SidebarTrigger, {}),
1460
- /* @__PURE__ */ jsx14(Sidebar, {}),
1823
+ connection && /* @__PURE__ */ jsx16(RoomAudioRenderer, {}),
1824
+ isMinimized && isConnected && /* @__PURE__ */ jsx16(MinimizedBubble, {}),
1825
+ /* @__PURE__ */ jsx16(SidebarTrigger, {}),
1826
+ /* @__PURE__ */ jsx16(Sidebar, {}),
1461
1827
  children
1462
1828
  ]
1463
1829
  });
1464
- return /* @__PURE__ */ jsx14(LiveAgentContext.Provider, {
1830
+ return /* @__PURE__ */ jsx16(LiveAgentContext.Provider, {
1465
1831
  value: ctx,
1466
- children: connection ? /* @__PURE__ */ jsx14(LiveKitRoom, {
1832
+ children: connection ? /* @__PURE__ */ jsx16(LiveKitRoom, {
1467
1833
  serverUrl: connection.livekitUrl,
1468
1834
  token: connection.token,
1469
1835
  connect: shouldConnect,