@wallavi/widget 1.7.0 → 1.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -8,6 +8,8 @@ var AvatarPrimitive = require('@radix-ui/react-avatar');
8
8
  var jsxRuntime = require('react/jsx-runtime');
9
9
  var ReactMarkdownLib = require('react-markdown');
10
10
  var remarkGfm = require('remark-gfm');
11
+ var componentsReact = require('@livekit/components-react');
12
+ require('@livekit/components-styles');
11
13
 
12
14
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
13
15
 
@@ -56,11 +58,11 @@ function styleInject(css, { insertAt } = {}) {
56
58
  }
57
59
 
58
60
  // src/styles.css
59
- 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");
61
+ 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");
60
62
  var twMerge = tailwindMerge.extendTailwindMerge({ prefix: "ww-" });
61
63
  var cn = (...inputs) => twMerge(clsx.clsx(inputs));
62
64
 
63
- // src/types.ts
65
+ // src/lib/types.ts
64
66
  function getContrastColor(hex) {
65
67
  const clean = hex.replace("#", "");
66
68
  if (clean.length < 6) return "#ffffff";
@@ -72,15 +74,16 @@ function getContrastColor(hex) {
72
74
  function formatToolName(name) {
73
75
  return name.replace(/([A-Z])/g, " $1").replace(/_/g, " ").trim();
74
76
  }
77
+
78
+ // src/core/stream-protocol.ts
75
79
  var STREAM_DELIMITER = "\u03B6\u236E";
76
80
  async function consumeStream(body, handler) {
77
81
  const reader = body.getReader();
78
82
  const decoder = new TextDecoder();
79
83
  let buffer = "";
80
- while (true) {
81
- const { done, value } = await reader.read();
82
- if (done) break;
83
- buffer += decoder.decode(value, { stream: true });
84
+ let textAccumulator = "";
85
+ let eventCount = 0;
86
+ const processBuffer = () => {
84
87
  const chunks = buffer.split(STREAM_DELIMITER);
85
88
  buffer = chunks.pop() ?? "";
86
89
  for (const chunk of chunks) {
@@ -88,19 +91,220 @@ async function consumeStream(body, handler) {
88
91
  if (!raw) continue;
89
92
  try {
90
93
  const parsed = JSON.parse(raw);
91
- if (parsed.data?.uiMessageProtocol) handler(parsed.data.uiMessageProtocol);
94
+ const proto = parsed.data?.uiMessageProtocol;
95
+ if (proto) {
96
+ eventCount++;
97
+ if (proto.type === "text-delta") {
98
+ textAccumulator += proto.delta ?? "";
99
+ }
100
+ handler(proto);
101
+ }
92
102
  } catch {
93
103
  }
94
104
  }
105
+ };
106
+ while (true) {
107
+ const { done, value } = await reader.read();
108
+ if (done) break;
109
+ buffer += decoder.decode(value, { stream: true });
110
+ processBuffer();
111
+ }
112
+ if (buffer.trim()) {
113
+ buffer += STREAM_DELIMITER;
114
+ processBuffer();
115
+ }
116
+ return { textAccumulator, eventCount };
117
+ }
118
+
119
+ // src/core/chat-reducer.ts
120
+ function applyUiEventToMessages(prev, proto, msgId) {
121
+ let idx = prev.findIndex((m) => m.id === msgId);
122
+ if (idx === -1) {
123
+ if (proto.type === "finish" || proto.type === "debug-trace" || proto.type === "step-start" || proto.type === "step-end" || proto.type === "navigate" || proto.type === "suggestions") {
124
+ return prev;
125
+ }
126
+ prev = [...prev, { id: msgId, role: "assistant", parts: [] }];
127
+ idx = prev.length - 1;
128
+ }
129
+ const existing = prev[idx];
130
+ const msg = {
131
+ id: existing.id,
132
+ role: existing.role,
133
+ parts: [...existing.parts]
134
+ };
135
+ switch (proto.type) {
136
+ case "text-delta": {
137
+ const textIdx = msg.parts.findIndex((p) => p.type === "text");
138
+ if (textIdx === -1) {
139
+ msg.parts.push({ type: "text", text: proto.delta });
140
+ } else {
141
+ const p = msg.parts[textIdx];
142
+ msg.parts[textIdx] = { type: "text", text: p.text + proto.delta };
143
+ }
144
+ break;
145
+ }
146
+ case "reasoning-delta": {
147
+ const rIdx = msg.parts.findIndex((p) => p.type === "reasoning");
148
+ if (rIdx === -1) {
149
+ msg.parts.unshift({ type: "reasoning", text: proto.delta });
150
+ } else {
151
+ const p = msg.parts[rIdx];
152
+ msg.parts[rIdx] = {
153
+ type: "reasoning",
154
+ text: p.text + proto.delta
155
+ };
156
+ }
157
+ break;
158
+ }
159
+ case "tool-input-available": {
160
+ msg.parts.push({
161
+ type: "tool",
162
+ toolCallId: proto.toolCallId,
163
+ toolName: proto.toolName,
164
+ input: proto.input ?? {},
165
+ status: "running"
166
+ });
167
+ break;
168
+ }
169
+ case "tool-output-available": {
170
+ const tIdx = msg.parts.findIndex(
171
+ (p) => p.type === "tool" && p.toolCallId === proto.toolCallId
172
+ );
173
+ if (tIdx !== -1) {
174
+ msg.parts[tIdx] = {
175
+ ...msg.parts[tIdx],
176
+ status: "done",
177
+ output: proto.output
178
+ };
179
+ }
180
+ break;
181
+ }
182
+ case "tool-output-error": {
183
+ const tIdx = msg.parts.findIndex(
184
+ (p) => p.type === "tool" && p.toolCallId === proto.toolCallId
185
+ );
186
+ if (tIdx !== -1) {
187
+ msg.parts[tIdx] = {
188
+ ...msg.parts[tIdx],
189
+ status: "error",
190
+ errorText: proto.errorText
191
+ };
192
+ }
193
+ break;
194
+ }
195
+ case "picker": {
196
+ const safeOptions = (proto.options ?? []).map((o) => ({
197
+ value: typeof o.value === "string" ? o.value : JSON.stringify(o.value ?? ""),
198
+ label: typeof o.label === "string" ? o.label : JSON.stringify(o.label ?? "")
199
+ }));
200
+ msg.parts.push({
201
+ type: "picker",
202
+ pickerId: proto.pickerId,
203
+ paramName: proto.paramName,
204
+ toolName: proto.toolName,
205
+ label: typeof proto.label === "string" ? proto.label : String(proto.label),
206
+ options: safeOptions
207
+ });
208
+ break;
209
+ }
210
+ case "plan-created": {
211
+ msg.parts.push({
212
+ type: "plan",
213
+ planId: proto.planId,
214
+ goal: proto.goal,
215
+ steps: proto.steps.map((s) => ({
216
+ ...s,
217
+ status: "pending"
218
+ }))
219
+ });
220
+ break;
221
+ }
222
+ case "plan-step-update": {
223
+ const pIdx = msg.parts.findIndex(
224
+ (p) => p.type === "plan" && p.planId === proto.planId
225
+ );
226
+ if (pIdx !== -1) {
227
+ const prevPlan = msg.parts[pIdx];
228
+ msg.parts[pIdx] = {
229
+ ...prevPlan,
230
+ steps: prevPlan.steps.map(
231
+ (s) => s.index === proto.stepIndex ? {
232
+ ...s,
233
+ status: proto.status,
234
+ ...proto.error ? { error: proto.error } : {}
235
+ } : s
236
+ )
237
+ };
238
+ }
239
+ break;
240
+ }
95
241
  }
242
+ const copy = [...prev];
243
+ copy[idx] = msg;
244
+ return copy;
96
245
  }
97
246
  var API_URL = process.env.NEXT_PUBLIC_API_URL ?? "https://wallavi-production.up.railway.app";
247
+ function useVoiceCall({
248
+ agentId,
249
+ threadId,
250
+ workspaceId,
251
+ customBackend
252
+ }) {
253
+ const [active, setActive] = react.useState(false);
254
+ const [token, setToken] = react.useState(null);
255
+ const [serverUrl, setServerUrl] = react.useState(null);
256
+ const [loading, setLoading] = react.useState(false);
257
+ const [error, setError] = react.useState(null);
258
+ const start = react.useCallback(async () => {
259
+ if (customBackend) return;
260
+ setLoading(true);
261
+ setError(null);
262
+ try {
263
+ const isPrivate = Boolean(workspaceId);
264
+ 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)}`;
265
+ const res = await fetch(url, {
266
+ headers: isPrivate && typeof window !== "undefined" ? (
267
+ // @ts-ignore
268
+ {
269
+ Authorization: `Bearer ${await window.Clerk?.session?.getToken()}`
270
+ }
271
+ ) : {}
272
+ });
273
+ const data = await res.json();
274
+ if (!res.ok) throw new Error(data.error || "Failed to start call");
275
+ setToken(data.token);
276
+ setServerUrl(data.url);
277
+ setActive(true);
278
+ } catch (err) {
279
+ setError(err.message);
280
+ }
281
+ setLoading(false);
282
+ }, [agentId, threadId, workspaceId, customBackend]);
283
+ const stop = react.useCallback(() => {
284
+ setActive(false);
285
+ setToken(null);
286
+ setServerUrl(null);
287
+ }, []);
288
+ return customBackend?.voiceCall ?? {
289
+ active,
290
+ token,
291
+ serverUrl,
292
+ start,
293
+ stop,
294
+ loading,
295
+ error
296
+ };
297
+ }
298
+
299
+ // src/hooks/use-chat.ts
300
+ var API_URL2 = process.env.NEXT_PUBLIC_API_URL ?? "https://wallavi-production.up.railway.app";
98
301
  function newId() {
99
302
  return Math.random().toString(36).slice(2, 10);
100
303
  }
101
304
  function useChat({
102
305
  agentId,
103
306
  workspaceId = "",
307
+ envId,
104
308
  source = "playground",
105
309
  userContext,
106
310
  persist = false,
@@ -108,7 +312,8 @@ function useChat({
108
312
  playgroundOverrides,
109
313
  customBackend
110
314
  }) {
111
- const persistKey = persist ? `wallavi_${agentId}` : null;
315
+ const userId = userContext?.userId;
316
+ const persistKey = persist ? userId ? `wallavi_${agentId}_${userId}` : `wallavi_${agentId}` : null;
112
317
  const onNavigateRef = react.useRef(onNavigate);
113
318
  react.useEffect(() => {
114
319
  onNavigateRef.current = onNavigate;
@@ -132,6 +337,119 @@ function useChat({
132
337
  return crypto.randomUUID();
133
338
  });
134
339
  const streamingMsgIdRef = react.useRef(null);
340
+ const [debugTraces, setDebugTraces] = react.useState([]);
341
+ const initializedThreadsRef = react.useRef(/* @__PURE__ */ new Set());
342
+ const messagesRef = react.useRef(messages);
343
+ react.useEffect(() => {
344
+ messagesRef.current = messages;
345
+ }, [messages]);
346
+ const applyStreamEvent = react.useCallback(
347
+ (proto, msgId) => {
348
+ if (proto.type === "navigate") {
349
+ if (proto.path.startsWith("/")) onNavigateRef.current?.(proto.path);
350
+ return;
351
+ }
352
+ if (proto.type === "debug-trace") {
353
+ setDebugTraces((prev) => [
354
+ ...prev,
355
+ proto.trace
356
+ ]);
357
+ return;
358
+ }
359
+ setMessages((prev) => applyUiEventToMessages(prev, proto, msgId));
360
+ },
361
+ []
362
+ );
363
+ const fetchAndStream = react.useCallback(
364
+ async (opts) => {
365
+ const { input: userInput, msgId, extraMetadata, attachments } = opts;
366
+ const isPrivate = Boolean(workspaceId);
367
+ const token = isPrivate && typeof window !== "undefined" ? await window.Clerk?.session?.getToken() : null;
368
+ const url = isPrivate ? `${API_URL2}/api/threads/${threadId}/stream` : `${API_URL2}/api/chat/stream`;
369
+ const res = await fetch(url, {
370
+ method: "POST",
371
+ headers: {
372
+ "Content-Type": "application/json",
373
+ ...token ? { Authorization: `Bearer ${token}` } : {}
374
+ },
375
+ body: JSON.stringify({
376
+ input: userInput,
377
+ agentId,
378
+ ...isPrivate ? {
379
+ workspaceId,
380
+ ...playgroundOverrides ? { playgroundOverrides } : {},
381
+ ...envId ? { envId } : {}
382
+ } : { threadId },
383
+ source,
384
+ ...attachments?.length ? { attachments } : {},
385
+ ...userContext?.userName ? { userName: userContext.userName } : {},
386
+ ...userContext?.userEmail ? { userEmail: userContext.userEmail } : {},
387
+ userMetadata: {
388
+ ...userContext?.metadata ?? {},
389
+ ...userContext?.pageContext ? { pageContext: userContext.pageContext } : {},
390
+ headers: {
391
+ ...token ? { Authorization: `Bearer ${token}` } : {},
392
+ ...userContext?.headers ?? {},
393
+ ...userContext?.metadata?.headers ?? {}
394
+ },
395
+ ...extraMetadata ?? {}
396
+ }
397
+ })
398
+ });
399
+ if (!res.ok) {
400
+ const errText = await res.text().catch(() => "");
401
+ throw new Error(errText || `API error ${res.status}`);
402
+ }
403
+ if (!res.body) throw new Error("No stream body");
404
+ const { textAccumulator, eventCount } = await consumeStream(
405
+ res.body,
406
+ (proto) => applyStreamEvent(proto, msgId)
407
+ );
408
+ if (textAccumulator) {
409
+ setMessages((prev) => {
410
+ const idx = prev.findIndex((m) => m.id === msgId);
411
+ if (idx === -1) {
412
+ return [
413
+ ...prev,
414
+ {
415
+ id: msgId,
416
+ role: "assistant",
417
+ parts: [{ type: "text", text: textAccumulator }]
418
+ }
419
+ ];
420
+ }
421
+ const existing = prev[idx];
422
+ const textPart = existing.parts.find((p) => p.type === "text");
423
+ if (!textPart?.text || textPart.text.length < textAccumulator.length) {
424
+ const copy = [...prev];
425
+ const parts = [
426
+ ...existing.parts.filter((p) => p.type !== "text"),
427
+ { type: "text", text: textAccumulator }
428
+ ];
429
+ copy[idx] = { ...existing, parts };
430
+ return copy;
431
+ }
432
+ return prev;
433
+ });
434
+ }
435
+ },
436
+ [
437
+ agentId,
438
+ workspaceId,
439
+ envId,
440
+ source,
441
+ threadId,
442
+ userContext,
443
+ playgroundOverrides,
444
+ applyStreamEvent
445
+ ]
446
+ );
447
+ const voiceCall = useVoiceCall({
448
+ agentId,
449
+ threadId,
450
+ workspaceId,
451
+ customBackend
452
+ });
135
453
  react.useEffect(() => {
136
454
  if (customBackend || !persistKey) return;
137
455
  try {
@@ -148,30 +466,108 @@ function useChat({
148
466
  }, [customBackend, persistKey, threadId]);
149
467
  react.useEffect(() => {
150
468
  if (customBackend || !persistKey || typeof window === "undefined") return;
151
- if (messages.length > 0) return;
152
- void (async () => {
153
- try {
154
- const res = await fetch(
155
- `${API_URL}/api/chat/messages?agentId=${encodeURIComponent(agentId)}&threadId=${encodeURIComponent(threadId)}`
156
- );
157
- if (!res.ok) return;
158
- const { data } = await res.json();
159
- if (!data?.length) return;
160
- const restored = data.map((m) => ({
161
- id: m.id,
162
- role: m.role,
163
- parts: m.parts.filter((p) => p?.type)
164
- })).filter((m) => m.parts.length > 0);
165
- if (restored.length > 0) setMessages(restored);
166
- } catch {
469
+ const savedTid = localStorage.getItem(`${persistKey}_tid`);
470
+ if (savedTid) {
471
+ if (savedTid !== threadId) {
472
+ setThreadId(savedTid);
473
+ const savedMsgs = sessionStorage.getItem(`${persistKey}_msgs`);
474
+ if (savedMsgs) {
475
+ try {
476
+ setMessages(JSON.parse(savedMsgs));
477
+ } catch {
478
+ }
479
+ } else {
480
+ setMessages([]);
481
+ }
167
482
  }
168
- })();
169
- }, []);
483
+ } else {
484
+ const newTid = crypto.randomUUID();
485
+ setThreadId(newTid);
486
+ setMessages([]);
487
+ localStorage.setItem(`${persistKey}_tid`, newTid);
488
+ }
489
+ }, [customBackend, persistKey]);
490
+ react.useEffect(() => {
491
+ if (customBackend || typeof window === "undefined") return;
492
+ if (initializedThreadsRef.current.has(threadId)) return;
493
+ initializedThreadsRef.current.add(threadId);
494
+ if (persistKey) {
495
+ if (messagesRef.current.length > 0) return;
496
+ void (async () => {
497
+ try {
498
+ const isPrivate = Boolean(workspaceId);
499
+ const token = isPrivate && typeof window !== "undefined" ? await window.Clerk?.session?.getToken() : null;
500
+ const url = isPrivate ? `${API_URL2}/api/threads/${threadId}/messages` : `${API_URL2}/api/chat/messages?agentId=${encodeURIComponent(agentId)}&threadId=${encodeURIComponent(threadId)}`;
501
+ const res = await fetch(url, {
502
+ headers: {
503
+ ...token ? { Authorization: `Bearer ${token}` } : {}
504
+ }
505
+ });
506
+ if (!res.ok) return;
507
+ const { data } = await res.json();
508
+ if (!data?.length) {
509
+ try {
510
+ fetchAndStream({
511
+ input: "__INIT__",
512
+ msgId: "init",
513
+ extraMetadata: { isSilentInit: true }
514
+ });
515
+ } catch {
516
+ }
517
+ return;
518
+ }
519
+ const restored = data.map((m) => ({
520
+ id: m.id,
521
+ role: m.role,
522
+ parts: m.parts.filter((p) => p?.type).map((p) => {
523
+ if (p.type === "picker" && Array.isArray(p.options)) {
524
+ return {
525
+ ...p,
526
+ label: typeof p.label === "string" ? p.label : String(p.label ?? ""),
527
+ options: p.options.map((o) => ({
528
+ ...o,
529
+ value: typeof o.value === "string" ? o.value : JSON.stringify(o.value ?? ""),
530
+ label: typeof o.label === "string" ? o.label : JSON.stringify(o.label ?? "")
531
+ }))
532
+ };
533
+ }
534
+ return p;
535
+ })
536
+ })).filter((m) => m.parts.length > 0);
537
+ if (restored.length > 0) {
538
+ setMessages(restored);
539
+ } else {
540
+ try {
541
+ fetchAndStream({
542
+ input: "__INIT__",
543
+ msgId: "init",
544
+ extraMetadata: { isSilentInit: true }
545
+ });
546
+ } catch {
547
+ }
548
+ }
549
+ } catch {
550
+ }
551
+ })();
552
+ } else {
553
+ if (messagesRef.current.length === 0) {
554
+ try {
555
+ fetchAndStream({
556
+ input: "__INIT__",
557
+ msgId: "init",
558
+ extraMetadata: { isSilentInit: true }
559
+ });
560
+ } catch {
561
+ }
562
+ }
563
+ }
564
+ }, [threadId, agentId, workspaceId, envId, persistKey, fetchAndStream]);
170
565
  const reset = react.useCallback(() => {
171
566
  setMessages([]);
172
567
  setInput("");
173
568
  setStreaming(false);
174
569
  setThreadId(crypto.randomUUID());
570
+ setDebugTraces([]);
175
571
  streamingMsgIdRef.current = null;
176
572
  if (persistKey) {
177
573
  try {
@@ -182,143 +578,6 @@ function useChat({
182
578
  }
183
579
  }
184
580
  }, [persistKey]);
185
- const applyStreamEvent = react.useCallback((proto, msgId) => {
186
- if (proto.type === "navigate") {
187
- if (proto.path.startsWith("/")) onNavigateRef.current?.(proto.path);
188
- return;
189
- }
190
- setMessages((prev) => {
191
- const idx = prev.findIndex((m) => m.id === msgId);
192
- if (idx === -1) return prev;
193
- const existing = prev[idx];
194
- const msg = { id: existing.id, role: existing.role, parts: [...existing.parts] };
195
- switch (proto.type) {
196
- case "text-delta": {
197
- const textIdx = msg.parts.findIndex((p) => p.type === "text");
198
- if (textIdx === -1) {
199
- msg.parts.push({ type: "text", text: proto.delta });
200
- } else {
201
- const p = msg.parts[textIdx];
202
- msg.parts[textIdx] = { type: "text", text: p.text + proto.delta };
203
- }
204
- break;
205
- }
206
- case "reasoning-delta": {
207
- const rIdx = msg.parts.findIndex((p) => p.type === "reasoning");
208
- if (rIdx === -1) {
209
- msg.parts.unshift({ type: "reasoning", text: proto.delta });
210
- } else {
211
- const p = msg.parts[rIdx];
212
- msg.parts[rIdx] = { type: "reasoning", text: p.text + proto.delta };
213
- }
214
- break;
215
- }
216
- case "tool-input-available": {
217
- msg.parts.push({
218
- type: "tool",
219
- toolCallId: proto.toolCallId,
220
- toolName: proto.toolName,
221
- input: proto.input ?? {},
222
- status: "running"
223
- });
224
- break;
225
- }
226
- case "tool-output-available": {
227
- const tIdx = msg.parts.findIndex(
228
- (p) => p.type === "tool" && p.toolCallId === proto.toolCallId
229
- );
230
- if (tIdx !== -1) {
231
- msg.parts[tIdx] = { ...msg.parts[tIdx], status: "done", output: proto.output };
232
- }
233
- break;
234
- }
235
- case "tool-output-error": {
236
- const tIdx = msg.parts.findIndex(
237
- (p) => p.type === "tool" && p.toolCallId === proto.toolCallId
238
- );
239
- if (tIdx !== -1) {
240
- msg.parts[tIdx] = { ...msg.parts[tIdx], status: "error", errorText: proto.errorText };
241
- }
242
- break;
243
- }
244
- case "picker": {
245
- msg.parts.push({
246
- type: "picker",
247
- pickerId: proto.pickerId,
248
- paramName: proto.paramName,
249
- toolName: proto.toolName,
250
- label: proto.label,
251
- options: proto.options
252
- });
253
- break;
254
- }
255
- case "plan-created": {
256
- msg.parts.push({
257
- type: "plan",
258
- planId: proto.planId,
259
- goal: proto.goal,
260
- steps: proto.steps.map((s) => ({ ...s, status: "pending" }))
261
- });
262
- break;
263
- }
264
- case "plan-step-update": {
265
- const pIdx = msg.parts.findIndex(
266
- (p) => p.type === "plan" && p.planId === proto.planId
267
- );
268
- if (pIdx !== -1) {
269
- const prev2 = msg.parts[pIdx];
270
- msg.parts[pIdx] = {
271
- ...prev2,
272
- steps: prev2.steps.map(
273
- (s) => s.index === proto.stepIndex ? { ...s, status: proto.status, ...proto.error ? { error: proto.error } : {} } : s
274
- )
275
- };
276
- }
277
- break;
278
- }
279
- }
280
- const copy = [...prev];
281
- copy[idx] = msg;
282
- return copy;
283
- });
284
- }, []);
285
- const fetchAndStream = react.useCallback(async (opts) => {
286
- const { input: userInput, msgId, extraMetadata, attachments } = opts;
287
- const isPrivate = Boolean(workspaceId);
288
- const token = isPrivate && typeof window !== "undefined" ? await window.Clerk?.session?.getToken() : null;
289
- const url = isPrivate ? `${API_URL}/api/threads/${threadId}/stream` : `${API_URL}/api/chat/stream`;
290
- const res = await fetch(url, {
291
- method: "POST",
292
- headers: {
293
- "Content-Type": "application/json",
294
- ...token ? { Authorization: `Bearer ${token}` } : {}
295
- },
296
- body: JSON.stringify({
297
- input: userInput,
298
- agentId,
299
- ...isPrivate ? { workspaceId, ...playgroundOverrides ? { playgroundOverrides } : {} } : { threadId },
300
- source,
301
- ...attachments?.length ? { attachments } : {},
302
- ...userContext?.userName ? { userName: userContext.userName } : {},
303
- ...userContext?.userEmail ? { userEmail: userContext.userEmail } : {},
304
- userMetadata: {
305
- ...userContext?.metadata ?? {},
306
- ...userContext?.pageContext ? { pageContext: userContext.pageContext } : {},
307
- headers: {
308
- ...token ? { Authorization: `Bearer ${token}` } : {},
309
- ...userContext?.headers ?? {}
310
- },
311
- ...extraMetadata ?? {}
312
- }
313
- })
314
- });
315
- if (!res.ok) {
316
- const errText = await res.text().catch(() => "");
317
- throw new Error(errText || `API error ${res.status}`);
318
- }
319
- if (!res.body) throw new Error("No stream body");
320
- await consumeStream(res.body, (proto) => applyStreamEvent(proto, msgId));
321
- }, [agentId, workspaceId, source, threadId, userContext, playgroundOverrides, applyStreamEvent]);
322
581
  const pendingAttachmentsRef = react.useRef([]);
323
582
  const send = react.useCallback(
324
583
  async (text) => {
@@ -326,7 +585,8 @@ function useChat({
326
585
  const content = (text ?? input).trim();
327
586
  const attachments2 = pendingAttachmentsRef.current.length > 0 ? [...pendingAttachmentsRef.current] : void 0;
328
587
  pendingAttachmentsRef.current = [];
329
- if (!content && !attachments2?.length || customBackend.streaming) return;
588
+ if (!content && !attachments2?.length || customBackend.streaming)
589
+ return;
330
590
  setInput("");
331
591
  await customBackend.send(content, attachments2);
332
592
  return;
@@ -349,16 +609,32 @@ function useChat({
349
609
  setStreaming(true);
350
610
  const assistantMsgId = newId();
351
611
  streamingMsgIdRef.current = assistantMsgId;
352
- setMessages((prev) => [...prev, { id: assistantMsgId, role: "assistant", parts: [] }]);
612
+ setMessages((prev) => [
613
+ ...prev,
614
+ { id: assistantMsgId, role: "assistant", parts: [] }
615
+ ]);
353
616
  try {
354
- await fetchAndStream({ input: userInput, msgId: assistantMsgId, attachments });
617
+ await fetchAndStream({
618
+ input: userInput,
619
+ msgId: assistantMsgId,
620
+ attachments
621
+ });
355
622
  } catch {
356
623
  setMessages((prev) => {
357
624
  const idx = prev.findIndex((m) => m.id === assistantMsgId);
358
625
  if (idx === -1) return prev;
359
626
  const copy = [...prev];
360
627
  const err = copy[idx];
361
- copy[idx] = { id: err.id, role: err.role, parts: [{ type: "text", text: "Sorry, something went wrong. Please try again." }] };
628
+ copy[idx] = {
629
+ id: err.id,
630
+ role: err.role,
631
+ parts: [
632
+ {
633
+ type: "text",
634
+ text: "Sorry, something went wrong. Please try again."
635
+ }
636
+ ]
637
+ };
362
638
  return copy;
363
639
  });
364
640
  }
@@ -374,19 +650,27 @@ function useChat({
374
650
  (prev) => prev.map((msg) => ({
375
651
  ...msg,
376
652
  parts: msg.parts.map(
377
- (part) => part.type === "picker" && part.pickerId === pickerId ? { ...part, selectedValue: value } : part
653
+ (part) => part.type === "picker" && part.pickerId === pickerId ? {
654
+ ...part,
655
+ selectedValue: value
656
+ } : part
378
657
  )
379
658
  }))
380
659
  );
381
660
  setStreaming(true);
382
661
  const assistantMsgId = newId();
383
662
  streamingMsgIdRef.current = assistantMsgId;
384
- setMessages((prev) => [...prev, { id: assistantMsgId, role: "assistant", parts: [] }]);
663
+ setMessages((prev) => [
664
+ ...prev,
665
+ { id: assistantMsgId, role: "assistant", parts: [] }
666
+ ]);
385
667
  try {
386
668
  await fetchAndStream({
387
669
  input: label,
388
670
  msgId: assistantMsgId,
389
- extraMetadata: { __pickerSelection: { pickerId, paramName, value, label } }
671
+ extraMetadata: {
672
+ __pickerSelection: { pickerId, paramName, value, label }
673
+ }
390
674
  });
391
675
  } catch {
392
676
  setMessages((prev) => {
@@ -394,7 +678,16 @@ function useChat({
394
678
  if (idx === -1) return prev;
395
679
  const copy = [...prev];
396
680
  const err = copy[idx];
397
- copy[idx] = { id: err.id, role: err.role, parts: [{ type: "text", text: "Sorry, something went wrong. Please try again." }] };
681
+ copy[idx] = {
682
+ id: err.id,
683
+ role: err.role,
684
+ parts: [
685
+ {
686
+ type: "text",
687
+ text: "Sorry, something went wrong. Please try again."
688
+ }
689
+ ]
690
+ };
398
691
  return copy;
399
692
  });
400
693
  }
@@ -429,7 +722,9 @@ function useChat({
429
722
  queueAttachments,
430
723
  regenerate,
431
724
  reset: customBackend?.reset ?? reset,
432
- selectPickerOption
725
+ selectPickerOption,
726
+ debugTraces,
727
+ voiceCall
433
728
  };
434
729
  }
435
730
  function getPreferredMimeType() {
@@ -449,7 +744,12 @@ function mimeTypeToExtension(mimeType) {
449
744
  return "webm";
450
745
  }
451
746
  var DEFAULT_API_URL = process.env.NEXT_PUBLIC_API_URL ?? "https://wallavi-production.up.railway.app";
452
- function useVoice({ agentId, apiUrl, onTranscript, onError }) {
747
+ function useVoice({
748
+ agentId,
749
+ apiUrl,
750
+ onTranscript,
751
+ onError
752
+ }) {
453
753
  const [voiceState, setVoiceState] = react.useState("idle");
454
754
  const recorderRef = react.useRef(null);
455
755
  const chunksRef = react.useRef([]);
@@ -465,10 +765,14 @@ function useVoice({ agentId, apiUrl, onTranscript, onError }) {
465
765
  const form = new FormData();
466
766
  form.append("audio", blob, `recording.${ext}`);
467
767
  form.append("agentId", agentId);
468
- const res = await fetch(`${base}/api/chat/transcribe`, { method: "POST", body: form });
768
+ const res = await fetch(`${base}/api/chat/transcribe`, {
769
+ method: "POST",
770
+ body: form
771
+ });
469
772
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
470
773
  const data = await res.json();
471
- if (!data.text?.trim()) throw new Error(data.error ?? "Empty transcript");
774
+ if (!data.text?.trim())
775
+ throw new Error(data.error ?? "Empty transcript");
472
776
  onTranscript(data.text.trim());
473
777
  setVoiceState("idle");
474
778
  } catch (err) {
@@ -486,14 +790,19 @@ function useVoice({ agentId, apiUrl, onTranscript, onError }) {
486
790
  const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
487
791
  streamRef.current = stream;
488
792
  const mimeType = getPreferredMimeType();
489
- const recorder = new MediaRecorder(stream, mimeType ? { mimeType } : void 0);
793
+ const recorder = new MediaRecorder(
794
+ stream,
795
+ mimeType ? { mimeType } : void 0
796
+ );
490
797
  chunksRef.current = [];
491
798
  recorder.ondataavailable = (e) => {
492
799
  if (e.data.size > 0) chunksRef.current.push(e.data);
493
800
  };
494
801
  recorder.onstop = async () => {
495
802
  stream.getTracks().forEach((t) => t.stop());
496
- const blob = new Blob(chunksRef.current, { type: mimeType || "audio/webm" });
803
+ const blob = new Blob(chunksRef.current, {
804
+ type: mimeType || "audio/webm"
805
+ });
497
806
  if (blob.size === 0) {
498
807
  onError?.("Recording was empty \u2014 please try again.");
499
808
  setVoiceState("idle");
@@ -519,7 +828,8 @@ function useVoice({ agentId, apiUrl, onTranscript, onError }) {
519
828
  react.useEffect(() => {
520
829
  return () => {
521
830
  if (errorTimerRef.current) clearTimeout(errorTimerRef.current);
522
- if (recorderRef.current?.state === "recording") recorderRef.current.stop();
831
+ if (recorderRef.current?.state === "recording")
832
+ recorderRef.current.stop();
523
833
  streamRef.current?.getTracks().forEach((t) => t.stop());
524
834
  };
525
835
  }, []);
@@ -542,7 +852,10 @@ function useAttachments({
542
852
  form.append("file", file);
543
853
  form.append("agentId", agentId);
544
854
  try {
545
- const res = await fetch(`${base}/api/chat/upload`, { method: "POST", body: form });
855
+ const res = await fetch(`${base}/api/chat/upload`, {
856
+ method: "POST",
857
+ body: form
858
+ });
546
859
  if (!res.ok) {
547
860
  const data = await res.json().catch(() => ({}));
548
861
  setAttachments(
@@ -558,7 +871,9 @@ function useAttachments({
558
871
  }
559
872
  const payload = await res.json();
560
873
  setAttachments(
561
- (prev) => prev.map((a) => a.id === id ? { ...a, status: "ready", payload } : a)
874
+ (prev) => prev.map(
875
+ (a) => a.id === id ? { ...a, status: "ready", payload } : a
876
+ )
562
877
  );
563
878
  } catch (err) {
564
879
  const msg = err instanceof Error ? err.message : "Upload failed";
@@ -602,16 +917,13 @@ function useAttachments({
602
917
  return { attachments, attach, remove, clear, isUploading, readyPayloads };
603
918
  }
604
919
  function DefaultIcon() {
605
- return /* @__PURE__ */ jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", fill: "none", style: { width: 26, height: 26 }, children: [
606
- /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "12", fill: "currentColor", opacity: 0.12 }),
607
- /* @__PURE__ */ jsxRuntime.jsx(
608
- "path",
609
- {
610
- 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",
611
- fill: "currentColor"
612
- }
613
- )
614
- ] });
920
+ return /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", style: { width: 26, height: 26 }, children: /* @__PURE__ */ jsxRuntime.jsx(
921
+ "path",
922
+ {
923
+ 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",
924
+ fill: "currentColor"
925
+ }
926
+ ) });
615
927
  }
616
928
  function ExpandIcon() {
617
929
  return /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", style: { width: 11, height: 11 }, children: /* @__PURE__ */ jsxRuntime.jsx(
@@ -637,9 +949,58 @@ function MinimizeIcon() {
637
949
  }
638
950
  ) });
639
951
  }
640
- var Avatar = ({ style, ...p }) => /* @__PURE__ */ jsxRuntime.jsx(AvatarPrimitive__namespace.Root, { style: { position: "relative", display: "flex", flexShrink: 0, overflow: "hidden", borderRadius: "9999px", ...style }, ...p });
641
- var AvatarImage = ({ style, ...p }) => /* @__PURE__ */ jsxRuntime.jsx(AvatarPrimitive__namespace.Image, { style: { position: "absolute", inset: 0, width: "100%", height: "100%", objectFit: "cover", ...style }, ...p });
642
- var AvatarFallback = ({ style, ...p }) => /* @__PURE__ */ jsxRuntime.jsx(AvatarPrimitive__namespace.Fallback, { style: { display: "flex", width: "100%", height: "100%", alignItems: "center", justifyContent: "center", borderRadius: "9999px", ...style }, ...p });
952
+ var Avatar = ({
953
+ style,
954
+ ...p
955
+ }) => /* @__PURE__ */ jsxRuntime.jsx(
956
+ AvatarPrimitive__namespace.Root,
957
+ {
958
+ style: {
959
+ position: "relative",
960
+ display: "flex",
961
+ flexShrink: 0,
962
+ overflow: "hidden",
963
+ borderRadius: "9999px",
964
+ ...style
965
+ },
966
+ ...p
967
+ }
968
+ );
969
+ var AvatarImage = ({
970
+ style,
971
+ ...p
972
+ }) => /* @__PURE__ */ jsxRuntime.jsx(
973
+ AvatarPrimitive__namespace.Image,
974
+ {
975
+ style: {
976
+ position: "absolute",
977
+ inset: 0,
978
+ width: "100%",
979
+ height: "100%",
980
+ objectFit: "cover",
981
+ ...style
982
+ },
983
+ ...p
984
+ }
985
+ );
986
+ var AvatarFallback = ({
987
+ style,
988
+ ...p
989
+ }) => /* @__PURE__ */ jsxRuntime.jsx(
990
+ AvatarPrimitive__namespace.Fallback,
991
+ {
992
+ style: {
993
+ display: "flex",
994
+ width: "100%",
995
+ height: "100%",
996
+ alignItems: "center",
997
+ justifyContent: "center",
998
+ borderRadius: "9999px",
999
+ ...style
1000
+ },
1001
+ ...p
1002
+ }
1003
+ );
643
1004
  function ChatHeader({
644
1005
  title,
645
1006
  profilePicture,
@@ -648,22 +1009,39 @@ function ChatHeader({
648
1009
  onReset,
649
1010
  onClose,
650
1011
  onExpand,
651
- expanded
1012
+ expanded,
1013
+ onCall,
1014
+ isCallLoading
652
1015
  }) {
653
1016
  return /* @__PURE__ */ jsxRuntime.jsxs(
654
1017
  "header",
655
1018
  {
656
- className: "ww-flex ww-items-center ww-justify-between ww-px-4 ww-py-3 ww-shrink-0",
657
- style: { backgroundColor: headerBg, color: headerText, borderBottom: `1px solid ${headerText}18` },
1019
+ className: "ww-flex ww-items-center ww-justify-between ww-px-4 ww-py-3 ww-shrink-0 ww-touch-none",
1020
+ style: {
1021
+ backgroundColor: headerBg,
1022
+ color: headerText,
1023
+ borderBottom: `1px solid ${headerText}18`
1024
+ },
658
1025
  children: [
659
1026
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ww-flex ww-items-center ww-gap-2.5 ww-min-w-0", children: [
660
- /* @__PURE__ */ jsxRuntime.jsx(Avatar, { style: { width: 32, height: 32, border: `2px solid ${headerText}30` }, children: profilePicture ? /* @__PURE__ */ jsxRuntime.jsx(AvatarImage, { src: profilePicture, alt: title }) : /* @__PURE__ */ jsxRuntime.jsx(
661
- AvatarFallback,
1027
+ /* @__PURE__ */ jsxRuntime.jsx(
1028
+ Avatar,
662
1029
  {
663
- style: { backgroundColor: `${headerText}20`, color: headerText, fontSize: 11, fontWeight: 700 },
664
- children: title.slice(0, 2).toUpperCase()
1030
+ style: { width: 32, height: 32, border: `2px solid ${headerText}30` },
1031
+ children: profilePicture ? /* @__PURE__ */ jsxRuntime.jsx(AvatarImage, { src: profilePicture, alt: title }) : /* @__PURE__ */ jsxRuntime.jsx(
1032
+ AvatarFallback,
1033
+ {
1034
+ style: {
1035
+ backgroundColor: `${headerText}20`,
1036
+ color: headerText,
1037
+ fontSize: 11,
1038
+ fontWeight: 700
1039
+ },
1040
+ children: title.slice(0, 2).toUpperCase()
1041
+ }
1042
+ )
665
1043
  }
666
- ) }),
1044
+ ),
667
1045
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ww-min-w-0", children: [
668
1046
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "ww-font-semibold ww-text-sm ww-truncate ww-leading-tight", children: title }),
669
1047
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "ww-text-[10px] ww-opacity-70 ww-leading-tight", children: "AI Assistant" })
@@ -679,13 +1057,64 @@ function ChatHeader({
679
1057
  children: expanded ? /* @__PURE__ */ jsxRuntime.jsx(MinimizeIcon, {}) : /* @__PURE__ */ jsxRuntime.jsx(ExpandIcon, {})
680
1058
  }
681
1059
  ),
1060
+ onCall && /* @__PURE__ */ jsxRuntime.jsx(
1061
+ "button",
1062
+ {
1063
+ onClick: onCall,
1064
+ disabled: isCallLoading,
1065
+ className: "ww-rounded-full ww-p-1.5 ww-transition-colors hover:ww-bg-white/10 disabled:ww-opacity-50",
1066
+ title: "Voice Call",
1067
+ children: isCallLoading ? /* @__PURE__ */ jsxRuntime.jsxs(
1068
+ "svg",
1069
+ {
1070
+ className: "ww-animate-spin ww-h-3.5 ww-w-3.5",
1071
+ viewBox: "0 0 24 24",
1072
+ fill: "none",
1073
+ style: { color: headerText },
1074
+ children: [
1075
+ /* @__PURE__ */ jsxRuntime.jsx(
1076
+ "circle",
1077
+ {
1078
+ className: "ww-opacity-25",
1079
+ cx: "12",
1080
+ cy: "12",
1081
+ r: "10",
1082
+ stroke: "currentColor",
1083
+ strokeWidth: "4"
1084
+ }
1085
+ ),
1086
+ /* @__PURE__ */ jsxRuntime.jsx(
1087
+ "path",
1088
+ {
1089
+ className: "ww-opacity-75",
1090
+ fill: "currentColor",
1091
+ 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"
1092
+ }
1093
+ )
1094
+ ]
1095
+ }
1096
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
1097
+ lucideReact.Phone,
1098
+ {
1099
+ className: "ww-h-3.5 ww-w-3.5",
1100
+ style: { color: headerText }
1101
+ }
1102
+ )
1103
+ }
1104
+ ),
682
1105
  /* @__PURE__ */ jsxRuntime.jsx(
683
1106
  "button",
684
1107
  {
685
1108
  onClick: onReset,
686
1109
  className: "ww-rounded-full ww-p-1.5 ww-transition-colors hover:ww-bg-white/10",
687
1110
  title: "New conversation",
688
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RotateCcw, { className: "ww-h-3.5 ww-w-3.5", style: { color: headerText } })
1111
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1112
+ lucideReact.RotateCcw,
1113
+ {
1114
+ className: "ww-h-3.5 ww-w-3.5",
1115
+ style: { color: headerText }
1116
+ }
1117
+ )
689
1118
  }
690
1119
  ),
691
1120
  onClose && /* @__PURE__ */ jsxRuntime.jsx(
@@ -709,8 +1138,10 @@ function formatBytes(bytes) {
709
1138
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
710
1139
  }
711
1140
  function ChipLeading({ a }) {
712
- if (a.status === "uploading") return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "ww-h-3 ww-w-3 ww-shrink-0 ww-animate-spin" });
713
- if (a.status === "error") return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircle, { className: "ww-h-3 ww-w-3 ww-shrink-0" });
1141
+ if (a.status === "uploading")
1142
+ return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "ww-h-3 ww-w-3 ww-shrink-0 ww-animate-spin" });
1143
+ if (a.status === "error")
1144
+ return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircle, { className: "ww-h-3 ww-w-3 ww-shrink-0" });
714
1145
  const thumbSrc = a.payload?.url ?? a.payload?.base64;
715
1146
  if (a.mimeType.startsWith("image/") && thumbSrc) {
716
1147
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -722,14 +1153,19 @@ function ChipLeading({ a }) {
722
1153
  }
723
1154
  );
724
1155
  }
725
- if (a.mimeType.startsWith("image/")) return /* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsx("span", { className: "ww-text-[8px] ww-font-semibold ww-text-muted-foreground/50", children: "IMG" }) });
726
- if (a.mimeType === "application/pdf") return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileText, { className: "ww-h-3 ww-w-3 ww-shrink-0 ww-text-red-400" });
1156
+ if (a.mimeType.startsWith("image/"))
1157
+ return /* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsx("span", { className: "ww-text-[8px] ww-font-semibold ww-text-muted-foreground/50", children: "IMG" }) });
1158
+ if (a.mimeType === "application/pdf")
1159
+ return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileText, { className: "ww-h-3 ww-w-3 ww-shrink-0 ww-text-red-400" });
727
1160
  if (a.mimeType.includes("csv") || a.mimeType.includes("spreadsheet") || a.name.endsWith(".csv")) {
728
1161
  return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileSpreadsheet, { className: "ww-h-3 ww-w-3 ww-shrink-0 ww-text-emerald-500" });
729
1162
  }
730
1163
  return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileText, { className: "ww-h-3 ww-w-3 ww-shrink-0" });
731
1164
  }
732
- function AttachmentChips({ attachments, onRemove }) {
1165
+ function AttachmentChips({
1166
+ attachments,
1167
+ onRemove
1168
+ }) {
733
1169
  if (attachments.length === 0) return null;
734
1170
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ww-flex ww-flex-wrap ww-gap-1.5 ww-px-1 ww-pb-1.5", children: attachments.map((a) => /* @__PURE__ */ jsxRuntime.jsxs(
735
1171
  "div",
@@ -757,41 +1193,49 @@ function AttachmentChips({ attachments, onRemove }) {
757
1193
  a.id
758
1194
  )) });
759
1195
  }
760
- var Avatar2 = ({ style, ...p }) => /* @__PURE__ */ jsxRuntime.jsx(AvatarPrimitive__namespace.Root, { style: { position: "relative", display: "flex", flexShrink: 0, overflow: "hidden", borderRadius: "9999px", ...style }, ...p });
761
- var AvatarImage2 = ({ style, ...p }) => /* @__PURE__ */ jsxRuntime.jsx(AvatarPrimitive__namespace.Image, { style: { position: "absolute", inset: 0, width: "100%", height: "100%", objectFit: "cover", ...style }, ...p });
762
- var AvatarFallback2 = ({ style, ...p }) => /* @__PURE__ */ jsxRuntime.jsx(AvatarPrimitive__namespace.Fallback, { style: { display: "flex", width: "100%", height: "100%", alignItems: "center", justifyContent: "center", borderRadius: "9999px", ...style }, ...p });
763
- var ReactMarkdown = ReactMarkdownLib__default.default;
764
- function SentAttachments({ attachments, contrastColor }) {
1196
+ function SentAttachments({
1197
+ attachments,
1198
+ contrastColor
1199
+ }) {
765
1200
  const images = attachments.filter((a) => a.contentType === "image");
766
1201
  const files = attachments.filter((a) => a.contentType !== "image");
767
1202
  const isDark = contrastColor === "#ffffff" || contrastColor === "#fff";
768
1203
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ww-flex ww-flex-col ww-gap-1.5 ww-w-full", children: [
769
- images.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("ww-flex ww-gap-1.5 ww-flex-wrap", images.length === 1 && ""), children: images.map((img) => {
770
- const src = img.url ?? img.base64;
771
- return src ? /* @__PURE__ */ jsxRuntime.jsx(
772
- "img",
773
- {
774
- src,
775
- alt: img.name,
776
- className: cn(
777
- "ww-rounded-xl ww-object-cover ww-border",
778
- images.length === 1 ? "ww-w-full ww-max-h-48" : "ww-h-20 ww-w-20",
779
- isDark ? "ww-border-white/20" : "ww-border-black/10"
780
- )
781
- },
782
- img.id
783
- ) : /* @__PURE__ */ jsxRuntime.jsx(
784
- "div",
785
- {
786
- className: cn(
787
- "ww-h-20 ww-w-20 ww-rounded-xl ww-flex ww-items-center ww-justify-center ww-text-[10px] ww-font-medium",
788
- isDark ? "ww-bg-white/10 ww-text-white/60" : "ww-bg-black/10 ww-text-black/40"
789
- ),
790
- children: "IMG"
791
- },
792
- img.id
793
- );
794
- }) }),
1204
+ images.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
1205
+ "div",
1206
+ {
1207
+ className: cn(
1208
+ "ww-flex ww-gap-1.5 ww-flex-wrap",
1209
+ images.length === 1 && ""
1210
+ ),
1211
+ children: images.map((img) => {
1212
+ const src = img.url ?? img.base64;
1213
+ return src ? /* @__PURE__ */ jsxRuntime.jsx(
1214
+ "img",
1215
+ {
1216
+ src,
1217
+ alt: img.name,
1218
+ className: cn(
1219
+ "ww-rounded-xl ww-object-cover ww-border",
1220
+ images.length === 1 ? "ww-w-full ww-max-h-48" : "ww-h-20 ww-w-20",
1221
+ isDark ? "ww-border-white/20" : "ww-border-black/10"
1222
+ )
1223
+ },
1224
+ img.id
1225
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
1226
+ "div",
1227
+ {
1228
+ className: cn(
1229
+ "ww-h-20 ww-w-20 ww-rounded-xl ww-flex ww-items-center ww-justify-center ww-text-[10px] ww-font-medium",
1230
+ isDark ? "ww-bg-white/10 ww-text-white/60" : "ww-bg-black/10 ww-text-black/40"
1231
+ ),
1232
+ children: "IMG"
1233
+ },
1234
+ img.id
1235
+ );
1236
+ })
1237
+ }
1238
+ ),
795
1239
  files.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ww-flex ww-flex-wrap ww-gap-1", children: files.map((f) => /* @__PURE__ */ jsxRuntime.jsxs(
796
1240
  "div",
797
1241
  {
@@ -809,12 +1253,19 @@ function SentAttachments({ attachments, contrastColor }) {
809
1253
  ] });
810
1254
  }
811
1255
  function PlanStepIcon({ status }) {
812
- if (status === "executing") return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "ww-h-3 ww-w-3 ww-animate-spin ww-text-primary" });
813
- if (status === "success") return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckCircle2, { className: "ww-h-3 ww-w-3 ww-text-emerald-500" });
814
- if (status === "failed") return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircle, { className: "ww-h-3 ww-w-3 ww-text-destructive" });
1256
+ if (status === "executing")
1257
+ return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "ww-h-3 ww-w-3 ww-animate-spin ww-text-primary" });
1258
+ if (status === "success")
1259
+ return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckCircle2, { className: "ww-h-3 ww-w-3 ww-text-emerald-500" });
1260
+ if (status === "failed")
1261
+ return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircle, { className: "ww-h-3 ww-w-3 ww-text-destructive" });
815
1262
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ww-h-3 ww-w-3 ww-rounded-full ww-border-2 ww-border-muted-foreground/30" });
816
1263
  }
817
- function PlanCard({ part, onSend, disabled }) {
1264
+ function PlanCard({
1265
+ part,
1266
+ onSend,
1267
+ disabled
1268
+ }) {
818
1269
  const successCount = part.steps.filter((s) => s.status === "success").length;
819
1270
  const hasExecuting = part.steps.some((s) => s.status === "executing");
820
1271
  const allDone = successCount === part.steps.length && part.steps.length > 0;
@@ -868,7 +1319,10 @@ function PlanCard({ part, onSend, disabled }) {
868
1319
  onClick: () => onSend("s\xED, proceder"),
869
1320
  disabled,
870
1321
  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",
871
- style: { backgroundColor: "var(--primary, #18181b)", color: "var(--primary-foreground, #fff)" },
1322
+ style: {
1323
+ backgroundColor: "var(--primary, #18181b)",
1324
+ color: "var(--primary-foreground, #fff)"
1325
+ },
872
1326
  children: [
873
1327
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "ww-h-3 ww-w-3 ww-shrink-0" }),
874
1328
  "Proceder"
@@ -950,7 +1404,15 @@ function ReasoningBlock({ text }) {
950
1404
  children: [
951
1405
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Zap, { className: "ww-h-3 ww-w-3" }),
952
1406
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Reasoning" }),
953
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: cn("ww-h-3 ww-w-3 ww-transition-transform", open && "ww-rotate-180") })
1407
+ /* @__PURE__ */ jsxRuntime.jsx(
1408
+ lucideReact.ChevronDown,
1409
+ {
1410
+ className: cn(
1411
+ "ww-h-3 ww-w-3 ww-transition-transform",
1412
+ open && "ww-rotate-180"
1413
+ )
1414
+ }
1415
+ )
954
1416
  ]
955
1417
  }
956
1418
  ),
@@ -972,98 +1434,292 @@ function PickerSelector({
972
1434
  }, [part.options, query]);
973
1435
  const isConsumed = !!part.selectedValue;
974
1436
  const handleClick = (opt) => {
975
- if (!disabled && !isConsumed) onSelect(part.pickerId, part.paramName, opt.value, opt.label);
1437
+ if (!disabled && !isConsumed)
1438
+ onSelect(part.pickerId, part.paramName, opt.value, opt.label);
976
1439
  };
977
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn(
978
- "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",
979
- "ww-border-border/50"
980
- ), children: [
981
- /* @__PURE__ */ jsxRuntime.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 }),
982
- mode === "list" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ww-relative", children: [
983
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.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" }),
984
- /* @__PURE__ */ jsxRuntime.jsx(
985
- "input",
986
- {
987
- type: "text",
988
- value: query,
989
- onChange: (e) => setQuery(e.target.value),
990
- placeholder: "Search\u2026",
991
- disabled: disabled || isConsumed,
992
- 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"
993
- }
994
- )
995
- ] }),
996
- mode === "pills" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ww-flex ww-flex-wrap ww-gap-1.5", children: part.options.map((opt) => {
997
- const sel = part.selectedValue === opt.value;
998
- const faded = isConsumed && !sel;
999
- return /* @__PURE__ */ jsxRuntime.jsxs(
1000
- "button",
1001
- {
1002
- disabled: disabled || isConsumed,
1003
- onClick: () => handleClick(opt),
1004
- className: cn(
1005
- "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",
1006
- 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",
1007
- disabled && !isConsumed && "ww-opacity-60 ww-pointer-events-none"
1008
- ),
1009
- children: [
1010
- sel && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "ww-h-3 ww-w-3 ww-shrink-0" }),
1011
- opt.label
1012
- ]
1013
- },
1014
- opt.value
1015
- );
1016
- }) }),
1017
- mode === "grid" && /* @__PURE__ */ jsxRuntime.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) => {
1018
- const sel = part.selectedValue === opt.value;
1019
- const faded = isConsumed && !sel;
1020
- return /* @__PURE__ */ jsxRuntime.jsxs(
1021
- "button",
1022
- {
1023
- disabled: disabled || isConsumed,
1024
- onClick: () => handleClick(opt),
1025
- className: cn(
1026
- "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",
1027
- 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",
1028
- disabled && !isConsumed && "ww-opacity-60 ww-pointer-events-none"
1029
- ),
1030
- children: [
1031
- sel ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "ww-h-3 ww-w-3 ww-shrink-0" }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ww-h-3 ww-w-3 ww-shrink-0" }),
1032
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ww-truncate", children: opt.label })
1033
- ]
1034
- },
1035
- opt.value
1036
- );
1037
- }) }),
1038
- mode === "list" && /* @__PURE__ */ jsxRuntime.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: [
1039
- filtered.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "ww-py-3 ww-text-center ww-text-xs ww-text-muted-foreground/50", children: "No results" }),
1040
- filtered.map((opt) => {
1041
- const sel = part.selectedValue === opt.value;
1042
- const faded = isConsumed && !sel;
1043
- return /* @__PURE__ */ jsxRuntime.jsxs(
1044
- "button",
1440
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1441
+ "div",
1442
+ {
1443
+ className: cn(
1444
+ "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",
1445
+ "ww-border-border/50"
1446
+ ),
1447
+ children: [
1448
+ /* @__PURE__ */ jsxRuntime.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 }),
1449
+ mode === "list" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ww-relative", children: [
1450
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.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" }),
1451
+ /* @__PURE__ */ jsxRuntime.jsx(
1452
+ "input",
1453
+ {
1454
+ type: "text",
1455
+ value: query,
1456
+ onChange: (e) => setQuery(e.target.value),
1457
+ placeholder: "Search\u2026",
1458
+ disabled: disabled || isConsumed,
1459
+ 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"
1460
+ }
1461
+ )
1462
+ ] }),
1463
+ mode === "pills" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ww-flex ww-flex-wrap ww-gap-1.5", children: part.options.map((opt, i) => {
1464
+ const sel = part.selectedValue === opt.value;
1465
+ const faded = isConsumed && !sel;
1466
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1467
+ "button",
1468
+ {
1469
+ disabled: disabled || isConsumed,
1470
+ onClick: () => handleClick(opt),
1471
+ className: cn(
1472
+ "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",
1473
+ 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",
1474
+ disabled && !isConsumed && "ww-opacity-60 ww-pointer-events-none"
1475
+ ),
1476
+ children: [
1477
+ sel && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "ww-h-3 ww-w-3 ww-shrink-0" }),
1478
+ opt.label
1479
+ ]
1480
+ },
1481
+ `${opt.value}-${i}`
1482
+ );
1483
+ }) }),
1484
+ mode === "grid" && /* @__PURE__ */ jsxRuntime.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) => {
1485
+ const sel = part.selectedValue === opt.value;
1486
+ const faded = isConsumed && !sel;
1487
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1488
+ "button",
1489
+ {
1490
+ disabled: disabled || isConsumed,
1491
+ onClick: () => handleClick(opt),
1492
+ className: cn(
1493
+ "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",
1494
+ 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",
1495
+ disabled && !isConsumed && "ww-opacity-60 ww-pointer-events-none"
1496
+ ),
1497
+ children: [
1498
+ sel ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "ww-h-3 ww-w-3 ww-shrink-0" }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ww-h-3 ww-w-3 ww-shrink-0" }),
1499
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ww-truncate", children: opt.label })
1500
+ ]
1501
+ },
1502
+ `${opt.value}-${i}`
1503
+ );
1504
+ }) }),
1505
+ mode === "list" && /* @__PURE__ */ jsxRuntime.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: [
1506
+ filtered.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "ww-py-3 ww-text-center ww-text-xs ww-text-muted-foreground/50", children: "No results" }),
1507
+ filtered.map((opt, i) => {
1508
+ const sel = part.selectedValue === opt.value;
1509
+ const faded = isConsumed && !sel;
1510
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1511
+ "button",
1512
+ {
1513
+ disabled: disabled || isConsumed,
1514
+ onClick: () => handleClick(opt),
1515
+ className: cn(
1516
+ "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",
1517
+ 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",
1518
+ disabled && !isConsumed && "ww-opacity-60 ww-pointer-events-none"
1519
+ ),
1520
+ children: [
1521
+ /* @__PURE__ */ jsxRuntime.jsx(
1522
+ "span",
1523
+ {
1524
+ className: cn(
1525
+ "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]",
1526
+ sel ? "ww-border-background ww-bg-background/20" : "ww-border-border/50"
1527
+ ),
1528
+ children: sel && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "ww-h-2.5 ww-w-2.5" })
1529
+ }
1530
+ ),
1531
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ww-truncate", children: opt.label })
1532
+ ]
1533
+ },
1534
+ `${opt.value}-${i}`
1535
+ );
1536
+ })
1537
+ ] })
1538
+ ]
1539
+ }
1540
+ );
1541
+ }
1542
+ var ReactMarkdown = ReactMarkdownLib__default.default;
1543
+ function MarkdownContent({ text }) {
1544
+ return /* @__PURE__ */ jsxRuntime.jsx(
1545
+ ReactMarkdown,
1546
+ {
1547
+ remarkPlugins: [remarkGfm__default.default],
1548
+ components: {
1549
+ p: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("p", { style: { margin: "4px 0" }, children }),
1550
+ h1: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx(
1551
+ "p",
1552
+ {
1553
+ style: {
1554
+ margin: "6px 0",
1555
+ fontWeight: 700,
1556
+ fontSize: "1em"
1557
+ },
1558
+ children
1559
+ }
1560
+ ),
1561
+ h2: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx(
1562
+ "p",
1563
+ {
1564
+ style: {
1565
+ margin: "6px 0",
1566
+ fontWeight: 700,
1567
+ fontSize: "1em"
1568
+ },
1569
+ children
1570
+ }
1571
+ ),
1572
+ h3: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx(
1573
+ "p",
1045
1574
  {
1046
- disabled: disabled || isConsumed,
1047
- onClick: () => handleClick(opt),
1048
- className: cn(
1049
- "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",
1050
- 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",
1051
- disabled && !isConsumed && "ww-opacity-60 ww-pointer-events-none"
1052
- ),
1053
- children: [
1054
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn(
1055
- "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]",
1056
- sel ? "ww-border-background ww-bg-background/20" : "ww-border-border/50"
1057
- ), children: sel && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "ww-h-2.5 ww-w-2.5" }) }),
1058
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ww-truncate", children: opt.label })
1059
- ]
1060
- },
1061
- opt.value
1062
- );
1063
- })
1064
- ] })
1065
- ] });
1575
+ style: {
1576
+ margin: "6px 0",
1577
+ fontWeight: 600,
1578
+ fontSize: "0.95em"
1579
+ },
1580
+ children
1581
+ }
1582
+ ),
1583
+ ul: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("ul", { style: { margin: "4px 0", paddingLeft: 16 }, children }),
1584
+ ol: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("ol", { style: { margin: "4px 0", paddingLeft: 16 }, children }),
1585
+ li: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("li", { style: { margin: "2px 0" }, children }),
1586
+ strong: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("strong", { style: { fontWeight: 600 }, children }),
1587
+ code: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx(
1588
+ "code",
1589
+ {
1590
+ style: {
1591
+ fontSize: "0.85em",
1592
+ backgroundColor: "rgba(0,0,0,0.07)",
1593
+ borderRadius: 4,
1594
+ padding: "1px 5px",
1595
+ fontFamily: "monospace"
1596
+ },
1597
+ children
1598
+ }
1599
+ ),
1600
+ pre: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx(
1601
+ "pre",
1602
+ {
1603
+ style: {
1604
+ overflowX: "auto",
1605
+ margin: "6px 0",
1606
+ padding: "8px 10px",
1607
+ backgroundColor: "rgba(0,0,0,0.06)",
1608
+ borderRadius: 8,
1609
+ fontSize: "0.82em",
1610
+ fontFamily: "monospace"
1611
+ },
1612
+ children
1613
+ }
1614
+ ),
1615
+ a: ({
1616
+ href,
1617
+ children
1618
+ }) => /* @__PURE__ */ jsxRuntime.jsx(
1619
+ "a",
1620
+ {
1621
+ href,
1622
+ target: "_blank",
1623
+ rel: "noopener noreferrer",
1624
+ style: { textDecoration: "underline", opacity: 0.75 },
1625
+ children
1626
+ }
1627
+ ),
1628
+ table: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx(
1629
+ "div",
1630
+ {
1631
+ style: {
1632
+ overflowX: "auto",
1633
+ margin: "6px 0",
1634
+ borderRadius: 8,
1635
+ border: "1px solid rgba(0,0,0,0.1)"
1636
+ },
1637
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1638
+ "table",
1639
+ {
1640
+ style: {
1641
+ borderCollapse: "collapse",
1642
+ minWidth: "100%",
1643
+ fontSize: "0.82em"
1644
+ },
1645
+ children
1646
+ }
1647
+ )
1648
+ }
1649
+ ),
1650
+ thead: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("thead", { style: { backgroundColor: "rgba(0,0,0,0.04)" }, children }),
1651
+ tbody: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("tbody", { children }),
1652
+ tr: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("tr", { style: { borderBottom: "1px solid rgba(0,0,0,0.07)" }, children }),
1653
+ th: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx(
1654
+ "th",
1655
+ {
1656
+ style: {
1657
+ padding: "6px 10px",
1658
+ textAlign: "left",
1659
+ fontWeight: 600,
1660
+ whiteSpace: "nowrap"
1661
+ },
1662
+ children
1663
+ }
1664
+ ),
1665
+ td: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("td", { style: { padding: "5px 10px", verticalAlign: "top" }, children })
1666
+ },
1667
+ children: text
1668
+ }
1669
+ );
1066
1670
  }
1671
+ var Avatar2 = ({
1672
+ style,
1673
+ ...p
1674
+ }) => /* @__PURE__ */ jsxRuntime.jsx(
1675
+ AvatarPrimitive__namespace.Root,
1676
+ {
1677
+ style: {
1678
+ position: "relative",
1679
+ display: "flex",
1680
+ flexShrink: 0,
1681
+ overflow: "hidden",
1682
+ borderRadius: "9999px",
1683
+ ...style
1684
+ },
1685
+ ...p
1686
+ }
1687
+ );
1688
+ var AvatarImage2 = ({
1689
+ style,
1690
+ ...p
1691
+ }) => /* @__PURE__ */ jsxRuntime.jsx(
1692
+ AvatarPrimitive__namespace.Image,
1693
+ {
1694
+ style: {
1695
+ position: "absolute",
1696
+ inset: 0,
1697
+ width: "100%",
1698
+ height: "100%",
1699
+ objectFit: "cover",
1700
+ ...style
1701
+ },
1702
+ ...p
1703
+ }
1704
+ );
1705
+ var AvatarFallback2 = ({
1706
+ style,
1707
+ ...p
1708
+ }) => /* @__PURE__ */ jsxRuntime.jsx(
1709
+ AvatarPrimitive__namespace.Fallback,
1710
+ {
1711
+ style: {
1712
+ display: "flex",
1713
+ width: "100%",
1714
+ height: "100%",
1715
+ alignItems: "center",
1716
+ justifyContent: "center",
1717
+ borderRadius: "9999px",
1718
+ ...style
1719
+ },
1720
+ ...p
1721
+ }
1722
+ );
1067
1723
  function MessageBubble({
1068
1724
  message,
1069
1725
  userColor,
@@ -1077,9 +1733,15 @@ function MessageBubble({
1077
1733
  const isUser = message.role === "user";
1078
1734
  const textPart = message.parts.find((p) => p.type === "text");
1079
1735
  const reasoningPart = message.parts.find((p) => p.type === "reasoning");
1080
- const toolParts = message.parts.filter((p) => p.type === "tool");
1081
- const pickerParts = message.parts.filter((p) => p.type === "picker");
1082
- const planParts = message.parts.filter((p) => p.type === "plan");
1736
+ const toolParts = message.parts.filter(
1737
+ (p) => p.type === "tool"
1738
+ );
1739
+ const pickerParts = message.parts.filter(
1740
+ (p) => p.type === "picker"
1741
+ );
1742
+ const planParts = message.parts.filter(
1743
+ (p) => p.type === "plan"
1744
+ );
1083
1745
  const contrastColor = getContrastColor(userColor);
1084
1746
  if (isUser) {
1085
1747
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ww-flex ww-justify-end", children: /* @__PURE__ */ jsxRuntime.jsxs(
@@ -1088,7 +1750,13 @@ function MessageBubble({
1088
1750
  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",
1089
1751
  style: { backgroundColor: userColor, color: contrastColor },
1090
1752
  children: [
1091
- message.attachments && message.attachments.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(SentAttachments, { attachments: message.attachments, contrastColor }),
1753
+ message.attachments && message.attachments.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
1754
+ SentAttachments,
1755
+ {
1756
+ attachments: message.attachments,
1757
+ contrastColor
1758
+ }
1759
+ ),
1092
1760
  textPart?.text && /* @__PURE__ */ jsxRuntime.jsx("span", { children: textPart.text })
1093
1761
  ]
1094
1762
  }
@@ -1097,10 +1765,40 @@ function MessageBubble({
1097
1765
  const visibleToolParts = showThinking ? toolParts : [];
1098
1766
  const isEmpty = !textPart?.text && visibleToolParts.length === 0 && pickerParts.length === 0 && planParts.length === 0;
1099
1767
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ww-flex ww-gap-2.5 ww-items-start", children: [
1100
- /* @__PURE__ */ jsxRuntime.jsx(Avatar2, { style: { width: 28, height: 28, marginTop: 2, border: "1px solid rgba(0,0,0,0.08)" }, children: profilePicture ? /* @__PURE__ */ jsxRuntime.jsx(AvatarImage2, { src: profilePicture, alt: agentName }) : /* @__PURE__ */ jsxRuntime.jsx(AvatarFallback2, { style: { fontSize: 10, fontWeight: 600, backgroundColor: "var(--primary, #19191c)", color: "var(--primary-foreground, #fff)" }, children: agentName.slice(0, 2).toUpperCase() }) }),
1768
+ /* @__PURE__ */ jsxRuntime.jsx(
1769
+ Avatar2,
1770
+ {
1771
+ style: {
1772
+ width: 28,
1773
+ height: 28,
1774
+ marginTop: 2,
1775
+ border: "1px solid rgba(0,0,0,0.08)"
1776
+ },
1777
+ children: profilePicture ? /* @__PURE__ */ jsxRuntime.jsx(AvatarImage2, { src: profilePicture, alt: agentName }) : /* @__PURE__ */ jsxRuntime.jsx(
1778
+ AvatarFallback2,
1779
+ {
1780
+ style: {
1781
+ fontSize: 10,
1782
+ fontWeight: 600,
1783
+ backgroundColor: "var(--primary, #19191c)",
1784
+ color: "var(--primary-foreground, #fff)"
1785
+ },
1786
+ children: agentName.slice(0, 2).toUpperCase()
1787
+ }
1788
+ )
1789
+ }
1790
+ ),
1101
1791
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ww-flex ww-flex-col ww-gap-1.5 ww-min-w-0 ww-max-w-[82%]", children: [
1102
1792
  showThinking && reasoningPart && /* @__PURE__ */ jsxRuntime.jsx(ReasoningBlock, { text: reasoningPart.text }),
1103
- planParts.map((p) => /* @__PURE__ */ jsxRuntime.jsx(PlanCard, { part: p, onSend, disabled: isStreaming }, p.planId)),
1793
+ planParts.map((p) => /* @__PURE__ */ jsxRuntime.jsx(
1794
+ PlanCard,
1795
+ {
1796
+ part: p,
1797
+ onSend,
1798
+ disabled: isStreaming
1799
+ },
1800
+ p.planId
1801
+ )),
1104
1802
  visibleToolParts.map((t) => /* @__PURE__ */ jsxRuntime.jsx(ToolCallBadge, { part: t }, t.toolCallId)),
1105
1803
  pickerParts.map((p) => /* @__PURE__ */ jsxRuntime.jsx(
1106
1804
  PickerSelector,
@@ -1113,40 +1811,39 @@ function MessageBubble({
1113
1811
  p.pickerId
1114
1812
  )),
1115
1813
  isEmpty && isStreaming ? /* @__PURE__ */ jsxRuntime.jsx(ThinkingDots, {}) : textPart?.text ? /* @__PURE__ */ jsxRuntime.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: [
1116
- /* @__PURE__ */ jsxRuntime.jsx(
1117
- ReactMarkdown,
1118
- {
1119
- remarkPlugins: [remarkGfm__default.default],
1120
- components: {
1121
- p: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("p", { style: { margin: "4px 0" }, children }),
1122
- h1: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("p", { style: { margin: "6px 0", fontWeight: 700, fontSize: "1em" }, children }),
1123
- h2: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("p", { style: { margin: "6px 0", fontWeight: 700, fontSize: "1em" }, children }),
1124
- h3: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("p", { style: { margin: "6px 0", fontWeight: 600, fontSize: "0.95em" }, children }),
1125
- ul: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("ul", { style: { margin: "4px 0", paddingLeft: 16 }, children }),
1126
- ol: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("ol", { style: { margin: "4px 0", paddingLeft: 16 }, children }),
1127
- li: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("li", { style: { margin: "2px 0" }, children }),
1128
- strong: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("strong", { style: { fontWeight: 600 }, children }),
1129
- code: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("code", { style: { fontSize: "0.85em", backgroundColor: "rgba(0,0,0,0.07)", borderRadius: 4, padding: "1px 5px", fontFamily: "monospace" }, children }),
1130
- pre: ({ children }) => /* @__PURE__ */ jsxRuntime.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 }),
1131
- a: ({ href, children }) => /* @__PURE__ */ jsxRuntime.jsx("a", { href, target: "_blank", rel: "noopener noreferrer", style: { textDecoration: "underline", opacity: 0.75 }, children }),
1132
- table: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("div", { style: { overflowX: "auto", margin: "6px 0", borderRadius: 8, border: "1px solid rgba(0,0,0,0.1)" }, children: /* @__PURE__ */ jsxRuntime.jsx("table", { style: { borderCollapse: "collapse", minWidth: "100%", fontSize: "0.82em" }, children }) }),
1133
- thead: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("thead", { style: { backgroundColor: "rgba(0,0,0,0.04)" }, children }),
1134
- tbody: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("tbody", { children }),
1135
- tr: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("tr", { style: { borderBottom: "1px solid rgba(0,0,0,0.07)" }, children }),
1136
- th: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("th", { style: { padding: "6px 10px", textAlign: "left", fontWeight: 600, whiteSpace: "nowrap" }, children }),
1137
- td: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("td", { style: { padding: "5px 10px", verticalAlign: "top" }, children })
1138
- },
1139
- children: textPart.text
1140
- }
1141
- ),
1814
+ /* @__PURE__ */ jsxRuntime.jsx(MarkdownContent, { text: textPart.text }),
1142
1815
  isStreaming && /* @__PURE__ */ jsxRuntime.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" })
1143
1816
  ] }) : null
1144
1817
  ] })
1145
1818
  ] });
1146
1819
  }
1147
- var Avatar3 = ({ className, ...p }) => /* @__PURE__ */ jsxRuntime.jsx(AvatarPrimitive__namespace.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 });
1820
+ var Avatar3 = ({
1821
+ className,
1822
+ ...p
1823
+ }) => /* @__PURE__ */ jsxRuntime.jsx(
1824
+ AvatarPrimitive__namespace.Root,
1825
+ {
1826
+ className: [
1827
+ "ww-relative ww-flex ww-h-10 ww-w-10 ww-shrink-0 ww-overflow-hidden ww-rounded-full",
1828
+ className
1829
+ ].filter(Boolean).join(" "),
1830
+ ...p
1831
+ }
1832
+ );
1148
1833
  var AvatarImage3 = AvatarPrimitive__namespace.Image;
1149
- var AvatarFallback3 = ({ className, ...p }) => /* @__PURE__ */ jsxRuntime.jsx(AvatarPrimitive__namespace.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 });
1834
+ var AvatarFallback3 = ({
1835
+ className,
1836
+ ...p
1837
+ }) => /* @__PURE__ */ jsxRuntime.jsx(
1838
+ AvatarPrimitive__namespace.Fallback,
1839
+ {
1840
+ className: [
1841
+ "ww-flex ww-h-full ww-w-full ww-items-center ww-justify-center ww-rounded-full ww-bg-muted",
1842
+ className
1843
+ ].filter(Boolean).join(" "),
1844
+ ...p
1845
+ }
1846
+ );
1150
1847
  function ChatMessages({
1151
1848
  messages,
1152
1849
  streaming,
@@ -1160,14 +1857,15 @@ function ChatMessages({
1160
1857
  onPickerSelect
1161
1858
  }) {
1162
1859
  const bottomRef = react.useRef(null);
1860
+ const greetText = initialMessages[0];
1163
1861
  const showGreeting = messages.length === 0;
1164
1862
  react.useEffect(() => {
1165
1863
  bottomRef.current?.scrollIntoView({ behavior: "smooth" });
1166
1864
  }, [messages, streaming]);
1167
1865
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ww-flex-1 ww-flex ww-flex-col ww-overflow-y-auto ww-overscroll-contain", children: [
1168
- showGreeting && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ww-flex ww-gap-2.5 ww-items-start ww-px-4 ww-pt-5", children: [
1866
+ showGreeting && greetText && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ww-flex ww-gap-2.5 ww-items-start ww-px-4 ww-pt-5", children: [
1169
1867
  /* @__PURE__ */ jsxRuntime.jsx(Avatar3, { className: "ww-h-7 ww-w-7 ww-shrink-0 ww-mt-0.5 ww-border", children: profilePicture ? /* @__PURE__ */ jsxRuntime.jsx(AvatarImage3, { src: profilePicture, alt: agentName }) : /* @__PURE__ */ jsxRuntime.jsx(AvatarFallback3, { className: "ww-text-[10px] ww-font-semibold ww-bg-primary ww-text-primary-foreground", children: agentName.slice(0, 2).toUpperCase() }) }),
1170
- /* @__PURE__ */ jsxRuntime.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?" })
1868
+ /* @__PURE__ */ jsxRuntime.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 })
1171
1869
  ] }),
1172
1870
  showGreeting && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ww-flex-1" }),
1173
1871
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ww-flex ww-flex-col ww-gap-4 ww-px-4 ww-py-4", children: messages.map((msg, i) => /* @__PURE__ */ jsxRuntime.jsx(
@@ -1196,7 +1894,13 @@ function ChatMessages({
1196
1894
  "div",
1197
1895
  {
1198
1896
  className: "ww-bg-muted",
1199
- style: { display: "inline-flex", alignItems: "center", gap: 6, borderRadius: "18px 18px 18px 4px", padding: "13px 18px" },
1897
+ style: {
1898
+ display: "inline-flex",
1899
+ alignItems: "center",
1900
+ gap: 6,
1901
+ borderRadius: "18px 18px 18px 4px",
1902
+ padding: "13px 18px"
1903
+ },
1200
1904
  children: [0, 1, 2].map((i) => /* @__PURE__ */ jsxRuntime.jsx(
1201
1905
  "span",
1202
1906
  {
@@ -1280,7 +1984,13 @@ function ChatInput({
1280
1984
  }
1281
1985
  ),
1282
1986
  /* @__PURE__ */ jsxRuntime.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: [
1283
- hasAttachments && attachments && attachments.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ww-px-2 ww-pt-2", children: /* @__PURE__ */ jsxRuntime.jsx(AttachmentChips, { attachments, onRemove: (id) => onRemoveAttachment?.(id) }) }),
1987
+ hasAttachments && attachments && attachments.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ww-px-2 ww-pt-2", children: /* @__PURE__ */ jsxRuntime.jsx(
1988
+ AttachmentChips,
1989
+ {
1990
+ attachments,
1991
+ onRemove: (id) => onRemoveAttachment?.(id)
1992
+ }
1993
+ ) }),
1284
1994
  hasAttachments && /* @__PURE__ */ jsxRuntime.jsx(
1285
1995
  "input",
1286
1996
  {
@@ -1370,7 +2080,10 @@ function ChatInput({
1370
2080
  "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",
1371
2081
  hasText || streaming ? "ww-opacity-100 ww-shadow-sm" : "ww-opacity-30"
1372
2082
  ),
1373
- style: hasText || streaming ? { backgroundColor: accentColor, color: getContrastColor(accentColor) } : { backgroundColor: "transparent", color: "currentColor" },
2083
+ style: hasText || streaming ? {
2084
+ backgroundColor: accentColor,
2085
+ color: getContrastColor(accentColor)
2086
+ } : { backgroundColor: "transparent", color: "currentColor" },
1374
2087
  children: streaming ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "ww-h-3.5 ww-w-3.5 ww-animate-spin" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUp, { className: "ww-h-3.5 ww-w-3.5" })
1375
2088
  }
1376
2089
  )
@@ -1378,6 +2091,126 @@ function ChatInput({
1378
2091
  ] })
1379
2092
  ] });
1380
2093
  }
2094
+ function CustomControlBar({ onClose }) {
2095
+ const { localParticipant, isMicrophoneEnabled } = componentsReact.useLocalParticipant();
2096
+ const room = componentsReact.useRoomContext();
2097
+ const toggleMic = async () => {
2098
+ if (isMicrophoneEnabled) {
2099
+ await localParticipant.setMicrophoneEnabled(false);
2100
+ } else {
2101
+ await localParticipant.setMicrophoneEnabled(true);
2102
+ }
2103
+ };
2104
+ const disconnect = () => {
2105
+ room.disconnect();
2106
+ onClose();
2107
+ };
2108
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ww-flex ww-items-center ww-justify-center ww-gap-6", children: [
2109
+ /* @__PURE__ */ jsxRuntime.jsx(
2110
+ "button",
2111
+ {
2112
+ onClick: toggleMic,
2113
+ 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"}`,
2114
+ title: isMicrophoneEnabled ? "Silenciar micr\xF3fono" : "Activar micr\xF3fono",
2115
+ children: isMicrophoneEnabled ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Mic, { className: "ww-w-6 ww-h-6" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MicOff, { className: "ww-w-6 ww-h-6" })
2116
+ }
2117
+ ),
2118
+ /* @__PURE__ */ jsxRuntime.jsx(
2119
+ "button",
2120
+ {
2121
+ onClick: disconnect,
2122
+ 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",
2123
+ style: { backgroundColor: "#ef4444" },
2124
+ title: "Desconectar",
2125
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.PhoneOff, { className: "ww-w-6 ww-h-6" })
2126
+ }
2127
+ )
2128
+ ] });
2129
+ }
2130
+ function ChatGPTOrb() {
2131
+ const { state, audioTrack } = componentsReact.useVoiceAssistant();
2132
+ const isSpeaking = state === "speaking";
2133
+ const isListening = state === "listening";
2134
+ const getGradient = () => {
2135
+ if (isSpeaking) {
2136
+ return "linear-gradient(135deg, rgba(59, 130, 246, 0.9), rgba(147, 51, 234, 0.9))";
2137
+ } else if (isListening) {
2138
+ return "linear-gradient(135deg, rgba(16, 185, 129, 0.85), rgba(20, 184, 166, 0.85))";
2139
+ }
2140
+ return "linear-gradient(135deg, rgba(25, 25, 28, 0.8), rgba(25, 25, 28, 0.95))";
2141
+ };
2142
+ const getGlow = () => {
2143
+ if (isSpeaking) return "ww-bg-purple-500/30 ww-animate-ping";
2144
+ if (isListening) return "ww-bg-emerald-500/20 ww-animate-pulse";
2145
+ return "ww-bg-transparent ww-scale-90";
2146
+ };
2147
+ const getRing = () => {
2148
+ if (isSpeaking) return "ww-bg-purple-500/20 ww-scale-110";
2149
+ if (isListening) return "ww-bg-emerald-500/10 ww-scale-105";
2150
+ return "ww-bg-foreground/5 ww-scale-100";
2151
+ };
2152
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ww-relative ww-w-48 ww-h-48 ww-flex ww-items-center ww-justify-center", children: [
2153
+ /* @__PURE__ */ jsxRuntime.jsx(
2154
+ "div",
2155
+ {
2156
+ className: `ww-absolute ww-inset-0 ww-rounded-full ww-transition-all ww-duration-1000 ${getGlow()}`
2157
+ }
2158
+ ),
2159
+ /* @__PURE__ */ jsxRuntime.jsx(
2160
+ "div",
2161
+ {
2162
+ className: `ww-absolute ww-inset-4 ww-rounded-full ww-transition-all ww-duration-500 ${getRing()}`
2163
+ }
2164
+ ),
2165
+ /* @__PURE__ */ jsxRuntime.jsx(
2166
+ "div",
2167
+ {
2168
+ 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",
2169
+ style: {
2170
+ background: getGradient(),
2171
+ transform: isSpeaking ? "scale(1.05)" : "scale(1)"
2172
+ },
2173
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ww-opacity-90 ww-flex ww-items-center ww-justify-center ww-w-full ww-h-full", children: /* @__PURE__ */ jsxRuntime.jsx(
2174
+ componentsReact.BarVisualizer,
2175
+ {
2176
+ state,
2177
+ barCount: 5,
2178
+ trackRef: audioTrack,
2179
+ options: { minHeight: 6 },
2180
+ style: { color: "rgba(255, 255, 255, 0.95)" }
2181
+ }
2182
+ ) })
2183
+ }
2184
+ )
2185
+ ] });
2186
+ }
2187
+ function AssistantVisualizer({ onClose }) {
2188
+ const { state } = componentsReact.useVoiceAssistant();
2189
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ww-flex ww-flex-col ww-items-center ww-justify-center ww-h-full ww-w-full", children: [
2190
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ww-flex-1 ww-flex ww-items-center ww-justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(ChatGPTOrb, {}) }),
2191
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ww-h-40 ww-flex ww-flex-col ww-items-center ww-justify-center ww-gap-8 ww-mb-12", children: [
2192
+ /* @__PURE__ */ jsxRuntime.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" }),
2193
+ /* @__PURE__ */ jsxRuntime.jsx(CustomControlBar, { onClose })
2194
+ ] })
2195
+ ] });
2196
+ }
2197
+ function VoiceOverlay({ token, serverUrl, onClose }) {
2198
+ return /* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsxs(
2199
+ componentsReact.LiveKitRoom,
2200
+ {
2201
+ token,
2202
+ serverUrl,
2203
+ connect: true,
2204
+ audio: true,
2205
+ video: false,
2206
+ className: "ww-flex-1",
2207
+ children: [
2208
+ /* @__PURE__ */ jsxRuntime.jsx(componentsReact.RoomAudioRenderer, {}),
2209
+ /* @__PURE__ */ jsxRuntime.jsx(AssistantVisualizer, { onClose })
2210
+ ]
2211
+ }
2212
+ ) });
2213
+ }
1381
2214
  function ChatWidget({
1382
2215
  agentId,
1383
2216
  workspaceId,
@@ -1409,9 +2242,21 @@ function ChatWidget({
1409
2242
  onReset,
1410
2243
  onExpand,
1411
2244
  expanded,
1412
- embedded = false
2245
+ embedded = false,
2246
+ envId,
2247
+ onDebugTrace
1413
2248
  }) {
1414
- const chat = useChat({ agentId, workspaceId, source, userContext, persist, onNavigate, playgroundOverrides, customBackend });
2249
+ const chat = useChat({
2250
+ agentId,
2251
+ workspaceId,
2252
+ envId,
2253
+ source,
2254
+ userContext,
2255
+ persist,
2256
+ onNavigate,
2257
+ playgroundOverrides,
2258
+ customBackend
2259
+ });
1415
2260
  const voice = useVoice({
1416
2261
  agentId,
1417
2262
  onTranscript: (text) => {
@@ -1423,29 +2268,47 @@ function ChatWidget({
1423
2268
  }
1424
2269
  });
1425
2270
  const attachmentHook = useAttachments({ agentId });
2271
+ const debugTraceLenRef = react.useRef(0);
2272
+ react.useEffect(() => {
2273
+ if (!onDebugTrace || chat.debugTraces.length <= debugTraceLenRef.current)
2274
+ return;
2275
+ for (let i = debugTraceLenRef.current; i < chat.debugTraces.length; i++) {
2276
+ onDebugTrace(chat.debugTraces[i]);
2277
+ }
2278
+ debugTraceLenRef.current = chat.debugTraces.length;
2279
+ }, [chat.debugTraces, onDebugTrace]);
1426
2280
  const [isDragOver, setIsDragOver] = react.useState(false);
1427
- const handleDragOver = react.useCallback((e) => {
1428
- if (!enableAttachments) return;
1429
- e.preventDefault();
1430
- e.stopPropagation();
1431
- if (e.dataTransfer.types.includes("Files")) setIsDragOver(true);
1432
- }, [enableAttachments]);
1433
- const handleDragLeave = react.useCallback((e) => {
1434
- if (!enableAttachments) return;
1435
- e.preventDefault();
1436
- e.stopPropagation();
1437
- if (!e.currentTarget.contains(e.relatedTarget)) {
2281
+ const handleDragOver = react.useCallback(
2282
+ (e) => {
2283
+ if (!enableAttachments) return;
2284
+ e.preventDefault();
2285
+ e.stopPropagation();
2286
+ if (e.dataTransfer.types.includes("Files")) setIsDragOver(true);
2287
+ },
2288
+ [enableAttachments]
2289
+ );
2290
+ const handleDragLeave = react.useCallback(
2291
+ (e) => {
2292
+ if (!enableAttachments) return;
2293
+ e.preventDefault();
2294
+ e.stopPropagation();
2295
+ if (!e.currentTarget.contains(e.relatedTarget)) {
2296
+ setIsDragOver(false);
2297
+ }
2298
+ },
2299
+ [enableAttachments]
2300
+ );
2301
+ const handleDrop = react.useCallback(
2302
+ (e) => {
2303
+ if (!enableAttachments) return;
2304
+ e.preventDefault();
2305
+ e.stopPropagation();
1438
2306
  setIsDragOver(false);
1439
- }
1440
- }, [enableAttachments]);
1441
- const handleDrop = react.useCallback((e) => {
1442
- if (!enableAttachments) return;
1443
- e.preventDefault();
1444
- e.stopPropagation();
1445
- setIsDragOver(false);
1446
- const files = e.dataTransfer.files;
1447
- if (files.length > 0) attachmentHook.attach(files);
1448
- }, [enableAttachments, attachmentHook]);
2307
+ const files = e.dataTransfer.files;
2308
+ if (files.length > 0) attachmentHook.attach(files);
2309
+ },
2310
+ [enableAttachments, attachmentHook]
2311
+ );
1449
2312
  const handleSend = () => {
1450
2313
  const payloads = attachmentHook.readyPayloads;
1451
2314
  if (payloads.length > 0) {
@@ -1487,7 +2350,7 @@ function ChatWidget({
1487
2350
  "div",
1488
2351
  {
1489
2352
  className: cn(
1490
- "wallavi-widget ww-flex ww-flex-col ww-overflow-hidden ww-bg-background ww-h-full ww-relative",
2353
+ "wallavi-widget ww-flex ww-flex-col ww-overflow-hidden ww-bg-background ww-h-full ww-relative ww-overscroll-none",
1491
2354
  !embedded && "ww-rounded-2xl ww-border ww-shadow-xl",
1492
2355
  isDragOver && "ww-ring-2 ww-ring-inset ww-ring-primary/60",
1493
2356
  className
@@ -1513,7 +2376,18 @@ function ChatWidget({
1513
2376
  onReset: handleReset,
1514
2377
  onClose: hideCloseButton ? void 0 : onClose,
1515
2378
  onExpand,
1516
- expanded
2379
+ expanded,
2380
+ onCall: enableVoice && chat.voiceCall ? () => chat.voiceCall?.start() : void 0,
2381
+ isCallLoading: chat.voiceCall?.loading
2382
+ }
2383
+ ),
2384
+ chat.voiceCall?.active && chat.voiceCall.token && chat.voiceCall.serverUrl && /* @__PURE__ */ jsxRuntime.jsx(
2385
+ VoiceOverlay,
2386
+ {
2387
+ token: chat.voiceCall.token,
2388
+ serverUrl: chat.voiceCall.serverUrl,
2389
+ onClose: chat.voiceCall.stop,
2390
+ accentColor: userMessageColor
1517
2391
  }
1518
2392
  ),
1519
2393
  customBackend?.mode === "human" && /* @__PURE__ */ jsxRuntime.jsxs(
@@ -1550,22 +2424,91 @@ function ChatWidget({
1550
2424
  {
1551
2425
  onClick: () => void customBackend.footerAction.onClick(),
1552
2426
  disabled: customBackend.footerAction.loading,
1553
- style: customBackend.footerAction.icon === "human" ? { backgroundColor: userMessageColor, color: "#ffffff", boxShadow: `0 2px 12px ${userMessageColor}55` } : { borderColor: `${userMessageColor}35`, backgroundColor: `${userMessageColor}0d`, color: userMessageColor },
2427
+ style: customBackend.footerAction.icon === "human" ? {
2428
+ backgroundColor: userMessageColor,
2429
+ color: "#ffffff",
2430
+ boxShadow: `0 2px 12px ${userMessageColor}55`
2431
+ } : {
2432
+ borderColor: `${userMessageColor}35`,
2433
+ backgroundColor: `${userMessageColor}0d`,
2434
+ color: userMessageColor
2435
+ },
1554
2436
  className: [
1555
2437
  "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",
1556
2438
  "hover:ww-opacity-85 active:ww-scale-[0.98] disabled:ww-opacity-50 disabled:ww-pointer-events-none",
1557
2439
  customBackend.footerAction.icon === "human" ? "" : "ww-border"
1558
2440
  ].join(" "),
1559
2441
  children: [
1560
- customBackend.footerAction.loading ? /* @__PURE__ */ jsxRuntime.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: [
1561
- /* @__PURE__ */ jsxRuntime.jsx("circle", { className: "ww-opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "3" }),
1562
- /* @__PURE__ */ jsxRuntime.jsx("path", { className: "ww-opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" })
1563
- ] }) : customBackend.footerAction.icon === "ai" ? /* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.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__ */ jsxRuntime.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: [
1564
- /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3 18v-6a9 9 0 0 1 18 0v6" }),
1565
- /* @__PURE__ */ jsxRuntime.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" })
1566
- ] }),
2442
+ customBackend.footerAction.loading ? /* @__PURE__ */ jsxRuntime.jsxs(
2443
+ "svg",
2444
+ {
2445
+ className: "ww-animate-spin ww-w-3.5 ww-h-3.5 ww-shrink-0",
2446
+ viewBox: "0 0 24 24",
2447
+ fill: "none",
2448
+ children: [
2449
+ /* @__PURE__ */ jsxRuntime.jsx(
2450
+ "circle",
2451
+ {
2452
+ className: "ww-opacity-25",
2453
+ cx: "12",
2454
+ cy: "12",
2455
+ r: "10",
2456
+ stroke: "currentColor",
2457
+ strokeWidth: "3"
2458
+ }
2459
+ ),
2460
+ /* @__PURE__ */ jsxRuntime.jsx(
2461
+ "path",
2462
+ {
2463
+ className: "ww-opacity-75",
2464
+ fill: "currentColor",
2465
+ d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"
2466
+ }
2467
+ )
2468
+ ]
2469
+ }
2470
+ ) : customBackend.footerAction.icon === "ai" ? /* @__PURE__ */ jsxRuntime.jsx(
2471
+ "svg",
2472
+ {
2473
+ className: "ww-w-3.5 ww-h-3.5 ww-shrink-0",
2474
+ viewBox: "0 0 24 24",
2475
+ fill: "none",
2476
+ stroke: "currentColor",
2477
+ strokeWidth: "2",
2478
+ strokeLinecap: "round",
2479
+ strokeLinejoin: "round",
2480
+ children: /* @__PURE__ */ jsxRuntime.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" })
2481
+ }
2482
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(
2483
+ "svg",
2484
+ {
2485
+ className: "ww-w-3.5 ww-h-3.5 ww-shrink-0",
2486
+ viewBox: "0 0 24 24",
2487
+ fill: "none",
2488
+ stroke: "currentColor",
2489
+ strokeWidth: "2",
2490
+ strokeLinecap: "round",
2491
+ strokeLinejoin: "round",
2492
+ children: [
2493
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3 18v-6a9 9 0 0 1 18 0v6" }),
2494
+ /* @__PURE__ */ jsxRuntime.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" })
2495
+ ]
2496
+ }
2497
+ ),
1567
2498
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ww-flex-1 ww-text-left ww-truncate", children: customBackend.footerAction.label }),
1568
- /* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsx("path", { d: "M9 18l6-6-6-6" }) })
2499
+ /* @__PURE__ */ jsxRuntime.jsx(
2500
+ "svg",
2501
+ {
2502
+ className: "ww-shrink-0 ww-w-3 ww-h-3 ww-opacity-60",
2503
+ viewBox: "0 0 24 24",
2504
+ fill: "none",
2505
+ stroke: "currentColor",
2506
+ strokeWidth: "2.5",
2507
+ strokeLinecap: "round",
2508
+ strokeLinejoin: "round",
2509
+ children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M9 18l6-6-6-6" })
2510
+ }
2511
+ )
1569
2512
  ]
1570
2513
  }
1571
2514
  ) }),
@@ -1593,7 +2536,7 @@ function ChatWidget({
1593
2536
  } : {}
1594
2537
  }
1595
2538
  ),
1596
- watermark && /* @__PURE__ */ jsxRuntime.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: [
2539
+ watermark && /* @__PURE__ */ jsxRuntime.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: [
1597
2540
  /* @__PURE__ */ jsxRuntime.jsxs(
1598
2541
  "a",
1599
2542
  {
@@ -1643,19 +2586,27 @@ function useAutoConfig(agentId, enabled) {
1643
2586
  if (cancelled) return;
1644
2587
  const cfg = body?.data ?? {};
1645
2588
  const remote = {};
1646
- if (cfg.profilePicture != null) remote.profilePicture = cfg.profilePicture;
1647
- if (cfg.displayName != null) remote.displayName = cfg.displayName;
2589
+ if (cfg.profilePicture != null)
2590
+ remote.profilePicture = cfg.profilePicture;
2591
+ if (cfg.displayName != null)
2592
+ remote.displayName = cfg.displayName;
1648
2593
  if (cfg.theme) remote.theme = cfg.theme;
1649
- if (cfg.userMessageColor) remote.userMessageColor = cfg.userMessageColor;
2594
+ if (cfg.userMessageColor)
2595
+ remote.userMessageColor = cfg.userMessageColor;
1650
2596
  if (Array.isArray(cfg.initialMessages) && cfg.initialMessages.length > 0)
1651
2597
  remote.initialMessages = cfg.initialMessages;
1652
2598
  if (Array.isArray(cfg.suggestedMessages))
1653
2599
  remote.suggestedMessages = cfg.suggestedMessages;
1654
- if (cfg.messagePlaceholder != null) remote.messagePlaceholder = cfg.messagePlaceholder;
2600
+ if (cfg.messagePlaceholder != null)
2601
+ remote.messagePlaceholder = cfg.messagePlaceholder;
1655
2602
  if (cfg.watermark != null) remote.watermark = cfg.watermark;
1656
2603
  if (cfg.footer != null) remote.footer = cfg.footer;
1657
- if (cfg.showThinking != null) remote.showThinking = cfg.showThinking;
1658
- if (cfg.regenerateMessage != null) remote.regenerateMessage = cfg.regenerateMessage;
2604
+ if (cfg.showThinking != null)
2605
+ remote.showThinking = cfg.showThinking;
2606
+ if (cfg.regenerateMessage != null)
2607
+ remote.regenerateMessage = cfg.regenerateMessage;
2608
+ if (cfg.enableVoice != null)
2609
+ remote.enableVoice = cfg.enableVoice;
1659
2610
  setResult({
1660
2611
  remoteConfig: remote,
1661
2612
  bubbleIconUrl: cfg.chatIcon || cfg.profilePicture || void 0,
@@ -1693,63 +2644,102 @@ function useSupportChat({
1693
2644
  const enabled = Boolean(inboxToken) && inboxToken !== "__disabled__";
1694
2645
  const STORAGE_KEY = `wlv_support_${inboxToken}`;
1695
2646
  const base = apiBase.replace(/\/$/, "");
1696
- const [session, setSession] = react.useState(null);
2647
+ const [session, setSession] = react.useState(() => {
2648
+ if (typeof window === "undefined") return null;
2649
+ try {
2650
+ const saved = localStorage.getItem(STORAGE_KEY);
2651
+ if (saved)
2652
+ return JSON.parse(saved).session ?? null;
2653
+ } catch {
2654
+ }
2655
+ return null;
2656
+ });
1697
2657
  const [rawMessages, setRawMessages] = react.useState([]);
1698
2658
  const [sending, setSending] = react.useState(false);
1699
- const [isAiMode, setIsAiMode] = react.useState(false);
2659
+ const [isAiMode, setIsAiMode] = react.useState(() => {
2660
+ if (typeof window === "undefined") return false;
2661
+ try {
2662
+ const saved = localStorage.getItem(STORAGE_KEY);
2663
+ if (saved)
2664
+ return JSON.parse(saved).isAiMode ?? false;
2665
+ } catch {
2666
+ }
2667
+ return false;
2668
+ });
1700
2669
  const [agentTyping, setAgentTyping] = react.useState(false);
1701
2670
  const [requestingHuman, setRequestingHuman] = react.useState(false);
1702
2671
  const [returningToAi, setReturningToAi] = react.useState(false);
2672
+ const [voiceActive, setVoiceActive] = react.useState(false);
2673
+ const [voiceToken, setVoiceToken] = react.useState(null);
2674
+ const [voiceServerUrl, setVoiceServerUrl] = react.useState(null);
2675
+ const [voiceLoading, setVoiceLoading] = react.useState(false);
2676
+ const [voiceError, setVoiceError] = react.useState(null);
1703
2677
  const typingTimerRef = react.useRef(null);
1704
2678
  react.useEffect(() => {
1705
2679
  if (!enabled) return;
1706
2680
  try {
1707
2681
  const saved = localStorage.getItem(STORAGE_KEY);
1708
- if (saved) {
1709
- const { session: s, isAiMode: ai } = JSON.parse(saved);
1710
- setSession(s);
1711
- setIsAiMode(ai ?? false);
1712
- }
2682
+ if (saved) JSON.parse(saved);
1713
2683
  } catch {
1714
2684
  localStorage.removeItem(STORAGE_KEY);
2685
+ setSession(null);
2686
+ setIsAiMode(false);
1715
2687
  }
1716
2688
  }, [STORAGE_KEY, enabled]);
1717
- const loadMessages = react.useCallback(async (sess) => {
1718
- try {
1719
- const res = await fetch(`${base}/api/support-chat/${sess.conversationId}/messages`, {
1720
- headers: { "x-visitor-token": sess.visitorToken }
1721
- });
1722
- const data = await res.json();
1723
- if (Array.isArray(data.data)) setRawMessages(data.data);
1724
- } catch {
1725
- }
1726
- }, [base]);
2689
+ const loadMessages = react.useCallback(
2690
+ async (sess) => {
2691
+ try {
2692
+ const res = await fetch(
2693
+ `${base}/api/support-chat/${sess.conversationId}/messages`,
2694
+ {
2695
+ headers: { "x-visitor-token": sess.visitorToken }
2696
+ }
2697
+ );
2698
+ const data = await res.json();
2699
+ if (Array.isArray(data.data)) setRawMessages(data.data);
2700
+ } catch {
2701
+ }
2702
+ },
2703
+ [base]
2704
+ );
1727
2705
  react.useEffect(() => {
1728
2706
  if (!enabled || !session) return;
1729
2707
  void loadMessages(session);
1730
2708
  let ablyClient = null;
1731
2709
  const setupAbly = async () => {
1732
2710
  try {
1733
- const res = await fetch(`${base}/api/support-chat/${session.conversationId}/ably-token`, {
1734
- headers: { "x-visitor-token": session.visitorToken }
1735
- });
2711
+ const res = await fetch(
2712
+ `${base}/api/support-chat/${session.conversationId}/ably-token`,
2713
+ {
2714
+ headers: { "x-visitor-token": session.visitorToken }
2715
+ }
2716
+ );
1736
2717
  if (!res.ok) return;
1737
2718
  const { tokenRequest, channel: channelName } = await res.json();
1738
2719
  const AblyLib = (await import('ably')).default;
1739
- ablyClient = new AblyLib.Realtime({ authCallback: (_, cb) => cb(null, tokenRequest) });
2720
+ ablyClient = new AblyLib.Realtime({
2721
+ authCallback: (_, cb) => cb(null, tokenRequest)
2722
+ });
1740
2723
  const ch = ablyClient.channels.get(channelName);
1741
2724
  ch.subscribe((msg) => {
1742
2725
  const event = { type: msg.name, ...msg.data };
1743
2726
  if (event.type === "message.created" && event.message) {
1744
2727
  setRawMessages((prev) => {
1745
- if (prev.some((m) => m.id === event.message.id)) return prev;
1746
- return [...prev.filter((m) => !m.id.startsWith("tmp_")), event.message];
2728
+ if (prev.some((m) => m.id === event.message.id))
2729
+ return prev;
2730
+ return [
2731
+ ...prev.filter((m) => !m.id.startsWith("tmp_")),
2732
+ event.message
2733
+ ];
1747
2734
  });
1748
2735
  } else if (event.type === "agent.typing") {
1749
2736
  setAgentTyping(!!event.isTyping);
1750
2737
  if (event.isTyping) {
1751
2738
  if (typingTimerRef.current) clearTimeout(typingTimerRef.current);
1752
- typingTimerRef.current = setTimeout(() => setAgentTyping(false), 6e3);
2739
+ typingTimerRef.current = setTimeout(
2740
+ () => setAgentTyping(false),
2741
+ 6e3
2742
+ );
1753
2743
  } else {
1754
2744
  if (typingTimerRef.current) clearTimeout(typingTimerRef.current);
1755
2745
  }
@@ -1757,7 +2747,11 @@ function useSupportChat({
1757
2747
  const newIsAi = event.mode === "auto";
1758
2748
  setIsAiMode(newIsAi);
1759
2749
  setSession((s) => {
1760
- if (s) localStorage.setItem(STORAGE_KEY, JSON.stringify({ session: s, isAiMode: newIsAi }));
2750
+ if (s)
2751
+ localStorage.setItem(
2752
+ STORAGE_KEY,
2753
+ JSON.stringify({ session: s, isAiMode: newIsAi })
2754
+ );
1761
2755
  return s;
1762
2756
  });
1763
2757
  }
@@ -1768,84 +2762,117 @@ function useSupportChat({
1768
2762
  void setupAbly();
1769
2763
  const pollId = setInterval(() => void loadMessages(session), 3e4);
1770
2764
  return () => {
1771
- if (ablyClient) try {
1772
- ablyClient.close();
1773
- } catch {
1774
- }
2765
+ if (ablyClient)
2766
+ try {
2767
+ ablyClient.close();
2768
+ } catch {
2769
+ }
1775
2770
  clearInterval(pollId);
1776
2771
  if (typingTimerRef.current) clearTimeout(typingTimerRef.current);
1777
2772
  };
1778
2773
  }, [session, STORAGE_KEY, base, loadMessages]);
1779
- const send = react.useCallback(async (text, attachments) => {
1780
- if (!text.trim() && !attachments?.length) return;
1781
- if (sending) return;
1782
- setSending(true);
1783
- if (!session) {
1784
- try {
1785
- const res = await fetch(`${base}/api/support-chat/start`, {
1786
- method: "POST",
1787
- headers: { "Content-Type": "application/json" },
1788
- body: JSON.stringify({
1789
- inboxToken,
1790
- message: text || `[${attachments.length} archivo(s)]`,
1791
- attachments
1792
- })
1793
- });
1794
- const data = await res.json();
1795
- if (data.data) {
1796
- const sess = {
1797
- conversationId: data.data.conversationId,
1798
- visitorToken: data.data.visitorToken
1799
- };
1800
- const ai = data.data.isAiMode ?? false;
1801
- localStorage.setItem(STORAGE_KEY, JSON.stringify({ session: sess, isAiMode: ai }));
1802
- setSession(sess);
1803
- setIsAiMode(ai);
1804
- if (Array.isArray(data.data.messages)) {
1805
- setRawMessages(data.data.messages);
2774
+ const send = react.useCallback(
2775
+ async (text, attachments) => {
2776
+ if (!text.trim() && !attachments?.length) return;
2777
+ if (sending) return;
2778
+ setSending(true);
2779
+ if (!session) {
2780
+ setRawMessages([
2781
+ {
2782
+ id: `tmp_${Date.now()}`,
2783
+ role: "customer",
2784
+ content: text || `[${attachments?.length ?? 0} archivo(s)]`,
2785
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
2786
+ metadata: attachments?.length ? { attachments } : void 0
1806
2787
  }
1807
- }
1808
- } catch {
1809
- }
1810
- } else {
1811
- setRawMessages((prev) => [...prev, {
1812
- id: `tmp_${Date.now()}`,
1813
- role: "customer",
1814
- content: text || `[${attachments.length} archivo(s)]`,
1815
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1816
- metadata: attachments?.length ? { attachments } : void 0
1817
- }]);
1818
- try {
1819
- const body = { content: text };
1820
- if (attachments?.length) body.attachments = attachments;
1821
- const res = await fetch(`${base}/api/support-chat/${session.conversationId}/messages`, {
1822
- method: "POST",
1823
- headers: { "Content-Type": "application/json", "x-visitor-token": session.visitorToken },
1824
- body: JSON.stringify(body)
1825
- });
1826
- const data = await res.json();
1827
- if (data.data?.aiReply) {
1828
- setRawMessages((prev) => {
1829
- const withoutTemp = prev.filter((m) => !m.id.startsWith("tmp_"));
1830
- return [...withoutTemp, data.data.aiReply];
2788
+ ]);
2789
+ try {
2790
+ const res = await fetch(`${base}/api/support-chat/start`, {
2791
+ method: "POST",
2792
+ headers: { "Content-Type": "application/json" },
2793
+ body: JSON.stringify({
2794
+ inboxToken,
2795
+ message: text || `[${attachments.length} archivo(s)]`,
2796
+ attachments
2797
+ })
1831
2798
  });
2799
+ const data = await res.json();
2800
+ if (data.data) {
2801
+ const sess = {
2802
+ conversationId: data.data.conversationId,
2803
+ visitorToken: data.data.visitorToken
2804
+ };
2805
+ const ai = data.data.isAiMode ?? false;
2806
+ localStorage.setItem(
2807
+ STORAGE_KEY,
2808
+ JSON.stringify({ session: sess, isAiMode: ai })
2809
+ );
2810
+ setSession(sess);
2811
+ setIsAiMode(ai);
2812
+ if (Array.isArray(data.data.messages)) {
2813
+ setRawMessages(data.data.messages);
2814
+ }
2815
+ }
2816
+ } catch {
2817
+ }
2818
+ } else {
2819
+ setRawMessages((prev) => [
2820
+ ...prev,
2821
+ {
2822
+ id: `tmp_${Date.now()}`,
2823
+ role: "customer",
2824
+ content: text || `[${attachments.length} archivo(s)]`,
2825
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
2826
+ metadata: attachments?.length ? { attachments } : void 0
2827
+ }
2828
+ ]);
2829
+ try {
2830
+ const body = { content: text };
2831
+ if (attachments?.length) body.attachments = attachments;
2832
+ const res = await fetch(
2833
+ `${base}/api/support-chat/${session.conversationId}/messages`,
2834
+ {
2835
+ method: "POST",
2836
+ headers: {
2837
+ "Content-Type": "application/json",
2838
+ "x-visitor-token": session.visitorToken
2839
+ },
2840
+ body: JSON.stringify(body)
2841
+ }
2842
+ );
2843
+ const data = await res.json();
2844
+ if (data.data?.aiReply) {
2845
+ setRawMessages((prev) => {
2846
+ const withoutTemp = prev.filter((m) => !m.id.startsWith("tmp_"));
2847
+ if (withoutTemp.some((m) => m.id === data.data.aiReply.id))
2848
+ return withoutTemp;
2849
+ return [...withoutTemp, data.data.aiReply];
2850
+ });
2851
+ }
2852
+ } catch {
1832
2853
  }
1833
- } catch {
1834
2854
  }
1835
- }
1836
- setSending(false);
1837
- }, [session, sending, inboxToken, base, STORAGE_KEY]);
2855
+ setSending(false);
2856
+ },
2857
+ [session, sending, inboxToken, base, STORAGE_KEY]
2858
+ );
1838
2859
  const requestHuman = react.useCallback(async () => {
1839
2860
  if (!session || !isAiMode || requestingHuman) return;
1840
2861
  setRequestingHuman(true);
1841
2862
  try {
1842
2863
  await fetch(`${base}/api/support-chat/${session.conversationId}/mode`, {
1843
2864
  method: "PATCH",
1844
- headers: { "Content-Type": "application/json", "x-visitor-token": session.visitorToken },
2865
+ headers: {
2866
+ "Content-Type": "application/json",
2867
+ "x-visitor-token": session.visitorToken
2868
+ },
1845
2869
  body: JSON.stringify({ mode: "human" })
1846
2870
  });
1847
2871
  setIsAiMode(false);
1848
- localStorage.setItem(STORAGE_KEY, JSON.stringify({ session, isAiMode: false }));
2872
+ localStorage.setItem(
2873
+ STORAGE_KEY,
2874
+ JSON.stringify({ session, isAiMode: false })
2875
+ );
1849
2876
  } catch {
1850
2877
  }
1851
2878
  setRequestingHuman(false);
@@ -1856,15 +2883,47 @@ function useSupportChat({
1856
2883
  try {
1857
2884
  await fetch(`${base}/api/support-chat/${session.conversationId}/mode`, {
1858
2885
  method: "PATCH",
1859
- headers: { "Content-Type": "application/json", "x-visitor-token": session.visitorToken },
2886
+ headers: {
2887
+ "Content-Type": "application/json",
2888
+ "x-visitor-token": session.visitorToken
2889
+ },
1860
2890
  body: JSON.stringify({ mode: "auto" })
1861
2891
  });
1862
2892
  setIsAiMode(true);
1863
- localStorage.setItem(STORAGE_KEY, JSON.stringify({ session, isAiMode: true }));
2893
+ localStorage.setItem(
2894
+ STORAGE_KEY,
2895
+ JSON.stringify({ session, isAiMode: true })
2896
+ );
1864
2897
  } catch {
1865
2898
  }
1866
2899
  setReturningToAi(false);
1867
2900
  }, [session, isAiMode, returningToAi, base, STORAGE_KEY]);
2901
+ const startVoiceCall = react.useCallback(async () => {
2902
+ if (!session || !isAiMode) return;
2903
+ setVoiceLoading(true);
2904
+ setVoiceError(null);
2905
+ try {
2906
+ const res = await fetch(
2907
+ `${base}/api/support-chat/${session.conversationId}/livekit-token`,
2908
+ {
2909
+ headers: { "x-visitor-token": session.visitorToken }
2910
+ }
2911
+ );
2912
+ const data = await res.json();
2913
+ if (!res.ok) throw new Error(data.error || "Failed to start call");
2914
+ setVoiceToken(data.token);
2915
+ setVoiceServerUrl(data.url);
2916
+ setVoiceActive(true);
2917
+ } catch (err) {
2918
+ setVoiceError(err.message);
2919
+ }
2920
+ setVoiceLoading(false);
2921
+ }, [session, isAiMode, base]);
2922
+ const stopVoiceCall = react.useCallback(() => {
2923
+ setVoiceActive(false);
2924
+ setVoiceToken(null);
2925
+ setVoiceServerUrl(null);
2926
+ }, []);
1868
2927
  const reset = react.useCallback(() => {
1869
2928
  localStorage.removeItem(STORAGE_KEY);
1870
2929
  setSession(null);
@@ -1873,28 +2932,78 @@ function useSupportChat({
1873
2932
  setAgentTyping(false);
1874
2933
  setRequestingHuman(false);
1875
2934
  setReturningToAi(false);
1876
- }, [STORAGE_KEY]);
2935
+ stopVoiceCall();
2936
+ }, [STORAGE_KEY, stopVoiceCall]);
1877
2937
  const messages = react.useMemo(
1878
2938
  () => rawMessages.filter((m) => m.role !== "system").map(toWidgetMsg),
1879
2939
  [rawMessages]
1880
2940
  );
1881
2941
  const footerAction = react.useMemo(() => {
1882
2942
  if (isAiMode) {
1883
- return { label: requestHumanLabel, sublabel: "Te conectamos con un agente disponible", icon: "human", onClick: requestHuman, loading: requestingHuman };
2943
+ return {
2944
+ label: requestHumanLabel,
2945
+ sublabel: "Te conectamos con un agente disponible",
2946
+ icon: "human",
2947
+ onClick: requestHuman,
2948
+ loading: requestingHuman
2949
+ };
1884
2950
  }
1885
2951
  if (!isAiMode && session && returnToAiLabel) {
1886
- return { label: returnToAiLabel, sublabel: "Respuesta instant\xE1nea con inteligencia artificial", icon: "ai", onClick: returnToAi, loading: returningToAi };
2952
+ return {
2953
+ label: returnToAiLabel,
2954
+ sublabel: "Respuesta instant\xE1nea con inteligencia artificial",
2955
+ icon: "ai",
2956
+ onClick: returnToAi,
2957
+ loading: returningToAi
2958
+ };
1887
2959
  }
1888
2960
  return void 0;
1889
- }, [isAiMode, session, requestHumanLabel, requestHuman, requestingHuman, returnToAiLabel, returnToAi, returningToAi]);
1890
- return react.useMemo(() => ({
1891
- messages,
1892
- streaming: sending || agentTyping,
1893
- send,
1894
- reset,
1895
- mode: isAiMode ? "ai" : "human",
1896
- ...footerAction ? { footerAction } : {}
1897
- }), [messages, sending, agentTyping, send, reset, isAiMode, footerAction]);
2961
+ }, [
2962
+ isAiMode,
2963
+ session,
2964
+ requestHumanLabel,
2965
+ requestHuman,
2966
+ requestingHuman,
2967
+ returnToAiLabel,
2968
+ returnToAi,
2969
+ returningToAi
2970
+ ]);
2971
+ return react.useMemo(
2972
+ () => ({
2973
+ messages,
2974
+ streaming: sending || agentTyping,
2975
+ send,
2976
+ reset,
2977
+ mode: isAiMode || !session ? "ai" : "human",
2978
+ ...footerAction ? { footerAction } : {},
2979
+ voiceCall: isAiMode && session ? {
2980
+ active: voiceActive,
2981
+ token: voiceToken,
2982
+ serverUrl: voiceServerUrl,
2983
+ start: startVoiceCall,
2984
+ stop: stopVoiceCall,
2985
+ loading: voiceLoading,
2986
+ error: voiceError
2987
+ } : void 0
2988
+ }),
2989
+ [
2990
+ messages,
2991
+ sending,
2992
+ agentTyping,
2993
+ send,
2994
+ reset,
2995
+ isAiMode,
2996
+ footerAction,
2997
+ voiceActive,
2998
+ voiceToken,
2999
+ voiceServerUrl,
3000
+ startVoiceCall,
3001
+ stopVoiceCall,
3002
+ voiceLoading,
3003
+ voiceError,
3004
+ session
3005
+ ]
3006
+ );
1898
3007
  }
1899
3008
  var KEY_EXPANDED = "wallavi_bubble_expanded";
1900
3009
  var KEY_DISMISSED = "wallavi_bubble_dismissed";
@@ -1921,35 +3030,46 @@ function BubbleWidget({
1921
3030
  ...chatProps
1922
3031
  }) {
1923
3032
  const supportBackend = useSupportChat(
1924
- inboxToken ? { inboxToken, apiBase: supportApiBase, requestHumanLabel, returnToAiLabel } : { inboxToken: "__disabled__", apiBase: supportApiBase }
3033
+ inboxToken ? {
3034
+ inboxToken,
3035
+ apiBase: supportApiBase,
3036
+ requestHumanLabel,
3037
+ returnToAiLabel
3038
+ } : { inboxToken: "__disabled__", apiBase: supportApiBase }
1925
3039
  );
1926
3040
  const isControlled = isOpenProp !== void 0;
1927
3041
  const [internalOpen, setInternalOpen] = react.useState(false);
1928
3042
  const open = isControlled ? isOpenProp : internalOpen;
1929
- const setOpen = react.useCallback((valueOrUpdater) => {
1930
- if (!isControlled) {
1931
- if (typeof valueOrUpdater === "function") {
1932
- setInternalOpen((prev) => {
1933
- const next = valueOrUpdater(prev);
1934
- onOpenChange?.(next);
1935
- return next;
1936
- });
3043
+ const setOpen = react.useCallback(
3044
+ (valueOrUpdater) => {
3045
+ if (!isControlled) {
3046
+ if (typeof valueOrUpdater === "function") {
3047
+ setInternalOpen((prev) => {
3048
+ const next = valueOrUpdater(prev);
3049
+ onOpenChange?.(next);
3050
+ return next;
3051
+ });
3052
+ } else {
3053
+ setInternalOpen(valueOrUpdater);
3054
+ onOpenChange?.(valueOrUpdater);
3055
+ }
1937
3056
  } else {
1938
- setInternalOpen(valueOrUpdater);
1939
- onOpenChange?.(valueOrUpdater);
3057
+ const next = typeof valueOrUpdater === "function" ? valueOrUpdater(open) : valueOrUpdater;
3058
+ onOpenChange?.(next);
1940
3059
  }
1941
- } else {
1942
- const next = typeof valueOrUpdater === "function" ? valueOrUpdater(open) : valueOrUpdater;
1943
- onOpenChange?.(next);
1944
- }
1945
- }, [isControlled, open, onOpenChange]);
3060
+ },
3061
+ [isControlled, open, onOpenChange]
3062
+ );
1946
3063
  const [expanded, setExpanded] = react.useState(false);
1947
3064
  const panelRef = react.useRef(null);
1948
3065
  const autoOpenedRef = react.useRef(false);
1949
3066
  react.useEffect(() => {
1950
3067
  if (localStorage.getItem(KEY_EXPANDED) === "true") setExpanded(true);
1951
3068
  }, []);
1952
- const remote = useAutoConfig(inboxToken ? "" : chatProps.agentId ?? "", !inboxToken && autoConfig);
3069
+ const remote = useAutoConfig(
3070
+ inboxToken ? "" : chatProps.agentId ?? "",
3071
+ !inboxToken && autoConfig
3072
+ );
1953
3073
  const resolvedPosition = positionProp ?? remote.position;
1954
3074
  const resolvedBubbleIcon = bubbleIconUrlProp ?? remote.bubbleIconUrl;
1955
3075
  const resolvedAutoOpen = autoOpenProp || remote.autoOpen;
@@ -1981,7 +3101,7 @@ function BubbleWidget({
1981
3101
  react.useEffect(() => {
1982
3102
  if (!resolvedKeyboardShortcut) return;
1983
3103
  const onKey = (e) => {
1984
- if ((e.metaKey || e.ctrlKey) && e.key === shortcutKey) {
3104
+ if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === shortcutKey.toLowerCase()) {
1985
3105
  e.preventDefault();
1986
3106
  setOpenRef.current((v) => !v);
1987
3107
  }
@@ -2000,7 +3120,10 @@ function BubbleWidget({
2000
3120
  const handleClose = () => {
2001
3121
  setOpen(false);
2002
3122
  if (!isControlled) {
2003
- localStorage.setItem(KEY_DISMISSED, String(Date.now() + 24 * 60 * 60 * 1e3));
3123
+ localStorage.setItem(
3124
+ KEY_DISMISSED,
3125
+ String(Date.now() + 24 * 60 * 60 * 1e3)
3126
+ );
2004
3127
  }
2005
3128
  };
2006
3129
  const toggleExpanded = () => setExpanded((v) => {
@@ -2009,7 +3132,10 @@ function BubbleWidget({
2009
3132
  return next;
2010
3133
  });
2011
3134
  const isLeft = resolvedPosition === "bottom-left";
2012
- const panelWidth = expanded ? Math.min(expandedWidth, typeof window !== "undefined" ? window.innerWidth - 40 : expandedWidth) : resolvedWidth;
3135
+ const panelWidth = expanded ? Math.min(
3136
+ expandedWidth,
3137
+ typeof window !== "undefined" ? window.innerWidth - 40 : expandedWidth
3138
+ ) : resolvedWidth;
2013
3139
  const panelHeight = expanded ? expandedHeight : resolvedHeight;
2014
3140
  return /* @__PURE__ */ jsxRuntime.jsxs(
2015
3141
  "div",
@@ -2067,15 +3193,20 @@ function BubbleWidget({
2067
3193
  alignItems: "center",
2068
3194
  justifyContent: "center",
2069
3195
  transition: "transform 0.2s ease, box-shadow 0.2s ease",
2070
- background: open ? "var(--foreground, #19191c)" : "var(--background, #fff)",
2071
- color: open ? "var(--background, #fff)" : "var(--foreground, #19191c)"
3196
+ background: open ? "#19191c" : "#ffffff",
3197
+ color: open ? "#ffffff" : "#19191c"
2072
3198
  },
2073
3199
  children: open ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { style: { width: 20, height: 20 } }) : resolvedBubbleIcon ? /* @__PURE__ */ jsxRuntime.jsx(
2074
3200
  "img",
2075
3201
  {
2076
3202
  src: resolvedBubbleIcon,
2077
3203
  alt: "",
2078
- style: { width: "100%", height: "100%", objectFit: "cover", display: "block" }
3204
+ style: {
3205
+ width: "100%",
3206
+ height: "100%",
3207
+ objectFit: "cover",
3208
+ display: "block"
3209
+ }
2079
3210
  }
2080
3211
  ) : /* @__PURE__ */ jsxRuntime.jsx(DefaultIcon, {})
2081
3212
  }
@@ -2087,6 +3218,9 @@ function BubbleWidget({
2087
3218
 
2088
3219
  exports.BubbleWidget = BubbleWidget;
2089
3220
  exports.ChatWidget = ChatWidget;
3221
+ exports.PlanCard = PlanCard;
3222
+ exports.ReasoningBlock = ReasoningBlock;
3223
+ exports.ToolCallBadge = ToolCallBadge;
2090
3224
  exports.formatToolName = formatToolName;
2091
3225
  exports.getContrastColor = getContrastColor;
2092
3226
  exports.useAttachments = useAttachments;