@skippr/live-agent-sdk 0.9.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -17,9 +17,14 @@ import { LiveAgent } from '@skippr/live-agent-sdk';
17
17
  import '@skippr/live-agent-sdk/styles';
18
18
 
19
19
  function App() {
20
- return <LiveAgent organizationId="your_org_id" agentId="your_agent_id" />;
20
+ return (
21
+ <LiveAgent
22
+ agentId="your_agent_id"
23
+ appKey="pk_live_your_key"
24
+ />
25
+ );
21
26
  }
22
- ```
27
+
23
28
 
24
29
  ## Custom Components
25
30
 
@@ -37,7 +42,7 @@ function ConnectionStatus() {
37
42
 
38
43
  function App() {
39
44
  return (
40
- <LiveAgent organizationId="your_org_id" agentId="your_agent_id">
45
+ <LiveAgent agentId="your_agent_id" appKey="pk_live_your_key">
41
46
  <ConnectionStatus />
42
47
  </LiveAgent>
43
48
  );
@@ -54,8 +59,9 @@ Self-contained widget component. Renders a floating button that opens a sidebar
54
59
 
55
60
  | Prop | Type | Default | Description |
56
61
  |------|------|---------|-------------|
57
- | `organizationId` | `string` | *required* | Organization ID provided by Skippr |
58
62
  | `agentId` | `string` | *required* | Agent ID provided by Skippr |
63
+ | `appKey` | `string` | — | Publishable App Key from the admin dashboard |
64
+ | `authToken` | `string` | — | Bearer token for authenticated users |
59
65
  | `defaultOpen` | `boolean` | `false` | Whether the panel starts open |
60
66
 
61
67
  ---
@@ -9,7 +9,14 @@ var LiveAgentContext = createContext(null);
9
9
  // src/hooks/useSession.ts
10
10
  import { useCallback, useState } from "react";
11
11
  var API_URL = "https://skipprapi-production.up.railway.app";
12
- function useSession({ organizationId, agentId, authToken }) {
12
+ function resolveAuthHeaders(authToken, appKey) {
13
+ if (authToken)
14
+ return { Authorization: `Bearer ${authToken}` };
15
+ if (appKey)
16
+ return { "X-App-Key": appKey };
17
+ return {};
18
+ }
19
+ function useSession({ agentId, authToken, appKey }) {
13
20
  const [connection, setConnection] = useState(null);
14
21
  const [shouldConnect, setShouldConnect] = useState(false);
15
22
  const [isStarting, setIsStarting] = useState(false);
@@ -19,11 +26,11 @@ function useSession({ organizationId, agentId, authToken }) {
19
26
  setIsStarting(true);
20
27
  setError("");
21
28
  try {
22
- const authHeaders = authToken ? { Authorization: `Bearer ${authToken}` } : {};
29
+ const authHeaders = resolveAuthHeaders(authToken, appKey);
23
30
  const createResp = await fetch(`${API_URL}/v1/sessions`, {
24
31
  method: "POST",
25
32
  headers: { "Content-Type": "application/json", ...authHeaders },
26
- body: JSON.stringify({ organizationId, agentId })
33
+ body: JSON.stringify({ agentId })
27
34
  });
28
35
  if (!createResp.ok) {
29
36
  throw new Error(`Failed to create session: ${createResp.status}`);
@@ -48,10 +55,10 @@ function useSession({ organizationId, agentId, authToken }) {
48
55
  } finally {
49
56
  setIsStarting(false);
50
57
  }
51
- }, [organizationId, agentId, authToken]);
58
+ }, [agentId, authToken, appKey]);
52
59
  const disconnect = useCallback(async () => {
53
60
  if (sessionId) {
54
- const authHeaders = authToken ? { Authorization: `Bearer ${authToken}` } : {};
61
+ const authHeaders = resolveAuthHeaders(authToken, appKey);
55
62
  try {
56
63
  await fetch(`${API_URL}/v1/sessions/${sessionId}/complete`, {
57
64
  method: "POST",
@@ -64,7 +71,7 @@ function useSession({ organizationId, agentId, authToken }) {
64
71
  setShouldConnect(false);
65
72
  setConnection(null);
66
73
  setSessionId(null);
67
- }, [sessionId, authToken]);
74
+ }, [sessionId, authToken, appKey]);
68
75
  return { connection, shouldConnect, isStarting, error, startSession, disconnect };
69
76
  }
70
77
 
@@ -250,12 +257,204 @@ import { twMerge } from "tailwind-merge";
250
257
  function cn(...inputs) {
251
258
  return twMerge(clsx(inputs));
252
259
  }
260
+ // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/createLucideIcon.js
261
+ import { forwardRef as forwardRef2, createElement as createElement2 } from "react";
253
262
 
254
- // src/components/ChatHeader.tsx
255
- import { Bot, X } from "lucide-react";
263
+ // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/shared/src/utils/mergeClasses.js
264
+ var mergeClasses = (...classes) => classes.filter((className, index, array) => {
265
+ return Boolean(className) && className.trim() !== "" && array.indexOf(className) === index;
266
+ }).join(" ").trim();
267
+
268
+ // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/shared/src/utils/toKebabCase.js
269
+ var toKebabCase = (string) => string.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
270
+
271
+ // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/shared/src/utils/toCamelCase.js
272
+ var toCamelCase = (string) => string.replace(/^([A-Z])|[\s-_]+(\w)/g, (match, p1, p2) => p2 ? p2.toUpperCase() : p1.toLowerCase());
273
+
274
+ // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/shared/src/utils/toPascalCase.js
275
+ var toPascalCase = (string) => {
276
+ const camelCase = toCamelCase(string);
277
+ return camelCase.charAt(0).toUpperCase() + camelCase.slice(1);
278
+ };
256
279
 
280
+ // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/Icon.js
281
+ import { forwardRef, createElement } from "react";
282
+
283
+ // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/defaultAttributes.js
284
+ var defaultAttributes = {
285
+ xmlns: "http://www.w3.org/2000/svg",
286
+ width: 24,
287
+ height: 24,
288
+ viewBox: "0 0 24 24",
289
+ fill: "none",
290
+ stroke: "currentColor",
291
+ strokeWidth: 2,
292
+ strokeLinecap: "round",
293
+ strokeLinejoin: "round"
294
+ };
295
+
296
+ // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/shared/src/utils/hasA11yProp.js
297
+ var hasA11yProp = (props) => {
298
+ for (const prop in props) {
299
+ if (prop.startsWith("aria-") || prop === "role" || prop === "title") {
300
+ return true;
301
+ }
302
+ }
303
+ return false;
304
+ };
305
+
306
+ // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/Icon.js
307
+ var Icon = forwardRef(({
308
+ color = "currentColor",
309
+ size = 24,
310
+ strokeWidth = 2,
311
+ absoluteStrokeWidth,
312
+ className = "",
313
+ children,
314
+ iconNode,
315
+ ...rest
316
+ }, ref) => createElement("svg", {
317
+ ref,
318
+ ...defaultAttributes,
319
+ width: size,
320
+ height: size,
321
+ stroke: color,
322
+ strokeWidth: absoluteStrokeWidth ? Number(strokeWidth) * 24 / Number(size) : strokeWidth,
323
+ className: mergeClasses("lucide", className),
324
+ ...!children && !hasA11yProp(rest) && { "aria-hidden": "true" },
325
+ ...rest
326
+ }, [
327
+ ...iconNode.map(([tag, attrs]) => createElement(tag, attrs)),
328
+ ...Array.isArray(children) ? children : [children]
329
+ ]));
330
+
331
+ // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/createLucideIcon.js
332
+ var createLucideIcon = (iconName, iconNode) => {
333
+ const Component = forwardRef2(({ className, ...props }, ref) => createElement2(Icon, {
334
+ ref,
335
+ iconNode,
336
+ className: mergeClasses(`lucide-${toKebabCase(toPascalCase(iconName))}`, `lucide-${iconName}`, className),
337
+ ...props
338
+ }));
339
+ Component.displayName = toPascalCase(iconName);
340
+ return Component;
341
+ };
342
+
343
+ // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/loader-circle.js
344
+ var __iconNode = [["path", { d: "M21 12a9 9 0 1 1-6.219-8.56", key: "13zald" }]];
345
+ var LoaderCircle = createLucideIcon("loader-circle", __iconNode);
346
+ // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/message-circle-question-mark.js
347
+ var __iconNode2 = [
348
+ [
349
+ "path",
350
+ {
351
+ 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",
352
+ key: "1sd12s"
353
+ }
354
+ ],
355
+ ["path", { d: "M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3", key: "1u773s" }],
356
+ ["path", { d: "M12 17h.01", key: "p32p05" }]
357
+ ];
358
+ var MessageCircleQuestionMark = createLucideIcon("message-circle-question-mark", __iconNode2);
359
+ // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/send-horizontal.js
360
+ var __iconNode3 = [
361
+ [
362
+ "path",
363
+ {
364
+ 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",
365
+ key: "117uat"
366
+ }
367
+ ],
368
+ ["path", { d: "M6 12h16", key: "s4cdu5" }]
369
+ ];
370
+ var SendHorizontal = createLucideIcon("send-horizontal", __iconNode3);
371
+ // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/bot.js
372
+ var __iconNode4 = [
373
+ ["path", { d: "M12 8V4H8", key: "hb8ula" }],
374
+ ["rect", { width: "16", height: "12", x: "4", y: "8", rx: "2", key: "enze0r" }],
375
+ ["path", { d: "M2 14h2", key: "vft8re" }],
376
+ ["path", { d: "M20 14h2", key: "4cs60a" }],
377
+ ["path", { d: "M15 13v2", key: "1xurst" }],
378
+ ["path", { d: "M9 13v2", key: "rq6x2g" }]
379
+ ];
380
+ var Bot = createLucideIcon("bot", __iconNode4);
381
+ // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/check.js
382
+ var __iconNode5 = [["path", { d: "M20 6 9 17l-5-5", key: "1gmf2c" }]];
383
+ var Check = createLucideIcon("check", __iconNode5);
384
+ // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/circle.js
385
+ var __iconNode6 = [["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }]];
386
+ var Circle = createLucideIcon("circle", __iconNode6);
387
+ // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/message-circle.js
388
+ var __iconNode7 = [
389
+ [
390
+ "path",
391
+ {
392
+ 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",
393
+ key: "1sd12s"
394
+ }
395
+ ]
396
+ ];
397
+ var MessageCircle = createLucideIcon("message-circle", __iconNode7);
398
+ // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/mic-off.js
399
+ var __iconNode8 = [
400
+ ["path", { d: "M12 19v3", key: "npa21l" }],
401
+ ["path", { d: "M15 9.34V5a3 3 0 0 0-5.68-1.33", key: "1gzdoj" }],
402
+ ["path", { d: "M16.95 16.95A7 7 0 0 1 5 12v-2", key: "cqa7eg" }],
403
+ ["path", { d: "M18.89 13.23A7 7 0 0 0 19 12v-2", key: "16hl24" }],
404
+ ["path", { d: "m2 2 20 20", key: "1ooewy" }],
405
+ ["path", { d: "M9 9v3a3 3 0 0 0 5.12 2.12", key: "r2i35w" }]
406
+ ];
407
+ var MicOff = createLucideIcon("mic-off", __iconNode8);
408
+ // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/mic.js
409
+ var __iconNode9 = [
410
+ ["path", { d: "M12 19v3", key: "npa21l" }],
411
+ ["path", { d: "M19 10v2a7 7 0 0 1-14 0v-2", key: "1vc78b" }],
412
+ ["rect", { x: "9", y: "2", width: "6", height: "13", rx: "3", key: "s6n7sd" }]
413
+ ];
414
+ var Mic = createLucideIcon("mic", __iconNode9);
415
+ // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/monitor-off.js
416
+ var __iconNode10 = [
417
+ ["path", { d: "M12 17v4", key: "1riwvh" }],
418
+ ["path", { d: "M17 17H4a2 2 0 0 1-2-2V5a2 2 0 0 1 1.184-1.826", key: "cv7jms" }],
419
+ ["path", { d: "m2 2 20 20", key: "1ooewy" }],
420
+ ["path", { d: "M8 21h8", key: "1ev6f3" }],
421
+ ["path", { d: "M8.656 3H20a2 2 0 0 1 2 2v10a2 2 0 0 1-.293 1.042", key: "z8ni2w" }]
422
+ ];
423
+ var MonitorOff = createLucideIcon("monitor-off", __iconNode10);
424
+ // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/monitor.js
425
+ var __iconNode11 = [
426
+ ["rect", { width: "20", height: "14", x: "2", y: "3", rx: "2", key: "48i651" }],
427
+ ["line", { x1: "8", x2: "16", y1: "21", y2: "21", key: "1svkeh" }],
428
+ ["line", { x1: "12", x2: "12", y1: "17", y2: "21", key: "vw1qmm" }]
429
+ ];
430
+ var Monitor = createLucideIcon("monitor", __iconNode11);
431
+ // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/phone-off.js
432
+ var __iconNode12 = [
433
+ [
434
+ "path",
435
+ {
436
+ 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",
437
+ key: "1wngk7"
438
+ }
439
+ ],
440
+ ["path", { d: "M22 2 2 22", key: "y4kqgn" }],
441
+ [
442
+ "path",
443
+ {
444
+ 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",
445
+ key: "10hv5p"
446
+ }
447
+ ]
448
+ ];
449
+ var PhoneOff = createLucideIcon("phone-off", __iconNode12);
450
+ // ../../node_modules/.bun/lucide-react@0.563.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/x.js
451
+ var __iconNode13 = [
452
+ ["path", { d: "M18 6 6 18", key: "1bl5f8" }],
453
+ ["path", { d: "m6 6 12 12", key: "d8bk6v" }]
454
+ ];
455
+ var X = createLucideIcon("x", __iconNode13);
257
456
  // src/components/ui/button.tsx
258
- import { forwardRef } from "react";
457
+ import { forwardRef as forwardRef3 } from "react";
259
458
  import { jsx } from "react/jsx-runtime";
260
459
  var variantClasses = {
261
460
  default: "skippr:bg-primary skippr:text-primary-foreground skippr:hover:bg-primary/90",
@@ -274,7 +473,7 @@ var sizeClasses = {
274
473
  "icon-sm": "skippr:size-8",
275
474
  "icon-lg": "skippr:size-10"
276
475
  };
277
- var Button = forwardRef(({ className, variant = "default", size = "default", ...props }, ref) => {
476
+ var Button = forwardRef3(({ className, variant = "default", size = "default", ...props }, ref) => {
278
477
  return /* @__PURE__ */ jsx("button", {
279
478
  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),
280
479
  ref,
@@ -318,7 +517,6 @@ function ChatHeader({ onClose }) {
318
517
  // src/components/MeetingControls.tsx
319
518
  import { useLocalParticipant as useLocalParticipant3 } from "@livekit/components-react";
320
519
  import { ScreenSharePresets } from "livekit-client";
321
- import { Mic, MicOff, Monitor, MonitorOff, PhoneOff } from "lucide-react";
322
520
  import { useCallback as useCallback4, useEffect as useEffect2, useState as useState3 } from "react";
323
521
 
324
522
  // src/lib/format.ts
@@ -411,7 +609,6 @@ function MeetingControls({ onHangUp }) {
411
609
  import { useEffect as useEffect3, useRef } from "react";
412
610
 
413
611
  // src/components/ChatInput.tsx
414
- import { SendHorizontal } from "lucide-react";
415
612
  import { useState as useState4 } from "react";
416
613
  import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
417
614
  function ChatInput({ sendChatMessage, isSendingChat }) {
@@ -526,7 +723,6 @@ function MessageList({
526
723
  }
527
724
 
528
725
  // src/components/QuickActions.tsx
529
- import { Loader2, MessageCircleQuestion } from "lucide-react";
530
726
  import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
531
727
  function QuickActions({ onStartSession, isStarting, error }) {
532
728
  return /* @__PURE__ */ jsxs6("div", {
@@ -542,9 +738,9 @@ function QuickActions({ onStartSession, isStarting, error }) {
542
738
  onClick: onStartSession,
543
739
  disabled: isStarting,
544
740
  children: [
545
- isStarting ? /* @__PURE__ */ jsx8(Loader2, {
741
+ isStarting ? /* @__PURE__ */ jsx8(LoaderCircle, {
546
742
  className: "skippr:size-4 skippr:animate-spin skippr:text-primary"
547
- }) : /* @__PURE__ */ jsx8(MessageCircleQuestion, {
743
+ }) : /* @__PURE__ */ jsx8(MessageCircleQuestionMark, {
548
744
  className: "skippr:size-4 skippr:text-primary"
549
745
  }),
550
746
  isStarting ? "Starting..." : "Start Session"
@@ -559,7 +755,6 @@ function QuickActions({ onStartSession, isStarting, error }) {
559
755
  }
560
756
 
561
757
  // src/components/SessionAgenda.tsx
562
- import { Check, Circle, Loader2 as Loader22 } from "lucide-react";
563
758
  import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
564
759
  function SessionAgenda({ phases, questions = [] }) {
565
760
  if (phases.length === 0) {
@@ -632,7 +827,7 @@ function PhaseIcon({ status }) {
632
827
  });
633
828
  }
634
829
  if (status === "active") {
635
- return /* @__PURE__ */ jsx9(Loader22, {
830
+ return /* @__PURE__ */ jsx9(LoaderCircle, {
636
831
  className: "skippr:size-4 skippr:shrink-0 skippr:text-primary skippr:animate-spin"
637
832
  });
638
833
  }
@@ -728,7 +923,6 @@ function ConnectedContent({ onDisconnect }) {
728
923
  }
729
924
 
730
925
  // src/components/SidebarTrigger.tsx
731
- import { MessageCircle, X as X2 } from "lucide-react";
732
926
  import { jsx as jsx11 } from "react/jsx-runtime";
733
927
  var TRIGGER_GAP = 16;
734
928
  var TRIGGER_DEFAULT_RIGHT = 24;
@@ -740,7 +934,7 @@ function SidebarTrigger() {
740
934
  className: "skippr:fixed skippr:bottom-6 skippr:z-[9998] skippr:size-14 skippr:rounded-full skippr:shadow-lg skippr:transition-all skippr:duration-300",
741
935
  style: { right: isPanelOpen ? SIDEBAR_WIDTH + TRIGGER_GAP : TRIGGER_DEFAULT_RIGHT },
742
936
  title: isPanelOpen ? "Close chat" : "Chat with us",
743
- children: isPanelOpen ? /* @__PURE__ */ jsx11(X2, {
937
+ children: isPanelOpen ? /* @__PURE__ */ jsx11(X, {
744
938
  className: "skippr:size-6"
745
939
  }) : /* @__PURE__ */ jsx11(MessageCircle, {
746
940
  className: "skippr:size-6"
@@ -751,16 +945,16 @@ function SidebarTrigger() {
751
945
  // src/components/LiveAgent.tsx
752
946
  import { jsx as jsx12, jsxs as jsxs9, Fragment as Fragment2 } from "react/jsx-runtime";
753
947
  function LiveAgent({
754
- organizationId,
755
948
  agentId,
756
949
  authToken,
950
+ appKey,
757
951
  defaultOpen = false,
758
952
  children
759
953
  }) {
760
954
  const { connection, shouldConnect, isStarting, error, startSession, disconnect } = useSession({
761
- organizationId,
762
955
  agentId,
763
- authToken
956
+ authToken,
957
+ appKey
764
958
  });
765
959
  const [isPanelOpen, setIsPanelOpen] = useState5(defaultOpen);
766
960
  const openPanel = useCallback5(() => setIsPanelOpen(true), []);