@wallavi/widget 1.6.8 → 1.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.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\\.5 {\n height: 0.625rem;\n}\n.ww-h-20 {\n height: 5rem;\n}\n.ww-h-3 {\n height: 0.75rem;\n}\n.ww-h-3\\.5 {\n height: 0.875rem;\n}\n.ww-h-4 {\n height: 1rem;\n}\n.ww-h-6 {\n height: 1.5rem;\n}\n.ww-h-7 {\n height: 1.75rem;\n}\n.ww-h-8 {\n height: 2rem;\n}\n.ww-h-full {\n height: 100%;\n}\n.ww-max-h-32 {\n max-height: 8rem;\n}\n.ww-max-h-48 {\n max-height: 12rem;\n}\n.ww-max-h-\\[168px\\] {\n max-height: 168px;\n}\n.ww-max-h-\\[180px\\] {\n max-height: 180px;\n}\n.ww-w-0\\.5 {\n width: 0.125rem;\n}\n.ww-w-10 {\n width: 2.5rem;\n}\n.ww-w-2\\.5 {\n width: 0.625rem;\n}\n.ww-w-20 {\n width: 5rem;\n}\n.ww-w-3 {\n width: 0.75rem;\n}\n.ww-w-3\\.5 {\n width: 0.875rem;\n}\n.ww-w-4 {\n width: 1rem;\n}\n.ww-w-6 {\n width: 1.5rem;\n}\n.ww-w-7 {\n width: 1.75rem;\n}\n.ww-w-8 {\n width: 2rem;\n}\n.ww-w-fit {\n width: -moz-fit-content;\n width: fit-content;\n}\n.ww-w-full {\n width: 100%;\n}\n.ww-min-w-0 {\n min-width: 0px;\n}\n.ww-max-w-\\[120px\\] {\n max-width: 120px;\n}\n.ww-max-w-\\[200px\\] {\n max-width: 200px;\n}\n.ww-max-w-\\[78\\%\\] {\n max-width: 78%;\n}\n.ww-max-w-\\[82\\%\\] {\n max-width: 82%;\n}\n.ww-max-w-none {\n max-width: none;\n}\n.ww-flex-1 {\n flex: 1 1 0%;\n}\n.ww-shrink-0 {\n flex-shrink: 0;\n}\n.ww-rotate-180 {\n --tw-rotate: 180deg;\n transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));\n}\n@keyframes ww-pulse {\n 50% {\n opacity: .5;\n }\n}\n.ww-animate-pulse {\n animation: ww-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n}\n@keyframes ww-spin {\n to {\n transform: rotate(360deg);\n }\n}\n.ww-animate-spin {\n animation: ww-spin 1s linear infinite;\n}\n.ww-cursor-default {\n cursor: default;\n}\n.ww-cursor-pointer {\n cursor: pointer;\n}\n.ww-select-none {\n -webkit-user-select: none;\n -moz-user-select: none;\n user-select: none;\n}\n.ww-resize-none {\n resize: none;\n}\n.ww-grid-cols-2 {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n}\n.ww-flex-col {\n flex-direction: column;\n}\n.ww-flex-wrap {\n flex-wrap: wrap;\n}\n.ww-items-start {\n align-items: flex-start;\n}\n.ww-items-end {\n align-items: flex-end;\n}\n.ww-items-center {\n align-items: center;\n}\n.ww-justify-end {\n justify-content: flex-end;\n}\n.ww-justify-center {\n justify-content: center;\n}\n.ww-justify-between {\n justify-content: space-between;\n}\n.ww-gap-0\\.5 {\n gap: 0.125rem;\n}\n.ww-gap-1 {\n gap: 0.25rem;\n}\n.ww-gap-1\\.5 {\n gap: 0.375rem;\n}\n.ww-gap-2 {\n gap: 0.5rem;\n}\n.ww-gap-2\\.5 {\n gap: 0.625rem;\n}\n.ww-gap-4 {\n gap: 1rem;\n}\n.ww-divide-y > :not([hidden]) ~ :not([hidden]) {\n --tw-divide-y-reverse: 0;\n border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse)));\n border-bottom-width: calc(1px * var(--tw-divide-y-reverse));\n}\n.ww-divide-border\\/40 > :not([hidden]) ~ :not([hidden]) {\n border-color: hsl(var(--border) / 0.4);\n}\n.ww-overflow-hidden {\n overflow: hidden;\n}\n.ww-overflow-y-auto {\n overflow-y: auto;\n}\n.ww-overscroll-contain {\n overscroll-behavior: contain;\n}\n.ww-truncate {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.ww-whitespace-pre-wrap {\n white-space: pre-wrap;\n}\n.ww-rounded {\n border-radius: 0.25rem;\n}\n.ww-rounded-2xl {\n border-radius: 1rem;\n}\n.ww-rounded-full {\n border-radius: 9999px;\n}\n.ww-rounded-lg {\n border-radius: var(--radius);\n}\n.ww-rounded-xl {\n border-radius: 0.75rem;\n}\n.ww-rounded-tl-none {\n border-top-left-radius: 0px;\n}\n.ww-rounded-tl-sm {\n border-top-left-radius: calc(var(--radius) - 4px);\n}\n.ww-rounded-tr-sm {\n border-top-right-radius: calc(var(--radius) - 4px);\n}\n.ww-border {\n border-width: 1px;\n}\n.ww-border-2 {\n border-width: 2px;\n}\n.ww-border-b {\n border-bottom-width: 1px;\n}\n.ww-border-l-2 {\n border-left-width: 2px;\n}\n.ww-border-t {\n border-top-width: 1px;\n}\n.ww-border-background {\n border-color: hsl(var(--background));\n}\n.ww-border-black\\/10 {\n border-color: rgb(0 0 0 / 0.1);\n}\n.ww-border-border {\n border-color: hsl(var(--border));\n}\n.ww-border-border\\/30 {\n border-color: hsl(var(--border) / 0.3);\n}\n.ww-border-border\\/40 {\n border-color: hsl(var(--border) / 0.4);\n}\n.ww-border-border\\/50 {\n border-color: hsl(var(--border) / 0.5);\n}\n.ww-border-border\\/60 {\n border-color: hsl(var(--border) / 0.6);\n}\n.ww-border-border\\/70 {\n border-color: hsl(var(--border) / 0.7);\n}\n.ww-border-muted {\n border-color: hsl(var(--muted));\n}\n.ww-border-muted-foreground\\/30 {\n border-color: hsl(var(--muted-foreground) / 0.3);\n}\n.ww-border-red-200 {\n --tw-border-opacity: 1;\n border-color: rgb(254 202 202 / var(--tw-border-opacity, 1));\n}\n.ww-border-white\\/20 {\n border-color: rgb(255 255 255 / 0.2);\n}\n.ww-bg-background {\n background-color: hsl(var(--background));\n}\n.ww-bg-background\\/20 {\n background-color: hsl(var(--background) / 0.2);\n}\n.ww-bg-background\\/40 {\n background-color: hsl(var(--background) / 0.4);\n}\n.ww-bg-background\\/50 {\n background-color: hsl(var(--background) / 0.5);\n}\n.ww-bg-background\\/90 {\n background-color: hsl(var(--background) / 0.9);\n}\n.ww-bg-black\\/10 {\n background-color: rgb(0 0 0 / 0.1);\n}\n.ww-bg-foreground {\n background-color: hsl(var(--foreground));\n}\n.ww-bg-foreground\\/60 {\n background-color: hsl(var(--foreground) / 0.6);\n}\n.ww-bg-muted {\n background-color: hsl(var(--muted));\n}\n.ww-bg-muted-foreground\\/10 {\n background-color: hsl(var(--muted-foreground) / 0.1);\n}\n.ww-bg-muted\\/50 {\n background-color: hsl(var(--muted) / 0.5);\n}\n.ww-bg-muted\\/60 {\n background-color: hsl(var(--muted) / 0.6);\n}\n.ww-bg-primary {\n background-color: hsl(var(--primary));\n}\n.ww-bg-primary\\/5 {\n background-color: hsl(var(--primary) / 0.05);\n}\n.ww-bg-red-50 {\n --tw-bg-opacity: 1;\n background-color: rgb(254 242 242 / var(--tw-bg-opacity, 1));\n}\n.ww-bg-transparent {\n background-color: transparent;\n}\n.ww-bg-white\\/10 {\n background-color: rgb(255 255 255 / 0.1);\n}\n.ww-bg-white\\/15 {\n background-color: rgb(255 255 255 / 0.15);\n}\n.ww-fill-current {\n fill: currentColor;\n}\n.ww-object-cover {\n -o-object-fit: cover;\n object-fit: cover;\n}\n.ww-p-0\\.5 {\n padding: 0.125rem;\n}\n.ww-p-1\\.5 {\n padding: 0.375rem;\n}\n.ww-p-3 {\n padding: 0.75rem;\n}\n.ww-px-1 {\n padding-left: 0.25rem;\n padding-right: 0.25rem;\n}\n.ww-px-2 {\n padding-left: 0.5rem;\n padding-right: 0.5rem;\n}\n.ww-px-2\\.5 {\n padding-left: 0.625rem;\n padding-right: 0.625rem;\n}\n.ww-px-3 {\n padding-left: 0.75rem;\n padding-right: 0.75rem;\n}\n.ww-px-3\\.5 {\n padding-left: 0.875rem;\n padding-right: 0.875rem;\n}\n.ww-px-4 {\n padding-left: 1rem;\n padding-right: 1rem;\n}\n.ww-py-0\\.5 {\n padding-top: 0.125rem;\n padding-bottom: 0.125rem;\n}\n.ww-py-1 {\n padding-top: 0.25rem;\n padding-bottom: 0.25rem;\n}\n.ww-py-1\\.5 {\n padding-top: 0.375rem;\n padding-bottom: 0.375rem;\n}\n.ww-py-2 {\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n}\n.ww-py-2\\.5 {\n padding-top: 0.625rem;\n padding-bottom: 0.625rem;\n}\n.ww-py-3 {\n padding-top: 0.75rem;\n padding-bottom: 0.75rem;\n}\n.ww-py-4 {\n padding-top: 1rem;\n padding-bottom: 1rem;\n}\n.ww-pb-1\\.5 {\n padding-bottom: 0.375rem;\n}\n.ww-pb-4 {\n padding-bottom: 1rem;\n}\n.ww-pl-2 {\n padding-left: 0.5rem;\n}\n.ww-pl-7 {\n padding-left: 1.75rem;\n}\n.ww-pr-0\\.5 {\n padding-right: 0.125rem;\n}\n.ww-pr-3 {\n padding-right: 0.75rem;\n}\n.ww-pt-2 {\n padding-top: 0.5rem;\n}\n.ww-pt-5 {\n padding-top: 1.25rem;\n}\n.ww-text-left {\n text-align: left;\n}\n.ww-text-center {\n text-align: center;\n}\n.ww-align-middle {\n vertical-align: middle;\n}\n.ww-text-\\[10\\.5px\\] {\n font-size: 10.5px;\n}\n.ww-text-\\[10px\\] {\n font-size: 10px;\n}\n.ww-text-\\[11px\\] {\n font-size: 11px;\n}\n.ww-text-\\[12\\.5px\\] {\n font-size: 12.5px;\n}\n.ww-text-\\[12px\\] {\n font-size: 12px;\n}\n.ww-text-\\[8px\\] {\n font-size: 8px;\n}\n.ww-text-sm {\n font-size: 0.875rem;\n line-height: 1.25rem;\n}\n.ww-text-xs {\n font-size: 0.75rem;\n line-height: 1rem;\n}\n.ww-font-medium {\n font-weight: 500;\n}\n.ww-font-semibold {\n font-weight: 600;\n}\n.ww-uppercase {\n text-transform: uppercase;\n}\n.ww-leading-none {\n line-height: 1;\n}\n.ww-leading-relaxed {\n line-height: 1.625;\n}\n.ww-leading-snug {\n line-height: 1.375;\n}\n.ww-leading-tight {\n line-height: 1.25;\n}\n.ww-tracking-widest {\n letter-spacing: 0.1em;\n}\n.ww-text-background {\n color: hsl(var(--background));\n}\n.ww-text-black\\/40 {\n color: rgb(0 0 0 / 0.4);\n}\n.ww-text-black\\/60 {\n color: rgb(0 0 0 / 0.6);\n}\n.ww-text-destructive {\n color: hsl(var(--destructive));\n}\n.ww-text-destructive\\/70 {\n color: hsl(var(--destructive) / 0.7);\n}\n.ww-text-emerald-500 {\n --tw-text-opacity: 1;\n color: rgb(16 185 129 / var(--tw-text-opacity, 1));\n}\n.ww-text-foreground {\n color: hsl(var(--foreground));\n}\n.ww-text-foreground\\/60 {\n color: hsl(var(--foreground) / 0.6);\n}\n.ww-text-foreground\\/70 {\n color: hsl(var(--foreground) / 0.7);\n}\n.ww-text-foreground\\/80 {\n color: hsl(var(--foreground) / 0.8);\n}\n.ww-text-muted-foreground {\n color: hsl(var(--muted-foreground));\n}\n.ww-text-muted-foreground\\/40 {\n color: hsl(var(--muted-foreground) / 0.4);\n}\n.ww-text-muted-foreground\\/50 {\n color: hsl(var(--muted-foreground) / 0.5);\n}\n.ww-text-muted-foreground\\/70 {\n color: hsl(var(--muted-foreground) / 0.7);\n}\n.ww-text-muted-foreground\\/80 {\n color: hsl(var(--muted-foreground) / 0.8);\n}\n.ww-text-primary {\n color: hsl(var(--primary));\n}\n.ww-text-primary-foreground {\n color: hsl(var(--primary-foreground));\n}\n.ww-text-primary\\/70 {\n color: hsl(var(--primary) / 0.7);\n}\n.ww-text-primary\\/80 {\n color: hsl(var(--primary) / 0.8);\n}\n.ww-text-red-400 {\n --tw-text-opacity: 1;\n color: rgb(248 113 113 / var(--tw-text-opacity, 1));\n}\n.ww-text-red-500 {\n --tw-text-opacity: 1;\n color: rgb(239 68 68 / var(--tw-text-opacity, 1));\n}\n.ww-text-red-600 {\n --tw-text-opacity: 1;\n color: rgb(220 38 38 / var(--tw-text-opacity, 1));\n}\n.ww-text-white\\/60 {\n color: rgb(255 255 255 / 0.6);\n}\n.ww-text-white\\/80 {\n color: rgb(255 255 255 / 0.8);\n}\n.ww-line-through {\n text-decoration-line: line-through;\n}\n.ww-no-underline {\n text-decoration-line: none;\n}\n.ww-decoration-foreground\\/20 {\n text-decoration-color: hsl(var(--foreground) / 0.2);\n}\n.ww-opacity-100 {\n opacity: 1;\n}\n.ww-opacity-30 {\n opacity: 0.3;\n}\n.ww-opacity-35 {\n opacity: 0.35;\n}\n.ww-opacity-40 {\n opacity: 0.4;\n}\n.ww-opacity-60 {\n opacity: 0.6;\n}\n.ww-opacity-70 {\n opacity: 0.7;\n}\n.ww-opacity-80 {\n opacity: 0.8;\n}\n.ww-shadow-2xl {\n --tw-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.25);\n --tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color);\n box-shadow:\n var(--tw-ring-offset-shadow, 0 0 #0000),\n var(--tw-ring-shadow, 0 0 #0000),\n var(--tw-shadow);\n}\n.ww-shadow-sm {\n --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);\n --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);\n box-shadow:\n var(--tw-ring-offset-shadow, 0 0 #0000),\n var(--tw-ring-shadow, 0 0 #0000),\n var(--tw-shadow);\n}\n.ww-shadow-xl {\n --tw-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);\n --tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);\n box-shadow:\n var(--tw-ring-offset-shadow, 0 0 #0000),\n var(--tw-ring-shadow, 0 0 #0000),\n var(--tw-shadow);\n}\n.ww-outline-none {\n outline: 2px solid transparent;\n outline-offset: 2px;\n}\n.ww-ring-2 {\n --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);\n --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);\n box-shadow:\n var(--tw-ring-offset-shadow),\n var(--tw-ring-shadow),\n var(--tw-shadow, 0 0 #0000);\n}\n.ww-ring-inset {\n --tw-ring-inset: inset;\n}\n.ww-ring-primary\\/60 {\n --tw-ring-color: hsl(var(--primary) / 0.6);\n}\n.ww-backdrop-blur-sm {\n --tw-backdrop-blur: blur(4px);\n backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);\n}\n.ww-transition-all {\n transition-property: all;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-duration: 150ms;\n}\n.ww-transition-colors {\n transition-property:\n color,\n background-color,\n border-color,\n text-decoration-color,\n fill,\n stroke;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-duration: 150ms;\n}\n.ww-transition-shadow {\n transition-property: box-shadow;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-duration: 150ms;\n}\n.ww-transition-transform {\n transition-property: transform;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-duration: 150ms;\n}\n.ww-duration-100 {\n transition-duration: 100ms;\n}\n.ww-duration-150 {\n transition-duration: 150ms;\n}\n.ww-duration-200 {\n transition-duration: 200ms;\n}\n@keyframes enter {\n from {\n opacity: var(--tw-enter-opacity, 1);\n transform: translate3d(var(--tw-enter-translate-x, 0), var(--tw-enter-translate-y, 0), 0) scale3d(var(--tw-enter-scale, 1), var(--tw-enter-scale, 1), var(--tw-enter-scale, 1)) rotate(var(--tw-enter-rotate, 0));\n }\n}\n@keyframes exit {\n to {\n opacity: var(--tw-exit-opacity, 1);\n transform: translate3d(var(--tw-exit-translate-x, 0), var(--tw-exit-translate-y, 0), 0) scale3d(var(--tw-exit-scale, 1), var(--tw-exit-scale, 1), var(--tw-exit-scale, 1)) rotate(var(--tw-exit-rotate, 0));\n }\n}\n.ww-duration-100 {\n animation-duration: 100ms;\n}\n.ww-duration-150 {\n animation-duration: 150ms;\n}\n.ww-duration-200 {\n animation-duration: 200ms;\n}\n.wallavi-widget *,\n.wallavi-widget *::before,\n.wallavi-widget *::after {\n box-sizing: border-box;\n border-width: 0;\n border-style: solid;\n}\n.wallavi-widget button {\n cursor: pointer;\n}\n.wallavi-widget img,\n.wallavi-widget video {\n max-width: 100%;\n height: auto;\n}\n.placeholder\\:ww-text-muted-foreground\\/40::-moz-placeholder {\n color: hsl(var(--muted-foreground) / 0.4);\n}\n.placeholder\\:ww-text-muted-foreground\\/40::placeholder {\n color: hsl(var(--muted-foreground) / 0.4);\n}\n.placeholder\\:ww-text-muted-foreground\\/50::-moz-placeholder {\n color: hsl(var(--muted-foreground) / 0.5);\n}\n.placeholder\\:ww-text-muted-foreground\\/50::placeholder {\n color: hsl(var(--muted-foreground) / 0.5);\n}\n.focus-within\\:ww-ring-1:focus-within {\n --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);\n --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);\n box-shadow:\n var(--tw-ring-offset-shadow),\n var(--tw-ring-shadow),\n var(--tw-shadow, 0 0 #0000);\n}\n.focus-within\\:ww-ring-ring\\/40:focus-within {\n --tw-ring-color: hsl(var(--ring) / 0.4);\n}\n.hover\\:ww-border-foreground\\/25:hover {\n border-color: hsl(var(--foreground) / 0.25);\n}\n.hover\\:ww-border-foreground\\/30:hover {\n border-color: hsl(var(--foreground) / 0.3);\n}\n.hover\\:ww-bg-background:hover {\n background-color: hsl(var(--background));\n}\n.hover\\:ww-bg-foreground\\/10:hover {\n background-color: hsl(var(--foreground) / 0.1);\n}\n.hover\\:ww-bg-muted:hover {\n background-color: hsl(var(--muted));\n}\n.hover\\:ww-bg-white\\/10:hover {\n background-color: rgb(255 255 255 / 0.1);\n}\n.hover\\:ww-text-foreground:hover {\n color: hsl(var(--foreground));\n}\n.focus\\:ww-ring-1:focus {\n --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);\n --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);\n box-shadow:\n var(--tw-ring-offset-shadow),\n var(--tw-ring-shadow),\n var(--tw-shadow, 0 0 #0000);\n}\n.focus\\:ww-ring-ring\\/40:focus {\n --tw-ring-color: hsl(var(--ring) / 0.4);\n}\n.disabled\\:ww-opacity-40:disabled {\n opacity: 0.4;\n}\n.disabled\\:ww-opacity-50:disabled {\n opacity: 0.5;\n}\n.dark\\:ww-border-red-800:is(.ww-dark *) {\n --tw-border-opacity: 1;\n border-color: rgb(153 27 27 / var(--tw-border-opacity, 1));\n}\n.dark\\:ww-bg-red-950:is(.ww-dark *) {\n --tw-bg-opacity: 1;\n background-color: rgb(69 10 10 / var(--tw-bg-opacity, 1));\n}\n.dark\\:ww-text-red-400:is(.ww-dark *) {\n --tw-text-opacity: 1;\n color: rgb(248 113 113 / var(--tw-text-opacity, 1));\n}\n");
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,26 +91,229 @@ 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,
107
311
  onNavigate,
108
- playgroundOverrides
312
+ playgroundOverrides,
313
+ customBackend
109
314
  }) {
110
- const persistKey = persist ? `wallavi_${agentId}` : null;
315
+ const userId = userContext?.userId;
316
+ const persistKey = persist ? userId ? `wallavi_${agentId}_${userId}` : `wallavi_${agentId}` : null;
111
317
  const onNavigateRef = react.useRef(onNavigate);
112
318
  react.useEffect(() => {
113
319
  onNavigateRef.current = onNavigate;
@@ -131,46 +337,237 @@ function useChat({
131
337
  return crypto.randomUUID();
132
338
  });
133
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
+ });
134
453
  react.useEffect(() => {
135
- if (!persistKey) return;
454
+ if (customBackend || !persistKey) return;
136
455
  try {
137
456
  sessionStorage.setItem(`${persistKey}_msgs`, JSON.stringify(messages));
138
457
  } catch {
139
458
  }
140
- }, [persistKey, messages]);
459
+ }, [customBackend, persistKey, messages]);
141
460
  react.useEffect(() => {
142
- if (!persistKey) return;
461
+ if (customBackend || !persistKey) return;
143
462
  try {
144
463
  localStorage.setItem(`${persistKey}_tid`, threadId);
145
464
  } catch {
146
465
  }
147
- }, [persistKey, threadId]);
466
+ }, [customBackend, persistKey, threadId]);
148
467
  react.useEffect(() => {
149
- if (!persistKey || typeof window === "undefined") return;
150
- if (messages.length > 0) return;
151
- void (async () => {
152
- try {
153
- const res = await fetch(
154
- `${API_URL}/api/chat/messages?agentId=${encodeURIComponent(agentId)}&threadId=${encodeURIComponent(threadId)}`
155
- );
156
- if (!res.ok) return;
157
- const { data } = await res.json();
158
- if (!data?.length) return;
159
- const restored = data.map((m) => ({
160
- id: m.id,
161
- role: m.role,
162
- parts: m.parts.filter((p) => p?.type)
163
- })).filter((m) => m.parts.length > 0);
164
- if (restored.length > 0) setMessages(restored);
165
- } catch {
468
+ if (customBackend || !persistKey || typeof window === "undefined") return;
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
+ }
166
482
  }
167
- })();
168
- }, []);
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]);
169
565
  const reset = react.useCallback(() => {
170
566
  setMessages([]);
171
567
  setInput("");
172
568
  setStreaming(false);
173
569
  setThreadId(crypto.randomUUID());
570
+ setDebugTraces([]);
174
571
  streamingMsgIdRef.current = null;
175
572
  if (persistKey) {
176
573
  try {
@@ -181,146 +578,19 @@ function useChat({
181
578
  }
182
579
  }
183
580
  }, [persistKey]);
184
- const applyStreamEvent = react.useCallback((proto, msgId) => {
185
- if (proto.type === "navigate") {
186
- if (proto.path.startsWith("/")) onNavigateRef.current?.(proto.path);
187
- return;
188
- }
189
- setMessages((prev) => {
190
- const idx = prev.findIndex((m) => m.id === msgId);
191
- if (idx === -1) return prev;
192
- const existing = prev[idx];
193
- const msg = { id: existing.id, role: existing.role, parts: [...existing.parts] };
194
- switch (proto.type) {
195
- case "text-delta": {
196
- const textIdx = msg.parts.findIndex((p) => p.type === "text");
197
- if (textIdx === -1) {
198
- msg.parts.push({ type: "text", text: proto.delta });
199
- } else {
200
- const p = msg.parts[textIdx];
201
- msg.parts[textIdx] = { type: "text", text: p.text + proto.delta };
202
- }
203
- break;
204
- }
205
- case "reasoning-delta": {
206
- const rIdx = msg.parts.findIndex((p) => p.type === "reasoning");
207
- if (rIdx === -1) {
208
- msg.parts.unshift({ type: "reasoning", text: proto.delta });
209
- } else {
210
- const p = msg.parts[rIdx];
211
- msg.parts[rIdx] = { type: "reasoning", text: p.text + proto.delta };
212
- }
213
- break;
214
- }
215
- case "tool-input-available": {
216
- msg.parts.push({
217
- type: "tool",
218
- toolCallId: proto.toolCallId,
219
- toolName: proto.toolName,
220
- input: proto.input ?? {},
221
- status: "running"
222
- });
223
- break;
224
- }
225
- case "tool-output-available": {
226
- const tIdx = msg.parts.findIndex(
227
- (p) => p.type === "tool" && p.toolCallId === proto.toolCallId
228
- );
229
- if (tIdx !== -1) {
230
- msg.parts[tIdx] = { ...msg.parts[tIdx], status: "done", output: proto.output };
231
- }
232
- break;
233
- }
234
- case "tool-output-error": {
235
- const tIdx = msg.parts.findIndex(
236
- (p) => p.type === "tool" && p.toolCallId === proto.toolCallId
237
- );
238
- if (tIdx !== -1) {
239
- msg.parts[tIdx] = { ...msg.parts[tIdx], status: "error", errorText: proto.errorText };
240
- }
241
- break;
242
- }
243
- case "picker": {
244
- msg.parts.push({
245
- type: "picker",
246
- pickerId: proto.pickerId,
247
- paramName: proto.paramName,
248
- toolName: proto.toolName,
249
- label: proto.label,
250
- options: proto.options
251
- });
252
- break;
253
- }
254
- case "plan-created": {
255
- msg.parts.push({
256
- type: "plan",
257
- planId: proto.planId,
258
- goal: proto.goal,
259
- steps: proto.steps.map((s) => ({ ...s, status: "pending" }))
260
- });
261
- break;
262
- }
263
- case "plan-step-update": {
264
- const pIdx = msg.parts.findIndex(
265
- (p) => p.type === "plan" && p.planId === proto.planId
266
- );
267
- if (pIdx !== -1) {
268
- const prev2 = msg.parts[pIdx];
269
- msg.parts[pIdx] = {
270
- ...prev2,
271
- steps: prev2.steps.map(
272
- (s) => s.index === proto.stepIndex ? { ...s, status: proto.status, ...proto.error ? { error: proto.error } : {} } : s
273
- )
274
- };
275
- }
276
- break;
277
- }
278
- }
279
- const copy = [...prev];
280
- copy[idx] = msg;
281
- return copy;
282
- });
283
- }, []);
284
- const fetchAndStream = react.useCallback(async (opts) => {
285
- const { input: userInput, msgId, extraMetadata, attachments } = opts;
286
- const isPrivate = Boolean(workspaceId);
287
- const token = isPrivate && typeof window !== "undefined" ? await window.Clerk?.session?.getToken() : null;
288
- const url = isPrivate ? `${API_URL}/api/threads/${threadId}/stream` : `${API_URL}/api/chat/stream`;
289
- const res = await fetch(url, {
290
- method: "POST",
291
- headers: {
292
- "Content-Type": "application/json",
293
- ...token ? { Authorization: `Bearer ${token}` } : {}
294
- },
295
- body: JSON.stringify({
296
- input: userInput,
297
- agentId,
298
- ...isPrivate ? { workspaceId, ...playgroundOverrides ? { playgroundOverrides } : {} } : { threadId },
299
- source,
300
- ...attachments?.length ? { attachments } : {},
301
- ...userContext?.userName ? { userName: userContext.userName } : {},
302
- ...userContext?.userEmail ? { userEmail: userContext.userEmail } : {},
303
- userMetadata: {
304
- ...userContext?.metadata ?? {},
305
- ...userContext?.pageContext ? { pageContext: userContext.pageContext } : {},
306
- headers: {
307
- ...token ? { Authorization: `Bearer ${token}` } : {},
308
- ...userContext?.headers ?? {}
309
- },
310
- ...extraMetadata ?? {}
311
- }
312
- })
313
- });
314
- if (!res.ok) {
315
- const errText = await res.text().catch(() => "");
316
- throw new Error(errText || `API error ${res.status}`);
317
- }
318
- if (!res.body) throw new Error("No stream body");
319
- await consumeStream(res.body, (proto) => applyStreamEvent(proto, msgId));
320
- }, [agentId, workspaceId, source, threadId, userContext, playgroundOverrides, applyStreamEvent]);
321
581
  const pendingAttachmentsRef = react.useRef([]);
322
582
  const send = react.useCallback(
323
583
  async (text) => {
584
+ if (customBackend) {
585
+ const content = (text ?? input).trim();
586
+ const attachments2 = pendingAttachmentsRef.current.length > 0 ? [...pendingAttachmentsRef.current] : void 0;
587
+ pendingAttachmentsRef.current = [];
588
+ if (!content && !attachments2?.length || customBackend.streaming)
589
+ return;
590
+ setInput("");
591
+ await customBackend.send(content, attachments2);
592
+ return;
593
+ }
324
594
  const userInput = (text ?? input).trim();
325
595
  if (!userInput || streaming) return;
326
596
  setInput("");
@@ -339,16 +609,32 @@ function useChat({
339
609
  setStreaming(true);
340
610
  const assistantMsgId = newId();
341
611
  streamingMsgIdRef.current = assistantMsgId;
342
- setMessages((prev) => [...prev, { id: assistantMsgId, role: "assistant", parts: [] }]);
612
+ setMessages((prev) => [
613
+ ...prev,
614
+ { id: assistantMsgId, role: "assistant", parts: [] }
615
+ ]);
343
616
  try {
344
- await fetchAndStream({ input: userInput, msgId: assistantMsgId, attachments });
617
+ await fetchAndStream({
618
+ input: userInput,
619
+ msgId: assistantMsgId,
620
+ attachments
621
+ });
345
622
  } catch {
346
623
  setMessages((prev) => {
347
624
  const idx = prev.findIndex((m) => m.id === assistantMsgId);
348
625
  if (idx === -1) return prev;
349
626
  const copy = [...prev];
350
627
  const err = copy[idx];
351
- 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
+ };
352
638
  return copy;
353
639
  });
354
640
  }
@@ -364,19 +650,27 @@ function useChat({
364
650
  (prev) => prev.map((msg) => ({
365
651
  ...msg,
366
652
  parts: msg.parts.map(
367
- (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
368
657
  )
369
658
  }))
370
659
  );
371
660
  setStreaming(true);
372
661
  const assistantMsgId = newId();
373
662
  streamingMsgIdRef.current = assistantMsgId;
374
- setMessages((prev) => [...prev, { id: assistantMsgId, role: "assistant", parts: [] }]);
663
+ setMessages((prev) => [
664
+ ...prev,
665
+ { id: assistantMsgId, role: "assistant", parts: [] }
666
+ ]);
375
667
  try {
376
668
  await fetchAndStream({
377
669
  input: label,
378
670
  msgId: assistantMsgId,
379
- extraMetadata: { __pickerSelection: { pickerId, paramName, value, label } }
671
+ extraMetadata: {
672
+ __pickerSelection: { pickerId, paramName, value, label }
673
+ }
380
674
  });
381
675
  } catch {
382
676
  setMessages((prev) => {
@@ -384,7 +678,16 @@ function useChat({
384
678
  if (idx === -1) return prev;
385
679
  const copy = [...prev];
386
680
  const err = copy[idx];
387
- 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
+ };
388
691
  return copy;
389
692
  });
390
693
  }
@@ -409,7 +712,20 @@ function useChat({
409
712
  });
410
713
  await send(lastText);
411
714
  }, [streaming, messages, send]);
412
- return { messages, input, setInput, streaming, threadId, send, queueAttachments, regenerate, reset, selectPickerOption };
715
+ return {
716
+ messages: customBackend ? customBackend.messages : messages,
717
+ streaming: customBackend ? customBackend.streaming : streaming,
718
+ input,
719
+ setInput,
720
+ threadId,
721
+ send,
722
+ queueAttachments,
723
+ regenerate,
724
+ reset: customBackend?.reset ?? reset,
725
+ selectPickerOption,
726
+ debugTraces,
727
+ voiceCall
728
+ };
413
729
  }
414
730
  function getPreferredMimeType() {
415
731
  if (typeof MediaRecorder === "undefined") return "";
@@ -428,7 +744,12 @@ function mimeTypeToExtension(mimeType) {
428
744
  return "webm";
429
745
  }
430
746
  var DEFAULT_API_URL = process.env.NEXT_PUBLIC_API_URL ?? "https://wallavi-production.up.railway.app";
431
- function useVoice({ agentId, apiUrl, onTranscript, onError }) {
747
+ function useVoice({
748
+ agentId,
749
+ apiUrl,
750
+ onTranscript,
751
+ onError
752
+ }) {
432
753
  const [voiceState, setVoiceState] = react.useState("idle");
433
754
  const recorderRef = react.useRef(null);
434
755
  const chunksRef = react.useRef([]);
@@ -444,10 +765,14 @@ function useVoice({ agentId, apiUrl, onTranscript, onError }) {
444
765
  const form = new FormData();
445
766
  form.append("audio", blob, `recording.${ext}`);
446
767
  form.append("agentId", agentId);
447
- 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
+ });
448
772
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
449
773
  const data = await res.json();
450
- if (!data.text?.trim()) throw new Error(data.error ?? "Empty transcript");
774
+ if (!data.text?.trim())
775
+ throw new Error(data.error ?? "Empty transcript");
451
776
  onTranscript(data.text.trim());
452
777
  setVoiceState("idle");
453
778
  } catch (err) {
@@ -465,14 +790,19 @@ function useVoice({ agentId, apiUrl, onTranscript, onError }) {
465
790
  const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
466
791
  streamRef.current = stream;
467
792
  const mimeType = getPreferredMimeType();
468
- const recorder = new MediaRecorder(stream, mimeType ? { mimeType } : void 0);
793
+ const recorder = new MediaRecorder(
794
+ stream,
795
+ mimeType ? { mimeType } : void 0
796
+ );
469
797
  chunksRef.current = [];
470
798
  recorder.ondataavailable = (e) => {
471
799
  if (e.data.size > 0) chunksRef.current.push(e.data);
472
800
  };
473
801
  recorder.onstop = async () => {
474
802
  stream.getTracks().forEach((t) => t.stop());
475
- const blob = new Blob(chunksRef.current, { type: mimeType || "audio/webm" });
803
+ const blob = new Blob(chunksRef.current, {
804
+ type: mimeType || "audio/webm"
805
+ });
476
806
  if (blob.size === 0) {
477
807
  onError?.("Recording was empty \u2014 please try again.");
478
808
  setVoiceState("idle");
@@ -498,7 +828,8 @@ function useVoice({ agentId, apiUrl, onTranscript, onError }) {
498
828
  react.useEffect(() => {
499
829
  return () => {
500
830
  if (errorTimerRef.current) clearTimeout(errorTimerRef.current);
501
- if (recorderRef.current?.state === "recording") recorderRef.current.stop();
831
+ if (recorderRef.current?.state === "recording")
832
+ recorderRef.current.stop();
502
833
  streamRef.current?.getTracks().forEach((t) => t.stop());
503
834
  };
504
835
  }, []);
@@ -521,7 +852,10 @@ function useAttachments({
521
852
  form.append("file", file);
522
853
  form.append("agentId", agentId);
523
854
  try {
524
- 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
+ });
525
859
  if (!res.ok) {
526
860
  const data = await res.json().catch(() => ({}));
527
861
  setAttachments(
@@ -537,7 +871,9 @@ function useAttachments({
537
871
  }
538
872
  const payload = await res.json();
539
873
  setAttachments(
540
- (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
+ )
541
877
  );
542
878
  } catch (err) {
543
879
  const msg = err instanceof Error ? err.message : "Upload failed";
@@ -581,16 +917,13 @@ function useAttachments({
581
917
  return { attachments, attach, remove, clear, isUploading, readyPayloads };
582
918
  }
583
919
  function DefaultIcon() {
584
- return /* @__PURE__ */ jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", fill: "none", style: { width: 26, height: 26 }, children: [
585
- /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "12", fill: "currentColor", opacity: 0.12 }),
586
- /* @__PURE__ */ jsxRuntime.jsx(
587
- "path",
588
- {
589
- 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",
590
- fill: "currentColor"
591
- }
592
- )
593
- ] });
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
+ ) });
594
927
  }
595
928
  function ExpandIcon() {
596
929
  return /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", style: { width: 11, height: 11 }, children: /* @__PURE__ */ jsxRuntime.jsx(
@@ -616,9 +949,58 @@ function MinimizeIcon() {
616
949
  }
617
950
  ) });
618
951
  }
619
- var Avatar = ({ style, ...p }) => /* @__PURE__ */ jsxRuntime.jsx(AvatarPrimitive__namespace.Root, { style: { position: "relative", display: "flex", flexShrink: 0, overflow: "hidden", borderRadius: "9999px", ...style }, ...p });
620
- var AvatarImage = ({ style, ...p }) => /* @__PURE__ */ jsxRuntime.jsx(AvatarPrimitive__namespace.Image, { style: { position: "absolute", inset: 0, width: "100%", height: "100%", objectFit: "cover", ...style }, ...p });
621
- 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
+ );
622
1004
  function ChatHeader({
623
1005
  title,
624
1006
  profilePicture,
@@ -627,22 +1009,39 @@ function ChatHeader({
627
1009
  onReset,
628
1010
  onClose,
629
1011
  onExpand,
630
- expanded
1012
+ expanded,
1013
+ onCall,
1014
+ isCallLoading
631
1015
  }) {
632
1016
  return /* @__PURE__ */ jsxRuntime.jsxs(
633
1017
  "header",
634
1018
  {
635
- className: "ww-flex ww-items-center ww-justify-between ww-px-4 ww-py-3 ww-shrink-0",
636
- 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
+ },
637
1025
  children: [
638
1026
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ww-flex ww-items-center ww-gap-2.5 ww-min-w-0", children: [
639
- /* @__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(
640
- AvatarFallback,
1027
+ /* @__PURE__ */ jsxRuntime.jsx(
1028
+ Avatar,
641
1029
  {
642
- style: { backgroundColor: `${headerText}20`, color: headerText, fontSize: 11, fontWeight: 700 },
643
- 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
+ )
644
1043
  }
645
- ) }),
1044
+ ),
646
1045
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ww-min-w-0", children: [
647
1046
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "ww-font-semibold ww-text-sm ww-truncate ww-leading-tight", children: title }),
648
1047
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "ww-text-[10px] ww-opacity-70 ww-leading-tight", children: "AI Assistant" })
@@ -658,13 +1057,64 @@ function ChatHeader({
658
1057
  children: expanded ? /* @__PURE__ */ jsxRuntime.jsx(MinimizeIcon, {}) : /* @__PURE__ */ jsxRuntime.jsx(ExpandIcon, {})
659
1058
  }
660
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
+ ),
661
1105
  /* @__PURE__ */ jsxRuntime.jsx(
662
1106
  "button",
663
1107
  {
664
1108
  onClick: onReset,
665
1109
  className: "ww-rounded-full ww-p-1.5 ww-transition-colors hover:ww-bg-white/10",
666
1110
  title: "New conversation",
667
- 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
+ )
668
1118
  }
669
1119
  ),
670
1120
  onClose && /* @__PURE__ */ jsxRuntime.jsx(
@@ -688,8 +1138,10 @@ function formatBytes(bytes) {
688
1138
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
689
1139
  }
690
1140
  function ChipLeading({ a }) {
691
- if (a.status === "uploading") return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "ww-h-3 ww-w-3 ww-shrink-0 ww-animate-spin" });
692
- 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" });
693
1145
  const thumbSrc = a.payload?.url ?? a.payload?.base64;
694
1146
  if (a.mimeType.startsWith("image/") && thumbSrc) {
695
1147
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -701,14 +1153,19 @@ function ChipLeading({ a }) {
701
1153
  }
702
1154
  );
703
1155
  }
704
- 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" }) });
705
- 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" });
706
1160
  if (a.mimeType.includes("csv") || a.mimeType.includes("spreadsheet") || a.name.endsWith(".csv")) {
707
1161
  return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileSpreadsheet, { className: "ww-h-3 ww-w-3 ww-shrink-0 ww-text-emerald-500" });
708
1162
  }
709
1163
  return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileText, { className: "ww-h-3 ww-w-3 ww-shrink-0" });
710
1164
  }
711
- function AttachmentChips({ attachments, onRemove }) {
1165
+ function AttachmentChips({
1166
+ attachments,
1167
+ onRemove
1168
+ }) {
712
1169
  if (attachments.length === 0) return null;
713
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(
714
1171
  "div",
@@ -736,41 +1193,49 @@ function AttachmentChips({ attachments, onRemove }) {
736
1193
  a.id
737
1194
  )) });
738
1195
  }
739
- var Avatar2 = ({ style, ...p }) => /* @__PURE__ */ jsxRuntime.jsx(AvatarPrimitive__namespace.Root, { style: { position: "relative", display: "flex", flexShrink: 0, overflow: "hidden", borderRadius: "9999px", ...style }, ...p });
740
- var AvatarImage2 = ({ style, ...p }) => /* @__PURE__ */ jsxRuntime.jsx(AvatarPrimitive__namespace.Image, { style: { position: "absolute", inset: 0, width: "100%", height: "100%", objectFit: "cover", ...style }, ...p });
741
- 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 });
742
- var ReactMarkdown = ReactMarkdownLib__default.default;
743
- function SentAttachments({ attachments, contrastColor }) {
1196
+ function SentAttachments({
1197
+ attachments,
1198
+ contrastColor
1199
+ }) {
744
1200
  const images = attachments.filter((a) => a.contentType === "image");
745
1201
  const files = attachments.filter((a) => a.contentType !== "image");
746
1202
  const isDark = contrastColor === "#ffffff" || contrastColor === "#fff";
747
1203
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ww-flex ww-flex-col ww-gap-1.5 ww-w-full", children: [
748
- 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) => {
749
- const src = img.url ?? img.base64;
750
- return src ? /* @__PURE__ */ jsxRuntime.jsx(
751
- "img",
752
- {
753
- src,
754
- alt: img.name,
755
- className: cn(
756
- "ww-rounded-xl ww-object-cover ww-border",
757
- images.length === 1 ? "ww-w-full ww-max-h-48" : "ww-h-20 ww-w-20",
758
- isDark ? "ww-border-white/20" : "ww-border-black/10"
759
- )
760
- },
761
- img.id
762
- ) : /* @__PURE__ */ jsxRuntime.jsx(
763
- "div",
764
- {
765
- className: cn(
766
- "ww-h-20 ww-w-20 ww-rounded-xl ww-flex ww-items-center ww-justify-center ww-text-[10px] ww-font-medium",
767
- isDark ? "ww-bg-white/10 ww-text-white/60" : "ww-bg-black/10 ww-text-black/40"
768
- ),
769
- children: "IMG"
770
- },
771
- img.id
772
- );
773
- }) }),
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
+ ),
774
1239
  files.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ww-flex ww-flex-wrap ww-gap-1", children: files.map((f) => /* @__PURE__ */ jsxRuntime.jsxs(
775
1240
  "div",
776
1241
  {
@@ -788,16 +1253,24 @@ function SentAttachments({ attachments, contrastColor }) {
788
1253
  ] });
789
1254
  }
790
1255
  function PlanStepIcon({ status }) {
791
- if (status === "executing") return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "ww-h-3 ww-w-3 ww-animate-spin ww-text-primary" });
792
- if (status === "success") return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckCircle2, { className: "ww-h-3 ww-w-3 ww-text-emerald-500" });
793
- 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" });
794
1262
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ww-h-3 ww-w-3 ww-rounded-full ww-border-2 ww-border-muted-foreground/30" });
795
1263
  }
796
- function PlanCard({ part }) {
1264
+ function PlanCard({
1265
+ part,
1266
+ onSend,
1267
+ disabled
1268
+ }) {
797
1269
  const successCount = part.steps.filter((s) => s.status === "success").length;
798
1270
  const hasExecuting = part.steps.some((s) => s.status === "executing");
799
1271
  const allDone = successCount === part.steps.length && part.steps.length > 0;
800
1272
  const anyFailed = part.steps.some((s) => s.status === "failed");
1273
+ const allPending = part.steps.length > 0 && part.steps.every((s) => s.status === "pending");
801
1274
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ww-rounded-xl ww-border ww-bg-background ww-overflow-hidden ww-text-xs ww-w-full ww-shadow-sm", children: [
802
1275
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ww-flex ww-items-center ww-gap-2 ww-px-3 ww-py-2 ww-bg-muted/50 ww-border-b", children: [
803
1276
  hasExecuting ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "ww-h-3.5 ww-w-3.5 ww-shrink-0 ww-animate-spin ww-text-primary/80" }) : allDone ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckCircle2, { className: "ww-h-3.5 ww-w-3.5 ww-shrink-0 ww-text-emerald-500" }) : anyFailed ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircle, { className: "ww-h-3.5 ww-w-3.5 ww-shrink-0 ww-text-destructive" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Zap, { className: "ww-h-3.5 ww-w-3.5 ww-shrink-0 ww-text-primary/70" }),
@@ -838,7 +1311,37 @@ function PlanCard({ part }) {
838
1311
  ]
839
1312
  },
840
1313
  step.index
841
- )) })
1314
+ )) }),
1315
+ allPending && onSend && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ww-flex ww-gap-2 ww-px-3 ww-py-2.5 ww-border-t", children: [
1316
+ /* @__PURE__ */ jsxRuntime.jsxs(
1317
+ "button",
1318
+ {
1319
+ onClick: () => onSend("s\xED, proceder"),
1320
+ disabled,
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",
1322
+ style: {
1323
+ backgroundColor: "var(--primary, #18181b)",
1324
+ color: "var(--primary-foreground, #fff)"
1325
+ },
1326
+ children: [
1327
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "ww-h-3 ww-w-3 ww-shrink-0" }),
1328
+ "Proceder"
1329
+ ]
1330
+ }
1331
+ ),
1332
+ /* @__PURE__ */ jsxRuntime.jsxs(
1333
+ "button",
1334
+ {
1335
+ onClick: () => onSend("cancelar"),
1336
+ disabled,
1337
+ className: "ww-flex ww-items-center ww-justify-center ww-gap-1.5 ww-rounded-lg ww-px-3 ww-py-1.5 ww-text-xs ww-font-medium ww-bg-muted ww-text-muted-foreground ww-transition-opacity hover:ww-opacity-80 active:ww-scale-[0.98] disabled:ww-opacity-50 disabled:ww-pointer-events-none",
1338
+ children: [
1339
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "ww-h-3 ww-w-3 ww-shrink-0" }),
1340
+ "Cancelar"
1341
+ ]
1342
+ }
1343
+ )
1344
+ ] })
842
1345
  ] });
843
1346
  }
844
1347
  function ThinkingDots() {
@@ -901,7 +1404,15 @@ function ReasoningBlock({ text }) {
901
1404
  children: [
902
1405
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Zap, { className: "ww-h-3 ww-w-3" }),
903
1406
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Reasoning" }),
904
- /* @__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
+ )
905
1416
  ]
906
1417
  }
907
1418
  ),
@@ -923,98 +1434,292 @@ function PickerSelector({
923
1434
  }, [part.options, query]);
924
1435
  const isConsumed = !!part.selectedValue;
925
1436
  const handleClick = (opt) => {
926
- 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);
927
1439
  };
928
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn(
929
- "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",
930
- "ww-border-border/50"
931
- ), children: [
932
- /* @__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 }),
933
- mode === "list" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ww-relative", children: [
934
- /* @__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" }),
935
- /* @__PURE__ */ jsxRuntime.jsx(
936
- "input",
937
- {
938
- type: "text",
939
- value: query,
940
- onChange: (e) => setQuery(e.target.value),
941
- placeholder: "Search\u2026",
942
- disabled: disabled || isConsumed,
943
- 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"
944
- }
945
- )
946
- ] }),
947
- mode === "pills" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ww-flex ww-flex-wrap ww-gap-1.5", children: part.options.map((opt) => {
948
- const sel = part.selectedValue === opt.value;
949
- const faded = isConsumed && !sel;
950
- return /* @__PURE__ */ jsxRuntime.jsxs(
951
- "button",
952
- {
953
- disabled: disabled || isConsumed,
954
- onClick: () => handleClick(opt),
955
- className: cn(
956
- "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",
957
- 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",
958
- disabled && !isConsumed && "ww-opacity-60 ww-pointer-events-none"
959
- ),
960
- children: [
961
- sel && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "ww-h-3 ww-w-3 ww-shrink-0" }),
962
- opt.label
963
- ]
964
- },
965
- opt.value
966
- );
967
- }) }),
968
- 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) => {
969
- const sel = part.selectedValue === opt.value;
970
- const faded = isConsumed && !sel;
971
- return /* @__PURE__ */ jsxRuntime.jsxs(
972
- "button",
973
- {
974
- disabled: disabled || isConsumed,
975
- onClick: () => handleClick(opt),
976
- className: cn(
977
- "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",
978
- 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",
979
- disabled && !isConsumed && "ww-opacity-60 ww-pointer-events-none"
980
- ),
981
- children: [
982
- 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" }),
983
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ww-truncate", children: opt.label })
984
- ]
985
- },
986
- opt.value
987
- );
988
- }) }),
989
- 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: [
990
- 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" }),
991
- filtered.map((opt) => {
992
- const sel = part.selectedValue === opt.value;
993
- const faded = isConsumed && !sel;
994
- return /* @__PURE__ */ jsxRuntime.jsxs(
995
- "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",
996
1552
  {
997
- disabled: disabled || isConsumed,
998
- onClick: () => handleClick(opt),
999
- className: cn(
1000
- "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",
1001
- 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",
1002
- disabled && !isConsumed && "ww-opacity-60 ww-pointer-events-none"
1003
- ),
1004
- children: [
1005
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn(
1006
- "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]",
1007
- sel ? "ww-border-background ww-bg-background/20" : "ww-border-border/50"
1008
- ), children: sel && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "ww-h-2.5 ww-w-2.5" }) }),
1009
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ww-truncate", children: opt.label })
1010
- ]
1011
- },
1012
- opt.value
1013
- );
1014
- })
1015
- ] })
1016
- ] });
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",
1574
+ {
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
+ );
1017
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
+ );
1018
1723
  function MessageBubble({
1019
1724
  message,
1020
1725
  userColor,
@@ -1022,14 +1727,21 @@ function MessageBubble({
1022
1727
  profilePicture,
1023
1728
  isStreaming,
1024
1729
  showThinking = true,
1025
- onPickerSelect
1730
+ onPickerSelect,
1731
+ onSend
1026
1732
  }) {
1027
1733
  const isUser = message.role === "user";
1028
1734
  const textPart = message.parts.find((p) => p.type === "text");
1029
1735
  const reasoningPart = message.parts.find((p) => p.type === "reasoning");
1030
- const toolParts = message.parts.filter((p) => p.type === "tool");
1031
- const pickerParts = message.parts.filter((p) => p.type === "picker");
1032
- 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
+ );
1033
1745
  const contrastColor = getContrastColor(userColor);
1034
1746
  if (isUser) {
1035
1747
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ww-flex ww-justify-end", children: /* @__PURE__ */ jsxRuntime.jsxs(
@@ -1038,19 +1750,55 @@ function MessageBubble({
1038
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",
1039
1751
  style: { backgroundColor: userColor, color: contrastColor },
1040
1752
  children: [
1041
- 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
+ ),
1042
1760
  textPart?.text && /* @__PURE__ */ jsxRuntime.jsx("span", { children: textPart.text })
1043
1761
  ]
1044
1762
  }
1045
1763
  ) });
1046
1764
  }
1047
1765
  const visibleToolParts = showThinking ? toolParts : [];
1048
- const isEmpty = !textPart?.text && visibleToolParts.length === 0 && pickerParts.length === 0;
1766
+ const isEmpty = !textPart?.text && visibleToolParts.length === 0 && pickerParts.length === 0 && planParts.length === 0;
1049
1767
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ww-flex ww-gap-2.5 ww-items-start", children: [
1050
- /* @__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
+ ),
1051
1791
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ww-flex ww-flex-col ww-gap-1.5 ww-min-w-0 ww-max-w-[82%]", children: [
1052
1792
  showThinking && reasoningPart && /* @__PURE__ */ jsxRuntime.jsx(ReasoningBlock, { text: reasoningPart.text }),
1053
- planParts.map((p) => /* @__PURE__ */ jsxRuntime.jsx(PlanCard, { part: p }, 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
+ )),
1054
1802
  visibleToolParts.map((t) => /* @__PURE__ */ jsxRuntime.jsx(ToolCallBadge, { part: t }, t.toolCallId)),
1055
1803
  pickerParts.map((p) => /* @__PURE__ */ jsxRuntime.jsx(
1056
1804
  PickerSelector,
@@ -1063,40 +1811,39 @@ function MessageBubble({
1063
1811
  p.pickerId
1064
1812
  )),
1065
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: [
1066
- /* @__PURE__ */ jsxRuntime.jsx(
1067
- ReactMarkdown,
1068
- {
1069
- remarkPlugins: [remarkGfm__default.default],
1070
- components: {
1071
- p: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("p", { style: { margin: "4px 0" }, children }),
1072
- h1: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("p", { style: { margin: "6px 0", fontWeight: 700, fontSize: "1em" }, children }),
1073
- h2: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("p", { style: { margin: "6px 0", fontWeight: 700, fontSize: "1em" }, children }),
1074
- h3: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("p", { style: { margin: "6px 0", fontWeight: 600, fontSize: "0.95em" }, children }),
1075
- ul: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("ul", { style: { margin: "4px 0", paddingLeft: 16 }, children }),
1076
- ol: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("ol", { style: { margin: "4px 0", paddingLeft: 16 }, children }),
1077
- li: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("li", { style: { margin: "2px 0" }, children }),
1078
- strong: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("strong", { style: { fontWeight: 600 }, children }),
1079
- 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 }),
1080
- 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 }),
1081
- a: ({ href, children }) => /* @__PURE__ */ jsxRuntime.jsx("a", { href, target: "_blank", rel: "noopener noreferrer", style: { textDecoration: "underline", opacity: 0.75 }, children }),
1082
- 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 }) }),
1083
- thead: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("thead", { style: { backgroundColor: "rgba(0,0,0,0.04)" }, children }),
1084
- tbody: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("tbody", { children }),
1085
- tr: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("tr", { style: { borderBottom: "1px solid rgba(0,0,0,0.07)" }, children }),
1086
- th: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("th", { style: { padding: "6px 10px", textAlign: "left", fontWeight: 600, whiteSpace: "nowrap" }, children }),
1087
- td: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("td", { style: { padding: "5px 10px", verticalAlign: "top" }, children })
1088
- },
1089
- children: textPart.text
1090
- }
1091
- ),
1814
+ /* @__PURE__ */ jsxRuntime.jsx(MarkdownContent, { text: textPart.text }),
1092
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" })
1093
1816
  ] }) : null
1094
1817
  ] })
1095
1818
  ] });
1096
1819
  }
1097
- 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
+ );
1098
1833
  var AvatarImage3 = AvatarPrimitive__namespace.Image;
1099
- 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
+ );
1100
1847
  function ChatMessages({
1101
1848
  messages,
1102
1849
  streaming,
@@ -1110,14 +1857,15 @@ function ChatMessages({
1110
1857
  onPickerSelect
1111
1858
  }) {
1112
1859
  const bottomRef = react.useRef(null);
1860
+ const greetText = initialMessages[0];
1113
1861
  const showGreeting = messages.length === 0;
1114
1862
  react.useEffect(() => {
1115
1863
  bottomRef.current?.scrollIntoView({ behavior: "smooth" });
1116
- }, [messages]);
1864
+ }, [messages, streaming]);
1117
1865
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ww-flex-1 ww-flex ww-flex-col ww-overflow-y-auto ww-overscroll-contain", children: [
1118
- 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: [
1119
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() }) }),
1120
- /* @__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 })
1121
1869
  ] }),
1122
1870
  showGreeting && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ww-flex-1" }),
1123
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(
@@ -1129,10 +1877,47 @@ function ChatMessages({
1129
1877
  profilePicture,
1130
1878
  isStreaming: streaming && i === messages.length - 1 && msg.role === "assistant",
1131
1879
  showThinking,
1132
- onPickerSelect
1880
+ onPickerSelect,
1881
+ onSend: i === messages.length - 1 ? onSuggest : void 0
1133
1882
  },
1134
1883
  msg.id
1135
1884
  )) }),
1885
+ streaming && messages.length > 0 && messages[messages.length - 1]?.role !== "assistant" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ww-flex ww-gap-2.5 ww-items-start ww-px-4 ww-pb-2", children: [
1886
+ /* @__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() }) }),
1887
+ /* @__PURE__ */ jsxRuntime.jsx("style", { children: `
1888
+ @keyframes wDotBounce {
1889
+ 0%, 80%, 100% { transform: translateY(0) scale(1); opacity: 0.35; }
1890
+ 40% { transform: translateY(-6px) scale(1.15); opacity: 1; }
1891
+ }
1892
+ ` }),
1893
+ /* @__PURE__ */ jsxRuntime.jsx(
1894
+ "div",
1895
+ {
1896
+ className: "ww-bg-muted",
1897
+ style: {
1898
+ display: "inline-flex",
1899
+ alignItems: "center",
1900
+ gap: 6,
1901
+ borderRadius: "18px 18px 18px 4px",
1902
+ padding: "13px 18px"
1903
+ },
1904
+ children: [0, 1, 2].map((i) => /* @__PURE__ */ jsxRuntime.jsx(
1905
+ "span",
1906
+ {
1907
+ style: {
1908
+ display: "block",
1909
+ width: 7,
1910
+ height: 7,
1911
+ borderRadius: "50%",
1912
+ backgroundColor: "currentColor",
1913
+ animation: `wDotBounce 1.2s cubic-bezier(0.4,0,0.2,1) ${i * 0.16}s infinite`
1914
+ }
1915
+ },
1916
+ i
1917
+ ))
1918
+ }
1919
+ )
1920
+ ] }),
1136
1921
  showGreeting && suggestedMessages.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ww-flex ww-flex-wrap ww-gap-2 ww-px-4 ww-pb-4", children: suggestedMessages.map((msg, i) => /* @__PURE__ */ jsxRuntime.jsx(
1137
1922
  "button",
1138
1923
  {
@@ -1199,7 +1984,13 @@ function ChatInput({
1199
1984
  }
1200
1985
  ),
1201
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: [
1202
- 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
+ ) }),
1203
1994
  hasAttachments && /* @__PURE__ */ jsxRuntime.jsx(
1204
1995
  "input",
1205
1996
  {
@@ -1289,7 +2080,10 @@ function ChatInput({
1289
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",
1290
2081
  hasText || streaming ? "ww-opacity-100 ww-shadow-sm" : "ww-opacity-30"
1291
2082
  ),
1292
- 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" },
1293
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" })
1294
2088
  }
1295
2089
  )
@@ -1297,6 +2091,126 @@ function ChatInput({
1297
2091
  ] })
1298
2092
  ] });
1299
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
+ }
1300
2214
  function ChatWidget({
1301
2215
  agentId,
1302
2216
  workspaceId,
@@ -1322,13 +2236,27 @@ function ChatWidget({
1322
2236
  enableVoice = false,
1323
2237
  voiceAutoSend = false,
1324
2238
  enableAttachments = false,
2239
+ customBackend,
1325
2240
  className,
1326
2241
  onClose,
1327
2242
  onReset,
1328
2243
  onExpand,
1329
- expanded
2244
+ expanded,
2245
+ embedded = false,
2246
+ envId,
2247
+ onDebugTrace
1330
2248
  }) {
1331
- const chat = useChat({ agentId, workspaceId, source, userContext, persist, onNavigate, playgroundOverrides });
2249
+ const chat = useChat({
2250
+ agentId,
2251
+ workspaceId,
2252
+ envId,
2253
+ source,
2254
+ userContext,
2255
+ persist,
2256
+ onNavigate,
2257
+ playgroundOverrides,
2258
+ customBackend
2259
+ });
1332
2260
  const voice = useVoice({
1333
2261
  agentId,
1334
2262
  onTranscript: (text) => {
@@ -1340,29 +2268,47 @@ function ChatWidget({
1340
2268
  }
1341
2269
  });
1342
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]);
1343
2280
  const [isDragOver, setIsDragOver] = react.useState(false);
1344
- const handleDragOver = react.useCallback((e) => {
1345
- if (!enableAttachments) return;
1346
- e.preventDefault();
1347
- e.stopPropagation();
1348
- if (e.dataTransfer.types.includes("Files")) setIsDragOver(true);
1349
- }, [enableAttachments]);
1350
- const handleDragLeave = react.useCallback((e) => {
1351
- if (!enableAttachments) return;
1352
- e.preventDefault();
1353
- e.stopPropagation();
1354
- 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();
1355
2306
  setIsDragOver(false);
1356
- }
1357
- }, [enableAttachments]);
1358
- const handleDrop = react.useCallback((e) => {
1359
- if (!enableAttachments) return;
1360
- e.preventDefault();
1361
- e.stopPropagation();
1362
- setIsDragOver(false);
1363
- const files = e.dataTransfer.files;
1364
- if (files.length > 0) attachmentHook.attach(files);
1365
- }, [enableAttachments, attachmentHook]);
2307
+ const files = e.dataTransfer.files;
2308
+ if (files.length > 0) attachmentHook.attach(files);
2309
+ },
2310
+ [enableAttachments, attachmentHook]
2311
+ );
1366
2312
  const handleSend = () => {
1367
2313
  const payloads = attachmentHook.readyPayloads;
1368
2314
  if (payloads.length > 0) {
@@ -1381,6 +2327,7 @@ function ChatWidget({
1381
2327
  };
1382
2328
  const isDark = theme === "dark";
1383
2329
  const cssVars = {
2330
+ fontFamily: 'ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
1384
2331
  colorScheme: theme,
1385
2332
  // Inline style has higher specificity than any host-site stylesheet,
1386
2333
  // so background-color set here cannot be overridden by host CSS.
@@ -1403,7 +2350,8 @@ function ChatWidget({
1403
2350
  "div",
1404
2351
  {
1405
2352
  className: cn(
1406
- "wallavi-widget ww-flex ww-flex-col ww-overflow-hidden ww-rounded-2xl ww-border ww-shadow-xl ww-bg-background ww-h-full ww-relative",
2353
+ "wallavi-widget ww-flex ww-flex-col ww-overflow-hidden ww-bg-background ww-h-full ww-relative ww-overscroll-none",
2354
+ !embedded && "ww-rounded-2xl ww-border ww-shadow-xl",
1407
2355
  isDragOver && "ww-ring-2 ww-ring-inset ww-ring-primary/60",
1408
2356
  className
1409
2357
  ),
@@ -1428,7 +2376,32 @@ function ChatWidget({
1428
2376
  onReset: handleReset,
1429
2377
  onClose: hideCloseButton ? void 0 : onClose,
1430
2378
  onExpand,
1431
- 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
2391
+ }
2392
+ ),
2393
+ customBackend?.mode === "human" && /* @__PURE__ */ jsxRuntime.jsxs(
2394
+ "div",
2395
+ {
2396
+ className: "ww-shrink-0 ww-flex ww-items-center ww-gap-2 ww-px-4 ww-py-1.5 ww-border-b",
2397
+ style: { backgroundColor: `${userMessageColor}0d` },
2398
+ children: [
2399
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "ww-relative ww-flex ww-h-2 ww-w-2 ww-shrink-0", children: [
2400
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ww-animate-ping ww-absolute ww-inline-flex ww-h-full ww-w-full ww-rounded-full ww-bg-emerald-400 ww-opacity-75" }),
2401
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ww-relative ww-inline-flex ww-rounded-full ww-h-2 ww-w-2 ww-bg-emerald-500" })
2402
+ ] }),
2403
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ww-text-xs ww-font-medium ww-text-foreground/60", children: "Hablando con un agente" })
2404
+ ]
1432
2405
  }
1433
2406
  ),
1434
2407
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -1446,6 +2419,99 @@ function ChatWidget({
1446
2419
  onPickerSelect: chat.selectPickerOption
1447
2420
  }
1448
2421
  ),
2422
+ customBackend?.footerAction && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ww-shrink-0 ww-px-3 ww-py-1.5 ww-border-t ww-bg-background", children: /* @__PURE__ */ jsxRuntime.jsxs(
2423
+ "button",
2424
+ {
2425
+ onClick: () => void customBackend.footerAction.onClick(),
2426
+ disabled: customBackend.footerAction.loading,
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
+ },
2436
+ className: [
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",
2438
+ "hover:ww-opacity-85 active:ww-scale-[0.98] disabled:ww-opacity-50 disabled:ww-pointer-events-none",
2439
+ customBackend.footerAction.icon === "human" ? "" : "ww-border"
2440
+ ].join(" "),
2441
+ children: [
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
+ ),
2498
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ww-flex-1 ww-text-left ww-truncate", children: customBackend.footerAction.label }),
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
+ )
2512
+ ]
2513
+ }
2514
+ ) }),
1449
2515
  /* @__PURE__ */ jsxRuntime.jsx(
1450
2516
  ChatInput,
1451
2517
  {
@@ -1470,7 +2536,7 @@ function ChatWidget({
1470
2536
  } : {}
1471
2537
  }
1472
2538
  ),
1473
- 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: [
1474
2540
  /* @__PURE__ */ jsxRuntime.jsxs(
1475
2541
  "a",
1476
2542
  {
@@ -1520,19 +2586,27 @@ function useAutoConfig(agentId, enabled) {
1520
2586
  if (cancelled) return;
1521
2587
  const cfg = body?.data ?? {};
1522
2588
  const remote = {};
1523
- if (cfg.profilePicture != null) remote.profilePicture = cfg.profilePicture;
1524
- 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;
1525
2593
  if (cfg.theme) remote.theme = cfg.theme;
1526
- if (cfg.userMessageColor) remote.userMessageColor = cfg.userMessageColor;
2594
+ if (cfg.userMessageColor)
2595
+ remote.userMessageColor = cfg.userMessageColor;
1527
2596
  if (Array.isArray(cfg.initialMessages) && cfg.initialMessages.length > 0)
1528
2597
  remote.initialMessages = cfg.initialMessages;
1529
2598
  if (Array.isArray(cfg.suggestedMessages))
1530
2599
  remote.suggestedMessages = cfg.suggestedMessages;
1531
- if (cfg.messagePlaceholder != null) remote.messagePlaceholder = cfg.messagePlaceholder;
2600
+ if (cfg.messagePlaceholder != null)
2601
+ remote.messagePlaceholder = cfg.messagePlaceholder;
1532
2602
  if (cfg.watermark != null) remote.watermark = cfg.watermark;
1533
2603
  if (cfg.footer != null) remote.footer = cfg.footer;
1534
- if (cfg.showThinking != null) remote.showThinking = cfg.showThinking;
1535
- 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;
1536
2610
  setResult({
1537
2611
  remoteConfig: remote,
1538
2612
  bubbleIconUrl: cfg.chatIcon || cfg.profilePicture || void 0,
@@ -1553,9 +2627,391 @@ function useAutoConfig(agentId, enabled) {
1553
2627
  }, [agentId, enabled]);
1554
2628
  return result;
1555
2629
  }
2630
+ function toWidgetMsg(m) {
2631
+ return {
2632
+ id: m.id,
2633
+ role: m.role === "customer" ? "user" : "assistant",
2634
+ parts: [{ type: "text", text: m.content }],
2635
+ attachments: m.metadata?.attachments ?? void 0
2636
+ };
2637
+ }
2638
+ function useSupportChat({
2639
+ inboxToken,
2640
+ apiBase = "https://app.wallavi.com",
2641
+ requestHumanLabel = "Hablar con un agente",
2642
+ returnToAiLabel
2643
+ }) {
2644
+ const enabled = Boolean(inboxToken) && inboxToken !== "__disabled__";
2645
+ const STORAGE_KEY = `wlv_support_${inboxToken}`;
2646
+ const base = apiBase.replace(/\/$/, "");
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
+ });
2657
+ const [rawMessages, setRawMessages] = react.useState([]);
2658
+ const [sending, setSending] = 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
+ });
2669
+ const [agentTyping, setAgentTyping] = react.useState(false);
2670
+ const [requestingHuman, setRequestingHuman] = react.useState(false);
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);
2677
+ const typingTimerRef = react.useRef(null);
2678
+ react.useEffect(() => {
2679
+ if (!enabled) return;
2680
+ try {
2681
+ const saved = localStorage.getItem(STORAGE_KEY);
2682
+ if (saved) JSON.parse(saved);
2683
+ } catch {
2684
+ localStorage.removeItem(STORAGE_KEY);
2685
+ setSession(null);
2686
+ setIsAiMode(false);
2687
+ }
2688
+ }, [STORAGE_KEY, enabled]);
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
+ );
2705
+ react.useEffect(() => {
2706
+ if (!enabled || !session) return;
2707
+ void loadMessages(session);
2708
+ let ablyClient = null;
2709
+ const setupAbly = async () => {
2710
+ try {
2711
+ const res = await fetch(
2712
+ `${base}/api/support-chat/${session.conversationId}/ably-token`,
2713
+ {
2714
+ headers: { "x-visitor-token": session.visitorToken }
2715
+ }
2716
+ );
2717
+ if (!res.ok) return;
2718
+ const { tokenRequest, channel: channelName } = await res.json();
2719
+ const AblyLib = (await import('ably')).default;
2720
+ ablyClient = new AblyLib.Realtime({
2721
+ authCallback: (_, cb) => cb(null, tokenRequest)
2722
+ });
2723
+ const ch = ablyClient.channels.get(channelName);
2724
+ ch.subscribe((msg) => {
2725
+ const event = { type: msg.name, ...msg.data };
2726
+ if (event.type === "message.created" && event.message) {
2727
+ setRawMessages((prev) => {
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
+ ];
2734
+ });
2735
+ } else if (event.type === "agent.typing") {
2736
+ setAgentTyping(!!event.isTyping);
2737
+ if (event.isTyping) {
2738
+ if (typingTimerRef.current) clearTimeout(typingTimerRef.current);
2739
+ typingTimerRef.current = setTimeout(
2740
+ () => setAgentTyping(false),
2741
+ 6e3
2742
+ );
2743
+ } else {
2744
+ if (typingTimerRef.current) clearTimeout(typingTimerRef.current);
2745
+ }
2746
+ } else if (event.type === "mode.changed") {
2747
+ const newIsAi = event.mode === "auto";
2748
+ setIsAiMode(newIsAi);
2749
+ setSession((s) => {
2750
+ if (s)
2751
+ localStorage.setItem(
2752
+ STORAGE_KEY,
2753
+ JSON.stringify({ session: s, isAiMode: newIsAi })
2754
+ );
2755
+ return s;
2756
+ });
2757
+ }
2758
+ });
2759
+ } catch {
2760
+ }
2761
+ };
2762
+ void setupAbly();
2763
+ const pollId = setInterval(() => void loadMessages(session), 3e4);
2764
+ return () => {
2765
+ if (ablyClient)
2766
+ try {
2767
+ ablyClient.close();
2768
+ } catch {
2769
+ }
2770
+ clearInterval(pollId);
2771
+ if (typingTimerRef.current) clearTimeout(typingTimerRef.current);
2772
+ };
2773
+ }, [session, STORAGE_KEY, base, loadMessages]);
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
2787
+ }
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
+ })
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 {
2853
+ }
2854
+ }
2855
+ setSending(false);
2856
+ },
2857
+ [session, sending, inboxToken, base, STORAGE_KEY]
2858
+ );
2859
+ const requestHuman = react.useCallback(async () => {
2860
+ if (!session || !isAiMode || requestingHuman) return;
2861
+ setRequestingHuman(true);
2862
+ try {
2863
+ await fetch(`${base}/api/support-chat/${session.conversationId}/mode`, {
2864
+ method: "PATCH",
2865
+ headers: {
2866
+ "Content-Type": "application/json",
2867
+ "x-visitor-token": session.visitorToken
2868
+ },
2869
+ body: JSON.stringify({ mode: "human" })
2870
+ });
2871
+ setIsAiMode(false);
2872
+ localStorage.setItem(
2873
+ STORAGE_KEY,
2874
+ JSON.stringify({ session, isAiMode: false })
2875
+ );
2876
+ } catch {
2877
+ }
2878
+ setRequestingHuman(false);
2879
+ }, [session, isAiMode, requestingHuman, base, STORAGE_KEY]);
2880
+ const returnToAi = react.useCallback(async () => {
2881
+ if (!session || isAiMode || returningToAi) return;
2882
+ setReturningToAi(true);
2883
+ try {
2884
+ await fetch(`${base}/api/support-chat/${session.conversationId}/mode`, {
2885
+ method: "PATCH",
2886
+ headers: {
2887
+ "Content-Type": "application/json",
2888
+ "x-visitor-token": session.visitorToken
2889
+ },
2890
+ body: JSON.stringify({ mode: "auto" })
2891
+ });
2892
+ setIsAiMode(true);
2893
+ localStorage.setItem(
2894
+ STORAGE_KEY,
2895
+ JSON.stringify({ session, isAiMode: true })
2896
+ );
2897
+ } catch {
2898
+ }
2899
+ setReturningToAi(false);
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
+ }, []);
2927
+ const reset = react.useCallback(() => {
2928
+ localStorage.removeItem(STORAGE_KEY);
2929
+ setSession(null);
2930
+ setRawMessages([]);
2931
+ setIsAiMode(false);
2932
+ setAgentTyping(false);
2933
+ setRequestingHuman(false);
2934
+ setReturningToAi(false);
2935
+ stopVoiceCall();
2936
+ }, [STORAGE_KEY, stopVoiceCall]);
2937
+ const messages = react.useMemo(
2938
+ () => rawMessages.filter((m) => m.role !== "system").map(toWidgetMsg),
2939
+ [rawMessages]
2940
+ );
2941
+ const footerAction = react.useMemo(() => {
2942
+ if (isAiMode) {
2943
+ return {
2944
+ label: requestHumanLabel,
2945
+ sublabel: "Te conectamos con un agente disponible",
2946
+ icon: "human",
2947
+ onClick: requestHuman,
2948
+ loading: requestingHuman
2949
+ };
2950
+ }
2951
+ if (!isAiMode && session && returnToAiLabel) {
2952
+ return {
2953
+ label: returnToAiLabel,
2954
+ sublabel: "Respuesta instant\xE1nea con inteligencia artificial",
2955
+ icon: "ai",
2956
+ onClick: returnToAi,
2957
+ loading: returningToAi
2958
+ };
2959
+ }
2960
+ return void 0;
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
+ );
3007
+ }
1556
3008
  var KEY_EXPANDED = "wallavi_bubble_expanded";
1557
3009
  var KEY_DISMISSED = "wallavi_bubble_dismissed";
1558
3010
  function BubbleWidget({
3011
+ inboxToken,
3012
+ supportApiBase,
3013
+ requestHumanLabel,
3014
+ returnToAiLabel,
1559
3015
  position: positionProp,
1560
3016
  width: widthProp,
1561
3017
  height: heightProp,
@@ -1573,33 +3029,47 @@ function BubbleWidget({
1573
3029
  onOpenChange,
1574
3030
  ...chatProps
1575
3031
  }) {
3032
+ const supportBackend = useSupportChat(
3033
+ inboxToken ? {
3034
+ inboxToken,
3035
+ apiBase: supportApiBase,
3036
+ requestHumanLabel,
3037
+ returnToAiLabel
3038
+ } : { inboxToken: "__disabled__", apiBase: supportApiBase }
3039
+ );
1576
3040
  const isControlled = isOpenProp !== void 0;
1577
3041
  const [internalOpen, setInternalOpen] = react.useState(false);
1578
3042
  const open = isControlled ? isOpenProp : internalOpen;
1579
- const setOpen = react.useCallback((valueOrUpdater) => {
1580
- if (!isControlled) {
1581
- if (typeof valueOrUpdater === "function") {
1582
- setInternalOpen((prev) => {
1583
- const next = valueOrUpdater(prev);
1584
- onOpenChange?.(next);
1585
- return next;
1586
- });
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
+ }
1587
3056
  } else {
1588
- setInternalOpen(valueOrUpdater);
1589
- onOpenChange?.(valueOrUpdater);
3057
+ const next = typeof valueOrUpdater === "function" ? valueOrUpdater(open) : valueOrUpdater;
3058
+ onOpenChange?.(next);
1590
3059
  }
1591
- } else {
1592
- const next = typeof valueOrUpdater === "function" ? valueOrUpdater(open) : valueOrUpdater;
1593
- onOpenChange?.(next);
1594
- }
1595
- }, [isControlled, open, onOpenChange]);
3060
+ },
3061
+ [isControlled, open, onOpenChange]
3062
+ );
1596
3063
  const [expanded, setExpanded] = react.useState(false);
1597
3064
  const panelRef = react.useRef(null);
1598
3065
  const autoOpenedRef = react.useRef(false);
1599
3066
  react.useEffect(() => {
1600
3067
  if (localStorage.getItem(KEY_EXPANDED) === "true") setExpanded(true);
1601
3068
  }, []);
1602
- const remote = useAutoConfig(chatProps.agentId, autoConfig);
3069
+ const remote = useAutoConfig(
3070
+ inboxToken ? "" : chatProps.agentId ?? "",
3071
+ !inboxToken && autoConfig
3072
+ );
1603
3073
  const resolvedPosition = positionProp ?? remote.position;
1604
3074
  const resolvedBubbleIcon = bubbleIconUrlProp ?? remote.bubbleIconUrl;
1605
3075
  const resolvedAutoOpen = autoOpenProp || remote.autoOpen;
@@ -1613,8 +3083,8 @@ function BubbleWidget({
1613
3083
  const mergedConfig = {
1614
3084
  ...remote.remoteConfig,
1615
3085
  ...definedChatProps,
1616
- agentId: chatProps.agentId,
1617
- agentName: chatProps.agentName
3086
+ agentId: chatProps.agentId ?? "",
3087
+ agentName: chatProps.agentName ?? ""
1618
3088
  };
1619
3089
  const setOpenRef = react.useRef(setOpen);
1620
3090
  react.useEffect(() => {
@@ -1631,7 +3101,7 @@ function BubbleWidget({
1631
3101
  react.useEffect(() => {
1632
3102
  if (!resolvedKeyboardShortcut) return;
1633
3103
  const onKey = (e) => {
1634
- if ((e.metaKey || e.ctrlKey) && e.key === shortcutKey) {
3104
+ if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === shortcutKey.toLowerCase()) {
1635
3105
  e.preventDefault();
1636
3106
  setOpenRef.current((v) => !v);
1637
3107
  }
@@ -1650,7 +3120,10 @@ function BubbleWidget({
1650
3120
  const handleClose = () => {
1651
3121
  setOpen(false);
1652
3122
  if (!isControlled) {
1653
- 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
+ );
1654
3127
  }
1655
3128
  };
1656
3129
  const toggleExpanded = () => setExpanded((v) => {
@@ -1659,7 +3132,10 @@ function BubbleWidget({
1659
3132
  return next;
1660
3133
  });
1661
3134
  const isLeft = resolvedPosition === "bottom-left";
1662
- 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;
1663
3139
  const panelHeight = expanded ? expandedHeight : resolvedHeight;
1664
3140
  return /* @__PURE__ */ jsxRuntime.jsxs(
1665
3141
  "div",
@@ -1690,6 +3166,8 @@ function BubbleWidget({
1690
3166
  ChatWidget,
1691
3167
  {
1692
3168
  ...mergedConfig,
3169
+ agentId: inboxToken ? "support" : chatProps.agentId ?? "",
3170
+ customBackend: inboxToken ? supportBackend : mergedConfig.customBackend,
1693
3171
  onClose: handleClose,
1694
3172
  onExpand: toggleExpanded,
1695
3173
  expanded,
@@ -1715,15 +3193,20 @@ function BubbleWidget({
1715
3193
  alignItems: "center",
1716
3194
  justifyContent: "center",
1717
3195
  transition: "transform 0.2s ease, box-shadow 0.2s ease",
1718
- background: open ? "var(--foreground, #19191c)" : "var(--background, #fff)",
1719
- color: open ? "var(--background, #fff)" : "var(--foreground, #19191c)"
3196
+ background: open ? "#19191c" : "#ffffff",
3197
+ color: open ? "#ffffff" : "#19191c"
1720
3198
  },
1721
3199
  children: open ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { style: { width: 20, height: 20 } }) : resolvedBubbleIcon ? /* @__PURE__ */ jsxRuntime.jsx(
1722
3200
  "img",
1723
3201
  {
1724
3202
  src: resolvedBubbleIcon,
1725
3203
  alt: "",
1726
- style: { width: "100%", height: "100%", objectFit: "cover", display: "block" }
3204
+ style: {
3205
+ width: "100%",
3206
+ height: "100%",
3207
+ objectFit: "cover",
3208
+ display: "block"
3209
+ }
1727
3210
  }
1728
3211
  ) : /* @__PURE__ */ jsxRuntime.jsx(DefaultIcon, {})
1729
3212
  }
@@ -1735,8 +3218,12 @@ function BubbleWidget({
1735
3218
 
1736
3219
  exports.BubbleWidget = BubbleWidget;
1737
3220
  exports.ChatWidget = ChatWidget;
3221
+ exports.PlanCard = PlanCard;
3222
+ exports.ReasoningBlock = ReasoningBlock;
3223
+ exports.ToolCallBadge = ToolCallBadge;
1738
3224
  exports.formatToolName = formatToolName;
1739
3225
  exports.getContrastColor = getContrastColor;
1740
3226
  exports.useAttachments = useAttachments;
1741
3227
  exports.useChat = useChat;
3228
+ exports.useSupportChat = useSupportChat;
1742
3229
  exports.useVoice = useVoice;