@wallavi/widget 1.7.0 → 1.7.2

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, Check, Search, 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 {\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-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 {\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-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-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-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-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-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-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-\\[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-25 {\n opacity: 0.25;\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-75 {\n opacity: 0.75;\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-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-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 -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.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.hover\\:ww-opacity-80:hover {\n opacity: 0.8;\n}\n.hover\\:ww-opacity-85:hover {\n opacity: 0.85;\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");
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,19 +65,220 @@ 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();
69
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
+ }
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,
@@ -82,7 +286,8 @@ function useChat({
82
286
  playgroundOverrides,
83
287
  customBackend
84
288
  }) {
85
- const persistKey = persist ? `wallavi_${agentId}` : null;
289
+ const userId = userContext?.userId;
290
+ const persistKey = persist ? userId ? `wallavi_${agentId}_${userId}` : `wallavi_${agentId}` : null;
86
291
  const onNavigateRef = useRef(onNavigate);
87
292
  useEffect(() => {
88
293
  onNavigateRef.current = onNavigate;
@@ -106,6 +311,119 @@ function useChat({
106
311
  return crypto.randomUUID();
107
312
  });
108
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
+ });
109
427
  useEffect(() => {
110
428
  if (customBackend || !persistKey) return;
111
429
  try {
@@ -122,30 +440,108 @@ function useChat({
122
440
  }, [customBackend, persistKey, threadId]);
123
441
  useEffect(() => {
124
442
  if (customBackend || !persistKey || typeof window === "undefined") return;
125
- if (messages.length > 0) return;
126
- void (async () => {
127
- try {
128
- const res = await fetch(
129
- `${API_URL}/api/chat/messages?agentId=${encodeURIComponent(agentId)}&threadId=${encodeURIComponent(threadId)}`
130
- );
131
- if (!res.ok) return;
132
- const { data } = await res.json();
133
- if (!data?.length) return;
134
- const restored = data.map((m) => ({
135
- id: m.id,
136
- role: m.role,
137
- parts: m.parts.filter((p) => p?.type)
138
- })).filter((m) => m.parts.length > 0);
139
- if (restored.length > 0) setMessages(restored);
140
- } catch {
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
+ }
141
456
  }
142
- })();
143
- }, []);
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]);
144
539
  const reset = useCallback(() => {
145
540
  setMessages([]);
146
541
  setInput("");
147
542
  setStreaming(false);
148
543
  setThreadId(crypto.randomUUID());
544
+ setDebugTraces([]);
149
545
  streamingMsgIdRef.current = null;
150
546
  if (persistKey) {
151
547
  try {
@@ -156,143 +552,6 @@ function useChat({
156
552
  }
157
553
  }
158
554
  }, [persistKey]);
159
- const applyStreamEvent = useCallback((proto, msgId) => {
160
- if (proto.type === "navigate") {
161
- if (proto.path.startsWith("/")) onNavigateRef.current?.(proto.path);
162
- return;
163
- }
164
- setMessages((prev) => {
165
- const idx = prev.findIndex((m) => m.id === msgId);
166
- if (idx === -1) return prev;
167
- const existing = prev[idx];
168
- const msg = { id: existing.id, role: existing.role, parts: [...existing.parts] };
169
- switch (proto.type) {
170
- case "text-delta": {
171
- const textIdx = msg.parts.findIndex((p) => p.type === "text");
172
- if (textIdx === -1) {
173
- msg.parts.push({ type: "text", text: proto.delta });
174
- } else {
175
- const p = msg.parts[textIdx];
176
- msg.parts[textIdx] = { type: "text", text: p.text + proto.delta };
177
- }
178
- break;
179
- }
180
- case "reasoning-delta": {
181
- const rIdx = msg.parts.findIndex((p) => p.type === "reasoning");
182
- if (rIdx === -1) {
183
- msg.parts.unshift({ type: "reasoning", text: proto.delta });
184
- } else {
185
- const p = msg.parts[rIdx];
186
- msg.parts[rIdx] = { type: "reasoning", text: p.text + proto.delta };
187
- }
188
- break;
189
- }
190
- case "tool-input-available": {
191
- msg.parts.push({
192
- type: "tool",
193
- toolCallId: proto.toolCallId,
194
- toolName: proto.toolName,
195
- input: proto.input ?? {},
196
- status: "running"
197
- });
198
- break;
199
- }
200
- case "tool-output-available": {
201
- const tIdx = msg.parts.findIndex(
202
- (p) => p.type === "tool" && p.toolCallId === proto.toolCallId
203
- );
204
- if (tIdx !== -1) {
205
- msg.parts[tIdx] = { ...msg.parts[tIdx], status: "done", output: proto.output };
206
- }
207
- break;
208
- }
209
- case "tool-output-error": {
210
- const tIdx = msg.parts.findIndex(
211
- (p) => p.type === "tool" && p.toolCallId === proto.toolCallId
212
- );
213
- if (tIdx !== -1) {
214
- msg.parts[tIdx] = { ...msg.parts[tIdx], status: "error", errorText: proto.errorText };
215
- }
216
- break;
217
- }
218
- case "picker": {
219
- msg.parts.push({
220
- type: "picker",
221
- pickerId: proto.pickerId,
222
- paramName: proto.paramName,
223
- toolName: proto.toolName,
224
- label: proto.label,
225
- options: proto.options
226
- });
227
- break;
228
- }
229
- case "plan-created": {
230
- msg.parts.push({
231
- type: "plan",
232
- planId: proto.planId,
233
- goal: proto.goal,
234
- steps: proto.steps.map((s) => ({ ...s, status: "pending" }))
235
- });
236
- break;
237
- }
238
- case "plan-step-update": {
239
- const pIdx = msg.parts.findIndex(
240
- (p) => p.type === "plan" && p.planId === proto.planId
241
- );
242
- if (pIdx !== -1) {
243
- const prev2 = msg.parts[pIdx];
244
- msg.parts[pIdx] = {
245
- ...prev2,
246
- steps: prev2.steps.map(
247
- (s) => s.index === proto.stepIndex ? { ...s, status: proto.status, ...proto.error ? { error: proto.error } : {} } : s
248
- )
249
- };
250
- }
251
- break;
252
- }
253
- }
254
- const copy = [...prev];
255
- copy[idx] = msg;
256
- return copy;
257
- });
258
- }, []);
259
- const fetchAndStream = useCallback(async (opts) => {
260
- const { input: userInput, msgId, extraMetadata, attachments } = opts;
261
- const isPrivate = Boolean(workspaceId);
262
- const token = isPrivate && typeof window !== "undefined" ? await window.Clerk?.session?.getToken() : null;
263
- const url = isPrivate ? `${API_URL}/api/threads/${threadId}/stream` : `${API_URL}/api/chat/stream`;
264
- const res = await fetch(url, {
265
- method: "POST",
266
- headers: {
267
- "Content-Type": "application/json",
268
- ...token ? { Authorization: `Bearer ${token}` } : {}
269
- },
270
- body: JSON.stringify({
271
- input: userInput,
272
- agentId,
273
- ...isPrivate ? { workspaceId, ...playgroundOverrides ? { playgroundOverrides } : {} } : { threadId },
274
- source,
275
- ...attachments?.length ? { attachments } : {},
276
- ...userContext?.userName ? { userName: userContext.userName } : {},
277
- ...userContext?.userEmail ? { userEmail: userContext.userEmail } : {},
278
- userMetadata: {
279
- ...userContext?.metadata ?? {},
280
- ...userContext?.pageContext ? { pageContext: userContext.pageContext } : {},
281
- headers: {
282
- ...token ? { Authorization: `Bearer ${token}` } : {},
283
- ...userContext?.headers ?? {}
284
- },
285
- ...extraMetadata ?? {}
286
- }
287
- })
288
- });
289
- if (!res.ok) {
290
- const errText = await res.text().catch(() => "");
291
- throw new Error(errText || `API error ${res.status}`);
292
- }
293
- if (!res.body) throw new Error("No stream body");
294
- await consumeStream(res.body, (proto) => applyStreamEvent(proto, msgId));
295
- }, [agentId, workspaceId, source, threadId, userContext, playgroundOverrides, applyStreamEvent]);
296
555
  const pendingAttachmentsRef = useRef([]);
297
556
  const send = useCallback(
298
557
  async (text) => {
@@ -300,7 +559,8 @@ function useChat({
300
559
  const content = (text ?? input).trim();
301
560
  const attachments2 = pendingAttachmentsRef.current.length > 0 ? [...pendingAttachmentsRef.current] : void 0;
302
561
  pendingAttachmentsRef.current = [];
303
- if (!content && !attachments2?.length || customBackend.streaming) return;
562
+ if (!content && !attachments2?.length || customBackend.streaming)
563
+ return;
304
564
  setInput("");
305
565
  await customBackend.send(content, attachments2);
306
566
  return;
@@ -323,16 +583,32 @@ function useChat({
323
583
  setStreaming(true);
324
584
  const assistantMsgId = newId();
325
585
  streamingMsgIdRef.current = assistantMsgId;
326
- setMessages((prev) => [...prev, { id: assistantMsgId, role: "assistant", parts: [] }]);
586
+ setMessages((prev) => [
587
+ ...prev,
588
+ { id: assistantMsgId, role: "assistant", parts: [] }
589
+ ]);
327
590
  try {
328
- await fetchAndStream({ input: userInput, msgId: assistantMsgId, attachments });
591
+ await fetchAndStream({
592
+ input: userInput,
593
+ msgId: assistantMsgId,
594
+ attachments
595
+ });
329
596
  } catch {
330
597
  setMessages((prev) => {
331
598
  const idx = prev.findIndex((m) => m.id === assistantMsgId);
332
599
  if (idx === -1) return prev;
333
600
  const copy = [...prev];
334
601
  const err = copy[idx];
335
- 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
+ };
336
612
  return copy;
337
613
  });
338
614
  }
@@ -348,19 +624,27 @@ function useChat({
348
624
  (prev) => prev.map((msg) => ({
349
625
  ...msg,
350
626
  parts: msg.parts.map(
351
- (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
352
631
  )
353
632
  }))
354
633
  );
355
634
  setStreaming(true);
356
635
  const assistantMsgId = newId();
357
636
  streamingMsgIdRef.current = assistantMsgId;
358
- setMessages((prev) => [...prev, { id: assistantMsgId, role: "assistant", parts: [] }]);
637
+ setMessages((prev) => [
638
+ ...prev,
639
+ { id: assistantMsgId, role: "assistant", parts: [] }
640
+ ]);
359
641
  try {
360
642
  await fetchAndStream({
361
643
  input: label,
362
644
  msgId: assistantMsgId,
363
- extraMetadata: { __pickerSelection: { pickerId, paramName, value, label } }
645
+ extraMetadata: {
646
+ __pickerSelection: { pickerId, paramName, value, label }
647
+ }
364
648
  });
365
649
  } catch {
366
650
  setMessages((prev) => {
@@ -368,7 +652,16 @@ function useChat({
368
652
  if (idx === -1) return prev;
369
653
  const copy = [...prev];
370
654
  const err = copy[idx];
371
- 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
+ };
372
665
  return copy;
373
666
  });
374
667
  }
@@ -403,7 +696,9 @@ function useChat({
403
696
  queueAttachments,
404
697
  regenerate,
405
698
  reset: customBackend?.reset ?? reset,
406
- selectPickerOption
699
+ selectPickerOption,
700
+ debugTraces,
701
+ voiceCall
407
702
  };
408
703
  }
409
704
  function getPreferredMimeType() {
@@ -423,7 +718,12 @@ function mimeTypeToExtension(mimeType) {
423
718
  return "webm";
424
719
  }
425
720
  var DEFAULT_API_URL = process.env.NEXT_PUBLIC_API_URL ?? "https://wallavi-production.up.railway.app";
426
- function useVoice({ agentId, apiUrl, onTranscript, onError }) {
721
+ function useVoice({
722
+ agentId,
723
+ apiUrl,
724
+ onTranscript,
725
+ onError
726
+ }) {
427
727
  const [voiceState, setVoiceState] = useState("idle");
428
728
  const recorderRef = useRef(null);
429
729
  const chunksRef = useRef([]);
@@ -439,10 +739,14 @@ function useVoice({ agentId, apiUrl, onTranscript, onError }) {
439
739
  const form = new FormData();
440
740
  form.append("audio", blob, `recording.${ext}`);
441
741
  form.append("agentId", agentId);
442
- 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
+ });
443
746
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
444
747
  const data = await res.json();
445
- if (!data.text?.trim()) throw new Error(data.error ?? "Empty transcript");
748
+ if (!data.text?.trim())
749
+ throw new Error(data.error ?? "Empty transcript");
446
750
  onTranscript(data.text.trim());
447
751
  setVoiceState("idle");
448
752
  } catch (err) {
@@ -460,14 +764,19 @@ function useVoice({ agentId, apiUrl, onTranscript, onError }) {
460
764
  const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
461
765
  streamRef.current = stream;
462
766
  const mimeType = getPreferredMimeType();
463
- const recorder = new MediaRecorder(stream, mimeType ? { mimeType } : void 0);
767
+ const recorder = new MediaRecorder(
768
+ stream,
769
+ mimeType ? { mimeType } : void 0
770
+ );
464
771
  chunksRef.current = [];
465
772
  recorder.ondataavailable = (e) => {
466
773
  if (e.data.size > 0) chunksRef.current.push(e.data);
467
774
  };
468
775
  recorder.onstop = async () => {
469
776
  stream.getTracks().forEach((t) => t.stop());
470
- const blob = new Blob(chunksRef.current, { type: mimeType || "audio/webm" });
777
+ const blob = new Blob(chunksRef.current, {
778
+ type: mimeType || "audio/webm"
779
+ });
471
780
  if (blob.size === 0) {
472
781
  onError?.("Recording was empty \u2014 please try again.");
473
782
  setVoiceState("idle");
@@ -493,7 +802,8 @@ function useVoice({ agentId, apiUrl, onTranscript, onError }) {
493
802
  useEffect(() => {
494
803
  return () => {
495
804
  if (errorTimerRef.current) clearTimeout(errorTimerRef.current);
496
- if (recorderRef.current?.state === "recording") recorderRef.current.stop();
805
+ if (recorderRef.current?.state === "recording")
806
+ recorderRef.current.stop();
497
807
  streamRef.current?.getTracks().forEach((t) => t.stop());
498
808
  };
499
809
  }, []);
@@ -516,7 +826,10 @@ function useAttachments({
516
826
  form.append("file", file);
517
827
  form.append("agentId", agentId);
518
828
  try {
519
- 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
+ });
520
833
  if (!res.ok) {
521
834
  const data = await res.json().catch(() => ({}));
522
835
  setAttachments(
@@ -532,7 +845,9 @@ function useAttachments({
532
845
  }
533
846
  const payload = await res.json();
534
847
  setAttachments(
535
- (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
+ )
536
851
  );
537
852
  } catch (err) {
538
853
  const msg = err instanceof Error ? err.message : "Upload failed";
@@ -576,16 +891,13 @@ function useAttachments({
576
891
  return { attachments, attach, remove, clear, isUploading, readyPayloads };
577
892
  }
578
893
  function DefaultIcon() {
579
- return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", fill: "none", style: { width: 26, height: 26 }, children: [
580
- /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "12", fill: "currentColor", opacity: 0.12 }),
581
- /* @__PURE__ */ jsx(
582
- "path",
583
- {
584
- 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",
585
- fill: "currentColor"
586
- }
587
- )
588
- ] });
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
+ ) });
589
901
  }
590
902
  function ExpandIcon() {
591
903
  return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", style: { width: 11, height: 11 }, children: /* @__PURE__ */ jsx(
@@ -611,9 +923,58 @@ function MinimizeIcon() {
611
923
  }
612
924
  ) });
613
925
  }
614
- var Avatar = ({ style, ...p }) => /* @__PURE__ */ jsx(AvatarPrimitive.Root, { style: { position: "relative", display: "flex", flexShrink: 0, overflow: "hidden", borderRadius: "9999px", ...style }, ...p });
615
- var AvatarImage = ({ style, ...p }) => /* @__PURE__ */ jsx(AvatarPrimitive.Image, { style: { position: "absolute", inset: 0, width: "100%", height: "100%", objectFit: "cover", ...style }, ...p });
616
- 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
+ );
617
978
  function ChatHeader({
618
979
  title,
619
980
  profilePicture,
@@ -622,22 +983,39 @@ function ChatHeader({
622
983
  onReset,
623
984
  onClose,
624
985
  onExpand,
625
- expanded
986
+ expanded,
987
+ onCall,
988
+ isCallLoading
626
989
  }) {
627
990
  return /* @__PURE__ */ jsxs(
628
991
  "header",
629
992
  {
630
- className: "ww-flex ww-items-center ww-justify-between ww-px-4 ww-py-3 ww-shrink-0",
631
- 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
+ },
632
999
  children: [
633
1000
  /* @__PURE__ */ jsxs("div", { className: "ww-flex ww-items-center ww-gap-2.5 ww-min-w-0", children: [
634
- /* @__PURE__ */ jsx(Avatar, { style: { width: 32, height: 32, border: `2px solid ${headerText}30` }, children: profilePicture ? /* @__PURE__ */ jsx(AvatarImage, { src: profilePicture, alt: title }) : /* @__PURE__ */ jsx(
635
- AvatarFallback,
1001
+ /* @__PURE__ */ jsx(
1002
+ Avatar,
636
1003
  {
637
- style: { backgroundColor: `${headerText}20`, color: headerText, fontSize: 11, fontWeight: 700 },
638
- 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
+ )
639
1017
  }
640
- ) }),
1018
+ ),
641
1019
  /* @__PURE__ */ jsxs("div", { className: "ww-min-w-0", children: [
642
1020
  /* @__PURE__ */ jsx("p", { className: "ww-font-semibold ww-text-sm ww-truncate ww-leading-tight", children: title }),
643
1021
  /* @__PURE__ */ jsx("p", { className: "ww-text-[10px] ww-opacity-70 ww-leading-tight", children: "AI Assistant" })
@@ -653,13 +1031,64 @@ function ChatHeader({
653
1031
  children: expanded ? /* @__PURE__ */ jsx(MinimizeIcon, {}) : /* @__PURE__ */ jsx(ExpandIcon, {})
654
1032
  }
655
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
+ ),
656
1079
  /* @__PURE__ */ jsx(
657
1080
  "button",
658
1081
  {
659
1082
  onClick: onReset,
660
1083
  className: "ww-rounded-full ww-p-1.5 ww-transition-colors hover:ww-bg-white/10",
661
1084
  title: "New conversation",
662
- 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
+ )
663
1092
  }
664
1093
  ),
665
1094
  onClose && /* @__PURE__ */ jsx(
@@ -683,8 +1112,10 @@ function formatBytes(bytes) {
683
1112
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
684
1113
  }
685
1114
  function ChipLeading({ a }) {
686
- if (a.status === "uploading") return /* @__PURE__ */ jsx(Loader2, { className: "ww-h-3 ww-w-3 ww-shrink-0 ww-animate-spin" });
687
- 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" });
688
1119
  const thumbSrc = a.payload?.url ?? a.payload?.base64;
689
1120
  if (a.mimeType.startsWith("image/") && thumbSrc) {
690
1121
  return /* @__PURE__ */ jsx(
@@ -696,14 +1127,19 @@ function ChipLeading({ a }) {
696
1127
  }
697
1128
  );
698
1129
  }
699
- 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" }) });
700
- 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" });
701
1134
  if (a.mimeType.includes("csv") || a.mimeType.includes("spreadsheet") || a.name.endsWith(".csv")) {
702
1135
  return /* @__PURE__ */ jsx(FileSpreadsheet, { className: "ww-h-3 ww-w-3 ww-shrink-0 ww-text-emerald-500" });
703
1136
  }
704
1137
  return /* @__PURE__ */ jsx(FileText, { className: "ww-h-3 ww-w-3 ww-shrink-0" });
705
1138
  }
706
- function AttachmentChips({ attachments, onRemove }) {
1139
+ function AttachmentChips({
1140
+ attachments,
1141
+ onRemove
1142
+ }) {
707
1143
  if (attachments.length === 0) return null;
708
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(
709
1145
  "div",
@@ -731,41 +1167,49 @@ function AttachmentChips({ attachments, onRemove }) {
731
1167
  a.id
732
1168
  )) });
733
1169
  }
734
- var Avatar2 = ({ style, ...p }) => /* @__PURE__ */ jsx(AvatarPrimitive.Root, { style: { position: "relative", display: "flex", flexShrink: 0, overflow: "hidden", borderRadius: "9999px", ...style }, ...p });
735
- var AvatarImage2 = ({ style, ...p }) => /* @__PURE__ */ jsx(AvatarPrimitive.Image, { style: { position: "absolute", inset: 0, width: "100%", height: "100%", objectFit: "cover", ...style }, ...p });
736
- var AvatarFallback2 = ({ style, ...p }) => /* @__PURE__ */ jsx(AvatarPrimitive.Fallback, { style: { display: "flex", width: "100%", height: "100%", alignItems: "center", justifyContent: "center", borderRadius: "9999px", ...style }, ...p });
737
- var ReactMarkdown = ReactMarkdownLib;
738
- function SentAttachments({ attachments, contrastColor }) {
1170
+ function SentAttachments({
1171
+ attachments,
1172
+ contrastColor
1173
+ }) {
739
1174
  const images = attachments.filter((a) => a.contentType === "image");
740
1175
  const files = attachments.filter((a) => a.contentType !== "image");
741
1176
  const isDark = contrastColor === "#ffffff" || contrastColor === "#fff";
742
1177
  return /* @__PURE__ */ jsxs("div", { className: "ww-flex ww-flex-col ww-gap-1.5 ww-w-full", children: [
743
- images.length > 0 && /* @__PURE__ */ jsx("div", { className: cn("ww-flex ww-gap-1.5 ww-flex-wrap", images.length === 1 && ""), children: images.map((img) => {
744
- const src = img.url ?? img.base64;
745
- return src ? /* @__PURE__ */ jsx(
746
- "img",
747
- {
748
- src,
749
- alt: img.name,
750
- className: cn(
751
- "ww-rounded-xl ww-object-cover ww-border",
752
- images.length === 1 ? "ww-w-full ww-max-h-48" : "ww-h-20 ww-w-20",
753
- isDark ? "ww-border-white/20" : "ww-border-black/10"
754
- )
755
- },
756
- img.id
757
- ) : /* @__PURE__ */ jsx(
758
- "div",
759
- {
760
- className: cn(
761
- "ww-h-20 ww-w-20 ww-rounded-xl ww-flex ww-items-center ww-justify-center ww-text-[10px] ww-font-medium",
762
- isDark ? "ww-bg-white/10 ww-text-white/60" : "ww-bg-black/10 ww-text-black/40"
763
- ),
764
- children: "IMG"
765
- },
766
- img.id
767
- );
768
- }) }),
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
+ ),
769
1213
  files.length > 0 && /* @__PURE__ */ jsx("div", { className: "ww-flex ww-flex-wrap ww-gap-1", children: files.map((f) => /* @__PURE__ */ jsxs(
770
1214
  "div",
771
1215
  {
@@ -783,12 +1227,19 @@ function SentAttachments({ attachments, contrastColor }) {
783
1227
  ] });
784
1228
  }
785
1229
  function PlanStepIcon({ status }) {
786
- if (status === "executing") return /* @__PURE__ */ jsx(Loader2, { className: "ww-h-3 ww-w-3 ww-animate-spin ww-text-primary" });
787
- if (status === "success") return /* @__PURE__ */ jsx(CheckCircle2, { className: "ww-h-3 ww-w-3 ww-text-emerald-500" });
788
- 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" });
789
1236
  return /* @__PURE__ */ jsx("div", { className: "ww-h-3 ww-w-3 ww-rounded-full ww-border-2 ww-border-muted-foreground/30" });
790
1237
  }
791
- function PlanCard({ part, onSend, disabled }) {
1238
+ function PlanCard({
1239
+ part,
1240
+ onSend,
1241
+ disabled
1242
+ }) {
792
1243
  const successCount = part.steps.filter((s) => s.status === "success").length;
793
1244
  const hasExecuting = part.steps.some((s) => s.status === "executing");
794
1245
  const allDone = successCount === part.steps.length && part.steps.length > 0;
@@ -842,7 +1293,10 @@ function PlanCard({ part, onSend, disabled }) {
842
1293
  onClick: () => onSend("s\xED, proceder"),
843
1294
  disabled,
844
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",
845
- style: { backgroundColor: "var(--primary, #18181b)", color: "var(--primary-foreground, #fff)" },
1296
+ style: {
1297
+ backgroundColor: "var(--primary, #18181b)",
1298
+ color: "var(--primary-foreground, #fff)"
1299
+ },
846
1300
  children: [
847
1301
  /* @__PURE__ */ jsx(Check, { className: "ww-h-3 ww-w-3 ww-shrink-0" }),
848
1302
  "Proceder"
@@ -924,7 +1378,15 @@ function ReasoningBlock({ text }) {
924
1378
  children: [
925
1379
  /* @__PURE__ */ jsx(Zap, { className: "ww-h-3 ww-w-3" }),
926
1380
  /* @__PURE__ */ jsx("span", { children: "Reasoning" }),
927
- /* @__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
+ )
928
1390
  ]
929
1391
  }
930
1392
  ),
@@ -946,98 +1408,292 @@ function PickerSelector({
946
1408
  }, [part.options, query]);
947
1409
  const isConsumed = !!part.selectedValue;
948
1410
  const handleClick = (opt) => {
949
- 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);
950
1413
  };
951
- return /* @__PURE__ */ jsxs("div", { className: cn(
952
- "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",
953
- "ww-border-border/50"
954
- ), children: [
955
- /* @__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 }),
956
- mode === "list" && /* @__PURE__ */ jsxs("div", { className: "ww-relative", children: [
957
- /* @__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" }),
958
- /* @__PURE__ */ jsx(
959
- "input",
960
- {
961
- type: "text",
962
- value: query,
963
- onChange: (e) => setQuery(e.target.value),
964
- placeholder: "Search\u2026",
965
- disabled: disabled || isConsumed,
966
- 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"
967
- }
968
- )
969
- ] }),
970
- mode === "pills" && /* @__PURE__ */ jsx("div", { className: "ww-flex ww-flex-wrap ww-gap-1.5", children: part.options.map((opt) => {
971
- const sel = part.selectedValue === opt.value;
972
- const faded = isConsumed && !sel;
973
- return /* @__PURE__ */ jsxs(
974
- "button",
975
- {
976
- disabled: disabled || isConsumed,
977
- onClick: () => handleClick(opt),
978
- className: cn(
979
- "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",
980
- 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",
981
- disabled && !isConsumed && "ww-opacity-60 ww-pointer-events-none"
982
- ),
983
- children: [
984
- sel && /* @__PURE__ */ jsx(Check, { className: "ww-h-3 ww-w-3 ww-shrink-0" }),
985
- opt.label
986
- ]
987
- },
988
- opt.value
989
- );
990
- }) }),
991
- 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) => {
992
- const sel = part.selectedValue === opt.value;
993
- const faded = isConsumed && !sel;
994
- return /* @__PURE__ */ jsxs(
995
- "button",
996
- {
997
- disabled: disabled || isConsumed,
998
- onClick: () => handleClick(opt),
999
- className: cn(
1000
- "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",
1001
- 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",
1002
- disabled && !isConsumed && "ww-opacity-60 ww-pointer-events-none"
1003
- ),
1004
- children: [
1005
- 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" }),
1006
- /* @__PURE__ */ jsx("span", { className: "ww-truncate", children: opt.label })
1007
- ]
1008
- },
1009
- opt.value
1010
- );
1011
- }) }),
1012
- 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: [
1013
- filtered.length === 0 && /* @__PURE__ */ jsx("p", { className: "ww-py-3 ww-text-center ww-text-xs ww-text-muted-foreground/50", children: "No results" }),
1014
- filtered.map((opt) => {
1015
- const sel = part.selectedValue === opt.value;
1016
- const faded = isConsumed && !sel;
1017
- return /* @__PURE__ */ jsxs(
1018
- "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",
1526
+ {
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",
1019
1548
  {
1020
- disabled: disabled || isConsumed,
1021
- onClick: () => handleClick(opt),
1022
- className: cn(
1023
- "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",
1024
- 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",
1025
- disabled && !isConsumed && "ww-opacity-60 ww-pointer-events-none"
1026
- ),
1027
- children: [
1028
- /* @__PURE__ */ jsx("span", { className: cn(
1029
- "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]",
1030
- sel ? "ww-border-background ww-bg-background/20" : "ww-border-border/50"
1031
- ), children: sel && /* @__PURE__ */ jsx(Check, { className: "ww-h-2.5 ww-w-2.5" }) }),
1032
- /* @__PURE__ */ jsx("span", { className: "ww-truncate", children: opt.label })
1033
- ]
1034
- },
1035
- opt.value
1036
- );
1037
- })
1038
- ] })
1039
- ] });
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
+ );
1040
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
+ );
1041
1697
  function MessageBubble({
1042
1698
  message,
1043
1699
  userColor,
@@ -1051,9 +1707,15 @@ function MessageBubble({
1051
1707
  const isUser = message.role === "user";
1052
1708
  const textPart = message.parts.find((p) => p.type === "text");
1053
1709
  const reasoningPart = message.parts.find((p) => p.type === "reasoning");
1054
- const toolParts = message.parts.filter((p) => p.type === "tool");
1055
- const pickerParts = message.parts.filter((p) => p.type === "picker");
1056
- 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
+ );
1057
1719
  const contrastColor = getContrastColor(userColor);
1058
1720
  if (isUser) {
1059
1721
  return /* @__PURE__ */ jsx("div", { className: "ww-flex ww-justify-end", children: /* @__PURE__ */ jsxs(
@@ -1062,7 +1724,13 @@ function MessageBubble({
1062
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",
1063
1725
  style: { backgroundColor: userColor, color: contrastColor },
1064
1726
  children: [
1065
- 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
+ ),
1066
1734
  textPart?.text && /* @__PURE__ */ jsx("span", { children: textPart.text })
1067
1735
  ]
1068
1736
  }
@@ -1071,10 +1739,40 @@ function MessageBubble({
1071
1739
  const visibleToolParts = showThinking ? toolParts : [];
1072
1740
  const isEmpty = !textPart?.text && visibleToolParts.length === 0 && pickerParts.length === 0 && planParts.length === 0;
1073
1741
  return /* @__PURE__ */ jsxs("div", { className: "ww-flex ww-gap-2.5 ww-items-start", children: [
1074
- /* @__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
+ ),
1075
1765
  /* @__PURE__ */ jsxs("div", { className: "ww-flex ww-flex-col ww-gap-1.5 ww-min-w-0 ww-max-w-[82%]", children: [
1076
1766
  showThinking && reasoningPart && /* @__PURE__ */ jsx(ReasoningBlock, { text: reasoningPart.text }),
1077
- planParts.map((p) => /* @__PURE__ */ jsx(PlanCard, { part: p, onSend, disabled: isStreaming }, p.planId)),
1767
+ planParts.map((p) => /* @__PURE__ */ jsx(
1768
+ PlanCard,
1769
+ {
1770
+ part: p,
1771
+ onSend,
1772
+ disabled: isStreaming
1773
+ },
1774
+ p.planId
1775
+ )),
1078
1776
  visibleToolParts.map((t) => /* @__PURE__ */ jsx(ToolCallBadge, { part: t }, t.toolCallId)),
1079
1777
  pickerParts.map((p) => /* @__PURE__ */ jsx(
1080
1778
  PickerSelector,
@@ -1087,40 +1785,39 @@ function MessageBubble({
1087
1785
  p.pickerId
1088
1786
  )),
1089
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: [
1090
- /* @__PURE__ */ jsx(
1091
- ReactMarkdown,
1092
- {
1093
- remarkPlugins: [remarkGfm],
1094
- components: {
1095
- p: ({ children }) => /* @__PURE__ */ jsx("p", { style: { margin: "4px 0" }, children }),
1096
- h1: ({ children }) => /* @__PURE__ */ jsx("p", { style: { margin: "6px 0", fontWeight: 700, fontSize: "1em" }, children }),
1097
- h2: ({ children }) => /* @__PURE__ */ jsx("p", { style: { margin: "6px 0", fontWeight: 700, fontSize: "1em" }, children }),
1098
- h3: ({ children }) => /* @__PURE__ */ jsx("p", { style: { margin: "6px 0", fontWeight: 600, fontSize: "0.95em" }, children }),
1099
- ul: ({ children }) => /* @__PURE__ */ jsx("ul", { style: { margin: "4px 0", paddingLeft: 16 }, children }),
1100
- ol: ({ children }) => /* @__PURE__ */ jsx("ol", { style: { margin: "4px 0", paddingLeft: 16 }, children }),
1101
- li: ({ children }) => /* @__PURE__ */ jsx("li", { style: { margin: "2px 0" }, children }),
1102
- strong: ({ children }) => /* @__PURE__ */ jsx("strong", { style: { fontWeight: 600 }, children }),
1103
- code: ({ children }) => /* @__PURE__ */ jsx("code", { style: { fontSize: "0.85em", backgroundColor: "rgba(0,0,0,0.07)", borderRadius: 4, padding: "1px 5px", fontFamily: "monospace" }, children }),
1104
- 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 }),
1105
- a: ({ href, children }) => /* @__PURE__ */ jsx("a", { href, target: "_blank", rel: "noopener noreferrer", style: { textDecoration: "underline", opacity: 0.75 }, children }),
1106
- 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 }) }),
1107
- thead: ({ children }) => /* @__PURE__ */ jsx("thead", { style: { backgroundColor: "rgba(0,0,0,0.04)" }, children }),
1108
- tbody: ({ children }) => /* @__PURE__ */ jsx("tbody", { children }),
1109
- tr: ({ children }) => /* @__PURE__ */ jsx("tr", { style: { borderBottom: "1px solid rgba(0,0,0,0.07)" }, children }),
1110
- th: ({ children }) => /* @__PURE__ */ jsx("th", { style: { padding: "6px 10px", textAlign: "left", fontWeight: 600, whiteSpace: "nowrap" }, children }),
1111
- td: ({ children }) => /* @__PURE__ */ jsx("td", { style: { padding: "5px 10px", verticalAlign: "top" }, children })
1112
- },
1113
- children: textPart.text
1114
- }
1115
- ),
1788
+ /* @__PURE__ */ jsx(MarkdownContent, { text: textPart.text }),
1116
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" })
1117
1790
  ] }) : null
1118
1791
  ] })
1119
1792
  ] });
1120
1793
  }
1121
- 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
+ );
1122
1807
  var AvatarImage3 = AvatarPrimitive.Image;
1123
- 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
+ );
1124
1821
  function ChatMessages({
1125
1822
  messages,
1126
1823
  streaming,
@@ -1134,14 +1831,15 @@ function ChatMessages({
1134
1831
  onPickerSelect
1135
1832
  }) {
1136
1833
  const bottomRef = useRef(null);
1834
+ const greetText = initialMessages[0];
1137
1835
  const showGreeting = messages.length === 0;
1138
1836
  useEffect(() => {
1139
1837
  bottomRef.current?.scrollIntoView({ behavior: "smooth" });
1140
1838
  }, [messages, streaming]);
1141
1839
  return /* @__PURE__ */ jsxs("div", { className: "ww-flex-1 ww-flex ww-flex-col ww-overflow-y-auto ww-overscroll-contain", children: [
1142
- 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: [
1143
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() }) }),
1144
- /* @__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 })
1145
1843
  ] }),
1146
1844
  showGreeting && /* @__PURE__ */ jsx("div", { className: "ww-flex-1" }),
1147
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(
@@ -1170,7 +1868,13 @@ function ChatMessages({
1170
1868
  "div",
1171
1869
  {
1172
1870
  className: "ww-bg-muted",
1173
- style: { display: "inline-flex", alignItems: "center", gap: 6, borderRadius: "18px 18px 18px 4px", padding: "13px 18px" },
1871
+ style: {
1872
+ display: "inline-flex",
1873
+ alignItems: "center",
1874
+ gap: 6,
1875
+ borderRadius: "18px 18px 18px 4px",
1876
+ padding: "13px 18px"
1877
+ },
1174
1878
  children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx(
1175
1879
  "span",
1176
1880
  {
@@ -1254,7 +1958,13 @@ function ChatInput({
1254
1958
  }
1255
1959
  ),
1256
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: [
1257
- 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
+ ) }),
1258
1968
  hasAttachments && /* @__PURE__ */ jsx(
1259
1969
  "input",
1260
1970
  {
@@ -1344,7 +2054,10 @@ function ChatInput({
1344
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",
1345
2055
  hasText || streaming ? "ww-opacity-100 ww-shadow-sm" : "ww-opacity-30"
1346
2056
  ),
1347
- 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" },
1348
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" })
1349
2062
  }
1350
2063
  )
@@ -1352,6 +2065,126 @@ function ChatInput({
1352
2065
  ] })
1353
2066
  ] });
1354
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
+ }
1355
2188
  function ChatWidget({
1356
2189
  agentId,
1357
2190
  workspaceId,
@@ -1383,9 +2216,21 @@ function ChatWidget({
1383
2216
  onReset,
1384
2217
  onExpand,
1385
2218
  expanded,
1386
- embedded = false
2219
+ embedded = false,
2220
+ envId,
2221
+ onDebugTrace
1387
2222
  }) {
1388
- const chat = useChat({ agentId, workspaceId, source, userContext, persist, onNavigate, playgroundOverrides, customBackend });
2223
+ const chat = useChat({
2224
+ agentId,
2225
+ workspaceId,
2226
+ envId,
2227
+ source,
2228
+ userContext,
2229
+ persist,
2230
+ onNavigate,
2231
+ playgroundOverrides,
2232
+ customBackend
2233
+ });
1389
2234
  const voice = useVoice({
1390
2235
  agentId,
1391
2236
  onTranscript: (text) => {
@@ -1397,29 +2242,47 @@ function ChatWidget({
1397
2242
  }
1398
2243
  });
1399
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]);
1400
2254
  const [isDragOver, setIsDragOver] = useState(false);
1401
- const handleDragOver = useCallback((e) => {
1402
- if (!enableAttachments) return;
1403
- e.preventDefault();
1404
- e.stopPropagation();
1405
- if (e.dataTransfer.types.includes("Files")) setIsDragOver(true);
1406
- }, [enableAttachments]);
1407
- const handleDragLeave = useCallback((e) => {
1408
- if (!enableAttachments) return;
1409
- e.preventDefault();
1410
- e.stopPropagation();
1411
- 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();
1412
2280
  setIsDragOver(false);
1413
- }
1414
- }, [enableAttachments]);
1415
- const handleDrop = useCallback((e) => {
1416
- if (!enableAttachments) return;
1417
- e.preventDefault();
1418
- e.stopPropagation();
1419
- setIsDragOver(false);
1420
- const files = e.dataTransfer.files;
1421
- if (files.length > 0) attachmentHook.attach(files);
1422
- }, [enableAttachments, attachmentHook]);
2281
+ const files = e.dataTransfer.files;
2282
+ if (files.length > 0) attachmentHook.attach(files);
2283
+ },
2284
+ [enableAttachments, attachmentHook]
2285
+ );
1423
2286
  const handleSend = () => {
1424
2287
  const payloads = attachmentHook.readyPayloads;
1425
2288
  if (payloads.length > 0) {
@@ -1461,7 +2324,7 @@ function ChatWidget({
1461
2324
  "div",
1462
2325
  {
1463
2326
  className: cn(
1464
- "wallavi-widget ww-flex ww-flex-col ww-overflow-hidden 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",
1465
2328
  !embedded && "ww-rounded-2xl ww-border ww-shadow-xl",
1466
2329
  isDragOver && "ww-ring-2 ww-ring-inset ww-ring-primary/60",
1467
2330
  className
@@ -1487,7 +2350,18 @@ function ChatWidget({
1487
2350
  onReset: handleReset,
1488
2351
  onClose: hideCloseButton ? void 0 : onClose,
1489
2352
  onExpand,
1490
- 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
1491
2365
  }
1492
2366
  ),
1493
2367
  customBackend?.mode === "human" && /* @__PURE__ */ jsxs(
@@ -1524,22 +2398,91 @@ function ChatWidget({
1524
2398
  {
1525
2399
  onClick: () => void customBackend.footerAction.onClick(),
1526
2400
  disabled: customBackend.footerAction.loading,
1527
- style: customBackend.footerAction.icon === "human" ? { backgroundColor: userMessageColor, color: "#ffffff", boxShadow: `0 2px 12px ${userMessageColor}55` } : { borderColor: `${userMessageColor}35`, backgroundColor: `${userMessageColor}0d`, color: userMessageColor },
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
+ },
1528
2410
  className: [
1529
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",
1530
2412
  "hover:ww-opacity-85 active:ww-scale-[0.98] disabled:ww-opacity-50 disabled:ww-pointer-events-none",
1531
2413
  customBackend.footerAction.icon === "human" ? "" : "ww-border"
1532
2414
  ].join(" "),
1533
2415
  children: [
1534
- customBackend.footerAction.loading ? /* @__PURE__ */ jsxs("svg", { className: "ww-animate-spin ww-w-3.5 ww-h-3.5 ww-shrink-0", viewBox: "0 0 24 24", fill: "none", children: [
1535
- /* @__PURE__ */ jsx("circle", { className: "ww-opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "3" }),
1536
- /* @__PURE__ */ jsx("path", { className: "ww-opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" })
1537
- ] }) : customBackend.footerAction.icon === "ai" ? /* @__PURE__ */ jsx("svg", { className: "ww-w-3.5 ww-h-3.5 ww-shrink-0", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", 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" }) }) : /* @__PURE__ */ jsxs("svg", { className: "ww-w-3.5 ww-h-3.5 ww-shrink-0", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
1538
- /* @__PURE__ */ jsx("path", { d: "M3 18v-6a9 9 0 0 1 18 0v6" }),
1539
- /* @__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" })
1540
- ] }),
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
+ ),
1541
2472
  /* @__PURE__ */ jsx("span", { className: "ww-flex-1 ww-text-left ww-truncate", children: customBackend.footerAction.label }),
1542
- /* @__PURE__ */ jsx("svg", { className: "ww-shrink-0 ww-w-3 ww-h-3 ww-opacity-60", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "M9 18l6-6-6-6" }) })
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
+ )
1543
2486
  ]
1544
2487
  }
1545
2488
  ) }),
@@ -1567,7 +2510,7 @@ function ChatWidget({
1567
2510
  } : {}
1568
2511
  }
1569
2512
  ),
1570
- 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: [
1571
2514
  /* @__PURE__ */ jsxs(
1572
2515
  "a",
1573
2516
  {
@@ -1617,19 +2560,27 @@ function useAutoConfig(agentId, enabled) {
1617
2560
  if (cancelled) return;
1618
2561
  const cfg = body?.data ?? {};
1619
2562
  const remote = {};
1620
- if (cfg.profilePicture != null) remote.profilePicture = cfg.profilePicture;
1621
- 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;
1622
2567
  if (cfg.theme) remote.theme = cfg.theme;
1623
- if (cfg.userMessageColor) remote.userMessageColor = cfg.userMessageColor;
2568
+ if (cfg.userMessageColor)
2569
+ remote.userMessageColor = cfg.userMessageColor;
1624
2570
  if (Array.isArray(cfg.initialMessages) && cfg.initialMessages.length > 0)
1625
2571
  remote.initialMessages = cfg.initialMessages;
1626
2572
  if (Array.isArray(cfg.suggestedMessages))
1627
2573
  remote.suggestedMessages = cfg.suggestedMessages;
1628
- if (cfg.messagePlaceholder != null) remote.messagePlaceholder = cfg.messagePlaceholder;
2574
+ if (cfg.messagePlaceholder != null)
2575
+ remote.messagePlaceholder = cfg.messagePlaceholder;
1629
2576
  if (cfg.watermark != null) remote.watermark = cfg.watermark;
1630
2577
  if (cfg.footer != null) remote.footer = cfg.footer;
1631
- if (cfg.showThinking != null) remote.showThinking = cfg.showThinking;
1632
- 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;
1633
2584
  setResult({
1634
2585
  remoteConfig: remote,
1635
2586
  bubbleIconUrl: cfg.chatIcon || cfg.profilePicture || void 0,
@@ -1667,63 +2618,102 @@ function useSupportChat({
1667
2618
  const enabled = Boolean(inboxToken) && inboxToken !== "__disabled__";
1668
2619
  const STORAGE_KEY = `wlv_support_${inboxToken}`;
1669
2620
  const base = apiBase.replace(/\/$/, "");
1670
- const [session, setSession] = useState(null);
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
+ });
1671
2631
  const [rawMessages, setRawMessages] = useState([]);
1672
2632
  const [sending, setSending] = useState(false);
1673
- const [isAiMode, setIsAiMode] = 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
+ });
1674
2643
  const [agentTyping, setAgentTyping] = useState(false);
1675
2644
  const [requestingHuman, setRequestingHuman] = useState(false);
1676
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);
1677
2651
  const typingTimerRef = useRef(null);
1678
2652
  useEffect(() => {
1679
2653
  if (!enabled) return;
1680
2654
  try {
1681
2655
  const saved = localStorage.getItem(STORAGE_KEY);
1682
- if (saved) {
1683
- const { session: s, isAiMode: ai } = JSON.parse(saved);
1684
- setSession(s);
1685
- setIsAiMode(ai ?? false);
1686
- }
2656
+ if (saved) JSON.parse(saved);
1687
2657
  } catch {
1688
2658
  localStorage.removeItem(STORAGE_KEY);
2659
+ setSession(null);
2660
+ setIsAiMode(false);
1689
2661
  }
1690
2662
  }, [STORAGE_KEY, enabled]);
1691
- const loadMessages = useCallback(async (sess) => {
1692
- try {
1693
- const res = await fetch(`${base}/api/support-chat/${sess.conversationId}/messages`, {
1694
- headers: { "x-visitor-token": sess.visitorToken }
1695
- });
1696
- const data = await res.json();
1697
- if (Array.isArray(data.data)) setRawMessages(data.data);
1698
- } catch {
1699
- }
1700
- }, [base]);
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
+ );
1701
2679
  useEffect(() => {
1702
2680
  if (!enabled || !session) return;
1703
2681
  void loadMessages(session);
1704
2682
  let ablyClient = null;
1705
2683
  const setupAbly = async () => {
1706
2684
  try {
1707
- const res = await fetch(`${base}/api/support-chat/${session.conversationId}/ably-token`, {
1708
- headers: { "x-visitor-token": session.visitorToken }
1709
- });
2685
+ const res = await fetch(
2686
+ `${base}/api/support-chat/${session.conversationId}/ably-token`,
2687
+ {
2688
+ headers: { "x-visitor-token": session.visitorToken }
2689
+ }
2690
+ );
1710
2691
  if (!res.ok) return;
1711
2692
  const { tokenRequest, channel: channelName } = await res.json();
1712
2693
  const AblyLib = (await import('ably')).default;
1713
- ablyClient = new AblyLib.Realtime({ authCallback: (_, cb) => cb(null, tokenRequest) });
2694
+ ablyClient = new AblyLib.Realtime({
2695
+ authCallback: (_, cb) => cb(null, tokenRequest)
2696
+ });
1714
2697
  const ch = ablyClient.channels.get(channelName);
1715
2698
  ch.subscribe((msg) => {
1716
2699
  const event = { type: msg.name, ...msg.data };
1717
2700
  if (event.type === "message.created" && event.message) {
1718
2701
  setRawMessages((prev) => {
1719
- if (prev.some((m) => m.id === event.message.id)) return prev;
1720
- return [...prev.filter((m) => !m.id.startsWith("tmp_")), event.message];
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
+ ];
1721
2708
  });
1722
2709
  } else if (event.type === "agent.typing") {
1723
2710
  setAgentTyping(!!event.isTyping);
1724
2711
  if (event.isTyping) {
1725
2712
  if (typingTimerRef.current) clearTimeout(typingTimerRef.current);
1726
- typingTimerRef.current = setTimeout(() => setAgentTyping(false), 6e3);
2713
+ typingTimerRef.current = setTimeout(
2714
+ () => setAgentTyping(false),
2715
+ 6e3
2716
+ );
1727
2717
  } else {
1728
2718
  if (typingTimerRef.current) clearTimeout(typingTimerRef.current);
1729
2719
  }
@@ -1731,7 +2721,11 @@ function useSupportChat({
1731
2721
  const newIsAi = event.mode === "auto";
1732
2722
  setIsAiMode(newIsAi);
1733
2723
  setSession((s) => {
1734
- if (s) localStorage.setItem(STORAGE_KEY, JSON.stringify({ session: s, isAiMode: newIsAi }));
2724
+ if (s)
2725
+ localStorage.setItem(
2726
+ STORAGE_KEY,
2727
+ JSON.stringify({ session: s, isAiMode: newIsAi })
2728
+ );
1735
2729
  return s;
1736
2730
  });
1737
2731
  }
@@ -1742,84 +2736,117 @@ function useSupportChat({
1742
2736
  void setupAbly();
1743
2737
  const pollId = setInterval(() => void loadMessages(session), 3e4);
1744
2738
  return () => {
1745
- if (ablyClient) try {
1746
- ablyClient.close();
1747
- } catch {
1748
- }
2739
+ if (ablyClient)
2740
+ try {
2741
+ ablyClient.close();
2742
+ } catch {
2743
+ }
1749
2744
  clearInterval(pollId);
1750
2745
  if (typingTimerRef.current) clearTimeout(typingTimerRef.current);
1751
2746
  };
1752
2747
  }, [session, STORAGE_KEY, base, loadMessages]);
1753
- const send = useCallback(async (text, attachments) => {
1754
- if (!text.trim() && !attachments?.length) return;
1755
- if (sending) return;
1756
- setSending(true);
1757
- if (!session) {
1758
- try {
1759
- const res = await fetch(`${base}/api/support-chat/start`, {
1760
- method: "POST",
1761
- headers: { "Content-Type": "application/json" },
1762
- body: JSON.stringify({
1763
- inboxToken,
1764
- message: text || `[${attachments.length} archivo(s)]`,
1765
- attachments
1766
- })
1767
- });
1768
- const data = await res.json();
1769
- if (data.data) {
1770
- const sess = {
1771
- conversationId: data.data.conversationId,
1772
- visitorToken: data.data.visitorToken
1773
- };
1774
- const ai = data.data.isAiMode ?? false;
1775
- localStorage.setItem(STORAGE_KEY, JSON.stringify({ session: sess, isAiMode: ai }));
1776
- setSession(sess);
1777
- setIsAiMode(ai);
1778
- if (Array.isArray(data.data.messages)) {
1779
- setRawMessages(data.data.messages);
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
1780
2761
  }
1781
- }
1782
- } catch {
1783
- }
1784
- } else {
1785
- setRawMessages((prev) => [...prev, {
1786
- id: `tmp_${Date.now()}`,
1787
- role: "customer",
1788
- content: text || `[${attachments.length} archivo(s)]`,
1789
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1790
- metadata: attachments?.length ? { attachments } : void 0
1791
- }]);
1792
- try {
1793
- const body = { content: text };
1794
- if (attachments?.length) body.attachments = attachments;
1795
- const res = await fetch(`${base}/api/support-chat/${session.conversationId}/messages`, {
1796
- method: "POST",
1797
- headers: { "Content-Type": "application/json", "x-visitor-token": session.visitorToken },
1798
- body: JSON.stringify(body)
1799
- });
1800
- const data = await res.json();
1801
- if (data.data?.aiReply) {
1802
- setRawMessages((prev) => {
1803
- const withoutTemp = prev.filter((m) => !m.id.startsWith("tmp_"));
1804
- return [...withoutTemp, data.data.aiReply];
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
+ })
1805
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 {
1806
2827
  }
1807
- } catch {
1808
2828
  }
1809
- }
1810
- setSending(false);
1811
- }, [session, sending, inboxToken, base, STORAGE_KEY]);
2829
+ setSending(false);
2830
+ },
2831
+ [session, sending, inboxToken, base, STORAGE_KEY]
2832
+ );
1812
2833
  const requestHuman = useCallback(async () => {
1813
2834
  if (!session || !isAiMode || requestingHuman) return;
1814
2835
  setRequestingHuman(true);
1815
2836
  try {
1816
2837
  await fetch(`${base}/api/support-chat/${session.conversationId}/mode`, {
1817
2838
  method: "PATCH",
1818
- headers: { "Content-Type": "application/json", "x-visitor-token": session.visitorToken },
2839
+ headers: {
2840
+ "Content-Type": "application/json",
2841
+ "x-visitor-token": session.visitorToken
2842
+ },
1819
2843
  body: JSON.stringify({ mode: "human" })
1820
2844
  });
1821
2845
  setIsAiMode(false);
1822
- localStorage.setItem(STORAGE_KEY, JSON.stringify({ session, isAiMode: false }));
2846
+ localStorage.setItem(
2847
+ STORAGE_KEY,
2848
+ JSON.stringify({ session, isAiMode: false })
2849
+ );
1823
2850
  } catch {
1824
2851
  }
1825
2852
  setRequestingHuman(false);
@@ -1830,15 +2857,47 @@ function useSupportChat({
1830
2857
  try {
1831
2858
  await fetch(`${base}/api/support-chat/${session.conversationId}/mode`, {
1832
2859
  method: "PATCH",
1833
- headers: { "Content-Type": "application/json", "x-visitor-token": session.visitorToken },
2860
+ headers: {
2861
+ "Content-Type": "application/json",
2862
+ "x-visitor-token": session.visitorToken
2863
+ },
1834
2864
  body: JSON.stringify({ mode: "auto" })
1835
2865
  });
1836
2866
  setIsAiMode(true);
1837
- localStorage.setItem(STORAGE_KEY, JSON.stringify({ session, isAiMode: true }));
2867
+ localStorage.setItem(
2868
+ STORAGE_KEY,
2869
+ JSON.stringify({ session, isAiMode: true })
2870
+ );
1838
2871
  } catch {
1839
2872
  }
1840
2873
  setReturningToAi(false);
1841
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
+ }, []);
1842
2901
  const reset = useCallback(() => {
1843
2902
  localStorage.removeItem(STORAGE_KEY);
1844
2903
  setSession(null);
@@ -1847,28 +2906,78 @@ function useSupportChat({
1847
2906
  setAgentTyping(false);
1848
2907
  setRequestingHuman(false);
1849
2908
  setReturningToAi(false);
1850
- }, [STORAGE_KEY]);
2909
+ stopVoiceCall();
2910
+ }, [STORAGE_KEY, stopVoiceCall]);
1851
2911
  const messages = useMemo(
1852
2912
  () => rawMessages.filter((m) => m.role !== "system").map(toWidgetMsg),
1853
2913
  [rawMessages]
1854
2914
  );
1855
2915
  const footerAction = useMemo(() => {
1856
2916
  if (isAiMode) {
1857
- return { label: requestHumanLabel, sublabel: "Te conectamos con un agente disponible", icon: "human", onClick: requestHuman, loading: requestingHuman };
2917
+ return {
2918
+ label: requestHumanLabel,
2919
+ sublabel: "Te conectamos con un agente disponible",
2920
+ icon: "human",
2921
+ onClick: requestHuman,
2922
+ loading: requestingHuman
2923
+ };
1858
2924
  }
1859
2925
  if (!isAiMode && session && returnToAiLabel) {
1860
- return { label: returnToAiLabel, sublabel: "Respuesta instant\xE1nea con inteligencia artificial", icon: "ai", onClick: returnToAi, loading: returningToAi };
2926
+ return {
2927
+ label: returnToAiLabel,
2928
+ sublabel: "Respuesta instant\xE1nea con inteligencia artificial",
2929
+ icon: "ai",
2930
+ onClick: returnToAi,
2931
+ loading: returningToAi
2932
+ };
1861
2933
  }
1862
2934
  return void 0;
1863
- }, [isAiMode, session, requestHumanLabel, requestHuman, requestingHuman, returnToAiLabel, returnToAi, returningToAi]);
1864
- return useMemo(() => ({
1865
- messages,
1866
- streaming: sending || agentTyping,
1867
- send,
1868
- reset,
1869
- mode: isAiMode ? "ai" : "human",
1870
- ...footerAction ? { footerAction } : {}
1871
- }), [messages, sending, agentTyping, send, reset, isAiMode, footerAction]);
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
+ );
1872
2981
  }
1873
2982
  var KEY_EXPANDED = "wallavi_bubble_expanded";
1874
2983
  var KEY_DISMISSED = "wallavi_bubble_dismissed";
@@ -1895,50 +3004,62 @@ function BubbleWidget({
1895
3004
  ...chatProps
1896
3005
  }) {
1897
3006
  const supportBackend = useSupportChat(
1898
- inboxToken ? { inboxToken, apiBase: supportApiBase, requestHumanLabel, returnToAiLabel } : { inboxToken: "__disabled__", apiBase: supportApiBase }
3007
+ inboxToken ? {
3008
+ inboxToken,
3009
+ apiBase: supportApiBase,
3010
+ requestHumanLabel,
3011
+ returnToAiLabel
3012
+ } : { inboxToken: "__disabled__", apiBase: supportApiBase }
1899
3013
  );
1900
3014
  const isControlled = isOpenProp !== void 0;
1901
3015
  const [internalOpen, setInternalOpen] = useState(false);
1902
3016
  const open = isControlled ? isOpenProp : internalOpen;
1903
- const setOpen = useCallback((valueOrUpdater) => {
1904
- if (!isControlled) {
1905
- if (typeof valueOrUpdater === "function") {
1906
- setInternalOpen((prev) => {
1907
- const next = valueOrUpdater(prev);
1908
- onOpenChange?.(next);
1909
- return next;
1910
- });
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
+ }
1911
3030
  } else {
1912
- setInternalOpen(valueOrUpdater);
1913
- onOpenChange?.(valueOrUpdater);
3031
+ const next = typeof valueOrUpdater === "function" ? valueOrUpdater(open) : valueOrUpdater;
3032
+ onOpenChange?.(next);
1914
3033
  }
1915
- } else {
1916
- const next = typeof valueOrUpdater === "function" ? valueOrUpdater(open) : valueOrUpdater;
1917
- onOpenChange?.(next);
1918
- }
1919
- }, [isControlled, open, onOpenChange]);
3034
+ },
3035
+ [isControlled, open, onOpenChange]
3036
+ );
1920
3037
  const [expanded, setExpanded] = useState(false);
1921
3038
  const panelRef = useRef(null);
1922
3039
  const autoOpenedRef = useRef(false);
1923
3040
  useEffect(() => {
1924
3041
  if (localStorage.getItem(KEY_EXPANDED) === "true") setExpanded(true);
1925
3042
  }, []);
1926
- const remote = useAutoConfig(inboxToken ? "" : chatProps.agentId ?? "", !inboxToken && autoConfig);
1927
- const resolvedPosition = positionProp ?? remote.position;
1928
- const resolvedBubbleIcon = bubbleIconUrlProp ?? remote.bubbleIconUrl;
1929
- const resolvedAutoOpen = autoOpenProp || remote.autoOpen;
1930
- const resolvedKeyboardShortcut = keyboardShortcutProp || remote.keyboardShortcut;
1931
- const resolvedBubbleSize = bubbleSizeProp ?? remote.bubbleSize;
1932
- const resolvedWidth = widthProp ?? remote.panelWidth;
1933
- const resolvedHeight = heightProp ?? remote.panelHeight;
3043
+ const remote = useAutoConfig(
3044
+ inboxToken ? "" : chatProps.agentId ?? "",
3045
+ !inboxToken && autoConfig
3046
+ );
3047
+ const resolvedPosition = remote.position ?? positionProp;
3048
+ const resolvedBubbleIcon = remote.bubbleIconUrl ?? bubbleIconUrlProp;
3049
+ const resolvedAutoOpen = remote.autoOpen || autoOpenProp;
3050
+ const resolvedKeyboardShortcut = remote.keyboardShortcut || keyboardShortcutProp;
3051
+ const resolvedBubbleSize = remote.bubbleSize ?? bubbleSizeProp;
3052
+ const resolvedWidth = remote.panelWidth ?? widthProp;
3053
+ const resolvedHeight = remote.panelHeight ?? heightProp;
1934
3054
  const definedChatProps = Object.fromEntries(
1935
3055
  Object.entries(chatProps).filter(([, v]) => v !== void 0)
1936
3056
  );
1937
3057
  const mergedConfig = {
1938
- ...remote.remoteConfig,
1939
3058
  ...definedChatProps,
3059
+ ...remote.remoteConfig,
1940
3060
  agentId: chatProps.agentId ?? "",
1941
- agentName: chatProps.agentName ?? ""
3061
+ agentName: remote.remoteConfig.agentName ?? chatProps.agentName ?? "Asistente",
3062
+ source: chatProps.source ?? remote.remoteConfig.source ?? "web"
1942
3063
  };
1943
3064
  const setOpenRef = useRef(setOpen);
1944
3065
  useEffect(() => {
@@ -1955,7 +3076,7 @@ function BubbleWidget({
1955
3076
  useEffect(() => {
1956
3077
  if (!resolvedKeyboardShortcut) return;
1957
3078
  const onKey = (e) => {
1958
- if ((e.metaKey || e.ctrlKey) && e.key === shortcutKey) {
3079
+ if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === shortcutKey.toLowerCase()) {
1959
3080
  e.preventDefault();
1960
3081
  setOpenRef.current((v) => !v);
1961
3082
  }
@@ -1974,7 +3095,10 @@ function BubbleWidget({
1974
3095
  const handleClose = () => {
1975
3096
  setOpen(false);
1976
3097
  if (!isControlled) {
1977
- localStorage.setItem(KEY_DISMISSED, String(Date.now() + 24 * 60 * 60 * 1e3));
3098
+ localStorage.setItem(
3099
+ KEY_DISMISSED,
3100
+ String(Date.now() + 24 * 60 * 60 * 1e3)
3101
+ );
1978
3102
  }
1979
3103
  };
1980
3104
  const toggleExpanded = () => setExpanded((v) => {
@@ -1983,7 +3107,10 @@ function BubbleWidget({
1983
3107
  return next;
1984
3108
  });
1985
3109
  const isLeft = resolvedPosition === "bottom-left";
1986
- const panelWidth = expanded ? Math.min(expandedWidth, typeof window !== "undefined" ? window.innerWidth - 40 : expandedWidth) : resolvedWidth;
3110
+ const panelWidth = expanded ? Math.min(
3111
+ expandedWidth,
3112
+ typeof window !== "undefined" ? window.innerWidth - 40 : expandedWidth
3113
+ ) : resolvedWidth;
1987
3114
  const panelHeight = expanded ? expandedHeight : resolvedHeight;
1988
3115
  return /* @__PURE__ */ jsxs(
1989
3116
  "div",
@@ -2041,15 +3168,20 @@ function BubbleWidget({
2041
3168
  alignItems: "center",
2042
3169
  justifyContent: "center",
2043
3170
  transition: "transform 0.2s ease, box-shadow 0.2s ease",
2044
- background: open ? "var(--foreground, #19191c)" : "var(--background, #fff)",
2045
- color: open ? "var(--background, #fff)" : "var(--foreground, #19191c)"
3171
+ background: open ? "#19191c" : "#ffffff",
3172
+ color: open ? "#ffffff" : "#19191c"
2046
3173
  },
2047
3174
  children: open ? /* @__PURE__ */ jsx(X, { style: { width: 20, height: 20 } }) : resolvedBubbleIcon ? /* @__PURE__ */ jsx(
2048
3175
  "img",
2049
3176
  {
2050
3177
  src: resolvedBubbleIcon,
2051
3178
  alt: "",
2052
- style: { width: "100%", height: "100%", objectFit: "cover", display: "block" }
3179
+ style: {
3180
+ width: "100%",
3181
+ height: "100%",
3182
+ objectFit: "cover",
3183
+ display: "block"
3184
+ }
2053
3185
  }
2054
3186
  ) : /* @__PURE__ */ jsx(DefaultIcon, {})
2055
3187
  }
@@ -2059,4 +3191,4 @@ function BubbleWidget({
2059
3191
  );
2060
3192
  }
2061
3193
 
2062
- export { BubbleWidget, ChatWidget, formatToolName, getContrastColor, useAttachments, useChat, useSupportChat, useVoice };
3194
+ export { BubbleWidget, ChatWidget, PlanCard, ReasoningBlock, ToolCallBadge, formatToolName, getContrastColor, useAttachments, useChat, useSupportChat, useVoice };