@wallavi/widget 1.6.8 → 1.7.1

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/dist/index.mjs CHANGED
@@ -1,11 +1,13 @@
1
1
  import { useRef, useEffect, useState, useCallback, useMemo } from 'react';
2
- import { UploadCloud, X, RotateCcw, Loader2, Square, Mic, Paperclip, ArrowUp, Zap, ChevronDown, CheckCircle2, AlertCircle, Search, Check, FileText, FileSpreadsheet } from 'lucide-react';
2
+ import { Loader2, CheckCircle2, AlertCircle, Zap, Check, X, ChevronDown, UploadCloud, Phone, RotateCcw, Square, Mic, Paperclip, ArrowUp, MicOff, PhoneOff, Search, FileText, FileSpreadsheet } from 'lucide-react';
3
3
  import { clsx } from 'clsx';
4
4
  import { extendTailwindMerge, twMerge as twMerge$1 } from 'tailwind-merge';
5
5
  import * as AvatarPrimitive from '@radix-ui/react-avatar';
6
6
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
7
7
  import ReactMarkdownLib from 'react-markdown';
8
8
  import remarkGfm from 'remark-gfm';
9
+ import { LiveKitRoom, RoomAudioRenderer, useVoiceAssistant, BarVisualizer, useLocalParticipant, useRoomContext } from '@livekit/components-react';
10
+ import '@livekit/components-styles';
9
11
 
10
12
  // #style-inject:#style-inject
11
13
  function styleInject(css, { insertAt } = {}) {
@@ -30,11 +32,11 @@ function styleInject(css, { insertAt } = {}) {
30
32
  }
31
33
 
32
34
  // src/styles.css
33
- styleInject(".ww-pointer-events-none {\n pointer-events: none;\n}\n.ww-absolute {\n position: absolute;\n}\n.ww-relative {\n position: relative;\n}\n.ww-inset-0 {\n inset: 0px;\n}\n.ww-left-2\\.5 {\n left: 0.625rem;\n}\n.ww-top-1\\/2 {\n top: 50%;\n}\n.ww-z-50 {\n z-index: 50;\n}\n.ww-mb-1 {\n margin-bottom: 0.25rem;\n}\n.ww-mb-2 {\n margin-bottom: 0.5rem;\n}\n.ww-ml-0\\.5 {\n margin-left: 0.125rem;\n}\n.ww-mt-0\\.5 {\n margin-top: 0.125rem;\n}\n.ww-mt-1 {\n margin-top: 0.25rem;\n}\n.ww-block {\n display: block;\n}\n.ww-inline-block {\n display: inline-block;\n}\n.ww-flex {\n display: flex;\n}\n.ww-inline-flex {\n display: inline-flex;\n}\n.ww-grid {\n display: grid;\n}\n.ww-hidden {\n display: none;\n}\n.ww-h-10 {\n height: 2.5rem;\n}\n.ww-h-2\\.5 {\n height: 0.625rem;\n}\n.ww-h-20 {\n height: 5rem;\n}\n.ww-h-3 {\n height: 0.75rem;\n}\n.ww-h-3\\.5 {\n height: 0.875rem;\n}\n.ww-h-4 {\n height: 1rem;\n}\n.ww-h-6 {\n height: 1.5rem;\n}\n.ww-h-7 {\n height: 1.75rem;\n}\n.ww-h-8 {\n height: 2rem;\n}\n.ww-h-full {\n height: 100%;\n}\n.ww-max-h-32 {\n max-height: 8rem;\n}\n.ww-max-h-48 {\n max-height: 12rem;\n}\n.ww-max-h-\\[168px\\] {\n max-height: 168px;\n}\n.ww-max-h-\\[180px\\] {\n max-height: 180px;\n}\n.ww-w-0\\.5 {\n width: 0.125rem;\n}\n.ww-w-10 {\n width: 2.5rem;\n}\n.ww-w-2\\.5 {\n width: 0.625rem;\n}\n.ww-w-20 {\n width: 5rem;\n}\n.ww-w-3 {\n width: 0.75rem;\n}\n.ww-w-3\\.5 {\n width: 0.875rem;\n}\n.ww-w-4 {\n width: 1rem;\n}\n.ww-w-6 {\n width: 1.5rem;\n}\n.ww-w-7 {\n width: 1.75rem;\n}\n.ww-w-8 {\n width: 2rem;\n}\n.ww-w-fit {\n width: -moz-fit-content;\n width: fit-content;\n}\n.ww-w-full {\n width: 100%;\n}\n.ww-min-w-0 {\n min-width: 0px;\n}\n.ww-max-w-\\[120px\\] {\n max-width: 120px;\n}\n.ww-max-w-\\[200px\\] {\n max-width: 200px;\n}\n.ww-max-w-\\[78\\%\\] {\n max-width: 78%;\n}\n.ww-max-w-\\[82\\%\\] {\n max-width: 82%;\n}\n.ww-max-w-none {\n max-width: none;\n}\n.ww-flex-1 {\n flex: 1 1 0%;\n}\n.ww-shrink-0 {\n flex-shrink: 0;\n}\n.ww-rotate-180 {\n --tw-rotate: 180deg;\n transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));\n}\n@keyframes ww-pulse {\n 50% {\n opacity: .5;\n }\n}\n.ww-animate-pulse {\n animation: ww-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n}\n@keyframes ww-spin {\n to {\n transform: rotate(360deg);\n }\n}\n.ww-animate-spin {\n animation: ww-spin 1s linear infinite;\n}\n.ww-cursor-default {\n cursor: default;\n}\n.ww-cursor-pointer {\n cursor: pointer;\n}\n.ww-select-none {\n -webkit-user-select: none;\n -moz-user-select: none;\n user-select: none;\n}\n.ww-resize-none {\n resize: none;\n}\n.ww-grid-cols-2 {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n}\n.ww-flex-col {\n flex-direction: column;\n}\n.ww-flex-wrap {\n flex-wrap: wrap;\n}\n.ww-items-start {\n align-items: flex-start;\n}\n.ww-items-end {\n align-items: flex-end;\n}\n.ww-items-center {\n align-items: center;\n}\n.ww-justify-end {\n justify-content: flex-end;\n}\n.ww-justify-center {\n justify-content: center;\n}\n.ww-justify-between {\n justify-content: space-between;\n}\n.ww-gap-0\\.5 {\n gap: 0.125rem;\n}\n.ww-gap-1 {\n gap: 0.25rem;\n}\n.ww-gap-1\\.5 {\n gap: 0.375rem;\n}\n.ww-gap-2 {\n gap: 0.5rem;\n}\n.ww-gap-2\\.5 {\n gap: 0.625rem;\n}\n.ww-gap-4 {\n gap: 1rem;\n}\n.ww-divide-y > :not([hidden]) ~ :not([hidden]) {\n --tw-divide-y-reverse: 0;\n border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse)));\n border-bottom-width: calc(1px * var(--tw-divide-y-reverse));\n}\n.ww-divide-border\\/40 > :not([hidden]) ~ :not([hidden]) {\n border-color: hsl(var(--border) / 0.4);\n}\n.ww-overflow-hidden {\n overflow: hidden;\n}\n.ww-overflow-y-auto {\n overflow-y: auto;\n}\n.ww-overscroll-contain {\n overscroll-behavior: contain;\n}\n.ww-truncate {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.ww-whitespace-pre-wrap {\n white-space: pre-wrap;\n}\n.ww-rounded {\n border-radius: 0.25rem;\n}\n.ww-rounded-2xl {\n border-radius: 1rem;\n}\n.ww-rounded-full {\n border-radius: 9999px;\n}\n.ww-rounded-lg {\n border-radius: var(--radius);\n}\n.ww-rounded-xl {\n border-radius: 0.75rem;\n}\n.ww-rounded-tl-none {\n border-top-left-radius: 0px;\n}\n.ww-rounded-tl-sm {\n border-top-left-radius: calc(var(--radius) - 4px);\n}\n.ww-rounded-tr-sm {\n border-top-right-radius: calc(var(--radius) - 4px);\n}\n.ww-border {\n border-width: 1px;\n}\n.ww-border-2 {\n border-width: 2px;\n}\n.ww-border-b {\n border-bottom-width: 1px;\n}\n.ww-border-l-2 {\n border-left-width: 2px;\n}\n.ww-border-t {\n border-top-width: 1px;\n}\n.ww-border-background {\n border-color: hsl(var(--background));\n}\n.ww-border-black\\/10 {\n border-color: rgb(0 0 0 / 0.1);\n}\n.ww-border-border {\n border-color: hsl(var(--border));\n}\n.ww-border-border\\/30 {\n border-color: hsl(var(--border) / 0.3);\n}\n.ww-border-border\\/40 {\n border-color: hsl(var(--border) / 0.4);\n}\n.ww-border-border\\/50 {\n border-color: hsl(var(--border) / 0.5);\n}\n.ww-border-border\\/60 {\n border-color: hsl(var(--border) / 0.6);\n}\n.ww-border-border\\/70 {\n border-color: hsl(var(--border) / 0.7);\n}\n.ww-border-muted {\n border-color: hsl(var(--muted));\n}\n.ww-border-muted-foreground\\/30 {\n border-color: hsl(var(--muted-foreground) / 0.3);\n}\n.ww-border-red-200 {\n --tw-border-opacity: 1;\n border-color: rgb(254 202 202 / var(--tw-border-opacity, 1));\n}\n.ww-border-white\\/20 {\n border-color: rgb(255 255 255 / 0.2);\n}\n.ww-bg-background {\n background-color: hsl(var(--background));\n}\n.ww-bg-background\\/20 {\n background-color: hsl(var(--background) / 0.2);\n}\n.ww-bg-background\\/40 {\n background-color: hsl(var(--background) / 0.4);\n}\n.ww-bg-background\\/50 {\n background-color: hsl(var(--background) / 0.5);\n}\n.ww-bg-background\\/90 {\n background-color: hsl(var(--background) / 0.9);\n}\n.ww-bg-black\\/10 {\n background-color: rgb(0 0 0 / 0.1);\n}\n.ww-bg-foreground {\n background-color: hsl(var(--foreground));\n}\n.ww-bg-foreground\\/60 {\n background-color: hsl(var(--foreground) / 0.6);\n}\n.ww-bg-muted {\n background-color: hsl(var(--muted));\n}\n.ww-bg-muted-foreground\\/10 {\n background-color: hsl(var(--muted-foreground) / 0.1);\n}\n.ww-bg-muted\\/50 {\n background-color: hsl(var(--muted) / 0.5);\n}\n.ww-bg-muted\\/60 {\n background-color: hsl(var(--muted) / 0.6);\n}\n.ww-bg-primary {\n background-color: hsl(var(--primary));\n}\n.ww-bg-primary\\/5 {\n background-color: hsl(var(--primary) / 0.05);\n}\n.ww-bg-red-50 {\n --tw-bg-opacity: 1;\n background-color: rgb(254 242 242 / var(--tw-bg-opacity, 1));\n}\n.ww-bg-transparent {\n background-color: transparent;\n}\n.ww-bg-white\\/10 {\n background-color: rgb(255 255 255 / 0.1);\n}\n.ww-bg-white\\/15 {\n background-color: rgb(255 255 255 / 0.15);\n}\n.ww-fill-current {\n fill: currentColor;\n}\n.ww-object-cover {\n -o-object-fit: cover;\n object-fit: cover;\n}\n.ww-p-0\\.5 {\n padding: 0.125rem;\n}\n.ww-p-1\\.5 {\n padding: 0.375rem;\n}\n.ww-p-3 {\n padding: 0.75rem;\n}\n.ww-px-1 {\n padding-left: 0.25rem;\n padding-right: 0.25rem;\n}\n.ww-px-2 {\n padding-left: 0.5rem;\n padding-right: 0.5rem;\n}\n.ww-px-2\\.5 {\n padding-left: 0.625rem;\n padding-right: 0.625rem;\n}\n.ww-px-3 {\n padding-left: 0.75rem;\n padding-right: 0.75rem;\n}\n.ww-px-3\\.5 {\n padding-left: 0.875rem;\n padding-right: 0.875rem;\n}\n.ww-px-4 {\n padding-left: 1rem;\n padding-right: 1rem;\n}\n.ww-py-0\\.5 {\n padding-top: 0.125rem;\n padding-bottom: 0.125rem;\n}\n.ww-py-1 {\n padding-top: 0.25rem;\n padding-bottom: 0.25rem;\n}\n.ww-py-1\\.5 {\n padding-top: 0.375rem;\n padding-bottom: 0.375rem;\n}\n.ww-py-2 {\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n}\n.ww-py-2\\.5 {\n padding-top: 0.625rem;\n padding-bottom: 0.625rem;\n}\n.ww-py-3 {\n padding-top: 0.75rem;\n padding-bottom: 0.75rem;\n}\n.ww-py-4 {\n padding-top: 1rem;\n padding-bottom: 1rem;\n}\n.ww-pb-1\\.5 {\n padding-bottom: 0.375rem;\n}\n.ww-pb-4 {\n padding-bottom: 1rem;\n}\n.ww-pl-2 {\n padding-left: 0.5rem;\n}\n.ww-pl-7 {\n padding-left: 1.75rem;\n}\n.ww-pr-0\\.5 {\n padding-right: 0.125rem;\n}\n.ww-pr-3 {\n padding-right: 0.75rem;\n}\n.ww-pt-2 {\n padding-top: 0.5rem;\n}\n.ww-pt-5 {\n padding-top: 1.25rem;\n}\n.ww-text-left {\n text-align: left;\n}\n.ww-text-center {\n text-align: center;\n}\n.ww-align-middle {\n vertical-align: middle;\n}\n.ww-text-\\[10\\.5px\\] {\n font-size: 10.5px;\n}\n.ww-text-\\[10px\\] {\n font-size: 10px;\n}\n.ww-text-\\[11px\\] {\n font-size: 11px;\n}\n.ww-text-\\[12\\.5px\\] {\n font-size: 12.5px;\n}\n.ww-text-\\[12px\\] {\n font-size: 12px;\n}\n.ww-text-\\[8px\\] {\n font-size: 8px;\n}\n.ww-text-sm {\n font-size: 0.875rem;\n line-height: 1.25rem;\n}\n.ww-text-xs {\n font-size: 0.75rem;\n line-height: 1rem;\n}\n.ww-font-medium {\n font-weight: 500;\n}\n.ww-font-semibold {\n font-weight: 600;\n}\n.ww-uppercase {\n text-transform: uppercase;\n}\n.ww-leading-none {\n line-height: 1;\n}\n.ww-leading-relaxed {\n line-height: 1.625;\n}\n.ww-leading-snug {\n line-height: 1.375;\n}\n.ww-leading-tight {\n line-height: 1.25;\n}\n.ww-tracking-widest {\n letter-spacing: 0.1em;\n}\n.ww-text-background {\n color: hsl(var(--background));\n}\n.ww-text-black\\/40 {\n color: rgb(0 0 0 / 0.4);\n}\n.ww-text-black\\/60 {\n color: rgb(0 0 0 / 0.6);\n}\n.ww-text-destructive {\n color: hsl(var(--destructive));\n}\n.ww-text-destructive\\/70 {\n color: hsl(var(--destructive) / 0.7);\n}\n.ww-text-emerald-500 {\n --tw-text-opacity: 1;\n color: rgb(16 185 129 / var(--tw-text-opacity, 1));\n}\n.ww-text-foreground {\n color: hsl(var(--foreground));\n}\n.ww-text-foreground\\/60 {\n color: hsl(var(--foreground) / 0.6);\n}\n.ww-text-foreground\\/70 {\n color: hsl(var(--foreground) / 0.7);\n}\n.ww-text-foreground\\/80 {\n color: hsl(var(--foreground) / 0.8);\n}\n.ww-text-muted-foreground {\n color: hsl(var(--muted-foreground));\n}\n.ww-text-muted-foreground\\/40 {\n color: hsl(var(--muted-foreground) / 0.4);\n}\n.ww-text-muted-foreground\\/50 {\n color: hsl(var(--muted-foreground) / 0.5);\n}\n.ww-text-muted-foreground\\/70 {\n color: hsl(var(--muted-foreground) / 0.7);\n}\n.ww-text-muted-foreground\\/80 {\n color: hsl(var(--muted-foreground) / 0.8);\n}\n.ww-text-primary {\n color: hsl(var(--primary));\n}\n.ww-text-primary-foreground {\n color: hsl(var(--primary-foreground));\n}\n.ww-text-primary\\/70 {\n color: hsl(var(--primary) / 0.7);\n}\n.ww-text-primary\\/80 {\n color: hsl(var(--primary) / 0.8);\n}\n.ww-text-red-400 {\n --tw-text-opacity: 1;\n color: rgb(248 113 113 / var(--tw-text-opacity, 1));\n}\n.ww-text-red-500 {\n --tw-text-opacity: 1;\n color: rgb(239 68 68 / var(--tw-text-opacity, 1));\n}\n.ww-text-red-600 {\n --tw-text-opacity: 1;\n color: rgb(220 38 38 / var(--tw-text-opacity, 1));\n}\n.ww-text-white\\/60 {\n color: rgb(255 255 255 / 0.6);\n}\n.ww-text-white\\/80 {\n color: rgb(255 255 255 / 0.8);\n}\n.ww-line-through {\n text-decoration-line: line-through;\n}\n.ww-no-underline {\n text-decoration-line: none;\n}\n.ww-decoration-foreground\\/20 {\n text-decoration-color: hsl(var(--foreground) / 0.2);\n}\n.ww-opacity-100 {\n opacity: 1;\n}\n.ww-opacity-30 {\n opacity: 0.3;\n}\n.ww-opacity-35 {\n opacity: 0.35;\n}\n.ww-opacity-40 {\n opacity: 0.4;\n}\n.ww-opacity-60 {\n opacity: 0.6;\n}\n.ww-opacity-70 {\n opacity: 0.7;\n}\n.ww-opacity-80 {\n opacity: 0.8;\n}\n.ww-shadow-2xl {\n --tw-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.25);\n --tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color);\n box-shadow:\n var(--tw-ring-offset-shadow, 0 0 #0000),\n var(--tw-ring-shadow, 0 0 #0000),\n var(--tw-shadow);\n}\n.ww-shadow-sm {\n --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);\n --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);\n box-shadow:\n var(--tw-ring-offset-shadow, 0 0 #0000),\n var(--tw-ring-shadow, 0 0 #0000),\n var(--tw-shadow);\n}\n.ww-shadow-xl {\n --tw-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);\n --tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);\n box-shadow:\n var(--tw-ring-offset-shadow, 0 0 #0000),\n var(--tw-ring-shadow, 0 0 #0000),\n var(--tw-shadow);\n}\n.ww-outline-none {\n outline: 2px solid transparent;\n outline-offset: 2px;\n}\n.ww-ring-2 {\n --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);\n --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);\n box-shadow:\n var(--tw-ring-offset-shadow),\n var(--tw-ring-shadow),\n var(--tw-shadow, 0 0 #0000);\n}\n.ww-ring-inset {\n --tw-ring-inset: inset;\n}\n.ww-ring-primary\\/60 {\n --tw-ring-color: hsl(var(--primary) / 0.6);\n}\n.ww-backdrop-blur-sm {\n --tw-backdrop-blur: blur(4px);\n backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);\n}\n.ww-transition-all {\n transition-property: all;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-duration: 150ms;\n}\n.ww-transition-colors {\n transition-property:\n color,\n background-color,\n border-color,\n text-decoration-color,\n fill,\n stroke;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-duration: 150ms;\n}\n.ww-transition-shadow {\n transition-property: box-shadow;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-duration: 150ms;\n}\n.ww-transition-transform {\n transition-property: transform;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-duration: 150ms;\n}\n.ww-duration-100 {\n transition-duration: 100ms;\n}\n.ww-duration-150 {\n transition-duration: 150ms;\n}\n.ww-duration-200 {\n transition-duration: 200ms;\n}\n@keyframes enter {\n from {\n opacity: var(--tw-enter-opacity, 1);\n transform: translate3d(var(--tw-enter-translate-x, 0), var(--tw-enter-translate-y, 0), 0) scale3d(var(--tw-enter-scale, 1), var(--tw-enter-scale, 1), var(--tw-enter-scale, 1)) rotate(var(--tw-enter-rotate, 0));\n }\n}\n@keyframes exit {\n to {\n opacity: var(--tw-exit-opacity, 1);\n transform: translate3d(var(--tw-exit-translate-x, 0), var(--tw-exit-translate-y, 0), 0) scale3d(var(--tw-exit-scale, 1), var(--tw-exit-scale, 1), var(--tw-exit-scale, 1)) rotate(var(--tw-exit-rotate, 0));\n }\n}\n.ww-duration-100 {\n animation-duration: 100ms;\n}\n.ww-duration-150 {\n animation-duration: 150ms;\n}\n.ww-duration-200 {\n animation-duration: 200ms;\n}\n.wallavi-widget *,\n.wallavi-widget *::before,\n.wallavi-widget *::after {\n box-sizing: border-box;\n border-width: 0;\n border-style: solid;\n}\n.wallavi-widget button {\n cursor: pointer;\n}\n.wallavi-widget img,\n.wallavi-widget video {\n max-width: 100%;\n height: auto;\n}\n.placeholder\\:ww-text-muted-foreground\\/40::-moz-placeholder {\n color: hsl(var(--muted-foreground) / 0.4);\n}\n.placeholder\\:ww-text-muted-foreground\\/40::placeholder {\n color: hsl(var(--muted-foreground) / 0.4);\n}\n.placeholder\\:ww-text-muted-foreground\\/50::-moz-placeholder {\n color: hsl(var(--muted-foreground) / 0.5);\n}\n.placeholder\\:ww-text-muted-foreground\\/50::placeholder {\n color: hsl(var(--muted-foreground) / 0.5);\n}\n.focus-within\\:ww-ring-1:focus-within {\n --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);\n --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);\n box-shadow:\n var(--tw-ring-offset-shadow),\n var(--tw-ring-shadow),\n var(--tw-shadow, 0 0 #0000);\n}\n.focus-within\\:ww-ring-ring\\/40:focus-within {\n --tw-ring-color: hsl(var(--ring) / 0.4);\n}\n.hover\\:ww-border-foreground\\/25:hover {\n border-color: hsl(var(--foreground) / 0.25);\n}\n.hover\\:ww-border-foreground\\/30:hover {\n border-color: hsl(var(--foreground) / 0.3);\n}\n.hover\\:ww-bg-background:hover {\n background-color: hsl(var(--background));\n}\n.hover\\:ww-bg-foreground\\/10:hover {\n background-color: hsl(var(--foreground) / 0.1);\n}\n.hover\\:ww-bg-muted:hover {\n background-color: hsl(var(--muted));\n}\n.hover\\:ww-bg-white\\/10:hover {\n background-color: rgb(255 255 255 / 0.1);\n}\n.hover\\:ww-text-foreground:hover {\n color: hsl(var(--foreground));\n}\n.focus\\:ww-ring-1:focus {\n --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);\n --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);\n box-shadow:\n var(--tw-ring-offset-shadow),\n var(--tw-ring-shadow),\n var(--tw-shadow, 0 0 #0000);\n}\n.focus\\:ww-ring-ring\\/40:focus {\n --tw-ring-color: hsl(var(--ring) / 0.4);\n}\n.disabled\\:ww-opacity-40:disabled {\n opacity: 0.4;\n}\n.disabled\\:ww-opacity-50:disabled {\n opacity: 0.5;\n}\n.dark\\:ww-border-red-800:is(.ww-dark *) {\n --tw-border-opacity: 1;\n border-color: rgb(153 27 27 / var(--tw-border-opacity, 1));\n}\n.dark\\:ww-bg-red-950:is(.ww-dark *) {\n --tw-bg-opacity: 1;\n background-color: rgb(69 10 10 / var(--tw-bg-opacity, 1));\n}\n.dark\\:ww-text-red-400:is(.ww-dark *) {\n --tw-text-opacity: 1;\n color: rgb(248 113 113 / var(--tw-text-opacity, 1));\n}\n");
35
+ styleInject(".ww-pointer-events-none {\n pointer-events: none;\n}\n.ww-absolute {\n position: absolute;\n}\n.ww-relative {\n position: relative;\n}\n.ww-inset-0 {\n inset: 0px;\n}\n.ww-inset-4 {\n inset: 1rem;\n}\n.ww-inset-8 {\n inset: 2rem;\n}\n.ww-left-2\\.5 {\n left: 0.625rem;\n}\n.ww-top-1\\/2 {\n top: 50%;\n}\n.ww-z-50 {\n z-index: 50;\n}\n.ww-z-\\[100\\] {\n z-index: 100;\n}\n.ww-mb-1 {\n margin-bottom: 0.25rem;\n}\n.ww-mb-12 {\n margin-bottom: 3rem;\n}\n.ww-mb-2 {\n margin-bottom: 0.5rem;\n}\n.ww-ml-0\\.5 {\n margin-left: 0.125rem;\n}\n.ww-mt-0\\.5 {\n margin-top: 0.125rem;\n}\n.ww-mt-1 {\n margin-top: 0.25rem;\n}\n.ww-block {\n display: block;\n}\n.ww-inline-block {\n display: inline-block;\n}\n.ww-flex {\n display: flex;\n}\n.ww-inline-flex {\n display: inline-flex;\n}\n.ww-grid {\n display: grid;\n}\n.ww-hidden {\n display: none;\n}\n.ww-h-10 {\n height: 2.5rem;\n}\n.ww-h-16 {\n height: 4rem;\n}\n.ww-h-2 {\n height: 0.5rem;\n}\n.ww-h-2\\.5 {\n height: 0.625rem;\n}\n.ww-h-20 {\n height: 5rem;\n}\n.ww-h-3 {\n height: 0.75rem;\n}\n.ww-h-3\\.5 {\n height: 0.875rem;\n}\n.ww-h-4 {\n height: 1rem;\n}\n.ww-h-40 {\n height: 10rem;\n}\n.ww-h-48 {\n height: 12rem;\n}\n.ww-h-6 {\n height: 1.5rem;\n}\n.ww-h-7 {\n height: 1.75rem;\n}\n.ww-h-8 {\n height: 2rem;\n}\n.ww-h-full {\n height: 100%;\n}\n.ww-max-h-32 {\n max-height: 8rem;\n}\n.ww-max-h-48 {\n max-height: 12rem;\n}\n.ww-max-h-\\[168px\\] {\n max-height: 168px;\n}\n.ww-max-h-\\[180px\\] {\n max-height: 180px;\n}\n.ww-w-0\\.5 {\n width: 0.125rem;\n}\n.ww-w-10 {\n width: 2.5rem;\n}\n.ww-w-16 {\n width: 4rem;\n}\n.ww-w-2 {\n width: 0.5rem;\n}\n.ww-w-2\\.5 {\n width: 0.625rem;\n}\n.ww-w-20 {\n width: 5rem;\n}\n.ww-w-3 {\n width: 0.75rem;\n}\n.ww-w-3\\.5 {\n width: 0.875rem;\n}\n.ww-w-4 {\n width: 1rem;\n}\n.ww-w-48 {\n width: 12rem;\n}\n.ww-w-6 {\n width: 1.5rem;\n}\n.ww-w-7 {\n width: 1.75rem;\n}\n.ww-w-8 {\n width: 2rem;\n}\n.ww-w-fit {\n width: -moz-fit-content;\n width: fit-content;\n}\n.ww-w-full {\n width: 100%;\n}\n.ww-min-w-0 {\n min-width: 0px;\n}\n.ww-max-w-\\[120px\\] {\n max-width: 120px;\n}\n.ww-max-w-\\[200px\\] {\n max-width: 200px;\n}\n.ww-max-w-\\[78\\%\\] {\n max-width: 78%;\n}\n.ww-max-w-\\[82\\%\\] {\n max-width: 82%;\n}\n.ww-max-w-none {\n max-width: none;\n}\n.ww-flex-1 {\n flex: 1 1 0%;\n}\n.ww-shrink-0 {\n flex-shrink: 0;\n}\n.ww-rotate-180 {\n --tw-rotate: 180deg;\n transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));\n}\n.ww-scale-100 {\n --tw-scale-x: 1;\n --tw-scale-y: 1;\n transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));\n}\n.ww-scale-105 {\n --tw-scale-x: 1.05;\n --tw-scale-y: 1.05;\n transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));\n}\n.ww-scale-110 {\n --tw-scale-x: 1.1;\n --tw-scale-y: 1.1;\n transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));\n}\n.ww-scale-90 {\n --tw-scale-x: .9;\n --tw-scale-y: .9;\n transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));\n}\n@keyframes ww-ping {\n 75%, 100% {\n transform: scale(2);\n opacity: 0;\n }\n}\n.ww-animate-ping {\n animation: ww-ping 1s cubic-bezier(0, 0, 0.2, 1) infinite;\n}\n@keyframes ww-pulse {\n 50% {\n opacity: .5;\n }\n}\n.ww-animate-pulse {\n animation: ww-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n}\n@keyframes ww-spin {\n to {\n transform: rotate(360deg);\n }\n}\n.ww-animate-spin {\n animation: ww-spin 1s linear infinite;\n}\n.ww-cursor-default {\n cursor: default;\n}\n.ww-cursor-pointer {\n cursor: pointer;\n}\n.ww-touch-none {\n touch-action: none;\n}\n.ww-select-none {\n -webkit-user-select: none;\n -moz-user-select: none;\n user-select: none;\n}\n.ww-resize-none {\n resize: none;\n}\n.ww-grid-cols-2 {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n}\n.ww-flex-col {\n flex-direction: column;\n}\n.ww-flex-wrap {\n flex-wrap: wrap;\n}\n.ww-items-start {\n align-items: flex-start;\n}\n.ww-items-end {\n align-items: flex-end;\n}\n.ww-items-center {\n align-items: center;\n}\n.ww-justify-end {\n justify-content: flex-end;\n}\n.ww-justify-center {\n justify-content: center;\n}\n.ww-justify-between {\n justify-content: space-between;\n}\n.ww-gap-0\\.5 {\n gap: 0.125rem;\n}\n.ww-gap-1 {\n gap: 0.25rem;\n}\n.ww-gap-1\\.5 {\n gap: 0.375rem;\n}\n.ww-gap-2 {\n gap: 0.5rem;\n}\n.ww-gap-2\\.5 {\n gap: 0.625rem;\n}\n.ww-gap-4 {\n gap: 1rem;\n}\n.ww-gap-6 {\n gap: 1.5rem;\n}\n.ww-gap-8 {\n gap: 2rem;\n}\n.ww-divide-y > :not([hidden]) ~ :not([hidden]) {\n --tw-divide-y-reverse: 0;\n border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse)));\n border-bottom-width: calc(1px * var(--tw-divide-y-reverse));\n}\n.ww-divide-border\\/40 > :not([hidden]) ~ :not([hidden]) {\n border-color: hsl(var(--border) / 0.4);\n}\n.ww-overflow-hidden {\n overflow: hidden;\n}\n.ww-overflow-y-auto {\n overflow-y: auto;\n}\n.ww-overscroll-contain {\n overscroll-behavior: contain;\n}\n.ww-overscroll-none {\n overscroll-behavior: none;\n}\n.ww-truncate {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.ww-whitespace-pre-wrap {\n white-space: pre-wrap;\n}\n.ww-rounded {\n border-radius: 0.25rem;\n}\n.ww-rounded-2xl {\n border-radius: 1rem;\n}\n.ww-rounded-full {\n border-radius: 9999px;\n}\n.ww-rounded-lg {\n border-radius: var(--radius);\n}\n.ww-rounded-xl {\n border-radius: 0.75rem;\n}\n.ww-rounded-tl-none {\n border-top-left-radius: 0px;\n}\n.ww-rounded-tl-sm {\n border-top-left-radius: calc(var(--radius) - 4px);\n}\n.ww-rounded-tr-sm {\n border-top-right-radius: calc(var(--radius) - 4px);\n}\n.ww-border {\n border-width: 1px;\n}\n.ww-border-2 {\n border-width: 2px;\n}\n.ww-border-b {\n border-bottom-width: 1px;\n}\n.ww-border-l-2 {\n border-left-width: 2px;\n}\n.ww-border-t {\n border-top-width: 1px;\n}\n.ww-border-background {\n border-color: hsl(var(--background));\n}\n.ww-border-black\\/10 {\n border-color: rgb(0 0 0 / 0.1);\n}\n.ww-border-border {\n border-color: hsl(var(--border));\n}\n.ww-border-border\\/30 {\n border-color: hsl(var(--border) / 0.3);\n}\n.ww-border-border\\/40 {\n border-color: hsl(var(--border) / 0.4);\n}\n.ww-border-border\\/50 {\n border-color: hsl(var(--border) / 0.5);\n}\n.ww-border-border\\/60 {\n border-color: hsl(var(--border) / 0.6);\n}\n.ww-border-border\\/80 {\n border-color: hsl(var(--border) / 0.8);\n}\n.ww-border-muted {\n border-color: hsl(var(--muted));\n}\n.ww-border-muted-foreground\\/30 {\n border-color: hsl(var(--muted-foreground) / 0.3);\n}\n.ww-border-red-200 {\n --tw-border-opacity: 1;\n border-color: rgb(254 202 202 / var(--tw-border-opacity, 1));\n}\n.ww-border-transparent {\n border-color: transparent;\n}\n.ww-border-white\\/20 {\n border-color: rgb(255 255 255 / 0.2);\n}\n.ww-bg-background {\n background-color: hsl(var(--background));\n}\n.ww-bg-background\\/20 {\n background-color: hsl(var(--background) / 0.2);\n}\n.ww-bg-background\\/90 {\n background-color: hsl(var(--background) / 0.9);\n}\n.ww-bg-background\\/95 {\n background-color: hsl(var(--background) / 0.95);\n}\n.ww-bg-black\\/10 {\n background-color: rgb(0 0 0 / 0.1);\n}\n.ww-bg-emerald-400 {\n --tw-bg-opacity: 1;\n background-color: rgb(52 211 153 / var(--tw-bg-opacity, 1));\n}\n.ww-bg-emerald-500 {\n --tw-bg-opacity: 1;\n background-color: rgb(16 185 129 / var(--tw-bg-opacity, 1));\n}\n.ww-bg-emerald-500\\/10 {\n background-color: rgb(16 185 129 / 0.1);\n}\n.ww-bg-emerald-500\\/20 {\n background-color: rgb(16 185 129 / 0.2);\n}\n.ww-bg-foreground\\/20 {\n background-color: hsl(var(--foreground) / 0.2);\n}\n.ww-bg-foreground\\/5 {\n background-color: hsl(var(--foreground) / 0.05);\n}\n.ww-bg-foreground\\/60 {\n background-color: hsl(var(--foreground) / 0.6);\n}\n.ww-bg-muted {\n background-color: hsl(var(--muted));\n}\n.ww-bg-muted-foreground\\/10 {\n background-color: hsl(var(--muted-foreground) / 0.1);\n}\n.ww-bg-muted\\/30 {\n background-color: hsl(var(--muted) / 0.3);\n}\n.ww-bg-muted\\/40 {\n background-color: hsl(var(--muted) / 0.4);\n}\n.ww-bg-muted\\/50 {\n background-color: hsl(var(--muted) / 0.5);\n}\n.ww-bg-muted\\/60 {\n background-color: hsl(var(--muted) / 0.6);\n}\n.ww-bg-primary {\n background-color: hsl(var(--primary));\n}\n.ww-bg-primary\\/5 {\n background-color: hsl(var(--primary) / 0.05);\n}\n.ww-bg-purple-500\\/20 {\n background-color: rgb(168 85 247 / 0.2);\n}\n.ww-bg-purple-500\\/30 {\n background-color: rgb(168 85 247 / 0.3);\n}\n.ww-bg-red-50 {\n --tw-bg-opacity: 1;\n background-color: rgb(254 242 242 / var(--tw-bg-opacity, 1));\n}\n.ww-bg-transparent {\n background-color: transparent;\n}\n.ww-bg-white\\/10 {\n background-color: rgb(255 255 255 / 0.1);\n}\n.ww-bg-white\\/15 {\n background-color: rgb(255 255 255 / 0.15);\n}\n.ww-fill-current {\n fill: currentColor;\n}\n.ww-object-cover {\n -o-object-fit: cover;\n object-fit: cover;\n}\n.ww-p-0\\.5 {\n padding: 0.125rem;\n}\n.ww-p-1\\.5 {\n padding: 0.375rem;\n}\n.ww-p-3 {\n padding: 0.75rem;\n}\n.ww-px-1 {\n padding-left: 0.25rem;\n padding-right: 0.25rem;\n}\n.ww-px-2 {\n padding-left: 0.5rem;\n padding-right: 0.5rem;\n}\n.ww-px-3 {\n padding-left: 0.75rem;\n padding-right: 0.75rem;\n}\n.ww-px-3\\.5 {\n padding-left: 0.875rem;\n padding-right: 0.875rem;\n}\n.ww-px-4 {\n padding-left: 1rem;\n padding-right: 1rem;\n}\n.ww-py-0\\.5 {\n padding-top: 0.125rem;\n padding-bottom: 0.125rem;\n}\n.ww-py-1 {\n padding-top: 0.25rem;\n padding-bottom: 0.25rem;\n}\n.ww-py-1\\.5 {\n padding-top: 0.375rem;\n padding-bottom: 0.375rem;\n}\n.ww-py-2 {\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n}\n.ww-py-2\\.5 {\n padding-top: 0.625rem;\n padding-bottom: 0.625rem;\n}\n.ww-py-3 {\n padding-top: 0.75rem;\n padding-bottom: 0.75rem;\n}\n.ww-py-4 {\n padding-top: 1rem;\n padding-bottom: 1rem;\n}\n.ww-pb-1\\.5 {\n padding-bottom: 0.375rem;\n}\n.ww-pb-2 {\n padding-bottom: 0.5rem;\n}\n.ww-pb-4 {\n padding-bottom: 1rem;\n}\n.ww-pl-2 {\n padding-left: 0.5rem;\n}\n.ww-pl-7 {\n padding-left: 1.75rem;\n}\n.ww-pr-0\\.5 {\n padding-right: 0.125rem;\n}\n.ww-pr-3 {\n padding-right: 0.75rem;\n}\n.ww-pt-2 {\n padding-top: 0.5rem;\n}\n.ww-pt-5 {\n padding-top: 1.25rem;\n}\n.ww-text-left {\n text-align: left;\n}\n.ww-text-center {\n text-align: center;\n}\n.ww-align-middle {\n vertical-align: middle;\n}\n.ww-text-\\[10\\.5px\\] {\n font-size: 10.5px;\n}\n.ww-text-\\[10px\\] {\n font-size: 10px;\n}\n.ww-text-\\[11px\\] {\n font-size: 11px;\n}\n.ww-text-\\[12\\.5px\\] {\n font-size: 12.5px;\n}\n.ww-text-\\[13px\\] {\n font-size: 13px;\n}\n.ww-text-\\[8px\\] {\n font-size: 8px;\n}\n.ww-text-sm {\n font-size: 0.875rem;\n line-height: 1.25rem;\n}\n.ww-text-xs {\n font-size: 0.75rem;\n line-height: 1rem;\n}\n.ww-font-medium {\n font-weight: 500;\n}\n.ww-font-semibold {\n font-weight: 600;\n}\n.ww-uppercase {\n text-transform: uppercase;\n}\n.ww-leading-none {\n line-height: 1;\n}\n.ww-leading-relaxed {\n line-height: 1.625;\n}\n.ww-leading-snug {\n line-height: 1.375;\n}\n.ww-leading-tight {\n line-height: 1.25;\n}\n.ww-tracking-wide {\n letter-spacing: 0.025em;\n}\n.ww-tracking-widest {\n letter-spacing: 0.1em;\n}\n.ww-text-black\\/40 {\n color: rgb(0 0 0 / 0.4);\n}\n.ww-text-black\\/60 {\n color: rgb(0 0 0 / 0.6);\n}\n.ww-text-destructive {\n color: hsl(var(--destructive));\n}\n.ww-text-destructive\\/70 {\n color: hsl(var(--destructive) / 0.7);\n}\n.ww-text-emerald-500 {\n --tw-text-opacity: 1;\n color: rgb(16 185 129 / var(--tw-text-opacity, 1));\n}\n.ww-text-foreground {\n color: hsl(var(--foreground));\n}\n.ww-text-foreground\\/60 {\n color: hsl(var(--foreground) / 0.6);\n}\n.ww-text-foreground\\/70 {\n color: hsl(var(--foreground) / 0.7);\n}\n.ww-text-foreground\\/80 {\n color: hsl(var(--foreground) / 0.8);\n}\n.ww-text-muted-foreground {\n color: hsl(var(--muted-foreground));\n}\n.ww-text-muted-foreground\\/40 {\n color: hsl(var(--muted-foreground) / 0.4);\n}\n.ww-text-muted-foreground\\/50 {\n color: hsl(var(--muted-foreground) / 0.5);\n}\n.ww-text-muted-foreground\\/70 {\n color: hsl(var(--muted-foreground) / 0.7);\n}\n.ww-text-muted-foreground\\/80 {\n color: hsl(var(--muted-foreground) / 0.8);\n}\n.ww-text-primary {\n color: hsl(var(--primary));\n}\n.ww-text-primary-foreground {\n color: hsl(var(--primary-foreground));\n}\n.ww-text-primary\\/70 {\n color: hsl(var(--primary) / 0.7);\n}\n.ww-text-primary\\/80 {\n color: hsl(var(--primary) / 0.8);\n}\n.ww-text-red-400 {\n --tw-text-opacity: 1;\n color: rgb(248 113 113 / var(--tw-text-opacity, 1));\n}\n.ww-text-red-500 {\n --tw-text-opacity: 1;\n color: rgb(239 68 68 / var(--tw-text-opacity, 1));\n}\n.ww-text-red-600 {\n --tw-text-opacity: 1;\n color: rgb(220 38 38 / var(--tw-text-opacity, 1));\n}\n.ww-text-white {\n --tw-text-opacity: 1;\n color: rgb(255 255 255 / var(--tw-text-opacity, 1));\n}\n.ww-text-white\\/60 {\n color: rgb(255 255 255 / 0.6);\n}\n.ww-text-white\\/80 {\n color: rgb(255 255 255 / 0.8);\n}\n.ww-line-through {\n text-decoration-line: line-through;\n}\n.ww-no-underline {\n text-decoration-line: none;\n}\n.ww-decoration-foreground\\/20 {\n text-decoration-color: hsl(var(--foreground) / 0.2);\n}\n.ww-opacity-100 {\n opacity: 1;\n}\n.ww-opacity-25 {\n opacity: 0.25;\n}\n.ww-opacity-30 {\n opacity: 0.3;\n}\n.ww-opacity-40 {\n opacity: 0.4;\n}\n.ww-opacity-60 {\n opacity: 0.6;\n}\n.ww-opacity-70 {\n opacity: 0.7;\n}\n.ww-opacity-75 {\n opacity: 0.75;\n}\n.ww-opacity-80 {\n opacity: 0.8;\n}\n.ww-opacity-90 {\n opacity: 0.9;\n}\n.ww-shadow-2xl {\n --tw-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.25);\n --tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color);\n box-shadow:\n var(--tw-ring-offset-shadow, 0 0 #0000),\n var(--tw-ring-shadow, 0 0 #0000),\n var(--tw-shadow);\n}\n.ww-shadow-lg {\n --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);\n --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);\n box-shadow:\n var(--tw-ring-offset-shadow, 0 0 #0000),\n var(--tw-ring-shadow, 0 0 #0000),\n var(--tw-shadow);\n}\n.ww-shadow-md {\n --tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);\n --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);\n box-shadow:\n var(--tw-ring-offset-shadow, 0 0 #0000),\n var(--tw-ring-shadow, 0 0 #0000),\n var(--tw-shadow);\n}\n.ww-shadow-sm {\n --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);\n --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);\n box-shadow:\n var(--tw-ring-offset-shadow, 0 0 #0000),\n var(--tw-ring-shadow, 0 0 #0000),\n var(--tw-shadow);\n}\n.ww-shadow-xl {\n --tw-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);\n --tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);\n box-shadow:\n var(--tw-ring-offset-shadow, 0 0 #0000),\n var(--tw-ring-shadow, 0 0 #0000),\n var(--tw-shadow);\n}\n.ww-outline-none {\n outline: 2px solid transparent;\n outline-offset: 2px;\n}\n.ww-ring-2 {\n --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);\n --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);\n box-shadow:\n var(--tw-ring-offset-shadow),\n var(--tw-ring-shadow),\n var(--tw-shadow, 0 0 #0000);\n}\n.ww-ring-inset {\n --tw-ring-inset: inset;\n}\n.ww-ring-primary\\/20 {\n --tw-ring-color: hsl(var(--primary) / 0.2);\n}\n.ww-ring-primary\\/60 {\n --tw-ring-color: hsl(var(--primary) / 0.6);\n}\n.ww-backdrop-blur-sm {\n --tw-backdrop-blur: blur(4px);\n backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);\n}\n.ww-backdrop-blur-xl {\n --tw-backdrop-blur: blur(24px);\n backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);\n}\n.ww-transition-all {\n transition-property: all;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-duration: 150ms;\n}\n.ww-transition-colors {\n transition-property:\n color,\n background-color,\n border-color,\n text-decoration-color,\n fill,\n stroke;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-duration: 150ms;\n}\n.ww-transition-opacity {\n transition-property: opacity;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-duration: 150ms;\n}\n.ww-transition-shadow {\n transition-property: box-shadow;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-duration: 150ms;\n}\n.ww-transition-transform {\n transition-property: transform;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-duration: 150ms;\n}\n.ww-duration-1000 {\n transition-duration: 1000ms;\n}\n.ww-duration-200 {\n transition-duration: 200ms;\n}\n.ww-duration-300 {\n transition-duration: 300ms;\n}\n.ww-duration-500 {\n transition-duration: 500ms;\n}\n.ww-duration-700 {\n transition-duration: 700ms;\n}\n@keyframes enter {\n from {\n opacity: var(--tw-enter-opacity, 1);\n transform: translate3d(var(--tw-enter-translate-x, 0), var(--tw-enter-translate-y, 0), 0) scale3d(var(--tw-enter-scale, 1), var(--tw-enter-scale, 1), var(--tw-enter-scale, 1)) rotate(var(--tw-enter-rotate, 0));\n }\n}\n@keyframes exit {\n to {\n opacity: var(--tw-exit-opacity, 1);\n transform: translate3d(var(--tw-exit-translate-x, 0), var(--tw-exit-translate-y, 0), 0) scale3d(var(--tw-exit-scale, 1), var(--tw-exit-scale, 1), var(--tw-exit-scale, 1)) rotate(var(--tw-exit-rotate, 0));\n }\n}\n.ww-duration-1000 {\n animation-duration: 1000ms;\n}\n.ww-duration-200 {\n animation-duration: 200ms;\n}\n.ww-duration-300 {\n animation-duration: 300ms;\n}\n.ww-duration-500 {\n animation-duration: 500ms;\n}\n.ww-duration-700 {\n animation-duration: 700ms;\n}\n.wallavi-widget *,\n.wallavi-widget *::before,\n.wallavi-widget *::after {\n box-sizing: border-box;\n border-width: 0;\n border-style: solid;\n}\n.wallavi-widget button {\n -moz-appearance: none;\n appearance: none;\n -webkit-appearance: none;\n background: transparent;\n border: none;\n cursor: pointer;\n}\n.wallavi-widget textarea,\n.wallavi-widget input,\n.wallavi-widget select {\n font-family: inherit;\n font-size: inherit;\n}\n.wallavi-widget img,\n.wallavi-widget video {\n max-width: 100%;\n height: auto;\n}\n.wallavi-widget .lk-control-bar {\n background: transparent !important;\n border: none !important;\n box-shadow: none !important;\n padding: 0 !important;\n gap: 1.5rem !important;\n justify-content: center !important;\n}\n.wallavi-widget .lk-button {\n background: rgba(25, 25, 28, 0.05) !important;\n border-radius: 9999px !important;\n padding: 1.25rem !important;\n color: var(--ww-fg, #19191c) !important;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1) !important;\n border: 1px solid rgba(25, 25, 28, 0.1) !important;\n}\n.wallavi-widget .lk-button:hover {\n background: rgba(25, 25, 28, 0.1) !important;\n transform: scale(1.05) !important;\n}\n.wallavi-widget .lk-disconnect-button {\n background: #ef4444 !important;\n color: white !important;\n border: none !important;\n}\n.wallavi-widget .lk-disconnect-button:hover {\n background: #dc2626 !important;\n}\n.wallavi-widget .lk-device-menu {\n bottom: 100% !important;\n top: auto !important;\n margin-bottom: 1rem !important;\n max-width: 250px !important;\n width: -moz-max-content !important;\n width: max-content !important;\n left: 50% !important;\n transform: translateX(-50%) !important;\n background: #ffffff !important;\n border-radius: 16px !important;\n box-shadow: 0 20px 40px -10px rgba(0, 0, 0, 0.15), 0 10px 20px -10px rgba(0, 0, 0, 0.1) !important;\n z-index: 1000 !important;\n overflow: hidden !important;\n border: 1px solid rgba(0, 0, 0, 0.05) !important;\n padding: 0.5rem !important;\n}\n.wallavi-widget .lk-device-menu > li {\n padding: 0.75rem 1rem !important;\n font-size: 0.875rem !important;\n color: #19191c !important;\n white-space: nowrap !important;\n overflow: hidden !important;\n text-overflow: ellipsis !important;\n border-radius: 8px !important;\n transition: background 0.15s !important;\n}\n.wallavi-widget .lk-device-menu > li:hover {\n background: rgba(0, 0, 0, 0.05) !important;\n}\n.wallavi-widget .lk-device-menu > li.lk-active {\n background: rgba(0, 0, 0, 0.08) !important;\n font-weight: 600 !important;\n}\n.placeholder\\:ww-text-muted-foreground\\/40::-moz-placeholder {\n color: hsl(var(--muted-foreground) / 0.4);\n}\n.placeholder\\:ww-text-muted-foreground\\/40::placeholder {\n color: hsl(var(--muted-foreground) / 0.4);\n}\n.placeholder\\:ww-text-muted-foreground\\/50::-moz-placeholder {\n color: hsl(var(--muted-foreground) / 0.5);\n}\n.placeholder\\:ww-text-muted-foreground\\/50::placeholder {\n color: hsl(var(--muted-foreground) / 0.5);\n}\n.focus-within\\:ww-ring-1:focus-within {\n --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);\n --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);\n box-shadow:\n var(--tw-ring-offset-shadow),\n var(--tw-ring-shadow),\n var(--tw-shadow, 0 0 #0000);\n}\n.focus-within\\:ww-ring-ring\\/40:focus-within {\n --tw-ring-color: hsl(var(--ring) / 0.4);\n}\n.hover\\:-ww-translate-y-1:hover {\n --tw-translate-y: -0.25rem;\n transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));\n}\n.hover\\:ww-border-primary\\/40:hover {\n border-color: hsl(var(--primary) / 0.4);\n}\n.hover\\:ww-bg-accent:hover {\n background-color: hsl(var(--accent));\n}\n.hover\\:ww-bg-foreground\\/10:hover {\n background-color: hsl(var(--foreground) / 0.1);\n}\n.hover\\:ww-bg-foreground\\/30:hover {\n background-color: hsl(var(--foreground) / 0.3);\n}\n.hover\\:ww-bg-muted:hover {\n background-color: hsl(var(--muted));\n}\n.hover\\:ww-bg-white\\/10:hover {\n background-color: rgb(255 255 255 / 0.1);\n}\n.hover\\:ww-text-accent-foreground:hover {\n color: hsl(var(--accent-foreground));\n}\n.hover\\:ww-text-foreground:hover {\n color: hsl(var(--foreground));\n}\n.hover\\:ww-text-primary:hover {\n color: hsl(var(--primary));\n}\n.hover\\:ww-opacity-80:hover {\n opacity: 0.8;\n}\n.hover\\:ww-opacity-85:hover {\n opacity: 0.85;\n}\n.hover\\:ww-shadow-sm:hover {\n --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);\n --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);\n box-shadow:\n var(--tw-ring-offset-shadow, 0 0 #0000),\n var(--tw-ring-shadow, 0 0 #0000),\n var(--tw-shadow);\n}\n.hover\\:ww-shadow-xl:hover {\n --tw-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);\n --tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);\n box-shadow:\n var(--tw-ring-offset-shadow, 0 0 #0000),\n var(--tw-ring-shadow, 0 0 #0000),\n var(--tw-shadow);\n}\n.focus\\:ww-ring-1:focus {\n --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);\n --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);\n box-shadow:\n var(--tw-ring-offset-shadow),\n var(--tw-ring-shadow),\n var(--tw-shadow, 0 0 #0000);\n}\n.focus\\:ww-ring-ring\\/40:focus {\n --tw-ring-color: hsl(var(--ring) / 0.4);\n}\n.active\\:ww-scale-\\[0\\.98\\]:active {\n --tw-scale-x: 0.98;\n --tw-scale-y: 0.98;\n transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));\n}\n.disabled\\:ww-pointer-events-none:disabled {\n pointer-events: none;\n}\n.disabled\\:ww-opacity-40:disabled {\n opacity: 0.4;\n}\n.disabled\\:ww-opacity-50:disabled {\n opacity: 0.5;\n}\n.dark\\:ww-border-red-800:is(.ww-dark *) {\n --tw-border-opacity: 1;\n border-color: rgb(153 27 27 / var(--tw-border-opacity, 1));\n}\n.dark\\:ww-bg-red-950:is(.ww-dark *) {\n --tw-bg-opacity: 1;\n background-color: rgb(69 10 10 / var(--tw-bg-opacity, 1));\n}\n.dark\\:ww-text-red-400:is(.ww-dark *) {\n --tw-text-opacity: 1;\n color: rgb(248 113 113 / var(--tw-text-opacity, 1));\n}\n");
34
36
  var twMerge = extendTailwindMerge({ prefix: "ww-" });
35
37
  var cn = (...inputs) => twMerge(clsx(inputs));
36
38
 
37
- // src/types.ts
39
+ // src/lib/types.ts
38
40
  function getContrastColor(hex) {
39
41
  const clean = hex.replace("#", "");
40
42
  if (clean.length < 6) return "#ffffff";
@@ -46,15 +48,16 @@ function getContrastColor(hex) {
46
48
  function formatToolName(name) {
47
49
  return name.replace(/([A-Z])/g, " $1").replace(/_/g, " ").trim();
48
50
  }
51
+
52
+ // src/core/stream-protocol.ts
49
53
  var STREAM_DELIMITER = "\u03B6\u236E";
50
54
  async function consumeStream(body, handler) {
51
55
  const reader = body.getReader();
52
56
  const decoder = new TextDecoder();
53
57
  let buffer = "";
54
- while (true) {
55
- const { done, value } = await reader.read();
56
- if (done) break;
57
- buffer += decoder.decode(value, { stream: true });
58
+ let textAccumulator = "";
59
+ let eventCount = 0;
60
+ const processBuffer = () => {
58
61
  const chunks = buffer.split(STREAM_DELIMITER);
59
62
  buffer = chunks.pop() ?? "";
60
63
  for (const chunk of chunks) {
@@ -62,26 +65,229 @@ async function consumeStream(body, handler) {
62
65
  if (!raw) continue;
63
66
  try {
64
67
  const parsed = JSON.parse(raw);
65
- if (parsed.data?.uiMessageProtocol) handler(parsed.data.uiMessageProtocol);
68
+ const proto = parsed.data?.uiMessageProtocol;
69
+ if (proto) {
70
+ eventCount++;
71
+ if (proto.type === "text-delta") {
72
+ textAccumulator += proto.delta ?? "";
73
+ }
74
+ handler(proto);
75
+ }
66
76
  } catch {
67
77
  }
68
78
  }
79
+ };
80
+ while (true) {
81
+ const { done, value } = await reader.read();
82
+ if (done) break;
83
+ buffer += decoder.decode(value, { stream: true });
84
+ processBuffer();
85
+ }
86
+ if (buffer.trim()) {
87
+ buffer += STREAM_DELIMITER;
88
+ processBuffer();
89
+ }
90
+ return { textAccumulator, eventCount };
91
+ }
92
+
93
+ // src/core/chat-reducer.ts
94
+ function applyUiEventToMessages(prev, proto, msgId) {
95
+ let idx = prev.findIndex((m) => m.id === msgId);
96
+ if (idx === -1) {
97
+ if (proto.type === "finish" || proto.type === "debug-trace" || proto.type === "step-start" || proto.type === "step-end" || proto.type === "navigate" || proto.type === "suggestions") {
98
+ return prev;
99
+ }
100
+ prev = [...prev, { id: msgId, role: "assistant", parts: [] }];
101
+ idx = prev.length - 1;
102
+ }
103
+ const existing = prev[idx];
104
+ const msg = {
105
+ id: existing.id,
106
+ role: existing.role,
107
+ parts: [...existing.parts]
108
+ };
109
+ switch (proto.type) {
110
+ case "text-delta": {
111
+ const textIdx = msg.parts.findIndex((p) => p.type === "text");
112
+ if (textIdx === -1) {
113
+ msg.parts.push({ type: "text", text: proto.delta });
114
+ } else {
115
+ const p = msg.parts[textIdx];
116
+ msg.parts[textIdx] = { type: "text", text: p.text + proto.delta };
117
+ }
118
+ break;
119
+ }
120
+ case "reasoning-delta": {
121
+ const rIdx = msg.parts.findIndex((p) => p.type === "reasoning");
122
+ if (rIdx === -1) {
123
+ msg.parts.unshift({ type: "reasoning", text: proto.delta });
124
+ } else {
125
+ const p = msg.parts[rIdx];
126
+ msg.parts[rIdx] = {
127
+ type: "reasoning",
128
+ text: p.text + proto.delta
129
+ };
130
+ }
131
+ break;
132
+ }
133
+ case "tool-input-available": {
134
+ msg.parts.push({
135
+ type: "tool",
136
+ toolCallId: proto.toolCallId,
137
+ toolName: proto.toolName,
138
+ input: proto.input ?? {},
139
+ status: "running"
140
+ });
141
+ break;
142
+ }
143
+ case "tool-output-available": {
144
+ const tIdx = msg.parts.findIndex(
145
+ (p) => p.type === "tool" && p.toolCallId === proto.toolCallId
146
+ );
147
+ if (tIdx !== -1) {
148
+ msg.parts[tIdx] = {
149
+ ...msg.parts[tIdx],
150
+ status: "done",
151
+ output: proto.output
152
+ };
153
+ }
154
+ break;
155
+ }
156
+ case "tool-output-error": {
157
+ const tIdx = msg.parts.findIndex(
158
+ (p) => p.type === "tool" && p.toolCallId === proto.toolCallId
159
+ );
160
+ if (tIdx !== -1) {
161
+ msg.parts[tIdx] = {
162
+ ...msg.parts[tIdx],
163
+ status: "error",
164
+ errorText: proto.errorText
165
+ };
166
+ }
167
+ break;
168
+ }
169
+ case "picker": {
170
+ const safeOptions = (proto.options ?? []).map((o) => ({
171
+ value: typeof o.value === "string" ? o.value : JSON.stringify(o.value ?? ""),
172
+ label: typeof o.label === "string" ? o.label : JSON.stringify(o.label ?? "")
173
+ }));
174
+ msg.parts.push({
175
+ type: "picker",
176
+ pickerId: proto.pickerId,
177
+ paramName: proto.paramName,
178
+ toolName: proto.toolName,
179
+ label: typeof proto.label === "string" ? proto.label : String(proto.label),
180
+ options: safeOptions
181
+ });
182
+ break;
183
+ }
184
+ case "plan-created": {
185
+ msg.parts.push({
186
+ type: "plan",
187
+ planId: proto.planId,
188
+ goal: proto.goal,
189
+ steps: proto.steps.map((s) => ({
190
+ ...s,
191
+ status: "pending"
192
+ }))
193
+ });
194
+ break;
195
+ }
196
+ case "plan-step-update": {
197
+ const pIdx = msg.parts.findIndex(
198
+ (p) => p.type === "plan" && p.planId === proto.planId
199
+ );
200
+ if (pIdx !== -1) {
201
+ const prevPlan = msg.parts[pIdx];
202
+ msg.parts[pIdx] = {
203
+ ...prevPlan,
204
+ steps: prevPlan.steps.map(
205
+ (s) => s.index === proto.stepIndex ? {
206
+ ...s,
207
+ status: proto.status,
208
+ ...proto.error ? { error: proto.error } : {}
209
+ } : s
210
+ )
211
+ };
212
+ }
213
+ break;
214
+ }
69
215
  }
216
+ const copy = [...prev];
217
+ copy[idx] = msg;
218
+ return copy;
70
219
  }
71
220
  var API_URL = process.env.NEXT_PUBLIC_API_URL ?? "https://wallavi-production.up.railway.app";
221
+ function useVoiceCall({
222
+ agentId,
223
+ threadId,
224
+ workspaceId,
225
+ customBackend
226
+ }) {
227
+ const [active, setActive] = useState(false);
228
+ const [token, setToken] = useState(null);
229
+ const [serverUrl, setServerUrl] = useState(null);
230
+ const [loading, setLoading] = useState(false);
231
+ const [error, setError] = useState(null);
232
+ const start = useCallback(async () => {
233
+ if (customBackend) return;
234
+ setLoading(true);
235
+ setError(null);
236
+ try {
237
+ const isPrivate = Boolean(workspaceId);
238
+ const url = isPrivate ? `${API_URL}/api/threads/${threadId}/livekit-token?agentId=${encodeURIComponent(agentId)}` : `${API_URL}/api/chat/livekit-token?agentId=${encodeURIComponent(agentId)}&threadId=${encodeURIComponent(threadId)}`;
239
+ const res = await fetch(url, {
240
+ headers: isPrivate && typeof window !== "undefined" ? (
241
+ // @ts-ignore
242
+ {
243
+ Authorization: `Bearer ${await window.Clerk?.session?.getToken()}`
244
+ }
245
+ ) : {}
246
+ });
247
+ const data = await res.json();
248
+ if (!res.ok) throw new Error(data.error || "Failed to start call");
249
+ setToken(data.token);
250
+ setServerUrl(data.url);
251
+ setActive(true);
252
+ } catch (err) {
253
+ setError(err.message);
254
+ }
255
+ setLoading(false);
256
+ }, [agentId, threadId, workspaceId, customBackend]);
257
+ const stop = useCallback(() => {
258
+ setActive(false);
259
+ setToken(null);
260
+ setServerUrl(null);
261
+ }, []);
262
+ return customBackend?.voiceCall ?? {
263
+ active,
264
+ token,
265
+ serverUrl,
266
+ start,
267
+ stop,
268
+ loading,
269
+ error
270
+ };
271
+ }
272
+
273
+ // src/hooks/use-chat.ts
274
+ var API_URL2 = process.env.NEXT_PUBLIC_API_URL ?? "https://wallavi-production.up.railway.app";
72
275
  function newId() {
73
276
  return Math.random().toString(36).slice(2, 10);
74
277
  }
75
278
  function useChat({
76
279
  agentId,
77
280
  workspaceId = "",
281
+ envId,
78
282
  source = "playground",
79
283
  userContext,
80
284
  persist = false,
81
285
  onNavigate,
82
- playgroundOverrides
286
+ playgroundOverrides,
287
+ customBackend
83
288
  }) {
84
- const persistKey = persist ? `wallavi_${agentId}` : null;
289
+ const userId = userContext?.userId;
290
+ const persistKey = persist ? userId ? `wallavi_${agentId}_${userId}` : `wallavi_${agentId}` : null;
85
291
  const onNavigateRef = useRef(onNavigate);
86
292
  useEffect(() => {
87
293
  onNavigateRef.current = onNavigate;
@@ -105,46 +311,237 @@ function useChat({
105
311
  return crypto.randomUUID();
106
312
  });
107
313
  const streamingMsgIdRef = useRef(null);
314
+ const [debugTraces, setDebugTraces] = useState([]);
315
+ const initializedThreadsRef = useRef(/* @__PURE__ */ new Set());
316
+ const messagesRef = useRef(messages);
317
+ useEffect(() => {
318
+ messagesRef.current = messages;
319
+ }, [messages]);
320
+ const applyStreamEvent = useCallback(
321
+ (proto, msgId) => {
322
+ if (proto.type === "navigate") {
323
+ if (proto.path.startsWith("/")) onNavigateRef.current?.(proto.path);
324
+ return;
325
+ }
326
+ if (proto.type === "debug-trace") {
327
+ setDebugTraces((prev) => [
328
+ ...prev,
329
+ proto.trace
330
+ ]);
331
+ return;
332
+ }
333
+ setMessages((prev) => applyUiEventToMessages(prev, proto, msgId));
334
+ },
335
+ []
336
+ );
337
+ const fetchAndStream = useCallback(
338
+ async (opts) => {
339
+ const { input: userInput, msgId, extraMetadata, attachments } = opts;
340
+ const isPrivate = Boolean(workspaceId);
341
+ const token = isPrivate && typeof window !== "undefined" ? await window.Clerk?.session?.getToken() : null;
342
+ const url = isPrivate ? `${API_URL2}/api/threads/${threadId}/stream` : `${API_URL2}/api/chat/stream`;
343
+ const res = await fetch(url, {
344
+ method: "POST",
345
+ headers: {
346
+ "Content-Type": "application/json",
347
+ ...token ? { Authorization: `Bearer ${token}` } : {}
348
+ },
349
+ body: JSON.stringify({
350
+ input: userInput,
351
+ agentId,
352
+ ...isPrivate ? {
353
+ workspaceId,
354
+ ...playgroundOverrides ? { playgroundOverrides } : {},
355
+ ...envId ? { envId } : {}
356
+ } : { threadId },
357
+ source,
358
+ ...attachments?.length ? { attachments } : {},
359
+ ...userContext?.userName ? { userName: userContext.userName } : {},
360
+ ...userContext?.userEmail ? { userEmail: userContext.userEmail } : {},
361
+ userMetadata: {
362
+ ...userContext?.metadata ?? {},
363
+ ...userContext?.pageContext ? { pageContext: userContext.pageContext } : {},
364
+ headers: {
365
+ ...token ? { Authorization: `Bearer ${token}` } : {},
366
+ ...userContext?.headers ?? {},
367
+ ...userContext?.metadata?.headers ?? {}
368
+ },
369
+ ...extraMetadata ?? {}
370
+ }
371
+ })
372
+ });
373
+ if (!res.ok) {
374
+ const errText = await res.text().catch(() => "");
375
+ throw new Error(errText || `API error ${res.status}`);
376
+ }
377
+ if (!res.body) throw new Error("No stream body");
378
+ const { textAccumulator, eventCount } = await consumeStream(
379
+ res.body,
380
+ (proto) => applyStreamEvent(proto, msgId)
381
+ );
382
+ if (textAccumulator) {
383
+ setMessages((prev) => {
384
+ const idx = prev.findIndex((m) => m.id === msgId);
385
+ if (idx === -1) {
386
+ return [
387
+ ...prev,
388
+ {
389
+ id: msgId,
390
+ role: "assistant",
391
+ parts: [{ type: "text", text: textAccumulator }]
392
+ }
393
+ ];
394
+ }
395
+ const existing = prev[idx];
396
+ const textPart = existing.parts.find((p) => p.type === "text");
397
+ if (!textPart?.text || textPart.text.length < textAccumulator.length) {
398
+ const copy = [...prev];
399
+ const parts = [
400
+ ...existing.parts.filter((p) => p.type !== "text"),
401
+ { type: "text", text: textAccumulator }
402
+ ];
403
+ copy[idx] = { ...existing, parts };
404
+ return copy;
405
+ }
406
+ return prev;
407
+ });
408
+ }
409
+ },
410
+ [
411
+ agentId,
412
+ workspaceId,
413
+ envId,
414
+ source,
415
+ threadId,
416
+ userContext,
417
+ playgroundOverrides,
418
+ applyStreamEvent
419
+ ]
420
+ );
421
+ const voiceCall = useVoiceCall({
422
+ agentId,
423
+ threadId,
424
+ workspaceId,
425
+ customBackend
426
+ });
108
427
  useEffect(() => {
109
- if (!persistKey) return;
428
+ if (customBackend || !persistKey) return;
110
429
  try {
111
430
  sessionStorage.setItem(`${persistKey}_msgs`, JSON.stringify(messages));
112
431
  } catch {
113
432
  }
114
- }, [persistKey, messages]);
433
+ }, [customBackend, persistKey, messages]);
115
434
  useEffect(() => {
116
- if (!persistKey) return;
435
+ if (customBackend || !persistKey) return;
117
436
  try {
118
437
  localStorage.setItem(`${persistKey}_tid`, threadId);
119
438
  } catch {
120
439
  }
121
- }, [persistKey, threadId]);
440
+ }, [customBackend, persistKey, threadId]);
122
441
  useEffect(() => {
123
- if (!persistKey || typeof window === "undefined") return;
124
- if (messages.length > 0) return;
125
- void (async () => {
126
- try {
127
- const res = await fetch(
128
- `${API_URL}/api/chat/messages?agentId=${encodeURIComponent(agentId)}&threadId=${encodeURIComponent(threadId)}`
129
- );
130
- if (!res.ok) return;
131
- const { data } = await res.json();
132
- if (!data?.length) return;
133
- const restored = data.map((m) => ({
134
- id: m.id,
135
- role: m.role,
136
- parts: m.parts.filter((p) => p?.type)
137
- })).filter((m) => m.parts.length > 0);
138
- if (restored.length > 0) setMessages(restored);
139
- } catch {
442
+ if (customBackend || !persistKey || typeof window === "undefined") return;
443
+ const savedTid = localStorage.getItem(`${persistKey}_tid`);
444
+ if (savedTid) {
445
+ if (savedTid !== threadId) {
446
+ setThreadId(savedTid);
447
+ const savedMsgs = sessionStorage.getItem(`${persistKey}_msgs`);
448
+ if (savedMsgs) {
449
+ try {
450
+ setMessages(JSON.parse(savedMsgs));
451
+ } catch {
452
+ }
453
+ } else {
454
+ setMessages([]);
455
+ }
140
456
  }
141
- })();
142
- }, []);
457
+ } else {
458
+ const newTid = crypto.randomUUID();
459
+ setThreadId(newTid);
460
+ setMessages([]);
461
+ localStorage.setItem(`${persistKey}_tid`, newTid);
462
+ }
463
+ }, [customBackend, persistKey]);
464
+ useEffect(() => {
465
+ if (customBackend || typeof window === "undefined") return;
466
+ if (initializedThreadsRef.current.has(threadId)) return;
467
+ initializedThreadsRef.current.add(threadId);
468
+ if (persistKey) {
469
+ if (messagesRef.current.length > 0) return;
470
+ void (async () => {
471
+ try {
472
+ const isPrivate = Boolean(workspaceId);
473
+ const token = isPrivate && typeof window !== "undefined" ? await window.Clerk?.session?.getToken() : null;
474
+ const url = isPrivate ? `${API_URL2}/api/threads/${threadId}/messages` : `${API_URL2}/api/chat/messages?agentId=${encodeURIComponent(agentId)}&threadId=${encodeURIComponent(threadId)}`;
475
+ const res = await fetch(url, {
476
+ headers: {
477
+ ...token ? { Authorization: `Bearer ${token}` } : {}
478
+ }
479
+ });
480
+ if (!res.ok) return;
481
+ const { data } = await res.json();
482
+ if (!data?.length) {
483
+ try {
484
+ fetchAndStream({
485
+ input: "__INIT__",
486
+ msgId: "init",
487
+ extraMetadata: { isSilentInit: true }
488
+ });
489
+ } catch {
490
+ }
491
+ return;
492
+ }
493
+ const restored = data.map((m) => ({
494
+ id: m.id,
495
+ role: m.role,
496
+ parts: m.parts.filter((p) => p?.type).map((p) => {
497
+ if (p.type === "picker" && Array.isArray(p.options)) {
498
+ return {
499
+ ...p,
500
+ label: typeof p.label === "string" ? p.label : String(p.label ?? ""),
501
+ options: p.options.map((o) => ({
502
+ ...o,
503
+ value: typeof o.value === "string" ? o.value : JSON.stringify(o.value ?? ""),
504
+ label: typeof o.label === "string" ? o.label : JSON.stringify(o.label ?? "")
505
+ }))
506
+ };
507
+ }
508
+ return p;
509
+ })
510
+ })).filter((m) => m.parts.length > 0);
511
+ if (restored.length > 0) {
512
+ setMessages(restored);
513
+ } else {
514
+ try {
515
+ fetchAndStream({
516
+ input: "__INIT__",
517
+ msgId: "init",
518
+ extraMetadata: { isSilentInit: true }
519
+ });
520
+ } catch {
521
+ }
522
+ }
523
+ } catch {
524
+ }
525
+ })();
526
+ } else {
527
+ if (messagesRef.current.length === 0) {
528
+ try {
529
+ fetchAndStream({
530
+ input: "__INIT__",
531
+ msgId: "init",
532
+ extraMetadata: { isSilentInit: true }
533
+ });
534
+ } catch {
535
+ }
536
+ }
537
+ }
538
+ }, [threadId, agentId, workspaceId, envId, persistKey, fetchAndStream]);
143
539
  const reset = useCallback(() => {
144
540
  setMessages([]);
145
541
  setInput("");
146
542
  setStreaming(false);
147
543
  setThreadId(crypto.randomUUID());
544
+ setDebugTraces([]);
148
545
  streamingMsgIdRef.current = null;
149
546
  if (persistKey) {
150
547
  try {
@@ -155,146 +552,19 @@ function useChat({
155
552
  }
156
553
  }
157
554
  }, [persistKey]);
158
- const applyStreamEvent = useCallback((proto, msgId) => {
159
- if (proto.type === "navigate") {
160
- if (proto.path.startsWith("/")) onNavigateRef.current?.(proto.path);
161
- return;
162
- }
163
- setMessages((prev) => {
164
- const idx = prev.findIndex((m) => m.id === msgId);
165
- if (idx === -1) return prev;
166
- const existing = prev[idx];
167
- const msg = { id: existing.id, role: existing.role, parts: [...existing.parts] };
168
- switch (proto.type) {
169
- case "text-delta": {
170
- const textIdx = msg.parts.findIndex((p) => p.type === "text");
171
- if (textIdx === -1) {
172
- msg.parts.push({ type: "text", text: proto.delta });
173
- } else {
174
- const p = msg.parts[textIdx];
175
- msg.parts[textIdx] = { type: "text", text: p.text + proto.delta };
176
- }
177
- break;
178
- }
179
- case "reasoning-delta": {
180
- const rIdx = msg.parts.findIndex((p) => p.type === "reasoning");
181
- if (rIdx === -1) {
182
- msg.parts.unshift({ type: "reasoning", text: proto.delta });
183
- } else {
184
- const p = msg.parts[rIdx];
185
- msg.parts[rIdx] = { type: "reasoning", text: p.text + proto.delta };
186
- }
187
- break;
188
- }
189
- case "tool-input-available": {
190
- msg.parts.push({
191
- type: "tool",
192
- toolCallId: proto.toolCallId,
193
- toolName: proto.toolName,
194
- input: proto.input ?? {},
195
- status: "running"
196
- });
197
- break;
198
- }
199
- case "tool-output-available": {
200
- const tIdx = msg.parts.findIndex(
201
- (p) => p.type === "tool" && p.toolCallId === proto.toolCallId
202
- );
203
- if (tIdx !== -1) {
204
- msg.parts[tIdx] = { ...msg.parts[tIdx], status: "done", output: proto.output };
205
- }
206
- break;
207
- }
208
- case "tool-output-error": {
209
- const tIdx = msg.parts.findIndex(
210
- (p) => p.type === "tool" && p.toolCallId === proto.toolCallId
211
- );
212
- if (tIdx !== -1) {
213
- msg.parts[tIdx] = { ...msg.parts[tIdx], status: "error", errorText: proto.errorText };
214
- }
215
- break;
216
- }
217
- case "picker": {
218
- msg.parts.push({
219
- type: "picker",
220
- pickerId: proto.pickerId,
221
- paramName: proto.paramName,
222
- toolName: proto.toolName,
223
- label: proto.label,
224
- options: proto.options
225
- });
226
- break;
227
- }
228
- case "plan-created": {
229
- msg.parts.push({
230
- type: "plan",
231
- planId: proto.planId,
232
- goal: proto.goal,
233
- steps: proto.steps.map((s) => ({ ...s, status: "pending" }))
234
- });
235
- break;
236
- }
237
- case "plan-step-update": {
238
- const pIdx = msg.parts.findIndex(
239
- (p) => p.type === "plan" && p.planId === proto.planId
240
- );
241
- if (pIdx !== -1) {
242
- const prev2 = msg.parts[pIdx];
243
- msg.parts[pIdx] = {
244
- ...prev2,
245
- steps: prev2.steps.map(
246
- (s) => s.index === proto.stepIndex ? { ...s, status: proto.status, ...proto.error ? { error: proto.error } : {} } : s
247
- )
248
- };
249
- }
250
- break;
251
- }
252
- }
253
- const copy = [...prev];
254
- copy[idx] = msg;
255
- return copy;
256
- });
257
- }, []);
258
- const fetchAndStream = useCallback(async (opts) => {
259
- const { input: userInput, msgId, extraMetadata, attachments } = opts;
260
- const isPrivate = Boolean(workspaceId);
261
- const token = isPrivate && typeof window !== "undefined" ? await window.Clerk?.session?.getToken() : null;
262
- const url = isPrivate ? `${API_URL}/api/threads/${threadId}/stream` : `${API_URL}/api/chat/stream`;
263
- const res = await fetch(url, {
264
- method: "POST",
265
- headers: {
266
- "Content-Type": "application/json",
267
- ...token ? { Authorization: `Bearer ${token}` } : {}
268
- },
269
- body: JSON.stringify({
270
- input: userInput,
271
- agentId,
272
- ...isPrivate ? { workspaceId, ...playgroundOverrides ? { playgroundOverrides } : {} } : { threadId },
273
- source,
274
- ...attachments?.length ? { attachments } : {},
275
- ...userContext?.userName ? { userName: userContext.userName } : {},
276
- ...userContext?.userEmail ? { userEmail: userContext.userEmail } : {},
277
- userMetadata: {
278
- ...userContext?.metadata ?? {},
279
- ...userContext?.pageContext ? { pageContext: userContext.pageContext } : {},
280
- headers: {
281
- ...token ? { Authorization: `Bearer ${token}` } : {},
282
- ...userContext?.headers ?? {}
283
- },
284
- ...extraMetadata ?? {}
285
- }
286
- })
287
- });
288
- if (!res.ok) {
289
- const errText = await res.text().catch(() => "");
290
- throw new Error(errText || `API error ${res.status}`);
291
- }
292
- if (!res.body) throw new Error("No stream body");
293
- await consumeStream(res.body, (proto) => applyStreamEvent(proto, msgId));
294
- }, [agentId, workspaceId, source, threadId, userContext, playgroundOverrides, applyStreamEvent]);
295
555
  const pendingAttachmentsRef = useRef([]);
296
556
  const send = useCallback(
297
557
  async (text) => {
558
+ if (customBackend) {
559
+ const content = (text ?? input).trim();
560
+ const attachments2 = pendingAttachmentsRef.current.length > 0 ? [...pendingAttachmentsRef.current] : void 0;
561
+ pendingAttachmentsRef.current = [];
562
+ if (!content && !attachments2?.length || customBackend.streaming)
563
+ return;
564
+ setInput("");
565
+ await customBackend.send(content, attachments2);
566
+ return;
567
+ }
298
568
  const userInput = (text ?? input).trim();
299
569
  if (!userInput || streaming) return;
300
570
  setInput("");
@@ -313,16 +583,32 @@ function useChat({
313
583
  setStreaming(true);
314
584
  const assistantMsgId = newId();
315
585
  streamingMsgIdRef.current = assistantMsgId;
316
- setMessages((prev) => [...prev, { id: assistantMsgId, role: "assistant", parts: [] }]);
586
+ setMessages((prev) => [
587
+ ...prev,
588
+ { id: assistantMsgId, role: "assistant", parts: [] }
589
+ ]);
317
590
  try {
318
- await fetchAndStream({ input: userInput, msgId: assistantMsgId, attachments });
591
+ await fetchAndStream({
592
+ input: userInput,
593
+ msgId: assistantMsgId,
594
+ attachments
595
+ });
319
596
  } catch {
320
597
  setMessages((prev) => {
321
598
  const idx = prev.findIndex((m) => m.id === assistantMsgId);
322
599
  if (idx === -1) return prev;
323
600
  const copy = [...prev];
324
601
  const err = copy[idx];
325
- copy[idx] = { id: err.id, role: err.role, parts: [{ type: "text", text: "Sorry, something went wrong. Please try again." }] };
602
+ copy[idx] = {
603
+ id: err.id,
604
+ role: err.role,
605
+ parts: [
606
+ {
607
+ type: "text",
608
+ text: "Sorry, something went wrong. Please try again."
609
+ }
610
+ ]
611
+ };
326
612
  return copy;
327
613
  });
328
614
  }
@@ -338,19 +624,27 @@ function useChat({
338
624
  (prev) => prev.map((msg) => ({
339
625
  ...msg,
340
626
  parts: msg.parts.map(
341
- (part) => part.type === "picker" && part.pickerId === pickerId ? { ...part, selectedValue: value } : part
627
+ (part) => part.type === "picker" && part.pickerId === pickerId ? {
628
+ ...part,
629
+ selectedValue: value
630
+ } : part
342
631
  )
343
632
  }))
344
633
  );
345
634
  setStreaming(true);
346
635
  const assistantMsgId = newId();
347
636
  streamingMsgIdRef.current = assistantMsgId;
348
- setMessages((prev) => [...prev, { id: assistantMsgId, role: "assistant", parts: [] }]);
637
+ setMessages((prev) => [
638
+ ...prev,
639
+ { id: assistantMsgId, role: "assistant", parts: [] }
640
+ ]);
349
641
  try {
350
642
  await fetchAndStream({
351
643
  input: label,
352
644
  msgId: assistantMsgId,
353
- extraMetadata: { __pickerSelection: { pickerId, paramName, value, label } }
645
+ extraMetadata: {
646
+ __pickerSelection: { pickerId, paramName, value, label }
647
+ }
354
648
  });
355
649
  } catch {
356
650
  setMessages((prev) => {
@@ -358,7 +652,16 @@ function useChat({
358
652
  if (idx === -1) return prev;
359
653
  const copy = [...prev];
360
654
  const err = copy[idx];
361
- copy[idx] = { id: err.id, role: err.role, parts: [{ type: "text", text: "Sorry, something went wrong. Please try again." }] };
655
+ copy[idx] = {
656
+ id: err.id,
657
+ role: err.role,
658
+ parts: [
659
+ {
660
+ type: "text",
661
+ text: "Sorry, something went wrong. Please try again."
662
+ }
663
+ ]
664
+ };
362
665
  return copy;
363
666
  });
364
667
  }
@@ -383,7 +686,20 @@ function useChat({
383
686
  });
384
687
  await send(lastText);
385
688
  }, [streaming, messages, send]);
386
- return { messages, input, setInput, streaming, threadId, send, queueAttachments, regenerate, reset, selectPickerOption };
689
+ return {
690
+ messages: customBackend ? customBackend.messages : messages,
691
+ streaming: customBackend ? customBackend.streaming : streaming,
692
+ input,
693
+ setInput,
694
+ threadId,
695
+ send,
696
+ queueAttachments,
697
+ regenerate,
698
+ reset: customBackend?.reset ?? reset,
699
+ selectPickerOption,
700
+ debugTraces,
701
+ voiceCall
702
+ };
387
703
  }
388
704
  function getPreferredMimeType() {
389
705
  if (typeof MediaRecorder === "undefined") return "";
@@ -402,7 +718,12 @@ function mimeTypeToExtension(mimeType) {
402
718
  return "webm";
403
719
  }
404
720
  var DEFAULT_API_URL = process.env.NEXT_PUBLIC_API_URL ?? "https://wallavi-production.up.railway.app";
405
- function useVoice({ agentId, apiUrl, onTranscript, onError }) {
721
+ function useVoice({
722
+ agentId,
723
+ apiUrl,
724
+ onTranscript,
725
+ onError
726
+ }) {
406
727
  const [voiceState, setVoiceState] = useState("idle");
407
728
  const recorderRef = useRef(null);
408
729
  const chunksRef = useRef([]);
@@ -418,10 +739,14 @@ function useVoice({ agentId, apiUrl, onTranscript, onError }) {
418
739
  const form = new FormData();
419
740
  form.append("audio", blob, `recording.${ext}`);
420
741
  form.append("agentId", agentId);
421
- const res = await fetch(`${base}/api/chat/transcribe`, { method: "POST", body: form });
742
+ const res = await fetch(`${base}/api/chat/transcribe`, {
743
+ method: "POST",
744
+ body: form
745
+ });
422
746
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
423
747
  const data = await res.json();
424
- if (!data.text?.trim()) throw new Error(data.error ?? "Empty transcript");
748
+ if (!data.text?.trim())
749
+ throw new Error(data.error ?? "Empty transcript");
425
750
  onTranscript(data.text.trim());
426
751
  setVoiceState("idle");
427
752
  } catch (err) {
@@ -439,14 +764,19 @@ function useVoice({ agentId, apiUrl, onTranscript, onError }) {
439
764
  const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
440
765
  streamRef.current = stream;
441
766
  const mimeType = getPreferredMimeType();
442
- const recorder = new MediaRecorder(stream, mimeType ? { mimeType } : void 0);
767
+ const recorder = new MediaRecorder(
768
+ stream,
769
+ mimeType ? { mimeType } : void 0
770
+ );
443
771
  chunksRef.current = [];
444
772
  recorder.ondataavailable = (e) => {
445
773
  if (e.data.size > 0) chunksRef.current.push(e.data);
446
774
  };
447
775
  recorder.onstop = async () => {
448
776
  stream.getTracks().forEach((t) => t.stop());
449
- const blob = new Blob(chunksRef.current, { type: mimeType || "audio/webm" });
777
+ const blob = new Blob(chunksRef.current, {
778
+ type: mimeType || "audio/webm"
779
+ });
450
780
  if (blob.size === 0) {
451
781
  onError?.("Recording was empty \u2014 please try again.");
452
782
  setVoiceState("idle");
@@ -472,7 +802,8 @@ function useVoice({ agentId, apiUrl, onTranscript, onError }) {
472
802
  useEffect(() => {
473
803
  return () => {
474
804
  if (errorTimerRef.current) clearTimeout(errorTimerRef.current);
475
- if (recorderRef.current?.state === "recording") recorderRef.current.stop();
805
+ if (recorderRef.current?.state === "recording")
806
+ recorderRef.current.stop();
476
807
  streamRef.current?.getTracks().forEach((t) => t.stop());
477
808
  };
478
809
  }, []);
@@ -495,7 +826,10 @@ function useAttachments({
495
826
  form.append("file", file);
496
827
  form.append("agentId", agentId);
497
828
  try {
498
- const res = await fetch(`${base}/api/chat/upload`, { method: "POST", body: form });
829
+ const res = await fetch(`${base}/api/chat/upload`, {
830
+ method: "POST",
831
+ body: form
832
+ });
499
833
  if (!res.ok) {
500
834
  const data = await res.json().catch(() => ({}));
501
835
  setAttachments(
@@ -511,7 +845,9 @@ function useAttachments({
511
845
  }
512
846
  const payload = await res.json();
513
847
  setAttachments(
514
- (prev) => prev.map((a) => a.id === id ? { ...a, status: "ready", payload } : a)
848
+ (prev) => prev.map(
849
+ (a) => a.id === id ? { ...a, status: "ready", payload } : a
850
+ )
515
851
  );
516
852
  } catch (err) {
517
853
  const msg = err instanceof Error ? err.message : "Upload failed";
@@ -555,16 +891,13 @@ function useAttachments({
555
891
  return { attachments, attach, remove, clear, isUploading, readyPayloads };
556
892
  }
557
893
  function DefaultIcon() {
558
- return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", fill: "none", style: { width: 26, height: 26 }, children: [
559
- /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "12", fill: "currentColor", opacity: 0.12 }),
560
- /* @__PURE__ */ jsx(
561
- "path",
562
- {
563
- d: "M4 8.5C4 6.57 5.57 5 7.5 5h9C18.43 5 20 6.57 20 8.5v5c0 1.93-1.57 3.5-3.5 3.5H13l-3 2.5V17H7.5C5.57 17 4 15.43 4 13.5v-5Z",
564
- fill: "currentColor"
565
- }
566
- )
567
- ] });
894
+ return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", style: { width: 26, height: 26 }, children: /* @__PURE__ */ jsx(
895
+ "path",
896
+ {
897
+ d: "M4 8.5C4 6.57 5.57 5 7.5 5h9C18.43 5 20 6.57 20 8.5v5c0 1.93-1.57 3.5-3.5 3.5H13l-3 2.5V17H7.5C5.57 17 4 15.43 4 13.5v-5Z",
898
+ fill: "currentColor"
899
+ }
900
+ ) });
568
901
  }
569
902
  function ExpandIcon() {
570
903
  return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", style: { width: 11, height: 11 }, children: /* @__PURE__ */ jsx(
@@ -590,9 +923,58 @@ function MinimizeIcon() {
590
923
  }
591
924
  ) });
592
925
  }
593
- var Avatar = ({ style, ...p }) => /* @__PURE__ */ jsx(AvatarPrimitive.Root, { style: { position: "relative", display: "flex", flexShrink: 0, overflow: "hidden", borderRadius: "9999px", ...style }, ...p });
594
- var AvatarImage = ({ style, ...p }) => /* @__PURE__ */ jsx(AvatarPrimitive.Image, { style: { position: "absolute", inset: 0, width: "100%", height: "100%", objectFit: "cover", ...style }, ...p });
595
- var AvatarFallback = ({ style, ...p }) => /* @__PURE__ */ jsx(AvatarPrimitive.Fallback, { style: { display: "flex", width: "100%", height: "100%", alignItems: "center", justifyContent: "center", borderRadius: "9999px", ...style }, ...p });
926
+ var Avatar = ({
927
+ style,
928
+ ...p
929
+ }) => /* @__PURE__ */ jsx(
930
+ AvatarPrimitive.Root,
931
+ {
932
+ style: {
933
+ position: "relative",
934
+ display: "flex",
935
+ flexShrink: 0,
936
+ overflow: "hidden",
937
+ borderRadius: "9999px",
938
+ ...style
939
+ },
940
+ ...p
941
+ }
942
+ );
943
+ var AvatarImage = ({
944
+ style,
945
+ ...p
946
+ }) => /* @__PURE__ */ jsx(
947
+ AvatarPrimitive.Image,
948
+ {
949
+ style: {
950
+ position: "absolute",
951
+ inset: 0,
952
+ width: "100%",
953
+ height: "100%",
954
+ objectFit: "cover",
955
+ ...style
956
+ },
957
+ ...p
958
+ }
959
+ );
960
+ var AvatarFallback = ({
961
+ style,
962
+ ...p
963
+ }) => /* @__PURE__ */ jsx(
964
+ AvatarPrimitive.Fallback,
965
+ {
966
+ style: {
967
+ display: "flex",
968
+ width: "100%",
969
+ height: "100%",
970
+ alignItems: "center",
971
+ justifyContent: "center",
972
+ borderRadius: "9999px",
973
+ ...style
974
+ },
975
+ ...p
976
+ }
977
+ );
596
978
  function ChatHeader({
597
979
  title,
598
980
  profilePicture,
@@ -601,22 +983,39 @@ function ChatHeader({
601
983
  onReset,
602
984
  onClose,
603
985
  onExpand,
604
- expanded
986
+ expanded,
987
+ onCall,
988
+ isCallLoading
605
989
  }) {
606
990
  return /* @__PURE__ */ jsxs(
607
991
  "header",
608
992
  {
609
- className: "ww-flex ww-items-center ww-justify-between ww-px-4 ww-py-3 ww-shrink-0",
610
- style: { backgroundColor: headerBg, color: headerText, borderBottom: `1px solid ${headerText}18` },
993
+ className: "ww-flex ww-items-center ww-justify-between ww-px-4 ww-py-3 ww-shrink-0 ww-touch-none",
994
+ style: {
995
+ backgroundColor: headerBg,
996
+ color: headerText,
997
+ borderBottom: `1px solid ${headerText}18`
998
+ },
611
999
  children: [
612
1000
  /* @__PURE__ */ jsxs("div", { className: "ww-flex ww-items-center ww-gap-2.5 ww-min-w-0", children: [
613
- /* @__PURE__ */ jsx(Avatar, { style: { width: 32, height: 32, border: `2px solid ${headerText}30` }, children: profilePicture ? /* @__PURE__ */ jsx(AvatarImage, { src: profilePicture, alt: title }) : /* @__PURE__ */ jsx(
614
- AvatarFallback,
1001
+ /* @__PURE__ */ jsx(
1002
+ Avatar,
615
1003
  {
616
- style: { backgroundColor: `${headerText}20`, color: headerText, fontSize: 11, fontWeight: 700 },
617
- children: title.slice(0, 2).toUpperCase()
1004
+ style: { width: 32, height: 32, border: `2px solid ${headerText}30` },
1005
+ children: profilePicture ? /* @__PURE__ */ jsx(AvatarImage, { src: profilePicture, alt: title }) : /* @__PURE__ */ jsx(
1006
+ AvatarFallback,
1007
+ {
1008
+ style: {
1009
+ backgroundColor: `${headerText}20`,
1010
+ color: headerText,
1011
+ fontSize: 11,
1012
+ fontWeight: 700
1013
+ },
1014
+ children: title.slice(0, 2).toUpperCase()
1015
+ }
1016
+ )
618
1017
  }
619
- ) }),
1018
+ ),
620
1019
  /* @__PURE__ */ jsxs("div", { className: "ww-min-w-0", children: [
621
1020
  /* @__PURE__ */ jsx("p", { className: "ww-font-semibold ww-text-sm ww-truncate ww-leading-tight", children: title }),
622
1021
  /* @__PURE__ */ jsx("p", { className: "ww-text-[10px] ww-opacity-70 ww-leading-tight", children: "AI Assistant" })
@@ -632,13 +1031,64 @@ function ChatHeader({
632
1031
  children: expanded ? /* @__PURE__ */ jsx(MinimizeIcon, {}) : /* @__PURE__ */ jsx(ExpandIcon, {})
633
1032
  }
634
1033
  ),
1034
+ onCall && /* @__PURE__ */ jsx(
1035
+ "button",
1036
+ {
1037
+ onClick: onCall,
1038
+ disabled: isCallLoading,
1039
+ className: "ww-rounded-full ww-p-1.5 ww-transition-colors hover:ww-bg-white/10 disabled:ww-opacity-50",
1040
+ title: "Voice Call",
1041
+ children: isCallLoading ? /* @__PURE__ */ jsxs(
1042
+ "svg",
1043
+ {
1044
+ className: "ww-animate-spin ww-h-3.5 ww-w-3.5",
1045
+ viewBox: "0 0 24 24",
1046
+ fill: "none",
1047
+ style: { color: headerText },
1048
+ children: [
1049
+ /* @__PURE__ */ jsx(
1050
+ "circle",
1051
+ {
1052
+ className: "ww-opacity-25",
1053
+ cx: "12",
1054
+ cy: "12",
1055
+ r: "10",
1056
+ stroke: "currentColor",
1057
+ strokeWidth: "4"
1058
+ }
1059
+ ),
1060
+ /* @__PURE__ */ jsx(
1061
+ "path",
1062
+ {
1063
+ className: "ww-opacity-75",
1064
+ fill: "currentColor",
1065
+ d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
1066
+ }
1067
+ )
1068
+ ]
1069
+ }
1070
+ ) : /* @__PURE__ */ jsx(
1071
+ Phone,
1072
+ {
1073
+ className: "ww-h-3.5 ww-w-3.5",
1074
+ style: { color: headerText }
1075
+ }
1076
+ )
1077
+ }
1078
+ ),
635
1079
  /* @__PURE__ */ jsx(
636
1080
  "button",
637
1081
  {
638
1082
  onClick: onReset,
639
1083
  className: "ww-rounded-full ww-p-1.5 ww-transition-colors hover:ww-bg-white/10",
640
1084
  title: "New conversation",
641
- children: /* @__PURE__ */ jsx(RotateCcw, { className: "ww-h-3.5 ww-w-3.5", style: { color: headerText } })
1085
+ children: /* @__PURE__ */ jsx(
1086
+ RotateCcw,
1087
+ {
1088
+ className: "ww-h-3.5 ww-w-3.5",
1089
+ style: { color: headerText }
1090
+ }
1091
+ )
642
1092
  }
643
1093
  ),
644
1094
  onClose && /* @__PURE__ */ jsx(
@@ -662,8 +1112,10 @@ function formatBytes(bytes) {
662
1112
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
663
1113
  }
664
1114
  function ChipLeading({ a }) {
665
- if (a.status === "uploading") return /* @__PURE__ */ jsx(Loader2, { className: "ww-h-3 ww-w-3 ww-shrink-0 ww-animate-spin" });
666
- if (a.status === "error") return /* @__PURE__ */ jsx(AlertCircle, { className: "ww-h-3 ww-w-3 ww-shrink-0" });
1115
+ if (a.status === "uploading")
1116
+ return /* @__PURE__ */ jsx(Loader2, { className: "ww-h-3 ww-w-3 ww-shrink-0 ww-animate-spin" });
1117
+ if (a.status === "error")
1118
+ return /* @__PURE__ */ jsx(AlertCircle, { className: "ww-h-3 ww-w-3 ww-shrink-0" });
667
1119
  const thumbSrc = a.payload?.url ?? a.payload?.base64;
668
1120
  if (a.mimeType.startsWith("image/") && thumbSrc) {
669
1121
  return /* @__PURE__ */ jsx(
@@ -675,14 +1127,19 @@ function ChipLeading({ a }) {
675
1127
  }
676
1128
  );
677
1129
  }
678
- if (a.mimeType.startsWith("image/")) return /* @__PURE__ */ jsx("div", { className: "ww-h-6 ww-w-6 ww-rounded ww-bg-muted-foreground/10 ww-border ww-border-border/30 ww-shrink-0 ww-flex ww-items-center ww-justify-center", children: /* @__PURE__ */ jsx("span", { className: "ww-text-[8px] ww-font-semibold ww-text-muted-foreground/50", children: "IMG" }) });
679
- if (a.mimeType === "application/pdf") return /* @__PURE__ */ jsx(FileText, { className: "ww-h-3 ww-w-3 ww-shrink-0 ww-text-red-400" });
1130
+ if (a.mimeType.startsWith("image/"))
1131
+ return /* @__PURE__ */ jsx("div", { className: "ww-h-6 ww-w-6 ww-rounded ww-bg-muted-foreground/10 ww-border ww-border-border/30 ww-shrink-0 ww-flex ww-items-center ww-justify-center", children: /* @__PURE__ */ jsx("span", { className: "ww-text-[8px] ww-font-semibold ww-text-muted-foreground/50", children: "IMG" }) });
1132
+ if (a.mimeType === "application/pdf")
1133
+ return /* @__PURE__ */ jsx(FileText, { className: "ww-h-3 ww-w-3 ww-shrink-0 ww-text-red-400" });
680
1134
  if (a.mimeType.includes("csv") || a.mimeType.includes("spreadsheet") || a.name.endsWith(".csv")) {
681
1135
  return /* @__PURE__ */ jsx(FileSpreadsheet, { className: "ww-h-3 ww-w-3 ww-shrink-0 ww-text-emerald-500" });
682
1136
  }
683
1137
  return /* @__PURE__ */ jsx(FileText, { className: "ww-h-3 ww-w-3 ww-shrink-0" });
684
1138
  }
685
- function AttachmentChips({ attachments, onRemove }) {
1139
+ function AttachmentChips({
1140
+ attachments,
1141
+ onRemove
1142
+ }) {
686
1143
  if (attachments.length === 0) return null;
687
1144
  return /* @__PURE__ */ jsx("div", { className: "ww-flex ww-flex-wrap ww-gap-1.5 ww-px-1 ww-pb-1.5", children: attachments.map((a) => /* @__PURE__ */ jsxs(
688
1145
  "div",
@@ -710,41 +1167,49 @@ function AttachmentChips({ attachments, onRemove }) {
710
1167
  a.id
711
1168
  )) });
712
1169
  }
713
- var Avatar2 = ({ style, ...p }) => /* @__PURE__ */ jsx(AvatarPrimitive.Root, { style: { position: "relative", display: "flex", flexShrink: 0, overflow: "hidden", borderRadius: "9999px", ...style }, ...p });
714
- var AvatarImage2 = ({ style, ...p }) => /* @__PURE__ */ jsx(AvatarPrimitive.Image, { style: { position: "absolute", inset: 0, width: "100%", height: "100%", objectFit: "cover", ...style }, ...p });
715
- var AvatarFallback2 = ({ style, ...p }) => /* @__PURE__ */ jsx(AvatarPrimitive.Fallback, { style: { display: "flex", width: "100%", height: "100%", alignItems: "center", justifyContent: "center", borderRadius: "9999px", ...style }, ...p });
716
- var ReactMarkdown = ReactMarkdownLib;
717
- function SentAttachments({ attachments, contrastColor }) {
1170
+ function SentAttachments({
1171
+ attachments,
1172
+ contrastColor
1173
+ }) {
718
1174
  const images = attachments.filter((a) => a.contentType === "image");
719
1175
  const files = attachments.filter((a) => a.contentType !== "image");
720
1176
  const isDark = contrastColor === "#ffffff" || contrastColor === "#fff";
721
1177
  return /* @__PURE__ */ jsxs("div", { className: "ww-flex ww-flex-col ww-gap-1.5 ww-w-full", children: [
722
- images.length > 0 && /* @__PURE__ */ jsx("div", { className: cn("ww-flex ww-gap-1.5 ww-flex-wrap", images.length === 1 && ""), children: images.map((img) => {
723
- const src = img.url ?? img.base64;
724
- return src ? /* @__PURE__ */ jsx(
725
- "img",
726
- {
727
- src,
728
- alt: img.name,
729
- className: cn(
730
- "ww-rounded-xl ww-object-cover ww-border",
731
- images.length === 1 ? "ww-w-full ww-max-h-48" : "ww-h-20 ww-w-20",
732
- isDark ? "ww-border-white/20" : "ww-border-black/10"
733
- )
734
- },
735
- img.id
736
- ) : /* @__PURE__ */ jsx(
737
- "div",
738
- {
739
- className: cn(
740
- "ww-h-20 ww-w-20 ww-rounded-xl ww-flex ww-items-center ww-justify-center ww-text-[10px] ww-font-medium",
741
- isDark ? "ww-bg-white/10 ww-text-white/60" : "ww-bg-black/10 ww-text-black/40"
742
- ),
743
- children: "IMG"
744
- },
745
- img.id
746
- );
747
- }) }),
1178
+ images.length > 0 && /* @__PURE__ */ jsx(
1179
+ "div",
1180
+ {
1181
+ className: cn(
1182
+ "ww-flex ww-gap-1.5 ww-flex-wrap",
1183
+ images.length === 1 && ""
1184
+ ),
1185
+ children: images.map((img) => {
1186
+ const src = img.url ?? img.base64;
1187
+ return src ? /* @__PURE__ */ jsx(
1188
+ "img",
1189
+ {
1190
+ src,
1191
+ alt: img.name,
1192
+ className: cn(
1193
+ "ww-rounded-xl ww-object-cover ww-border",
1194
+ images.length === 1 ? "ww-w-full ww-max-h-48" : "ww-h-20 ww-w-20",
1195
+ isDark ? "ww-border-white/20" : "ww-border-black/10"
1196
+ )
1197
+ },
1198
+ img.id
1199
+ ) : /* @__PURE__ */ jsx(
1200
+ "div",
1201
+ {
1202
+ className: cn(
1203
+ "ww-h-20 ww-w-20 ww-rounded-xl ww-flex ww-items-center ww-justify-center ww-text-[10px] ww-font-medium",
1204
+ isDark ? "ww-bg-white/10 ww-text-white/60" : "ww-bg-black/10 ww-text-black/40"
1205
+ ),
1206
+ children: "IMG"
1207
+ },
1208
+ img.id
1209
+ );
1210
+ })
1211
+ }
1212
+ ),
748
1213
  files.length > 0 && /* @__PURE__ */ jsx("div", { className: "ww-flex ww-flex-wrap ww-gap-1", children: files.map((f) => /* @__PURE__ */ jsxs(
749
1214
  "div",
750
1215
  {
@@ -762,16 +1227,24 @@ function SentAttachments({ attachments, contrastColor }) {
762
1227
  ] });
763
1228
  }
764
1229
  function PlanStepIcon({ status }) {
765
- if (status === "executing") return /* @__PURE__ */ jsx(Loader2, { className: "ww-h-3 ww-w-3 ww-animate-spin ww-text-primary" });
766
- if (status === "success") return /* @__PURE__ */ jsx(CheckCircle2, { className: "ww-h-3 ww-w-3 ww-text-emerald-500" });
767
- if (status === "failed") return /* @__PURE__ */ jsx(AlertCircle, { className: "ww-h-3 ww-w-3 ww-text-destructive" });
1230
+ if (status === "executing")
1231
+ return /* @__PURE__ */ jsx(Loader2, { className: "ww-h-3 ww-w-3 ww-animate-spin ww-text-primary" });
1232
+ if (status === "success")
1233
+ return /* @__PURE__ */ jsx(CheckCircle2, { className: "ww-h-3 ww-w-3 ww-text-emerald-500" });
1234
+ if (status === "failed")
1235
+ return /* @__PURE__ */ jsx(AlertCircle, { className: "ww-h-3 ww-w-3 ww-text-destructive" });
768
1236
  return /* @__PURE__ */ jsx("div", { className: "ww-h-3 ww-w-3 ww-rounded-full ww-border-2 ww-border-muted-foreground/30" });
769
1237
  }
770
- function PlanCard({ part }) {
1238
+ function PlanCard({
1239
+ part,
1240
+ onSend,
1241
+ disabled
1242
+ }) {
771
1243
  const successCount = part.steps.filter((s) => s.status === "success").length;
772
1244
  const hasExecuting = part.steps.some((s) => s.status === "executing");
773
1245
  const allDone = successCount === part.steps.length && part.steps.length > 0;
774
1246
  const anyFailed = part.steps.some((s) => s.status === "failed");
1247
+ const allPending = part.steps.length > 0 && part.steps.every((s) => s.status === "pending");
775
1248
  return /* @__PURE__ */ jsxs("div", { className: "ww-rounded-xl ww-border ww-bg-background ww-overflow-hidden ww-text-xs ww-w-full ww-shadow-sm", children: [
776
1249
  /* @__PURE__ */ jsxs("div", { className: "ww-flex ww-items-center ww-gap-2 ww-px-3 ww-py-2 ww-bg-muted/50 ww-border-b", children: [
777
1250
  hasExecuting ? /* @__PURE__ */ jsx(Loader2, { className: "ww-h-3.5 ww-w-3.5 ww-shrink-0 ww-animate-spin ww-text-primary/80" }) : allDone ? /* @__PURE__ */ jsx(CheckCircle2, { className: "ww-h-3.5 ww-w-3.5 ww-shrink-0 ww-text-emerald-500" }) : anyFailed ? /* @__PURE__ */ jsx(AlertCircle, { className: "ww-h-3.5 ww-w-3.5 ww-shrink-0 ww-text-destructive" }) : /* @__PURE__ */ jsx(Zap, { className: "ww-h-3.5 ww-w-3.5 ww-shrink-0 ww-text-primary/70" }),
@@ -812,7 +1285,37 @@ function PlanCard({ part }) {
812
1285
  ]
813
1286
  },
814
1287
  step.index
815
- )) })
1288
+ )) }),
1289
+ allPending && onSend && /* @__PURE__ */ jsxs("div", { className: "ww-flex ww-gap-2 ww-px-3 ww-py-2.5 ww-border-t", children: [
1290
+ /* @__PURE__ */ jsxs(
1291
+ "button",
1292
+ {
1293
+ onClick: () => onSend("s\xED, proceder"),
1294
+ disabled,
1295
+ className: "ww-flex-1 ww-flex ww-items-center ww-justify-center ww-gap-1.5 ww-rounded-lg ww-py-1.5 ww-text-xs ww-font-semibold ww-transition-opacity hover:ww-opacity-85 active:ww-scale-[0.98] disabled:ww-opacity-50 disabled:ww-pointer-events-none",
1296
+ style: {
1297
+ backgroundColor: "var(--primary, #18181b)",
1298
+ color: "var(--primary-foreground, #fff)"
1299
+ },
1300
+ children: [
1301
+ /* @__PURE__ */ jsx(Check, { className: "ww-h-3 ww-w-3 ww-shrink-0" }),
1302
+ "Proceder"
1303
+ ]
1304
+ }
1305
+ ),
1306
+ /* @__PURE__ */ jsxs(
1307
+ "button",
1308
+ {
1309
+ onClick: () => onSend("cancelar"),
1310
+ disabled,
1311
+ className: "ww-flex ww-items-center ww-justify-center ww-gap-1.5 ww-rounded-lg ww-px-3 ww-py-1.5 ww-text-xs ww-font-medium ww-bg-muted ww-text-muted-foreground ww-transition-opacity hover:ww-opacity-80 active:ww-scale-[0.98] disabled:ww-opacity-50 disabled:ww-pointer-events-none",
1312
+ children: [
1313
+ /* @__PURE__ */ jsx(X, { className: "ww-h-3 ww-w-3 ww-shrink-0" }),
1314
+ "Cancelar"
1315
+ ]
1316
+ }
1317
+ )
1318
+ ] })
816
1319
  ] });
817
1320
  }
818
1321
  function ThinkingDots() {
@@ -875,7 +1378,15 @@ function ReasoningBlock({ text }) {
875
1378
  children: [
876
1379
  /* @__PURE__ */ jsx(Zap, { className: "ww-h-3 ww-w-3" }),
877
1380
  /* @__PURE__ */ jsx("span", { children: "Reasoning" }),
878
- /* @__PURE__ */ jsx(ChevronDown, { className: cn("ww-h-3 ww-w-3 ww-transition-transform", open && "ww-rotate-180") })
1381
+ /* @__PURE__ */ jsx(
1382
+ ChevronDown,
1383
+ {
1384
+ className: cn(
1385
+ "ww-h-3 ww-w-3 ww-transition-transform",
1386
+ open && "ww-rotate-180"
1387
+ )
1388
+ }
1389
+ )
879
1390
  ]
880
1391
  }
881
1392
  ),
@@ -897,98 +1408,292 @@ function PickerSelector({
897
1408
  }, [part.options, query]);
898
1409
  const isConsumed = !!part.selectedValue;
899
1410
  const handleClick = (opt) => {
900
- if (!disabled && !isConsumed) onSelect(part.pickerId, part.paramName, opt.value, opt.label);
1411
+ if (!disabled && !isConsumed)
1412
+ onSelect(part.pickerId, part.paramName, opt.value, opt.label);
901
1413
  };
902
- return /* @__PURE__ */ jsxs("div", { className: cn(
903
- "ww-flex ww-flex-col ww-gap-2.5 ww-rounded-xl ww-rounded-tl-none ww-border ww-bg-muted/60 ww-p-3",
904
- "ww-border-border/50"
905
- ), children: [
906
- /* @__PURE__ */ jsx("p", { className: "ww-text-[10.5px] ww-font-semibold ww-uppercase ww-tracking-widest ww-text-muted-foreground/70 ww-leading-none", children: part.label }),
907
- mode === "list" && /* @__PURE__ */ jsxs("div", { className: "ww-relative", children: [
908
- /* @__PURE__ */ jsx(Search, { className: "ww-absolute ww-left-2.5 ww-top-1/2 -translate-y-1/2 ww-h-3 ww-w-3 ww-text-muted-foreground/50 ww-pointer-events-none" }),
909
- /* @__PURE__ */ jsx(
910
- "input",
911
- {
912
- type: "text",
913
- value: query,
914
- onChange: (e) => setQuery(e.target.value),
915
- placeholder: "Search\u2026",
916
- disabled: disabled || isConsumed,
917
- className: "ww-w-full ww-rounded-lg ww-border ww-bg-background ww-pl-7 ww-pr-3 ww-py-1.5 ww-text-xs ww-outline-none focus:ww-ring-1 focus:ww-ring-ring/40 placeholder:ww-text-muted-foreground/40 disabled:ww-opacity-50"
918
- }
919
- )
920
- ] }),
921
- mode === "pills" && /* @__PURE__ */ jsx("div", { className: "ww-flex ww-flex-wrap ww-gap-1.5", children: part.options.map((opt) => {
922
- const sel = part.selectedValue === opt.value;
923
- const faded = isConsumed && !sel;
924
- return /* @__PURE__ */ jsxs(
925
- "button",
926
- {
927
- disabled: disabled || isConsumed,
928
- onClick: () => handleClick(opt),
929
- className: cn(
930
- "ww-inline-flex ww-items-center ww-gap-1 ww-rounded-full ww-px-3.5 ww-py-1.5 ww-text-[12.5px] ww-font-medium ww-transition-all ww-duration-150 ww-select-none",
931
- sel ? "ww-bg-foreground ww-text-background ww-shadow-sm" : faded ? "ww-bg-background/50 ww-text-muted-foreground ww-opacity-35 ww-cursor-default" : "ww-bg-background ww-border ww-border-border/70 ww-text-foreground hover:ww-border-foreground/30 hover:ww-bg-background ww-cursor-pointer",
932
- disabled && !isConsumed && "ww-opacity-60 ww-pointer-events-none"
933
- ),
934
- children: [
935
- sel && /* @__PURE__ */ jsx(Check, { className: "ww-h-3 ww-w-3 ww-shrink-0" }),
936
- opt.label
937
- ]
938
- },
939
- opt.value
940
- );
941
- }) }),
942
- mode === "grid" && /* @__PURE__ */ jsx("div", { className: "ww-grid ww-grid-cols-2 ww-gap-1.5 ww-max-h-[168px] ww-overflow-y-auto ww-pr-0.5 scrollbar-thin", children: part.options.map((opt) => {
943
- const sel = part.selectedValue === opt.value;
944
- const faded = isConsumed && !sel;
945
- return /* @__PURE__ */ jsxs(
946
- "button",
947
- {
948
- disabled: disabled || isConsumed,
949
- onClick: () => handleClick(opt),
950
- className: cn(
951
- "ww-flex ww-items-center ww-gap-1.5 ww-rounded-lg ww-px-2.5 ww-py-1.5 ww-text-[12px] ww-font-medium ww-text-left ww-transition-all ww-duration-150 ww-select-none ww-truncate",
952
- sel ? "ww-bg-foreground ww-text-background" : faded ? "ww-bg-background/40 ww-text-muted-foreground ww-opacity-35 ww-cursor-default" : "ww-bg-background ww-border ww-border-border/60 ww-text-foreground hover:ww-border-foreground/25 ww-cursor-pointer",
953
- disabled && !isConsumed && "ww-opacity-60 ww-pointer-events-none"
954
- ),
955
- children: [
956
- sel ? /* @__PURE__ */ jsx(Check, { className: "ww-h-3 ww-w-3 ww-shrink-0" }) : /* @__PURE__ */ jsx("span", { className: "ww-h-3 ww-w-3 ww-shrink-0" }),
957
- /* @__PURE__ */ jsx("span", { className: "ww-truncate", children: opt.label })
958
- ]
959
- },
960
- opt.value
961
- );
962
- }) }),
963
- mode === "list" && /* @__PURE__ */ jsxs("div", { className: "ww-flex ww-flex-col ww-gap-0.5 ww-max-h-[180px] ww-overflow-y-auto ww-pr-0.5", children: [
964
- filtered.length === 0 && /* @__PURE__ */ jsx("p", { className: "ww-py-3 ww-text-center ww-text-xs ww-text-muted-foreground/50", children: "No results" }),
965
- filtered.map((opt) => {
966
- const sel = part.selectedValue === opt.value;
967
- const faded = isConsumed && !sel;
968
- return /* @__PURE__ */ jsxs(
969
- "button",
1414
+ return /* @__PURE__ */ jsxs(
1415
+ "div",
1416
+ {
1417
+ className: cn(
1418
+ "ww-flex ww-flex-col ww-gap-2.5 ww-rounded-xl ww-rounded-tl-none ww-border ww-bg-muted/60 ww-p-3",
1419
+ "ww-border-border/50"
1420
+ ),
1421
+ children: [
1422
+ /* @__PURE__ */ jsx("p", { className: "ww-text-[10.5px] ww-font-semibold ww-uppercase ww-tracking-widest ww-text-muted-foreground/70 ww-leading-none", children: part.label }),
1423
+ mode === "list" && /* @__PURE__ */ jsxs("div", { className: "ww-relative", children: [
1424
+ /* @__PURE__ */ jsx(Search, { className: "ww-absolute ww-left-2.5 ww-top-1/2 -translate-y-1/2 ww-h-3 ww-w-3 ww-text-muted-foreground/50 ww-pointer-events-none" }),
1425
+ /* @__PURE__ */ jsx(
1426
+ "input",
1427
+ {
1428
+ type: "text",
1429
+ value: query,
1430
+ onChange: (e) => setQuery(e.target.value),
1431
+ placeholder: "Search\u2026",
1432
+ disabled: disabled || isConsumed,
1433
+ className: "ww-w-full ww-rounded-lg ww-border ww-bg-background ww-pl-7 ww-pr-3 ww-py-1.5 ww-text-xs ww-outline-none focus:ww-ring-1 focus:ww-ring-ring/40 placeholder:ww-text-muted-foreground/40 disabled:ww-opacity-50"
1434
+ }
1435
+ )
1436
+ ] }),
1437
+ mode === "pills" && /* @__PURE__ */ jsx("div", { className: "ww-flex ww-flex-wrap ww-gap-1.5", children: part.options.map((opt, i) => {
1438
+ const sel = part.selectedValue === opt.value;
1439
+ const faded = isConsumed && !sel;
1440
+ return /* @__PURE__ */ jsxs(
1441
+ "button",
1442
+ {
1443
+ disabled: disabled || isConsumed,
1444
+ onClick: () => handleClick(opt),
1445
+ className: cn(
1446
+ "ww-inline-flex ww-items-center ww-gap-1.5 ww-rounded-full ww-px-3.5 ww-py-1.5 ww-text-[12.5px] ww-font-medium ww-transition-all ww-duration-200 ww-select-none",
1447
+ sel ? "ww-bg-primary ww-text-primary-foreground ww-shadow-md ww-ring-2 ww-ring-primary/20" : faded ? "ww-bg-muted/50 ww-border ww-border-transparent ww-text-muted-foreground ww-opacity-70 ww-cursor-default" : "ww-bg-background ww-border ww-border-border/80 ww-text-foreground hover:ww-border-primary/40 hover:ww-shadow-sm hover:ww-text-primary ww-cursor-pointer",
1448
+ disabled && !isConsumed && "ww-opacity-60 ww-pointer-events-none"
1449
+ ),
1450
+ children: [
1451
+ sel && /* @__PURE__ */ jsx(Check, { className: "ww-h-3 ww-w-3 ww-shrink-0" }),
1452
+ opt.label
1453
+ ]
1454
+ },
1455
+ `${opt.value}-${i}`
1456
+ );
1457
+ }) }),
1458
+ mode === "grid" && /* @__PURE__ */ jsx("div", { className: "ww-grid ww-grid-cols-2 ww-gap-1.5 ww-max-h-[168px] ww-overflow-y-auto ww-pr-0.5 scrollbar-thin", children: part.options.map((opt, i) => {
1459
+ const sel = part.selectedValue === opt.value;
1460
+ const faded = isConsumed && !sel;
1461
+ return /* @__PURE__ */ jsxs(
1462
+ "button",
1463
+ {
1464
+ disabled: disabled || isConsumed,
1465
+ onClick: () => handleClick(opt),
1466
+ className: cn(
1467
+ "ww-flex ww-items-center ww-gap-2 ww-rounded-xl ww-px-3 ww-py-2 ww-text-[12.5px] ww-font-medium ww-text-left ww-transition-all ww-duration-200 ww-select-none ww-truncate",
1468
+ sel ? "ww-bg-primary ww-text-primary-foreground ww-shadow-md ww-ring-2 ww-ring-primary/20" : faded ? "ww-bg-muted/40 ww-border ww-border-transparent ww-text-muted-foreground ww-opacity-70 ww-cursor-default" : "ww-bg-background ww-border ww-border-border/60 ww-text-foreground hover:ww-border-primary/40 hover:ww-shadow-sm hover:ww-text-primary ww-cursor-pointer",
1469
+ disabled && !isConsumed && "ww-opacity-60 ww-pointer-events-none"
1470
+ ),
1471
+ children: [
1472
+ sel ? /* @__PURE__ */ jsx(Check, { className: "ww-h-3 ww-w-3 ww-shrink-0" }) : /* @__PURE__ */ jsx("span", { className: "ww-h-3 ww-w-3 ww-shrink-0" }),
1473
+ /* @__PURE__ */ jsx("span", { className: "ww-truncate", children: opt.label })
1474
+ ]
1475
+ },
1476
+ `${opt.value}-${i}`
1477
+ );
1478
+ }) }),
1479
+ mode === "list" && /* @__PURE__ */ jsxs("div", { className: "ww-flex ww-flex-col ww-gap-0.5 ww-max-h-[180px] ww-overflow-y-auto ww-pr-0.5", children: [
1480
+ filtered.length === 0 && /* @__PURE__ */ jsx("p", { className: "ww-py-3 ww-text-center ww-text-xs ww-text-muted-foreground/50", children: "No results" }),
1481
+ filtered.map((opt, i) => {
1482
+ const sel = part.selectedValue === opt.value;
1483
+ const faded = isConsumed && !sel;
1484
+ return /* @__PURE__ */ jsxs(
1485
+ "button",
1486
+ {
1487
+ disabled: disabled || isConsumed,
1488
+ onClick: () => handleClick(opt),
1489
+ className: cn(
1490
+ "ww-flex ww-items-center ww-gap-2.5 ww-rounded-xl ww-px-3 ww-py-2.5 ww-text-[13px] ww-text-left ww-transition-all ww-duration-200 ww-select-none",
1491
+ sel ? "ww-bg-primary ww-text-primary-foreground ww-shadow-md ww-font-semibold ww-ring-2 ww-ring-primary/20" : faded ? "ww-opacity-70 ww-text-muted-foreground ww-bg-muted/30 ww-cursor-default" : "hover:ww-bg-accent hover:ww-text-accent-foreground ww-text-foreground ww-cursor-pointer",
1492
+ disabled && !isConsumed && "ww-opacity-60 ww-pointer-events-none"
1493
+ ),
1494
+ children: [
1495
+ /* @__PURE__ */ jsx(
1496
+ "span",
1497
+ {
1498
+ className: cn(
1499
+ "ww-flex ww-h-4 ww-w-4 ww-shrink-0 ww-items-center ww-justify-center ww-rounded-full ww-border ww-text-[10px]",
1500
+ sel ? "ww-border-background ww-bg-background/20" : "ww-border-border/50"
1501
+ ),
1502
+ children: sel && /* @__PURE__ */ jsx(Check, { className: "ww-h-2.5 ww-w-2.5" })
1503
+ }
1504
+ ),
1505
+ /* @__PURE__ */ jsx("span", { className: "ww-truncate", children: opt.label })
1506
+ ]
1507
+ },
1508
+ `${opt.value}-${i}`
1509
+ );
1510
+ })
1511
+ ] })
1512
+ ]
1513
+ }
1514
+ );
1515
+ }
1516
+ var ReactMarkdown = ReactMarkdownLib;
1517
+ function MarkdownContent({ text }) {
1518
+ return /* @__PURE__ */ jsx(
1519
+ ReactMarkdown,
1520
+ {
1521
+ remarkPlugins: [remarkGfm],
1522
+ components: {
1523
+ p: ({ children }) => /* @__PURE__ */ jsx("p", { style: { margin: "4px 0" }, children }),
1524
+ h1: ({ children }) => /* @__PURE__ */ jsx(
1525
+ "p",
970
1526
  {
971
- disabled: disabled || isConsumed,
972
- onClick: () => handleClick(opt),
973
- className: cn(
974
- "ww-flex ww-items-center ww-gap-2 ww-rounded-lg ww-px-2.5 ww-py-2 ww-text-[12.5px] ww-text-left ww-transition-all ww-duration-100 ww-select-none",
975
- sel ? "ww-bg-foreground ww-text-background ww-font-medium" : faded ? "ww-opacity-30 ww-cursor-default" : "hover:ww-bg-background ww-text-foreground ww-cursor-pointer",
976
- disabled && !isConsumed && "ww-opacity-60 ww-pointer-events-none"
977
- ),
978
- children: [
979
- /* @__PURE__ */ jsx("span", { className: cn(
980
- "ww-flex ww-h-4 ww-w-4 ww-shrink-0 ww-items-center ww-justify-center ww-rounded-full ww-border ww-text-[10px]",
981
- sel ? "ww-border-background ww-bg-background/20" : "ww-border-border/50"
982
- ), children: sel && /* @__PURE__ */ jsx(Check, { className: "ww-h-2.5 ww-w-2.5" }) }),
983
- /* @__PURE__ */ jsx("span", { className: "ww-truncate", children: opt.label })
984
- ]
985
- },
986
- opt.value
987
- );
988
- })
989
- ] })
990
- ] });
1527
+ style: {
1528
+ margin: "6px 0",
1529
+ fontWeight: 700,
1530
+ fontSize: "1em"
1531
+ },
1532
+ children
1533
+ }
1534
+ ),
1535
+ h2: ({ children }) => /* @__PURE__ */ jsx(
1536
+ "p",
1537
+ {
1538
+ style: {
1539
+ margin: "6px 0",
1540
+ fontWeight: 700,
1541
+ fontSize: "1em"
1542
+ },
1543
+ children
1544
+ }
1545
+ ),
1546
+ h3: ({ children }) => /* @__PURE__ */ jsx(
1547
+ "p",
1548
+ {
1549
+ style: {
1550
+ margin: "6px 0",
1551
+ fontWeight: 600,
1552
+ fontSize: "0.95em"
1553
+ },
1554
+ children
1555
+ }
1556
+ ),
1557
+ ul: ({ children }) => /* @__PURE__ */ jsx("ul", { style: { margin: "4px 0", paddingLeft: 16 }, children }),
1558
+ ol: ({ children }) => /* @__PURE__ */ jsx("ol", { style: { margin: "4px 0", paddingLeft: 16 }, children }),
1559
+ li: ({ children }) => /* @__PURE__ */ jsx("li", { style: { margin: "2px 0" }, children }),
1560
+ strong: ({ children }) => /* @__PURE__ */ jsx("strong", { style: { fontWeight: 600 }, children }),
1561
+ code: ({ children }) => /* @__PURE__ */ jsx(
1562
+ "code",
1563
+ {
1564
+ style: {
1565
+ fontSize: "0.85em",
1566
+ backgroundColor: "rgba(0,0,0,0.07)",
1567
+ borderRadius: 4,
1568
+ padding: "1px 5px",
1569
+ fontFamily: "monospace"
1570
+ },
1571
+ children
1572
+ }
1573
+ ),
1574
+ pre: ({ children }) => /* @__PURE__ */ jsx(
1575
+ "pre",
1576
+ {
1577
+ style: {
1578
+ overflowX: "auto",
1579
+ margin: "6px 0",
1580
+ padding: "8px 10px",
1581
+ backgroundColor: "rgba(0,0,0,0.06)",
1582
+ borderRadius: 8,
1583
+ fontSize: "0.82em",
1584
+ fontFamily: "monospace"
1585
+ },
1586
+ children
1587
+ }
1588
+ ),
1589
+ a: ({
1590
+ href,
1591
+ children
1592
+ }) => /* @__PURE__ */ jsx(
1593
+ "a",
1594
+ {
1595
+ href,
1596
+ target: "_blank",
1597
+ rel: "noopener noreferrer",
1598
+ style: { textDecoration: "underline", opacity: 0.75 },
1599
+ children
1600
+ }
1601
+ ),
1602
+ table: ({ children }) => /* @__PURE__ */ jsx(
1603
+ "div",
1604
+ {
1605
+ style: {
1606
+ overflowX: "auto",
1607
+ margin: "6px 0",
1608
+ borderRadius: 8,
1609
+ border: "1px solid rgba(0,0,0,0.1)"
1610
+ },
1611
+ children: /* @__PURE__ */ jsx(
1612
+ "table",
1613
+ {
1614
+ style: {
1615
+ borderCollapse: "collapse",
1616
+ minWidth: "100%",
1617
+ fontSize: "0.82em"
1618
+ },
1619
+ children
1620
+ }
1621
+ )
1622
+ }
1623
+ ),
1624
+ thead: ({ children }) => /* @__PURE__ */ jsx("thead", { style: { backgroundColor: "rgba(0,0,0,0.04)" }, children }),
1625
+ tbody: ({ children }) => /* @__PURE__ */ jsx("tbody", { children }),
1626
+ tr: ({ children }) => /* @__PURE__ */ jsx("tr", { style: { borderBottom: "1px solid rgba(0,0,0,0.07)" }, children }),
1627
+ th: ({ children }) => /* @__PURE__ */ jsx(
1628
+ "th",
1629
+ {
1630
+ style: {
1631
+ padding: "6px 10px",
1632
+ textAlign: "left",
1633
+ fontWeight: 600,
1634
+ whiteSpace: "nowrap"
1635
+ },
1636
+ children
1637
+ }
1638
+ ),
1639
+ td: ({ children }) => /* @__PURE__ */ jsx("td", { style: { padding: "5px 10px", verticalAlign: "top" }, children })
1640
+ },
1641
+ children: text
1642
+ }
1643
+ );
991
1644
  }
1645
+ var Avatar2 = ({
1646
+ style,
1647
+ ...p
1648
+ }) => /* @__PURE__ */ jsx(
1649
+ AvatarPrimitive.Root,
1650
+ {
1651
+ style: {
1652
+ position: "relative",
1653
+ display: "flex",
1654
+ flexShrink: 0,
1655
+ overflow: "hidden",
1656
+ borderRadius: "9999px",
1657
+ ...style
1658
+ },
1659
+ ...p
1660
+ }
1661
+ );
1662
+ var AvatarImage2 = ({
1663
+ style,
1664
+ ...p
1665
+ }) => /* @__PURE__ */ jsx(
1666
+ AvatarPrimitive.Image,
1667
+ {
1668
+ style: {
1669
+ position: "absolute",
1670
+ inset: 0,
1671
+ width: "100%",
1672
+ height: "100%",
1673
+ objectFit: "cover",
1674
+ ...style
1675
+ },
1676
+ ...p
1677
+ }
1678
+ );
1679
+ var AvatarFallback2 = ({
1680
+ style,
1681
+ ...p
1682
+ }) => /* @__PURE__ */ jsx(
1683
+ AvatarPrimitive.Fallback,
1684
+ {
1685
+ style: {
1686
+ display: "flex",
1687
+ width: "100%",
1688
+ height: "100%",
1689
+ alignItems: "center",
1690
+ justifyContent: "center",
1691
+ borderRadius: "9999px",
1692
+ ...style
1693
+ },
1694
+ ...p
1695
+ }
1696
+ );
992
1697
  function MessageBubble({
993
1698
  message,
994
1699
  userColor,
@@ -996,14 +1701,21 @@ function MessageBubble({
996
1701
  profilePicture,
997
1702
  isStreaming,
998
1703
  showThinking = true,
999
- onPickerSelect
1704
+ onPickerSelect,
1705
+ onSend
1000
1706
  }) {
1001
1707
  const isUser = message.role === "user";
1002
1708
  const textPart = message.parts.find((p) => p.type === "text");
1003
1709
  const reasoningPart = message.parts.find((p) => p.type === "reasoning");
1004
- const toolParts = message.parts.filter((p) => p.type === "tool");
1005
- const pickerParts = message.parts.filter((p) => p.type === "picker");
1006
- const planParts = message.parts.filter((p) => p.type === "plan");
1710
+ const toolParts = message.parts.filter(
1711
+ (p) => p.type === "tool"
1712
+ );
1713
+ const pickerParts = message.parts.filter(
1714
+ (p) => p.type === "picker"
1715
+ );
1716
+ const planParts = message.parts.filter(
1717
+ (p) => p.type === "plan"
1718
+ );
1007
1719
  const contrastColor = getContrastColor(userColor);
1008
1720
  if (isUser) {
1009
1721
  return /* @__PURE__ */ jsx("div", { className: "ww-flex ww-justify-end", children: /* @__PURE__ */ jsxs(
@@ -1012,19 +1724,55 @@ function MessageBubble({
1012
1724
  className: "ww-max-w-[78%] ww-rounded-2xl ww-rounded-tr-sm ww-px-4 ww-py-2.5 ww-text-sm ww-leading-relaxed ww-flex ww-flex-col ww-gap-2",
1013
1725
  style: { backgroundColor: userColor, color: contrastColor },
1014
1726
  children: [
1015
- message.attachments && message.attachments.length > 0 && /* @__PURE__ */ jsx(SentAttachments, { attachments: message.attachments, contrastColor }),
1727
+ message.attachments && message.attachments.length > 0 && /* @__PURE__ */ jsx(
1728
+ SentAttachments,
1729
+ {
1730
+ attachments: message.attachments,
1731
+ contrastColor
1732
+ }
1733
+ ),
1016
1734
  textPart?.text && /* @__PURE__ */ jsx("span", { children: textPart.text })
1017
1735
  ]
1018
1736
  }
1019
1737
  ) });
1020
1738
  }
1021
1739
  const visibleToolParts = showThinking ? toolParts : [];
1022
- const isEmpty = !textPart?.text && visibleToolParts.length === 0 && pickerParts.length === 0;
1740
+ const isEmpty = !textPart?.text && visibleToolParts.length === 0 && pickerParts.length === 0 && planParts.length === 0;
1023
1741
  return /* @__PURE__ */ jsxs("div", { className: "ww-flex ww-gap-2.5 ww-items-start", children: [
1024
- /* @__PURE__ */ jsx(Avatar2, { style: { width: 28, height: 28, marginTop: 2, border: "1px solid rgba(0,0,0,0.08)" }, children: profilePicture ? /* @__PURE__ */ jsx(AvatarImage2, { src: profilePicture, alt: agentName }) : /* @__PURE__ */ jsx(AvatarFallback2, { style: { fontSize: 10, fontWeight: 600, backgroundColor: "var(--primary, #19191c)", color: "var(--primary-foreground, #fff)" }, children: agentName.slice(0, 2).toUpperCase() }) }),
1742
+ /* @__PURE__ */ jsx(
1743
+ Avatar2,
1744
+ {
1745
+ style: {
1746
+ width: 28,
1747
+ height: 28,
1748
+ marginTop: 2,
1749
+ border: "1px solid rgba(0,0,0,0.08)"
1750
+ },
1751
+ children: profilePicture ? /* @__PURE__ */ jsx(AvatarImage2, { src: profilePicture, alt: agentName }) : /* @__PURE__ */ jsx(
1752
+ AvatarFallback2,
1753
+ {
1754
+ style: {
1755
+ fontSize: 10,
1756
+ fontWeight: 600,
1757
+ backgroundColor: "var(--primary, #19191c)",
1758
+ color: "var(--primary-foreground, #fff)"
1759
+ },
1760
+ children: agentName.slice(0, 2).toUpperCase()
1761
+ }
1762
+ )
1763
+ }
1764
+ ),
1025
1765
  /* @__PURE__ */ jsxs("div", { className: "ww-flex ww-flex-col ww-gap-1.5 ww-min-w-0 ww-max-w-[82%]", children: [
1026
1766
  showThinking && reasoningPart && /* @__PURE__ */ jsx(ReasoningBlock, { text: reasoningPart.text }),
1027
- planParts.map((p) => /* @__PURE__ */ jsx(PlanCard, { part: p }, p.planId)),
1767
+ planParts.map((p) => /* @__PURE__ */ jsx(
1768
+ PlanCard,
1769
+ {
1770
+ part: p,
1771
+ onSend,
1772
+ disabled: isStreaming
1773
+ },
1774
+ p.planId
1775
+ )),
1028
1776
  visibleToolParts.map((t) => /* @__PURE__ */ jsx(ToolCallBadge, { part: t }, t.toolCallId)),
1029
1777
  pickerParts.map((p) => /* @__PURE__ */ jsx(
1030
1778
  PickerSelector,
@@ -1037,40 +1785,39 @@ function MessageBubble({
1037
1785
  p.pickerId
1038
1786
  )),
1039
1787
  isEmpty && isStreaming ? /* @__PURE__ */ jsx(ThinkingDots, {}) : textPart?.text ? /* @__PURE__ */ jsxs("div", { className: "ww-rounded-2xl ww-rounded-tl-sm ww-bg-muted ww-px-4 ww-py-2.5 ww-text-sm ww-leading-relaxed ww-overflow-hidden ww-prose ww-prose-sm ww-max-w-none prose-p:ww-my-1 prose-headings:ww-font-semibold prose-headings:ww-my-1.5 prose-ul:ww-my-1 prose-li:ww-my-0.5 prose-strong:ww-font-semibold", children: [
1040
- /* @__PURE__ */ jsx(
1041
- ReactMarkdown,
1042
- {
1043
- remarkPlugins: [remarkGfm],
1044
- components: {
1045
- p: ({ children }) => /* @__PURE__ */ jsx("p", { style: { margin: "4px 0" }, children }),
1046
- h1: ({ children }) => /* @__PURE__ */ jsx("p", { style: { margin: "6px 0", fontWeight: 700, fontSize: "1em" }, children }),
1047
- h2: ({ children }) => /* @__PURE__ */ jsx("p", { style: { margin: "6px 0", fontWeight: 700, fontSize: "1em" }, children }),
1048
- h3: ({ children }) => /* @__PURE__ */ jsx("p", { style: { margin: "6px 0", fontWeight: 600, fontSize: "0.95em" }, children }),
1049
- ul: ({ children }) => /* @__PURE__ */ jsx("ul", { style: { margin: "4px 0", paddingLeft: 16 }, children }),
1050
- ol: ({ children }) => /* @__PURE__ */ jsx("ol", { style: { margin: "4px 0", paddingLeft: 16 }, children }),
1051
- li: ({ children }) => /* @__PURE__ */ jsx("li", { style: { margin: "2px 0" }, children }),
1052
- strong: ({ children }) => /* @__PURE__ */ jsx("strong", { style: { fontWeight: 600 }, children }),
1053
- code: ({ children }) => /* @__PURE__ */ jsx("code", { style: { fontSize: "0.85em", backgroundColor: "rgba(0,0,0,0.07)", borderRadius: 4, padding: "1px 5px", fontFamily: "monospace" }, children }),
1054
- pre: ({ children }) => /* @__PURE__ */ jsx("pre", { style: { overflowX: "auto", margin: "6px 0", padding: "8px 10px", backgroundColor: "rgba(0,0,0,0.06)", borderRadius: 8, fontSize: "0.82em", fontFamily: "monospace" }, children }),
1055
- a: ({ href, children }) => /* @__PURE__ */ jsx("a", { href, target: "_blank", rel: "noopener noreferrer", style: { textDecoration: "underline", opacity: 0.75 }, children }),
1056
- table: ({ children }) => /* @__PURE__ */ jsx("div", { style: { overflowX: "auto", margin: "6px 0", borderRadius: 8, border: "1px solid rgba(0,0,0,0.1)" }, children: /* @__PURE__ */ jsx("table", { style: { borderCollapse: "collapse", minWidth: "100%", fontSize: "0.82em" }, children }) }),
1057
- thead: ({ children }) => /* @__PURE__ */ jsx("thead", { style: { backgroundColor: "rgba(0,0,0,0.04)" }, children }),
1058
- tbody: ({ children }) => /* @__PURE__ */ jsx("tbody", { children }),
1059
- tr: ({ children }) => /* @__PURE__ */ jsx("tr", { style: { borderBottom: "1px solid rgba(0,0,0,0.07)" }, children }),
1060
- th: ({ children }) => /* @__PURE__ */ jsx("th", { style: { padding: "6px 10px", textAlign: "left", fontWeight: 600, whiteSpace: "nowrap" }, children }),
1061
- td: ({ children }) => /* @__PURE__ */ jsx("td", { style: { padding: "5px 10px", verticalAlign: "top" }, children })
1062
- },
1063
- children: textPart.text
1064
- }
1065
- ),
1788
+ /* @__PURE__ */ jsx(MarkdownContent, { text: textPart.text }),
1066
1789
  isStreaming && /* @__PURE__ */ jsx("span", { className: "ww-inline-block ww-w-0.5 ww-h-3.5 ww-bg-foreground/60 ww-ml-0.5 ww-animate-pulse ww-align-middle" })
1067
1790
  ] }) : null
1068
1791
  ] })
1069
1792
  ] });
1070
1793
  }
1071
- var Avatar3 = ({ className, ...p }) => /* @__PURE__ */ jsx(AvatarPrimitive.Root, { className: ["ww-relative ww-flex ww-h-10 ww-w-10 ww-shrink-0 ww-overflow-hidden ww-rounded-full", className].filter(Boolean).join(" "), ...p });
1794
+ var Avatar3 = ({
1795
+ className,
1796
+ ...p
1797
+ }) => /* @__PURE__ */ jsx(
1798
+ AvatarPrimitive.Root,
1799
+ {
1800
+ className: [
1801
+ "ww-relative ww-flex ww-h-10 ww-w-10 ww-shrink-0 ww-overflow-hidden ww-rounded-full",
1802
+ className
1803
+ ].filter(Boolean).join(" "),
1804
+ ...p
1805
+ }
1806
+ );
1072
1807
  var AvatarImage3 = AvatarPrimitive.Image;
1073
- var AvatarFallback3 = ({ className, ...p }) => /* @__PURE__ */ jsx(AvatarPrimitive.Fallback, { className: ["ww-flex ww-h-full ww-w-full ww-items-center ww-justify-center ww-rounded-full ww-bg-muted", className].filter(Boolean).join(" "), ...p });
1808
+ var AvatarFallback3 = ({
1809
+ className,
1810
+ ...p
1811
+ }) => /* @__PURE__ */ jsx(
1812
+ AvatarPrimitive.Fallback,
1813
+ {
1814
+ className: [
1815
+ "ww-flex ww-h-full ww-w-full ww-items-center ww-justify-center ww-rounded-full ww-bg-muted",
1816
+ className
1817
+ ].filter(Boolean).join(" "),
1818
+ ...p
1819
+ }
1820
+ );
1074
1821
  function ChatMessages({
1075
1822
  messages,
1076
1823
  streaming,
@@ -1084,14 +1831,15 @@ function ChatMessages({
1084
1831
  onPickerSelect
1085
1832
  }) {
1086
1833
  const bottomRef = useRef(null);
1834
+ const greetText = initialMessages[0];
1087
1835
  const showGreeting = messages.length === 0;
1088
1836
  useEffect(() => {
1089
1837
  bottomRef.current?.scrollIntoView({ behavior: "smooth" });
1090
- }, [messages]);
1838
+ }, [messages, streaming]);
1091
1839
  return /* @__PURE__ */ jsxs("div", { className: "ww-flex-1 ww-flex ww-flex-col ww-overflow-y-auto ww-overscroll-contain", children: [
1092
- showGreeting && /* @__PURE__ */ jsxs("div", { className: "ww-flex ww-gap-2.5 ww-items-start ww-px-4 ww-pt-5", children: [
1840
+ showGreeting && greetText && /* @__PURE__ */ jsxs("div", { className: "ww-flex ww-gap-2.5 ww-items-start ww-px-4 ww-pt-5", children: [
1093
1841
  /* @__PURE__ */ jsx(Avatar3, { className: "ww-h-7 ww-w-7 ww-shrink-0 ww-mt-0.5 ww-border", children: profilePicture ? /* @__PURE__ */ jsx(AvatarImage3, { src: profilePicture, alt: agentName }) : /* @__PURE__ */ jsx(AvatarFallback3, { className: "ww-text-[10px] ww-font-semibold ww-bg-primary ww-text-primary-foreground", children: agentName.slice(0, 2).toUpperCase() }) }),
1094
- /* @__PURE__ */ jsx("div", { className: "ww-rounded-2xl ww-rounded-tl-sm ww-bg-muted ww-px-4 ww-py-2.5 ww-text-sm ww-leading-relaxed ww-max-w-[82%]", children: initialMessages[0] ?? "Hello! How can I help you today?" })
1842
+ /* @__PURE__ */ jsx("div", { className: "ww-rounded-2xl ww-rounded-tl-sm ww-bg-muted ww-px-4 ww-py-2.5 ww-text-sm ww-leading-relaxed ww-max-w-[82%]", children: greetText })
1095
1843
  ] }),
1096
1844
  showGreeting && /* @__PURE__ */ jsx("div", { className: "ww-flex-1" }),
1097
1845
  /* @__PURE__ */ jsx("div", { className: "ww-flex ww-flex-col ww-gap-4 ww-px-4 ww-py-4", children: messages.map((msg, i) => /* @__PURE__ */ jsx(
@@ -1103,10 +1851,47 @@ function ChatMessages({
1103
1851
  profilePicture,
1104
1852
  isStreaming: streaming && i === messages.length - 1 && msg.role === "assistant",
1105
1853
  showThinking,
1106
- onPickerSelect
1854
+ onPickerSelect,
1855
+ onSend: i === messages.length - 1 ? onSuggest : void 0
1107
1856
  },
1108
1857
  msg.id
1109
1858
  )) }),
1859
+ streaming && messages.length > 0 && messages[messages.length - 1]?.role !== "assistant" && /* @__PURE__ */ jsxs("div", { className: "ww-flex ww-gap-2.5 ww-items-start ww-px-4 ww-pb-2", children: [
1860
+ /* @__PURE__ */ jsx(Avatar3, { className: "ww-h-7 ww-w-7 ww-shrink-0 ww-mt-0.5 ww-border", children: profilePicture ? /* @__PURE__ */ jsx(AvatarImage3, { src: profilePicture, alt: agentName }) : /* @__PURE__ */ jsx(AvatarFallback3, { className: "ww-text-[10px] ww-font-semibold ww-bg-primary ww-text-primary-foreground", children: agentName.slice(0, 2).toUpperCase() }) }),
1861
+ /* @__PURE__ */ jsx("style", { children: `
1862
+ @keyframes wDotBounce {
1863
+ 0%, 80%, 100% { transform: translateY(0) scale(1); opacity: 0.35; }
1864
+ 40% { transform: translateY(-6px) scale(1.15); opacity: 1; }
1865
+ }
1866
+ ` }),
1867
+ /* @__PURE__ */ jsx(
1868
+ "div",
1869
+ {
1870
+ className: "ww-bg-muted",
1871
+ style: {
1872
+ display: "inline-flex",
1873
+ alignItems: "center",
1874
+ gap: 6,
1875
+ borderRadius: "18px 18px 18px 4px",
1876
+ padding: "13px 18px"
1877
+ },
1878
+ children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx(
1879
+ "span",
1880
+ {
1881
+ style: {
1882
+ display: "block",
1883
+ width: 7,
1884
+ height: 7,
1885
+ borderRadius: "50%",
1886
+ backgroundColor: "currentColor",
1887
+ animation: `wDotBounce 1.2s cubic-bezier(0.4,0,0.2,1) ${i * 0.16}s infinite`
1888
+ }
1889
+ },
1890
+ i
1891
+ ))
1892
+ }
1893
+ )
1894
+ ] }),
1110
1895
  showGreeting && suggestedMessages.length > 0 && /* @__PURE__ */ jsx("div", { className: "ww-flex ww-flex-wrap ww-gap-2 ww-px-4 ww-pb-4", children: suggestedMessages.map((msg, i) => /* @__PURE__ */ jsx(
1111
1896
  "button",
1112
1897
  {
@@ -1173,7 +1958,13 @@ function ChatInput({
1173
1958
  }
1174
1959
  ),
1175
1960
  /* @__PURE__ */ jsxs("div", { className: "ww-flex ww-flex-col ww-rounded-2xl ww-border ww-bg-background ww-shadow-sm focus-within:ww-ring-1 focus-within:ww-ring-ring/40 ww-transition-shadow", children: [
1176
- hasAttachments && attachments && attachments.length > 0 && /* @__PURE__ */ jsx("div", { className: "ww-px-2 ww-pt-2", children: /* @__PURE__ */ jsx(AttachmentChips, { attachments, onRemove: (id) => onRemoveAttachment?.(id) }) }),
1961
+ hasAttachments && attachments && attachments.length > 0 && /* @__PURE__ */ jsx("div", { className: "ww-px-2 ww-pt-2", children: /* @__PURE__ */ jsx(
1962
+ AttachmentChips,
1963
+ {
1964
+ attachments,
1965
+ onRemove: (id) => onRemoveAttachment?.(id)
1966
+ }
1967
+ ) }),
1177
1968
  hasAttachments && /* @__PURE__ */ jsx(
1178
1969
  "input",
1179
1970
  {
@@ -1263,7 +2054,10 @@ function ChatInput({
1263
2054
  "ww-h-7 ww-w-7 ww-shrink-0 ww-rounded-xl ww-flex ww-items-center ww-justify-center ww-transition-all ww-duration-200",
1264
2055
  hasText || streaming ? "ww-opacity-100 ww-shadow-sm" : "ww-opacity-30"
1265
2056
  ),
1266
- style: hasText || streaming ? { backgroundColor: accentColor, color: getContrastColor(accentColor) } : { backgroundColor: "transparent", color: "currentColor" },
2057
+ style: hasText || streaming ? {
2058
+ backgroundColor: accentColor,
2059
+ color: getContrastColor(accentColor)
2060
+ } : { backgroundColor: "transparent", color: "currentColor" },
1267
2061
  children: streaming ? /* @__PURE__ */ jsx(Loader2, { className: "ww-h-3.5 ww-w-3.5 ww-animate-spin" }) : /* @__PURE__ */ jsx(ArrowUp, { className: "ww-h-3.5 ww-w-3.5" })
1268
2062
  }
1269
2063
  )
@@ -1271,6 +2065,126 @@ function ChatInput({
1271
2065
  ] })
1272
2066
  ] });
1273
2067
  }
2068
+ function CustomControlBar({ onClose }) {
2069
+ const { localParticipant, isMicrophoneEnabled } = useLocalParticipant();
2070
+ const room = useRoomContext();
2071
+ const toggleMic = async () => {
2072
+ if (isMicrophoneEnabled) {
2073
+ await localParticipant.setMicrophoneEnabled(false);
2074
+ } else {
2075
+ await localParticipant.setMicrophoneEnabled(true);
2076
+ }
2077
+ };
2078
+ const disconnect = () => {
2079
+ room.disconnect();
2080
+ onClose();
2081
+ };
2082
+ return /* @__PURE__ */ jsxs("div", { className: "ww-flex ww-items-center ww-justify-center ww-gap-6", children: [
2083
+ /* @__PURE__ */ jsx(
2084
+ "button",
2085
+ {
2086
+ onClick: toggleMic,
2087
+ className: `ww-flex ww-items-center ww-justify-center ww-h-16 ww-w-16 ww-rounded-full ww-transition-all ww-duration-300 ${isMicrophoneEnabled ? "ww-bg-foreground/5 hover:ww-bg-foreground/10 ww-text-foreground" : "ww-bg-foreground/20 hover:ww-bg-foreground/30 ww-text-foreground"}`,
2088
+ title: isMicrophoneEnabled ? "Silenciar micr\xF3fono" : "Activar micr\xF3fono",
2089
+ children: isMicrophoneEnabled ? /* @__PURE__ */ jsx(Mic, { className: "ww-w-6 ww-h-6" }) : /* @__PURE__ */ jsx(MicOff, { className: "ww-w-6 ww-h-6" })
2090
+ }
2091
+ ),
2092
+ /* @__PURE__ */ jsx(
2093
+ "button",
2094
+ {
2095
+ onClick: disconnect,
2096
+ className: "ww-flex ww-items-center ww-justify-center ww-h-16 ww-w-16 ww-rounded-full ww-text-white ww-transition-all ww-duration-300 ww-shadow-lg hover:ww-shadow-xl hover:-ww-translate-y-1",
2097
+ style: { backgroundColor: "#ef4444" },
2098
+ title: "Desconectar",
2099
+ children: /* @__PURE__ */ jsx(PhoneOff, { className: "ww-w-6 ww-h-6" })
2100
+ }
2101
+ )
2102
+ ] });
2103
+ }
2104
+ function ChatGPTOrb() {
2105
+ const { state, audioTrack } = useVoiceAssistant();
2106
+ const isSpeaking = state === "speaking";
2107
+ const isListening = state === "listening";
2108
+ const getGradient = () => {
2109
+ if (isSpeaking) {
2110
+ return "linear-gradient(135deg, rgba(59, 130, 246, 0.9), rgba(147, 51, 234, 0.9))";
2111
+ } else if (isListening) {
2112
+ return "linear-gradient(135deg, rgba(16, 185, 129, 0.85), rgba(20, 184, 166, 0.85))";
2113
+ }
2114
+ return "linear-gradient(135deg, rgba(25, 25, 28, 0.8), rgba(25, 25, 28, 0.95))";
2115
+ };
2116
+ const getGlow = () => {
2117
+ if (isSpeaking) return "ww-bg-purple-500/30 ww-animate-ping";
2118
+ if (isListening) return "ww-bg-emerald-500/20 ww-animate-pulse";
2119
+ return "ww-bg-transparent ww-scale-90";
2120
+ };
2121
+ const getRing = () => {
2122
+ if (isSpeaking) return "ww-bg-purple-500/20 ww-scale-110";
2123
+ if (isListening) return "ww-bg-emerald-500/10 ww-scale-105";
2124
+ return "ww-bg-foreground/5 ww-scale-100";
2125
+ };
2126
+ return /* @__PURE__ */ jsxs("div", { className: "ww-relative ww-w-48 ww-h-48 ww-flex ww-items-center ww-justify-center", children: [
2127
+ /* @__PURE__ */ jsx(
2128
+ "div",
2129
+ {
2130
+ className: `ww-absolute ww-inset-0 ww-rounded-full ww-transition-all ww-duration-1000 ${getGlow()}`
2131
+ }
2132
+ ),
2133
+ /* @__PURE__ */ jsx(
2134
+ "div",
2135
+ {
2136
+ className: `ww-absolute ww-inset-4 ww-rounded-full ww-transition-all ww-duration-500 ${getRing()}`
2137
+ }
2138
+ ),
2139
+ /* @__PURE__ */ jsx(
2140
+ "div",
2141
+ {
2142
+ className: "ww-absolute ww-inset-8 ww-rounded-full ww-shadow-2xl ww-transition-all ww-duration-700 ww-flex ww-items-center ww-justify-center ww-overflow-hidden",
2143
+ style: {
2144
+ background: getGradient(),
2145
+ transform: isSpeaking ? "scale(1.05)" : "scale(1)"
2146
+ },
2147
+ children: /* @__PURE__ */ jsx("div", { className: "ww-opacity-90 ww-flex ww-items-center ww-justify-center ww-w-full ww-h-full", children: /* @__PURE__ */ jsx(
2148
+ BarVisualizer,
2149
+ {
2150
+ state,
2151
+ barCount: 5,
2152
+ trackRef: audioTrack,
2153
+ options: { minHeight: 6 },
2154
+ style: { color: "rgba(255, 255, 255, 0.95)" }
2155
+ }
2156
+ ) })
2157
+ }
2158
+ )
2159
+ ] });
2160
+ }
2161
+ function AssistantVisualizer({ onClose }) {
2162
+ const { state } = useVoiceAssistant();
2163
+ return /* @__PURE__ */ jsxs("div", { className: "ww-flex ww-flex-col ww-items-center ww-justify-center ww-h-full ww-w-full", children: [
2164
+ /* @__PURE__ */ jsx("div", { className: "ww-flex-1 ww-flex ww-items-center ww-justify-center", children: /* @__PURE__ */ jsx(ChatGPTOrb, {}) }),
2165
+ /* @__PURE__ */ jsxs("div", { className: "ww-h-40 ww-flex ww-flex-col ww-items-center ww-justify-center ww-gap-8 ww-mb-12", children: [
2166
+ /* @__PURE__ */ jsx("div", { className: "ww-text-sm ww-font-medium ww-tracking-wide ww-text-foreground/60 ww-uppercase", children: state === "connecting" || state === "initializing" ? "Conectando..." : state === "listening" ? "Escuchando..." : state === "speaking" ? "Hablando..." : "Listo" }),
2167
+ /* @__PURE__ */ jsx(CustomControlBar, { onClose })
2168
+ ] })
2169
+ ] });
2170
+ }
2171
+ function VoiceOverlay({ token, serverUrl, onClose }) {
2172
+ return /* @__PURE__ */ jsx("div", { className: "ww-absolute ww-inset-0 ww-z-[100] ww-flex ww-flex-col ww-bg-background/95 ww-backdrop-blur-xl", children: /* @__PURE__ */ jsxs(
2173
+ LiveKitRoom,
2174
+ {
2175
+ token,
2176
+ serverUrl,
2177
+ connect: true,
2178
+ audio: true,
2179
+ video: false,
2180
+ className: "ww-flex-1",
2181
+ children: [
2182
+ /* @__PURE__ */ jsx(RoomAudioRenderer, {}),
2183
+ /* @__PURE__ */ jsx(AssistantVisualizer, { onClose })
2184
+ ]
2185
+ }
2186
+ ) });
2187
+ }
1274
2188
  function ChatWidget({
1275
2189
  agentId,
1276
2190
  workspaceId,
@@ -1296,13 +2210,27 @@ function ChatWidget({
1296
2210
  enableVoice = false,
1297
2211
  voiceAutoSend = false,
1298
2212
  enableAttachments = false,
2213
+ customBackend,
1299
2214
  className,
1300
2215
  onClose,
1301
2216
  onReset,
1302
2217
  onExpand,
1303
- expanded
2218
+ expanded,
2219
+ embedded = false,
2220
+ envId,
2221
+ onDebugTrace
1304
2222
  }) {
1305
- const chat = useChat({ agentId, workspaceId, source, userContext, persist, onNavigate, playgroundOverrides });
2223
+ const chat = useChat({
2224
+ agentId,
2225
+ workspaceId,
2226
+ envId,
2227
+ source,
2228
+ userContext,
2229
+ persist,
2230
+ onNavigate,
2231
+ playgroundOverrides,
2232
+ customBackend
2233
+ });
1306
2234
  const voice = useVoice({
1307
2235
  agentId,
1308
2236
  onTranscript: (text) => {
@@ -1314,29 +2242,47 @@ function ChatWidget({
1314
2242
  }
1315
2243
  });
1316
2244
  const attachmentHook = useAttachments({ agentId });
2245
+ const debugTraceLenRef = useRef(0);
2246
+ useEffect(() => {
2247
+ if (!onDebugTrace || chat.debugTraces.length <= debugTraceLenRef.current)
2248
+ return;
2249
+ for (let i = debugTraceLenRef.current; i < chat.debugTraces.length; i++) {
2250
+ onDebugTrace(chat.debugTraces[i]);
2251
+ }
2252
+ debugTraceLenRef.current = chat.debugTraces.length;
2253
+ }, [chat.debugTraces, onDebugTrace]);
1317
2254
  const [isDragOver, setIsDragOver] = useState(false);
1318
- const handleDragOver = useCallback((e) => {
1319
- if (!enableAttachments) return;
1320
- e.preventDefault();
1321
- e.stopPropagation();
1322
- if (e.dataTransfer.types.includes("Files")) setIsDragOver(true);
1323
- }, [enableAttachments]);
1324
- const handleDragLeave = useCallback((e) => {
1325
- if (!enableAttachments) return;
1326
- e.preventDefault();
1327
- e.stopPropagation();
1328
- if (!e.currentTarget.contains(e.relatedTarget)) {
2255
+ const handleDragOver = useCallback(
2256
+ (e) => {
2257
+ if (!enableAttachments) return;
2258
+ e.preventDefault();
2259
+ e.stopPropagation();
2260
+ if (e.dataTransfer.types.includes("Files")) setIsDragOver(true);
2261
+ },
2262
+ [enableAttachments]
2263
+ );
2264
+ const handleDragLeave = useCallback(
2265
+ (e) => {
2266
+ if (!enableAttachments) return;
2267
+ e.preventDefault();
2268
+ e.stopPropagation();
2269
+ if (!e.currentTarget.contains(e.relatedTarget)) {
2270
+ setIsDragOver(false);
2271
+ }
2272
+ },
2273
+ [enableAttachments]
2274
+ );
2275
+ const handleDrop = useCallback(
2276
+ (e) => {
2277
+ if (!enableAttachments) return;
2278
+ e.preventDefault();
2279
+ e.stopPropagation();
1329
2280
  setIsDragOver(false);
1330
- }
1331
- }, [enableAttachments]);
1332
- const handleDrop = useCallback((e) => {
1333
- if (!enableAttachments) return;
1334
- e.preventDefault();
1335
- e.stopPropagation();
1336
- setIsDragOver(false);
1337
- const files = e.dataTransfer.files;
1338
- if (files.length > 0) attachmentHook.attach(files);
1339
- }, [enableAttachments, attachmentHook]);
2281
+ const files = e.dataTransfer.files;
2282
+ if (files.length > 0) attachmentHook.attach(files);
2283
+ },
2284
+ [enableAttachments, attachmentHook]
2285
+ );
1340
2286
  const handleSend = () => {
1341
2287
  const payloads = attachmentHook.readyPayloads;
1342
2288
  if (payloads.length > 0) {
@@ -1355,6 +2301,7 @@ function ChatWidget({
1355
2301
  };
1356
2302
  const isDark = theme === "dark";
1357
2303
  const cssVars = {
2304
+ fontFamily: 'ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
1358
2305
  colorScheme: theme,
1359
2306
  // Inline style has higher specificity than any host-site stylesheet,
1360
2307
  // so background-color set here cannot be overridden by host CSS.
@@ -1377,7 +2324,8 @@ function ChatWidget({
1377
2324
  "div",
1378
2325
  {
1379
2326
  className: cn(
1380
- "wallavi-widget ww-flex ww-flex-col ww-overflow-hidden ww-rounded-2xl ww-border ww-shadow-xl ww-bg-background ww-h-full ww-relative",
2327
+ "wallavi-widget ww-flex ww-flex-col ww-overflow-hidden ww-bg-background ww-h-full ww-relative ww-overscroll-none",
2328
+ !embedded && "ww-rounded-2xl ww-border ww-shadow-xl",
1381
2329
  isDragOver && "ww-ring-2 ww-ring-inset ww-ring-primary/60",
1382
2330
  className
1383
2331
  ),
@@ -1402,7 +2350,32 @@ function ChatWidget({
1402
2350
  onReset: handleReset,
1403
2351
  onClose: hideCloseButton ? void 0 : onClose,
1404
2352
  onExpand,
1405
- expanded
2353
+ expanded,
2354
+ onCall: enableVoice && chat.voiceCall ? () => chat.voiceCall?.start() : void 0,
2355
+ isCallLoading: chat.voiceCall?.loading
2356
+ }
2357
+ ),
2358
+ chat.voiceCall?.active && chat.voiceCall.token && chat.voiceCall.serverUrl && /* @__PURE__ */ jsx(
2359
+ VoiceOverlay,
2360
+ {
2361
+ token: chat.voiceCall.token,
2362
+ serverUrl: chat.voiceCall.serverUrl,
2363
+ onClose: chat.voiceCall.stop,
2364
+ accentColor: userMessageColor
2365
+ }
2366
+ ),
2367
+ customBackend?.mode === "human" && /* @__PURE__ */ jsxs(
2368
+ "div",
2369
+ {
2370
+ className: "ww-shrink-0 ww-flex ww-items-center ww-gap-2 ww-px-4 ww-py-1.5 ww-border-b",
2371
+ style: { backgroundColor: `${userMessageColor}0d` },
2372
+ children: [
2373
+ /* @__PURE__ */ jsxs("span", { className: "ww-relative ww-flex ww-h-2 ww-w-2 ww-shrink-0", children: [
2374
+ /* @__PURE__ */ jsx("span", { className: "ww-animate-ping ww-absolute ww-inline-flex ww-h-full ww-w-full ww-rounded-full ww-bg-emerald-400 ww-opacity-75" }),
2375
+ /* @__PURE__ */ jsx("span", { className: "ww-relative ww-inline-flex ww-rounded-full ww-h-2 ww-w-2 ww-bg-emerald-500" })
2376
+ ] }),
2377
+ /* @__PURE__ */ jsx("span", { className: "ww-text-xs ww-font-medium ww-text-foreground/60", children: "Hablando con un agente" })
2378
+ ]
1406
2379
  }
1407
2380
  ),
1408
2381
  /* @__PURE__ */ jsx(
@@ -1420,6 +2393,99 @@ function ChatWidget({
1420
2393
  onPickerSelect: chat.selectPickerOption
1421
2394
  }
1422
2395
  ),
2396
+ customBackend?.footerAction && /* @__PURE__ */ jsx("div", { className: "ww-shrink-0 ww-px-3 ww-py-1.5 ww-border-t ww-bg-background", children: /* @__PURE__ */ jsxs(
2397
+ "button",
2398
+ {
2399
+ onClick: () => void customBackend.footerAction.onClick(),
2400
+ disabled: customBackend.footerAction.loading,
2401
+ style: customBackend.footerAction.icon === "human" ? {
2402
+ backgroundColor: userMessageColor,
2403
+ color: "#ffffff",
2404
+ boxShadow: `0 2px 12px ${userMessageColor}55`
2405
+ } : {
2406
+ borderColor: `${userMessageColor}35`,
2407
+ backgroundColor: `${userMessageColor}0d`,
2408
+ color: userMessageColor
2409
+ },
2410
+ className: [
2411
+ "ww-w-full ww-flex ww-items-center ww-gap-2 ww-rounded-full ww-px-3.5 ww-py-1.5 ww-text-xs ww-font-semibold ww-transition-all",
2412
+ "hover:ww-opacity-85 active:ww-scale-[0.98] disabled:ww-opacity-50 disabled:ww-pointer-events-none",
2413
+ customBackend.footerAction.icon === "human" ? "" : "ww-border"
2414
+ ].join(" "),
2415
+ children: [
2416
+ customBackend.footerAction.loading ? /* @__PURE__ */ jsxs(
2417
+ "svg",
2418
+ {
2419
+ className: "ww-animate-spin ww-w-3.5 ww-h-3.5 ww-shrink-0",
2420
+ viewBox: "0 0 24 24",
2421
+ fill: "none",
2422
+ children: [
2423
+ /* @__PURE__ */ jsx(
2424
+ "circle",
2425
+ {
2426
+ className: "ww-opacity-25",
2427
+ cx: "12",
2428
+ cy: "12",
2429
+ r: "10",
2430
+ stroke: "currentColor",
2431
+ strokeWidth: "3"
2432
+ }
2433
+ ),
2434
+ /* @__PURE__ */ jsx(
2435
+ "path",
2436
+ {
2437
+ className: "ww-opacity-75",
2438
+ fill: "currentColor",
2439
+ d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"
2440
+ }
2441
+ )
2442
+ ]
2443
+ }
2444
+ ) : customBackend.footerAction.icon === "ai" ? /* @__PURE__ */ jsx(
2445
+ "svg",
2446
+ {
2447
+ className: "ww-w-3.5 ww-h-3.5 ww-shrink-0",
2448
+ viewBox: "0 0 24 24",
2449
+ fill: "none",
2450
+ stroke: "currentColor",
2451
+ strokeWidth: "2",
2452
+ strokeLinecap: "round",
2453
+ strokeLinejoin: "round",
2454
+ children: /* @__PURE__ */ jsx("path", { d: "m12 3-1.912 5.813a2 2 0 0 1-1.275 1.275L3 12l5.813 1.912a2 2 0 0 1 1.275 1.275L12 21l1.912-5.813a2 2 0 0 1 1.275-1.275L21 12l-5.813-1.912a2 2 0 0 1-1.275-1.275L12 3Z" })
2455
+ }
2456
+ ) : /* @__PURE__ */ jsxs(
2457
+ "svg",
2458
+ {
2459
+ className: "ww-w-3.5 ww-h-3.5 ww-shrink-0",
2460
+ viewBox: "0 0 24 24",
2461
+ fill: "none",
2462
+ stroke: "currentColor",
2463
+ strokeWidth: "2",
2464
+ strokeLinecap: "round",
2465
+ strokeLinejoin: "round",
2466
+ children: [
2467
+ /* @__PURE__ */ jsx("path", { d: "M3 18v-6a9 9 0 0 1 18 0v6" }),
2468
+ /* @__PURE__ */ jsx("path", { d: "M21 19a2 2 0 0 1-2 2h-1a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2h3zM3 19a2 2 0 0 0 2 2h1a2 2 0 0 0 2-2v-3a2 2 0 0 0-2-2H3z" })
2469
+ ]
2470
+ }
2471
+ ),
2472
+ /* @__PURE__ */ jsx("span", { className: "ww-flex-1 ww-text-left ww-truncate", children: customBackend.footerAction.label }),
2473
+ /* @__PURE__ */ jsx(
2474
+ "svg",
2475
+ {
2476
+ className: "ww-shrink-0 ww-w-3 ww-h-3 ww-opacity-60",
2477
+ viewBox: "0 0 24 24",
2478
+ fill: "none",
2479
+ stroke: "currentColor",
2480
+ strokeWidth: "2.5",
2481
+ strokeLinecap: "round",
2482
+ strokeLinejoin: "round",
2483
+ children: /* @__PURE__ */ jsx("path", { d: "M9 18l6-6-6-6" })
2484
+ }
2485
+ )
2486
+ ]
2487
+ }
2488
+ ) }),
1423
2489
  /* @__PURE__ */ jsx(
1424
2490
  ChatInput,
1425
2491
  {
@@ -1444,7 +2510,7 @@ function ChatWidget({
1444
2510
  } : {}
1445
2511
  }
1446
2512
  ),
1447
- watermark && /* @__PURE__ */ jsxs("footer", { className: "ww-shrink-0 ww-flex ww-items-center ww-justify-center ww-gap-1.5 ww-bg-muted/50 ww-py-1.5 ww-border-t", children: [
2513
+ watermark && /* @__PURE__ */ jsxs("footer", { className: "ww-shrink-0 ww-flex ww-items-center ww-justify-center ww-gap-1.5 ww-bg-muted/50 ww-py-1.5 ww-border-t ww-touch-none", children: [
1448
2514
  /* @__PURE__ */ jsxs(
1449
2515
  "a",
1450
2516
  {
@@ -1494,19 +2560,27 @@ function useAutoConfig(agentId, enabled) {
1494
2560
  if (cancelled) return;
1495
2561
  const cfg = body?.data ?? {};
1496
2562
  const remote = {};
1497
- if (cfg.profilePicture != null) remote.profilePicture = cfg.profilePicture;
1498
- if (cfg.displayName != null) remote.displayName = cfg.displayName;
2563
+ if (cfg.profilePicture != null)
2564
+ remote.profilePicture = cfg.profilePicture;
2565
+ if (cfg.displayName != null)
2566
+ remote.displayName = cfg.displayName;
1499
2567
  if (cfg.theme) remote.theme = cfg.theme;
1500
- if (cfg.userMessageColor) remote.userMessageColor = cfg.userMessageColor;
2568
+ if (cfg.userMessageColor)
2569
+ remote.userMessageColor = cfg.userMessageColor;
1501
2570
  if (Array.isArray(cfg.initialMessages) && cfg.initialMessages.length > 0)
1502
2571
  remote.initialMessages = cfg.initialMessages;
1503
2572
  if (Array.isArray(cfg.suggestedMessages))
1504
2573
  remote.suggestedMessages = cfg.suggestedMessages;
1505
- if (cfg.messagePlaceholder != null) remote.messagePlaceholder = cfg.messagePlaceholder;
2574
+ if (cfg.messagePlaceholder != null)
2575
+ remote.messagePlaceholder = cfg.messagePlaceholder;
1506
2576
  if (cfg.watermark != null) remote.watermark = cfg.watermark;
1507
2577
  if (cfg.footer != null) remote.footer = cfg.footer;
1508
- if (cfg.showThinking != null) remote.showThinking = cfg.showThinking;
1509
- if (cfg.regenerateMessage != null) remote.regenerateMessage = cfg.regenerateMessage;
2578
+ if (cfg.showThinking != null)
2579
+ remote.showThinking = cfg.showThinking;
2580
+ if (cfg.regenerateMessage != null)
2581
+ remote.regenerateMessage = cfg.regenerateMessage;
2582
+ if (cfg.enableVoice != null)
2583
+ remote.enableVoice = cfg.enableVoice;
1510
2584
  setResult({
1511
2585
  remoteConfig: remote,
1512
2586
  bubbleIconUrl: cfg.chatIcon || cfg.profilePicture || void 0,
@@ -1527,9 +2601,391 @@ function useAutoConfig(agentId, enabled) {
1527
2601
  }, [agentId, enabled]);
1528
2602
  return result;
1529
2603
  }
2604
+ function toWidgetMsg(m) {
2605
+ return {
2606
+ id: m.id,
2607
+ role: m.role === "customer" ? "user" : "assistant",
2608
+ parts: [{ type: "text", text: m.content }],
2609
+ attachments: m.metadata?.attachments ?? void 0
2610
+ };
2611
+ }
2612
+ function useSupportChat({
2613
+ inboxToken,
2614
+ apiBase = "https://app.wallavi.com",
2615
+ requestHumanLabel = "Hablar con un agente",
2616
+ returnToAiLabel
2617
+ }) {
2618
+ const enabled = Boolean(inboxToken) && inboxToken !== "__disabled__";
2619
+ const STORAGE_KEY = `wlv_support_${inboxToken}`;
2620
+ const base = apiBase.replace(/\/$/, "");
2621
+ const [session, setSession] = useState(() => {
2622
+ if (typeof window === "undefined") return null;
2623
+ try {
2624
+ const saved = localStorage.getItem(STORAGE_KEY);
2625
+ if (saved)
2626
+ return JSON.parse(saved).session ?? null;
2627
+ } catch {
2628
+ }
2629
+ return null;
2630
+ });
2631
+ const [rawMessages, setRawMessages] = useState([]);
2632
+ const [sending, setSending] = useState(false);
2633
+ const [isAiMode, setIsAiMode] = useState(() => {
2634
+ if (typeof window === "undefined") return false;
2635
+ try {
2636
+ const saved = localStorage.getItem(STORAGE_KEY);
2637
+ if (saved)
2638
+ return JSON.parse(saved).isAiMode ?? false;
2639
+ } catch {
2640
+ }
2641
+ return false;
2642
+ });
2643
+ const [agentTyping, setAgentTyping] = useState(false);
2644
+ const [requestingHuman, setRequestingHuman] = useState(false);
2645
+ const [returningToAi, setReturningToAi] = useState(false);
2646
+ const [voiceActive, setVoiceActive] = useState(false);
2647
+ const [voiceToken, setVoiceToken] = useState(null);
2648
+ const [voiceServerUrl, setVoiceServerUrl] = useState(null);
2649
+ const [voiceLoading, setVoiceLoading] = useState(false);
2650
+ const [voiceError, setVoiceError] = useState(null);
2651
+ const typingTimerRef = useRef(null);
2652
+ useEffect(() => {
2653
+ if (!enabled) return;
2654
+ try {
2655
+ const saved = localStorage.getItem(STORAGE_KEY);
2656
+ if (saved) JSON.parse(saved);
2657
+ } catch {
2658
+ localStorage.removeItem(STORAGE_KEY);
2659
+ setSession(null);
2660
+ setIsAiMode(false);
2661
+ }
2662
+ }, [STORAGE_KEY, enabled]);
2663
+ const loadMessages = useCallback(
2664
+ async (sess) => {
2665
+ try {
2666
+ const res = await fetch(
2667
+ `${base}/api/support-chat/${sess.conversationId}/messages`,
2668
+ {
2669
+ headers: { "x-visitor-token": sess.visitorToken }
2670
+ }
2671
+ );
2672
+ const data = await res.json();
2673
+ if (Array.isArray(data.data)) setRawMessages(data.data);
2674
+ } catch {
2675
+ }
2676
+ },
2677
+ [base]
2678
+ );
2679
+ useEffect(() => {
2680
+ if (!enabled || !session) return;
2681
+ void loadMessages(session);
2682
+ let ablyClient = null;
2683
+ const setupAbly = async () => {
2684
+ try {
2685
+ const res = await fetch(
2686
+ `${base}/api/support-chat/${session.conversationId}/ably-token`,
2687
+ {
2688
+ headers: { "x-visitor-token": session.visitorToken }
2689
+ }
2690
+ );
2691
+ if (!res.ok) return;
2692
+ const { tokenRequest, channel: channelName } = await res.json();
2693
+ const AblyLib = (await import('ably')).default;
2694
+ ablyClient = new AblyLib.Realtime({
2695
+ authCallback: (_, cb) => cb(null, tokenRequest)
2696
+ });
2697
+ const ch = ablyClient.channels.get(channelName);
2698
+ ch.subscribe((msg) => {
2699
+ const event = { type: msg.name, ...msg.data };
2700
+ if (event.type === "message.created" && event.message) {
2701
+ setRawMessages((prev) => {
2702
+ if (prev.some((m) => m.id === event.message.id))
2703
+ return prev;
2704
+ return [
2705
+ ...prev.filter((m) => !m.id.startsWith("tmp_")),
2706
+ event.message
2707
+ ];
2708
+ });
2709
+ } else if (event.type === "agent.typing") {
2710
+ setAgentTyping(!!event.isTyping);
2711
+ if (event.isTyping) {
2712
+ if (typingTimerRef.current) clearTimeout(typingTimerRef.current);
2713
+ typingTimerRef.current = setTimeout(
2714
+ () => setAgentTyping(false),
2715
+ 6e3
2716
+ );
2717
+ } else {
2718
+ if (typingTimerRef.current) clearTimeout(typingTimerRef.current);
2719
+ }
2720
+ } else if (event.type === "mode.changed") {
2721
+ const newIsAi = event.mode === "auto";
2722
+ setIsAiMode(newIsAi);
2723
+ setSession((s) => {
2724
+ if (s)
2725
+ localStorage.setItem(
2726
+ STORAGE_KEY,
2727
+ JSON.stringify({ session: s, isAiMode: newIsAi })
2728
+ );
2729
+ return s;
2730
+ });
2731
+ }
2732
+ });
2733
+ } catch {
2734
+ }
2735
+ };
2736
+ void setupAbly();
2737
+ const pollId = setInterval(() => void loadMessages(session), 3e4);
2738
+ return () => {
2739
+ if (ablyClient)
2740
+ try {
2741
+ ablyClient.close();
2742
+ } catch {
2743
+ }
2744
+ clearInterval(pollId);
2745
+ if (typingTimerRef.current) clearTimeout(typingTimerRef.current);
2746
+ };
2747
+ }, [session, STORAGE_KEY, base, loadMessages]);
2748
+ const send = useCallback(
2749
+ async (text, attachments) => {
2750
+ if (!text.trim() && !attachments?.length) return;
2751
+ if (sending) return;
2752
+ setSending(true);
2753
+ if (!session) {
2754
+ setRawMessages([
2755
+ {
2756
+ id: `tmp_${Date.now()}`,
2757
+ role: "customer",
2758
+ content: text || `[${attachments?.length ?? 0} archivo(s)]`,
2759
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
2760
+ metadata: attachments?.length ? { attachments } : void 0
2761
+ }
2762
+ ]);
2763
+ try {
2764
+ const res = await fetch(`${base}/api/support-chat/start`, {
2765
+ method: "POST",
2766
+ headers: { "Content-Type": "application/json" },
2767
+ body: JSON.stringify({
2768
+ inboxToken,
2769
+ message: text || `[${attachments.length} archivo(s)]`,
2770
+ attachments
2771
+ })
2772
+ });
2773
+ const data = await res.json();
2774
+ if (data.data) {
2775
+ const sess = {
2776
+ conversationId: data.data.conversationId,
2777
+ visitorToken: data.data.visitorToken
2778
+ };
2779
+ const ai = data.data.isAiMode ?? false;
2780
+ localStorage.setItem(
2781
+ STORAGE_KEY,
2782
+ JSON.stringify({ session: sess, isAiMode: ai })
2783
+ );
2784
+ setSession(sess);
2785
+ setIsAiMode(ai);
2786
+ if (Array.isArray(data.data.messages)) {
2787
+ setRawMessages(data.data.messages);
2788
+ }
2789
+ }
2790
+ } catch {
2791
+ }
2792
+ } else {
2793
+ setRawMessages((prev) => [
2794
+ ...prev,
2795
+ {
2796
+ id: `tmp_${Date.now()}`,
2797
+ role: "customer",
2798
+ content: text || `[${attachments.length} archivo(s)]`,
2799
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
2800
+ metadata: attachments?.length ? { attachments } : void 0
2801
+ }
2802
+ ]);
2803
+ try {
2804
+ const body = { content: text };
2805
+ if (attachments?.length) body.attachments = attachments;
2806
+ const res = await fetch(
2807
+ `${base}/api/support-chat/${session.conversationId}/messages`,
2808
+ {
2809
+ method: "POST",
2810
+ headers: {
2811
+ "Content-Type": "application/json",
2812
+ "x-visitor-token": session.visitorToken
2813
+ },
2814
+ body: JSON.stringify(body)
2815
+ }
2816
+ );
2817
+ const data = await res.json();
2818
+ if (data.data?.aiReply) {
2819
+ setRawMessages((prev) => {
2820
+ const withoutTemp = prev.filter((m) => !m.id.startsWith("tmp_"));
2821
+ if (withoutTemp.some((m) => m.id === data.data.aiReply.id))
2822
+ return withoutTemp;
2823
+ return [...withoutTemp, data.data.aiReply];
2824
+ });
2825
+ }
2826
+ } catch {
2827
+ }
2828
+ }
2829
+ setSending(false);
2830
+ },
2831
+ [session, sending, inboxToken, base, STORAGE_KEY]
2832
+ );
2833
+ const requestHuman = useCallback(async () => {
2834
+ if (!session || !isAiMode || requestingHuman) return;
2835
+ setRequestingHuman(true);
2836
+ try {
2837
+ await fetch(`${base}/api/support-chat/${session.conversationId}/mode`, {
2838
+ method: "PATCH",
2839
+ headers: {
2840
+ "Content-Type": "application/json",
2841
+ "x-visitor-token": session.visitorToken
2842
+ },
2843
+ body: JSON.stringify({ mode: "human" })
2844
+ });
2845
+ setIsAiMode(false);
2846
+ localStorage.setItem(
2847
+ STORAGE_KEY,
2848
+ JSON.stringify({ session, isAiMode: false })
2849
+ );
2850
+ } catch {
2851
+ }
2852
+ setRequestingHuman(false);
2853
+ }, [session, isAiMode, requestingHuman, base, STORAGE_KEY]);
2854
+ const returnToAi = useCallback(async () => {
2855
+ if (!session || isAiMode || returningToAi) return;
2856
+ setReturningToAi(true);
2857
+ try {
2858
+ await fetch(`${base}/api/support-chat/${session.conversationId}/mode`, {
2859
+ method: "PATCH",
2860
+ headers: {
2861
+ "Content-Type": "application/json",
2862
+ "x-visitor-token": session.visitorToken
2863
+ },
2864
+ body: JSON.stringify({ mode: "auto" })
2865
+ });
2866
+ setIsAiMode(true);
2867
+ localStorage.setItem(
2868
+ STORAGE_KEY,
2869
+ JSON.stringify({ session, isAiMode: true })
2870
+ );
2871
+ } catch {
2872
+ }
2873
+ setReturningToAi(false);
2874
+ }, [session, isAiMode, returningToAi, base, STORAGE_KEY]);
2875
+ const startVoiceCall = useCallback(async () => {
2876
+ if (!session || !isAiMode) return;
2877
+ setVoiceLoading(true);
2878
+ setVoiceError(null);
2879
+ try {
2880
+ const res = await fetch(
2881
+ `${base}/api/support-chat/${session.conversationId}/livekit-token`,
2882
+ {
2883
+ headers: { "x-visitor-token": session.visitorToken }
2884
+ }
2885
+ );
2886
+ const data = await res.json();
2887
+ if (!res.ok) throw new Error(data.error || "Failed to start call");
2888
+ setVoiceToken(data.token);
2889
+ setVoiceServerUrl(data.url);
2890
+ setVoiceActive(true);
2891
+ } catch (err) {
2892
+ setVoiceError(err.message);
2893
+ }
2894
+ setVoiceLoading(false);
2895
+ }, [session, isAiMode, base]);
2896
+ const stopVoiceCall = useCallback(() => {
2897
+ setVoiceActive(false);
2898
+ setVoiceToken(null);
2899
+ setVoiceServerUrl(null);
2900
+ }, []);
2901
+ const reset = useCallback(() => {
2902
+ localStorage.removeItem(STORAGE_KEY);
2903
+ setSession(null);
2904
+ setRawMessages([]);
2905
+ setIsAiMode(false);
2906
+ setAgentTyping(false);
2907
+ setRequestingHuman(false);
2908
+ setReturningToAi(false);
2909
+ stopVoiceCall();
2910
+ }, [STORAGE_KEY, stopVoiceCall]);
2911
+ const messages = useMemo(
2912
+ () => rawMessages.filter((m) => m.role !== "system").map(toWidgetMsg),
2913
+ [rawMessages]
2914
+ );
2915
+ const footerAction = useMemo(() => {
2916
+ if (isAiMode) {
2917
+ return {
2918
+ label: requestHumanLabel,
2919
+ sublabel: "Te conectamos con un agente disponible",
2920
+ icon: "human",
2921
+ onClick: requestHuman,
2922
+ loading: requestingHuman
2923
+ };
2924
+ }
2925
+ if (!isAiMode && session && returnToAiLabel) {
2926
+ return {
2927
+ label: returnToAiLabel,
2928
+ sublabel: "Respuesta instant\xE1nea con inteligencia artificial",
2929
+ icon: "ai",
2930
+ onClick: returnToAi,
2931
+ loading: returningToAi
2932
+ };
2933
+ }
2934
+ return void 0;
2935
+ }, [
2936
+ isAiMode,
2937
+ session,
2938
+ requestHumanLabel,
2939
+ requestHuman,
2940
+ requestingHuman,
2941
+ returnToAiLabel,
2942
+ returnToAi,
2943
+ returningToAi
2944
+ ]);
2945
+ return useMemo(
2946
+ () => ({
2947
+ messages,
2948
+ streaming: sending || agentTyping,
2949
+ send,
2950
+ reset,
2951
+ mode: isAiMode || !session ? "ai" : "human",
2952
+ ...footerAction ? { footerAction } : {},
2953
+ voiceCall: isAiMode && session ? {
2954
+ active: voiceActive,
2955
+ token: voiceToken,
2956
+ serverUrl: voiceServerUrl,
2957
+ start: startVoiceCall,
2958
+ stop: stopVoiceCall,
2959
+ loading: voiceLoading,
2960
+ error: voiceError
2961
+ } : void 0
2962
+ }),
2963
+ [
2964
+ messages,
2965
+ sending,
2966
+ agentTyping,
2967
+ send,
2968
+ reset,
2969
+ isAiMode,
2970
+ footerAction,
2971
+ voiceActive,
2972
+ voiceToken,
2973
+ voiceServerUrl,
2974
+ startVoiceCall,
2975
+ stopVoiceCall,
2976
+ voiceLoading,
2977
+ voiceError,
2978
+ session
2979
+ ]
2980
+ );
2981
+ }
1530
2982
  var KEY_EXPANDED = "wallavi_bubble_expanded";
1531
2983
  var KEY_DISMISSED = "wallavi_bubble_dismissed";
1532
2984
  function BubbleWidget({
2985
+ inboxToken,
2986
+ supportApiBase,
2987
+ requestHumanLabel,
2988
+ returnToAiLabel,
1533
2989
  position: positionProp,
1534
2990
  width: widthProp,
1535
2991
  height: heightProp,
@@ -1547,33 +3003,47 @@ function BubbleWidget({
1547
3003
  onOpenChange,
1548
3004
  ...chatProps
1549
3005
  }) {
3006
+ const supportBackend = useSupportChat(
3007
+ inboxToken ? {
3008
+ inboxToken,
3009
+ apiBase: supportApiBase,
3010
+ requestHumanLabel,
3011
+ returnToAiLabel
3012
+ } : { inboxToken: "__disabled__", apiBase: supportApiBase }
3013
+ );
1550
3014
  const isControlled = isOpenProp !== void 0;
1551
3015
  const [internalOpen, setInternalOpen] = useState(false);
1552
3016
  const open = isControlled ? isOpenProp : internalOpen;
1553
- const setOpen = useCallback((valueOrUpdater) => {
1554
- if (!isControlled) {
1555
- if (typeof valueOrUpdater === "function") {
1556
- setInternalOpen((prev) => {
1557
- const next = valueOrUpdater(prev);
1558
- onOpenChange?.(next);
1559
- return next;
1560
- });
3017
+ const setOpen = useCallback(
3018
+ (valueOrUpdater) => {
3019
+ if (!isControlled) {
3020
+ if (typeof valueOrUpdater === "function") {
3021
+ setInternalOpen((prev) => {
3022
+ const next = valueOrUpdater(prev);
3023
+ onOpenChange?.(next);
3024
+ return next;
3025
+ });
3026
+ } else {
3027
+ setInternalOpen(valueOrUpdater);
3028
+ onOpenChange?.(valueOrUpdater);
3029
+ }
1561
3030
  } else {
1562
- setInternalOpen(valueOrUpdater);
1563
- onOpenChange?.(valueOrUpdater);
3031
+ const next = typeof valueOrUpdater === "function" ? valueOrUpdater(open) : valueOrUpdater;
3032
+ onOpenChange?.(next);
1564
3033
  }
1565
- } else {
1566
- const next = typeof valueOrUpdater === "function" ? valueOrUpdater(open) : valueOrUpdater;
1567
- onOpenChange?.(next);
1568
- }
1569
- }, [isControlled, open, onOpenChange]);
3034
+ },
3035
+ [isControlled, open, onOpenChange]
3036
+ );
1570
3037
  const [expanded, setExpanded] = useState(false);
1571
3038
  const panelRef = useRef(null);
1572
3039
  const autoOpenedRef = useRef(false);
1573
3040
  useEffect(() => {
1574
3041
  if (localStorage.getItem(KEY_EXPANDED) === "true") setExpanded(true);
1575
3042
  }, []);
1576
- const remote = useAutoConfig(chatProps.agentId, autoConfig);
3043
+ const remote = useAutoConfig(
3044
+ inboxToken ? "" : chatProps.agentId ?? "",
3045
+ !inboxToken && autoConfig
3046
+ );
1577
3047
  const resolvedPosition = positionProp ?? remote.position;
1578
3048
  const resolvedBubbleIcon = bubbleIconUrlProp ?? remote.bubbleIconUrl;
1579
3049
  const resolvedAutoOpen = autoOpenProp || remote.autoOpen;
@@ -1587,8 +3057,8 @@ function BubbleWidget({
1587
3057
  const mergedConfig = {
1588
3058
  ...remote.remoteConfig,
1589
3059
  ...definedChatProps,
1590
- agentId: chatProps.agentId,
1591
- agentName: chatProps.agentName
3060
+ agentId: chatProps.agentId ?? "",
3061
+ agentName: chatProps.agentName ?? ""
1592
3062
  };
1593
3063
  const setOpenRef = useRef(setOpen);
1594
3064
  useEffect(() => {
@@ -1605,7 +3075,7 @@ function BubbleWidget({
1605
3075
  useEffect(() => {
1606
3076
  if (!resolvedKeyboardShortcut) return;
1607
3077
  const onKey = (e) => {
1608
- if ((e.metaKey || e.ctrlKey) && e.key === shortcutKey) {
3078
+ if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === shortcutKey.toLowerCase()) {
1609
3079
  e.preventDefault();
1610
3080
  setOpenRef.current((v) => !v);
1611
3081
  }
@@ -1624,7 +3094,10 @@ function BubbleWidget({
1624
3094
  const handleClose = () => {
1625
3095
  setOpen(false);
1626
3096
  if (!isControlled) {
1627
- localStorage.setItem(KEY_DISMISSED, String(Date.now() + 24 * 60 * 60 * 1e3));
3097
+ localStorage.setItem(
3098
+ KEY_DISMISSED,
3099
+ String(Date.now() + 24 * 60 * 60 * 1e3)
3100
+ );
1628
3101
  }
1629
3102
  };
1630
3103
  const toggleExpanded = () => setExpanded((v) => {
@@ -1633,7 +3106,10 @@ function BubbleWidget({
1633
3106
  return next;
1634
3107
  });
1635
3108
  const isLeft = resolvedPosition === "bottom-left";
1636
- const panelWidth = expanded ? Math.min(expandedWidth, typeof window !== "undefined" ? window.innerWidth - 40 : expandedWidth) : resolvedWidth;
3109
+ const panelWidth = expanded ? Math.min(
3110
+ expandedWidth,
3111
+ typeof window !== "undefined" ? window.innerWidth - 40 : expandedWidth
3112
+ ) : resolvedWidth;
1637
3113
  const panelHeight = expanded ? expandedHeight : resolvedHeight;
1638
3114
  return /* @__PURE__ */ jsxs(
1639
3115
  "div",
@@ -1664,6 +3140,8 @@ function BubbleWidget({
1664
3140
  ChatWidget,
1665
3141
  {
1666
3142
  ...mergedConfig,
3143
+ agentId: inboxToken ? "support" : chatProps.agentId ?? "",
3144
+ customBackend: inboxToken ? supportBackend : mergedConfig.customBackend,
1667
3145
  onClose: handleClose,
1668
3146
  onExpand: toggleExpanded,
1669
3147
  expanded,
@@ -1689,15 +3167,20 @@ function BubbleWidget({
1689
3167
  alignItems: "center",
1690
3168
  justifyContent: "center",
1691
3169
  transition: "transform 0.2s ease, box-shadow 0.2s ease",
1692
- background: open ? "var(--foreground, #19191c)" : "var(--background, #fff)",
1693
- color: open ? "var(--background, #fff)" : "var(--foreground, #19191c)"
3170
+ background: open ? "#19191c" : "#ffffff",
3171
+ color: open ? "#ffffff" : "#19191c"
1694
3172
  },
1695
3173
  children: open ? /* @__PURE__ */ jsx(X, { style: { width: 20, height: 20 } }) : resolvedBubbleIcon ? /* @__PURE__ */ jsx(
1696
3174
  "img",
1697
3175
  {
1698
3176
  src: resolvedBubbleIcon,
1699
3177
  alt: "",
1700
- style: { width: "100%", height: "100%", objectFit: "cover", display: "block" }
3178
+ style: {
3179
+ width: "100%",
3180
+ height: "100%",
3181
+ objectFit: "cover",
3182
+ display: "block"
3183
+ }
1701
3184
  }
1702
3185
  ) : /* @__PURE__ */ jsx(DefaultIcon, {})
1703
3186
  }
@@ -1707,4 +3190,4 @@ function BubbleWidget({
1707
3190
  );
1708
3191
  }
1709
3192
 
1710
- export { BubbleWidget, ChatWidget, formatToolName, getContrastColor, useAttachments, useChat, useVoice };
3193
+ export { BubbleWidget, ChatWidget, PlanCard, ReasoningBlock, ToolCallBadge, formatToolName, getContrastColor, useAttachments, useChat, useSupportChat, useVoice };