@nlweb-ai/search-components 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs ADDED
@@ -0,0 +1,1766 @@
1
+ // #style-inject:#style-inject
2
+ function styleInject(css, { insertAt } = {}) {
3
+ if (!css || typeof document === "undefined") return;
4
+ const head = document.head || document.getElementsByTagName("head")[0];
5
+ const style = document.createElement("style");
6
+ style.type = "text/css";
7
+ if (insertAt === "top") {
8
+ if (head.firstChild) {
9
+ head.insertBefore(style, head.firstChild);
10
+ } else {
11
+ head.appendChild(style);
12
+ }
13
+ } else {
14
+ head.appendChild(style);
15
+ }
16
+ if (style.styleSheet) {
17
+ style.styleSheet.cssText = css;
18
+ } else {
19
+ style.appendChild(document.createTextNode(css));
20
+ }
21
+ }
22
+
23
+ // src/styles.css
24
+ styleInject('/*! tailwindcss v4.1.18 | MIT License | https://tailwindcss.com */\n@layer properties;\n@layer theme, base, components, utilities;\n@layer theme {\n :root,\n :host {\n --font-sans:\n ui-sans-serif,\n system-ui,\n sans-serif,\n "Apple Color Emoji",\n "Segoe UI Emoji",\n "Segoe UI Symbol",\n "Noto Color Emoji";\n --font-mono:\n ui-monospace,\n SFMono-Regular,\n Menlo,\n Monaco,\n Consolas,\n "Liberation Mono",\n "Courier New",\n monospace;\n --color-red-500: oklch(63.7% 0.237 25.331);\n --color-yellow-500: oklch(79.5% 0.184 86.047);\n --color-blue-300: oklch(80.9% 0.105 251.813);\n --color-blue-600: oklch(54.6% 0.245 262.881);\n --color-gray-50: oklch(98.5% 0.002 247.839);\n --color-gray-100: oklch(96.7% 0.003 264.542);\n --color-gray-200: oklch(92.8% 0.006 264.531);\n --color-gray-300: oklch(87.2% 0.01 258.338);\n --color-gray-400: oklch(70.7% 0.022 261.325);\n --color-gray-500: oklch(55.1% 0.027 264.364);\n --color-gray-600: oklch(44.6% 0.03 256.802);\n --color-gray-700: oklch(37.3% 0.034 259.733);\n --color-gray-800: oklch(27.8% 0.033 256.848);\n --color-gray-900: oklch(21% 0.034 264.665);\n --color-black: #000;\n --color-white: #fff;\n --spacing: 0.25rem;\n --container-md: 28rem;\n --container-xl: 36rem;\n --container-2xl: 42rem;\n --container-3xl: 48rem;\n --container-7xl: 80rem;\n --text-xs: 0.75rem;\n --text-xs--line-height: calc(1 / 0.75);\n --text-sm: 0.875rem;\n --text-sm--line-height: calc(1.25 / 0.875);\n --text-base: 1rem;\n --text-base--line-height: calc(1.5 / 1);\n --text-lg: 1.125rem;\n --text-lg--line-height: calc(1.75 / 1.125);\n --text-2xl: 1.5rem;\n --text-2xl--line-height: calc(2 / 1.5);\n --font-weight-normal: 400;\n --font-weight-medium: 500;\n --font-weight-semibold: 600;\n --font-weight-bold: 700;\n --leading-relaxed: 1.625;\n --radius-sm: 0.25rem;\n --radius-md: 0.375rem;\n --radius-lg: 0.5rem;\n --radius-2xl: 1rem;\n --animate-spin: spin 1s linear infinite;\n --default-transition-duration: 150ms;\n --default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n --default-font-family: var(--font-sans);\n --default-mono-font-family: var(--font-mono);\n }\n}\n@layer base {\n *,\n ::after,\n ::before,\n ::backdrop,\n ::file-selector-button {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n border: 0 solid;\n }\n html,\n :host {\n line-height: 1.5;\n -webkit-text-size-adjust: 100%;\n tab-size: 4;\n font-family: var(--default-font-family, ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");\n font-feature-settings: var(--default-font-feature-settings, normal);\n font-variation-settings: var(--default-font-variation-settings, normal);\n -webkit-tap-highlight-color: transparent;\n }\n hr {\n height: 0;\n color: inherit;\n border-top-width: 1px;\n }\n abbr:where([title]) {\n -webkit-text-decoration: underline dotted;\n text-decoration: underline dotted;\n }\n h1,\n h2,\n h3,\n h4,\n h5,\n h6 {\n font-size: inherit;\n font-weight: inherit;\n }\n a {\n color: inherit;\n -webkit-text-decoration: inherit;\n text-decoration: inherit;\n }\n b,\n strong {\n font-weight: bolder;\n }\n code,\n kbd,\n samp,\n pre {\n font-family: var(--default-mono-font-family, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);\n font-feature-settings: var(--default-mono-font-feature-settings, normal);\n font-variation-settings: var(--default-mono-font-variation-settings, normal);\n font-size: 1em;\n }\n small {\n font-size: 80%;\n }\n sub,\n sup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n }\n sub {\n bottom: -0.25em;\n }\n sup {\n top: -0.5em;\n }\n table {\n text-indent: 0;\n border-color: inherit;\n border-collapse: collapse;\n }\n :-moz-focusring {\n outline: auto;\n }\n progress {\n vertical-align: baseline;\n }\n summary {\n display: list-item;\n }\n ol,\n ul,\n menu {\n list-style: none;\n }\n img,\n svg,\n video,\n canvas,\n audio,\n iframe,\n embed,\n object {\n display: block;\n vertical-align: middle;\n }\n img,\n video {\n max-width: 100%;\n height: auto;\n }\n button,\n input,\n select,\n optgroup,\n textarea,\n ::file-selector-button {\n font: inherit;\n font-feature-settings: inherit;\n font-variation-settings: inherit;\n letter-spacing: inherit;\n color: inherit;\n border-radius: 0;\n background-color: transparent;\n opacity: 1;\n }\n :where(select:is([multiple], [size])) optgroup {\n font-weight: bolder;\n }\n :where(select:is([multiple], [size])) optgroup option {\n padding-inline-start: 20px;\n }\n ::file-selector-button {\n margin-inline-end: 4px;\n }\n ::placeholder {\n opacity: 1;\n }\n @supports (not (-webkit-appearance: -apple-pay-button)) or (contain-intrinsic-size: 1px) {\n ::placeholder {\n color: currentcolor;\n @supports (color: color-mix(in lab, red, red)) {\n color: color-mix(in oklab, currentcolor 50%, transparent);\n }\n }\n }\n textarea {\n resize: vertical;\n }\n ::-webkit-search-decoration {\n -webkit-appearance: none;\n }\n ::-webkit-date-and-time-value {\n min-height: 1lh;\n text-align: inherit;\n }\n ::-webkit-datetime-edit {\n display: inline-flex;\n }\n ::-webkit-datetime-edit-fields-wrapper {\n padding: 0;\n }\n ::-webkit-datetime-edit,\n ::-webkit-datetime-edit-year-field,\n ::-webkit-datetime-edit-month-field,\n ::-webkit-datetime-edit-day-field,\n ::-webkit-datetime-edit-hour-field,\n ::-webkit-datetime-edit-minute-field,\n ::-webkit-datetime-edit-second-field,\n ::-webkit-datetime-edit-millisecond-field,\n ::-webkit-datetime-edit-meridiem-field {\n padding-block: 0;\n }\n ::-webkit-calendar-picker-indicator {\n line-height: 1;\n }\n :-moz-ui-invalid {\n box-shadow: none;\n }\n button,\n input:where([type=button], [type=reset], [type=submit]),\n ::file-selector-button {\n appearance: button;\n }\n ::-webkit-inner-spin-button,\n ::-webkit-outer-spin-button {\n height: auto;\n }\n [hidden]:where(:not([hidden=until-found])) {\n display: none !important;\n }\n}\n@layer utilities {\n .shimmer-container {\n container-type: inline-size;\n & .shimmer {\n --shimmer-track-width: 100cqw;\n --shimmer-track-height: max(100cqh, 50cqw, 200px);\n }\n }\n .pointer-events-none {\n pointer-events: none;\n }\n .invisible {\n visibility: hidden;\n }\n .absolute {\n position: absolute;\n }\n .fixed {\n position: fixed;\n }\n .relative {\n position: relative;\n }\n .inset-0 {\n inset: calc(var(--spacing) * 0);\n }\n .inset-y-0 {\n inset-block: calc(var(--spacing) * 0);\n }\n .top-0 {\n top: calc(var(--spacing) * 0);\n }\n .top-3 {\n top: calc(var(--spacing) * 3);\n }\n .top-4 {\n top: calc(var(--spacing) * 4);\n }\n .top-12 {\n top: calc(var(--spacing) * 12);\n }\n .top-\\[calc\\(100\\%_-_100px\\)\\] {\n top: calc(100% - 100px);\n }\n .right-0 {\n right: calc(var(--spacing) * 0);\n }\n .right-2 {\n right: calc(var(--spacing) * 2);\n }\n .right-4 {\n right: calc(var(--spacing) * 4);\n }\n .right-6 {\n right: calc(var(--spacing) * 6);\n }\n .right-10 {\n right: calc(var(--spacing) * 10);\n }\n .bottom-0 {\n bottom: calc(var(--spacing) * 0);\n }\n .bottom-8 {\n bottom: calc(var(--spacing) * 8);\n }\n .left-0 {\n left: calc(var(--spacing) * 0);\n }\n .left-2 {\n left: calc(var(--spacing) * 2);\n }\n .left-3\\.5 {\n left: calc(var(--spacing) * 3.5);\n }\n .left-4 {\n left: calc(var(--spacing) * 4);\n }\n .left-10 {\n left: calc(var(--spacing) * 10);\n }\n .z-10 {\n z-index: 10;\n }\n .z-30 {\n z-index: 30;\n }\n .z-50 {\n z-index: 50;\n }\n .container {\n width: 100%;\n @media (width >= 40rem) {\n max-width: 40rem;\n }\n @media (width >= 48rem) {\n max-width: 48rem;\n }\n @media (width >= 64rem) {\n max-width: 64rem;\n }\n @media (width >= 80rem) {\n max-width: 80rem;\n }\n @media (width >= 96rem) {\n max-width: 96rem;\n }\n }\n .m-0 {\n margin: calc(var(--spacing) * 0);\n }\n .-mx-2 {\n margin-inline: calc(var(--spacing) * -2);\n }\n .mx-auto {\n margin-inline: auto;\n }\n .mt-1 {\n margin-top: calc(var(--spacing) * 1);\n }\n .mt-4 {\n margin-top: calc(var(--spacing) * 4);\n }\n .mb-1 {\n margin-bottom: calc(var(--spacing) * 1);\n }\n .mb-2 {\n margin-bottom: calc(var(--spacing) * 2);\n }\n .mb-3 {\n margin-bottom: calc(var(--spacing) * 3);\n }\n .mb-4 {\n margin-bottom: calc(var(--spacing) * 4);\n }\n .mb-6 {\n margin-bottom: calc(var(--spacing) * 6);\n }\n .ml-auto {\n margin-left: auto;\n }\n .line-clamp-2 {\n overflow: hidden;\n display: -webkit-box;\n -webkit-box-orient: vertical;\n -webkit-line-clamp: 2;\n }\n .line-clamp-3 {\n overflow: hidden;\n display: -webkit-box;\n -webkit-box-orient: vertical;\n -webkit-line-clamp: 3;\n }\n .block {\n display: block;\n }\n .flex {\n display: flex;\n }\n .grid {\n display: grid;\n }\n .hidden {\n display: none;\n }\n .size-4 {\n width: calc(var(--spacing) * 4);\n height: calc(var(--spacing) * 4);\n }\n .size-5 {\n width: calc(var(--spacing) * 5);\n height: calc(var(--spacing) * 5);\n }\n .size-6 {\n width: calc(var(--spacing) * 6);\n height: calc(var(--spacing) * 6);\n }\n .h-2 {\n height: calc(var(--spacing) * 2);\n }\n .h-3 {\n height: calc(var(--spacing) * 3);\n }\n .h-4 {\n height: calc(var(--spacing) * 4);\n }\n .h-5 {\n height: calc(var(--spacing) * 5);\n }\n .h-7 {\n height: calc(var(--spacing) * 7);\n }\n .h-12 {\n height: calc(var(--spacing) * 12);\n }\n .h-36 {\n height: calc(var(--spacing) * 36);\n }\n .h-full\\! {\n height: 100% !important;\n }\n .h-screen {\n height: 100vh;\n }\n .max-h-12 {\n max-height: calc(var(--spacing) * 12);\n }\n .max-h-60 {\n max-height: calc(var(--spacing) * 60);\n }\n .max-h-\\[60vh\\] {\n max-height: 60vh;\n }\n .max-h-\\[80vh\\] {\n max-height: 80vh;\n }\n .w-1\\/6 {\n width: calc(1/6 * 100%);\n }\n .w-3 {\n width: calc(var(--spacing) * 3);\n }\n .w-3\\/4 {\n width: calc(3/4 * 100%);\n }\n .w-4 {\n width: calc(var(--spacing) * 4);\n }\n .w-5 {\n width: calc(var(--spacing) * 5);\n }\n .w-5\\/6 {\n width: calc(5/6 * 100%);\n }\n .w-7 {\n width: calc(var(--spacing) * 7);\n }\n .w-10\\/12 {\n width: calc(10/12 * 100%);\n }\n .w-11\\/12 {\n width: calc(11/12 * 100%);\n }\n .w-full {\n width: 100%;\n }\n .w-full\\! {\n width: 100% !important;\n }\n .w-screen {\n width: 100vw;\n }\n .max-w-2xl {\n max-width: var(--container-2xl);\n }\n .max-w-3xl {\n max-width: var(--container-3xl);\n }\n .max-w-7xl {\n max-width: var(--container-7xl);\n }\n .max-w-12 {\n max-width: calc(var(--spacing) * 12);\n }\n .max-w-70 {\n max-width: calc(var(--spacing) * 70);\n }\n .max-w-xl {\n max-width: var(--container-xl);\n }\n .min-w-0 {\n min-width: calc(var(--spacing) * 0);\n }\n .min-w-50 {\n min-width: calc(var(--spacing) * 50);\n }\n .min-w-64 {\n min-width: calc(var(--spacing) * 64);\n }\n .flex-1 {\n flex: 1;\n }\n .flex-shrink-0 {\n flex-shrink: 0;\n }\n .shimmer {\n --_gradient-width: calc(var(--_spread) + var(--shimmer-track-height) * tan(var(--shimmer-angle)));\n --_active-distance: calc(var(--shimmer-track-width, 200px) + var(--_gradient-width));\n --_duration: var(--shimmer-duration, calc(var(--_active-distance) / var(--_speed) / 1px * 1000));\n --_repeat-delay: var(--shimmer-repeat-delay, calc(20000 / var(--_speed)));\n --_repeat-delay-px: calc(var(--_repeat-delay) * var(--_active-distance) / var(--_duration));\n --_xy-offset-px: calc((var(--shimmer-x, 0) + var(--shimmer-y, 0) * tan(var(--shimmer-angle))) * 1px);\n --_bg-width: calc( 100% + var(--shimmer-track-width, 100%) + var(--_gradient-width) + var(--_repeat-delay-px) );\n --_position: calc( var(--shimmer-track-width, (100% - var(--_gradient-width) - var(--_repeat-delay-px)) / 2) + var(--_gradient-width) / 2 + var(--_repeat-delay-px) - var(--_xy-offset-px) );\n &:not(.shimmer-bg) {\n --_speed: var(--shimmer-speed, 200);\n --_spread: var(--shimmer-spread, calc(4ch + 80px));\n --_bg: currentColor;\n --_fg: var(--shimmer-color, oklch(from currentColor l c h / calc(alpha * 0.2)));\n background-clip: text;\n -webkit-text-fill-color: transparent;\n }\n &.shimmer-bg {\n --_speed: var(--shimmer-speed, 1000);\n --_spread: var(--shimmer-spread, 480px);\n --_bg: transparent;\n --_fg: var(--shimmer-color, oklch(from currentColor 0 c h / 0.06));\n }\n @media (prefers-color-scheme: dark) {\n &:not(.shimmer-bg) {\n --_fg: var(--shimmer-color, oklch(from currentColor max(0.8, calc(l + 0.4)) c h / calc(alpha + 0.4)));\n }\n &.shimmer-bg {\n --_fg: var(--shimmer-color, oklch(from currentColor 0 c h / 0.30));\n }\n }\n @-moz-document url-prefix() {\n --_duration: var(--shimmer-duration, calc(375000 / var(--_speed)));\n }\n --_mix-96: var(--_fg);\n @supports (color: color-mix(in lab, red, red)) {\n --_mix-96: color-mix(in oklch, var(--_fg), var(--_bg) 96%);\n }\n --_mix-83: var(--_fg);\n @supports (color: color-mix(in lab, red, red)) {\n --_mix-83: color-mix(in oklch, var(--_fg), var(--_bg) 83%);\n }\n --_mix-67: var(--_fg);\n @supports (color: color-mix(in lab, red, red)) {\n --_mix-67: color-mix(in oklch, var(--_fg), var(--_bg) 67%);\n }\n --_mix-50: var(--_fg);\n @supports (color: color-mix(in lab, red, red)) {\n --_mix-50: color-mix(in oklch, var(--_fg), var(--_bg) 50%);\n }\n --_mix-33: var(--_fg);\n @supports (color: color-mix(in lab, red, red)) {\n --_mix-33: color-mix(in oklch, var(--_fg), var(--_bg) 33%);\n }\n --_mix-17: var(--_fg);\n @supports (color: color-mix(in lab, red, red)) {\n --_mix-17: color-mix(in oklch, var(--_fg), var(--_bg) 17%);\n }\n --_mix-4: var(--_fg);\n @supports (color: color-mix(in lab, red, red)) {\n --_mix-4: color-mix(in oklch, var(--_fg), var(--_bg) 4%);\n }\n background:\n linear-gradient(\n calc(90deg + var(--shimmer-angle)),\n var(--_bg) calc(var(--_position) - var(--_spread) * 0.5),\n var(--_mix-96) calc(var(--_position) - var(--_spread) * 0.44),\n var(--_mix-83) calc(var(--_position) - var(--_spread) * 0.37),\n var(--_mix-67) calc(var(--_position) - var(--_spread) * 0.31),\n var(--_mix-50) calc(var(--_position) - var(--_spread) * 0.25),\n var(--_mix-33) calc(var(--_position) - var(--_spread) * 0.19),\n var(--_mix-17) calc(var(--_position) - var(--_spread) * 0.12),\n var(--_mix-4) calc(var(--_position) - var(--_spread) * 0.06),\n var(--_fg) var(--_position),\n var(--_mix-4) calc(var(--_position) + var(--_spread) * 0.06),\n var(--_mix-17) calc(var(--_position) + var(--_spread) * 0.12),\n var(--_mix-33) calc(var(--_position) + var(--_spread) * 0.19),\n var(--_mix-50) calc(var(--_position) + var(--_spread) * 0.25),\n var(--_mix-67) calc(var(--_position) + var(--_spread) * 0.31),\n var(--_mix-83) calc(var(--_position) + var(--_spread) * 0.37),\n var(--_mix-96) calc(var(--_position) + var(--_spread) * 0.44),\n var(--_bg) calc(var(--_position) + var(--_spread) * 0.5)) 0 0 / var(--_bg-width) 100% no-repeat;\n animation: tw-shimmer 1s linear 0s infinite backwards;\n animation-duration: calc((var(--_duration) + var(--_repeat-delay)) * 1ms);\n }\n .animate-spin {\n animation: var(--animate-spin);\n }\n .cursor-default {\n cursor: default;\n }\n .cursor-not-allowed {\n cursor: not-allowed;\n }\n .grid-cols-1 {\n grid-template-columns: repeat(1, minmax(0, 1fr));\n }\n .flex-col {\n flex-direction: column;\n }\n .flex-row {\n flex-direction: row;\n }\n .items-center {\n align-items: center;\n }\n .items-stretch {\n align-items: stretch;\n }\n .justify-center {\n justify-content: center;\n }\n .justify-end {\n justify-content: flex-end;\n }\n .justify-start {\n justify-content: flex-start;\n }\n .gap-0 {\n gap: calc(var(--spacing) * 0);\n }\n .gap-1 {\n gap: calc(var(--spacing) * 1);\n }\n .gap-2 {\n gap: calc(var(--spacing) * 2);\n }\n .gap-3 {\n gap: calc(var(--spacing) * 3);\n }\n .gap-6 {\n gap: calc(var(--spacing) * 6);\n }\n .space-y-1 {\n :where(& > :not(:last-child)) {\n --tw-space-y-reverse: 0;\n margin-block-start: calc(calc(var(--spacing) * 1) * var(--tw-space-y-reverse));\n margin-block-end: calc(calc(var(--spacing) * 1) * calc(1 - var(--tw-space-y-reverse)));\n }\n }\n .space-y-2 {\n :where(& > :not(:last-child)) {\n --tw-space-y-reverse: 0;\n margin-block-start: calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));\n margin-block-end: calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)));\n }\n }\n .space-y-3 {\n :where(& > :not(:last-child)) {\n --tw-space-y-reverse: 0;\n margin-block-start: calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));\n margin-block-end: calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)));\n }\n }\n .space-y-4 {\n :where(& > :not(:last-child)) {\n --tw-space-y-reverse: 0;\n margin-block-start: calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));\n margin-block-end: calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)));\n }\n }\n .divide-y {\n :where(& > :not(:last-child)) {\n --tw-divide-y-reverse: 0;\n border-bottom-style: var(--tw-border-style);\n border-top-style: var(--tw-border-style);\n border-top-width: calc(1px * var(--tw-divide-y-reverse));\n border-bottom-width: calc(1px * calc(1 - var(--tw-divide-y-reverse)));\n }\n }\n .truncate {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n .overflow-auto {\n overflow: auto;\n }\n .overflow-hidden {\n overflow: hidden;\n }\n .overflow-y-auto {\n overflow-y: auto;\n }\n .rounded {\n border-radius: 0.25rem;\n }\n .rounded\\! {\n border-radius: 0.25rem !important;\n }\n .rounded-2xl {\n border-radius: var(--radius-2xl);\n }\n .rounded-lg {\n border-radius: var(--radius-lg);\n }\n .rounded-md {\n border-radius: var(--radius-md);\n }\n .rounded-tr-sm {\n border-top-right-radius: var(--radius-sm);\n }\n .border {\n border-style: var(--tw-border-style);\n border-width: 1px;\n }\n .border-r {\n border-right-style: var(--tw-border-style);\n border-right-width: 1px;\n }\n .border-b {\n border-bottom-style: var(--tw-border-style);\n border-bottom-width: 1px;\n }\n .border-b-2 {\n border-bottom-style: var(--tw-border-style);\n border-bottom-width: 2px;\n }\n .border-none {\n --tw-border-style: none;\n border-style: none;\n }\n .border-gray-200 {\n border-color: var(--color-gray-200);\n }\n .border-gray-300 {\n border-color: var(--color-gray-300);\n }\n .border-gray-400 {\n border-color: var(--color-gray-400);\n }\n .border-gray-900 {\n border-color: var(--color-gray-900);\n }\n .bg-black {\n background-color: var(--color-black);\n }\n .bg-black\\/30 {\n background-color: color-mix(in srgb, #000 30%, transparent);\n @supports (color: color-mix(in lab, red, red)) {\n background-color: color-mix(in oklab, var(--color-black) 30%, transparent);\n }\n }\n .bg-blue-600 {\n background-color: var(--color-blue-600);\n }\n .bg-gray-50 {\n background-color: var(--color-gray-50);\n }\n .bg-gray-100 {\n background-color: var(--color-gray-100);\n }\n .bg-gray-200 {\n background-color: var(--color-gray-200);\n }\n .bg-gray-200\\/50 {\n background-color: color-mix(in srgb, oklch(92.8% 0.006 264.531) 50%, transparent);\n @supports (color: color-mix(in lab, red, red)) {\n background-color: color-mix(in oklab, var(--color-gray-200) 50%, transparent);\n }\n }\n .bg-gray-900 {\n background-color: var(--color-gray-900);\n }\n .bg-white {\n background-color: var(--color-white);\n }\n .bg-gradient-to-b {\n --tw-gradient-position: to bottom in oklab;\n background-image: linear-gradient(var(--tw-gradient-stops));\n }\n .from-transparent {\n --tw-gradient-from: transparent;\n --tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position));\n }\n .to-white {\n --tw-gradient-to: var(--color-white);\n --tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position));\n }\n .object-cover\\! {\n object-fit: cover !important;\n }\n .p-2 {\n padding: calc(var(--spacing) * 2);\n }\n .p-3 {\n padding: calc(var(--spacing) * 3);\n }\n .p-4 {\n padding: calc(var(--spacing) * 4);\n }\n .p-6 {\n padding: calc(var(--spacing) * 6);\n }\n .p-8 {\n padding: calc(var(--spacing) * 8);\n }\n .px-2 {\n padding-inline: calc(var(--spacing) * 2);\n }\n .px-3 {\n padding-inline: calc(var(--spacing) * 3);\n }\n .px-4 {\n padding-inline: calc(var(--spacing) * 4);\n }\n .px-5 {\n padding-inline: calc(var(--spacing) * 5);\n }\n .px-6 {\n padding-inline: calc(var(--spacing) * 6);\n }\n .py-1 {\n padding-block: calc(var(--spacing) * 1);\n }\n .py-1\\.5 {\n padding-block: calc(var(--spacing) * 1.5);\n }\n .py-2 {\n padding-block: calc(var(--spacing) * 2);\n }\n .py-3 {\n padding-block: calc(var(--spacing) * 3);\n }\n .py-6 {\n padding-block: calc(var(--spacing) * 6);\n }\n .py-8 {\n padding-block: calc(var(--spacing) * 8);\n }\n .pt-16 {\n padding-top: calc(var(--spacing) * 16);\n }\n .pr-2 {\n padding-right: calc(var(--spacing) * 2);\n }\n .pr-3 {\n padding-right: calc(var(--spacing) * 3);\n }\n .pr-4 {\n padding-right: calc(var(--spacing) * 4);\n }\n .pr-10 {\n padding-right: calc(var(--spacing) * 10);\n }\n .pr-12 {\n padding-right: calc(var(--spacing) * 12);\n }\n .pr-20 {\n padding-right: calc(var(--spacing) * 20);\n }\n .pb-0 {\n padding-bottom: calc(var(--spacing) * 0);\n }\n .pb-2 {\n padding-bottom: calc(var(--spacing) * 2);\n }\n .pb-24 {\n padding-bottom: calc(var(--spacing) * 24);\n }\n .pl-2 {\n padding-left: calc(var(--spacing) * 2);\n }\n .pl-3 {\n padding-left: calc(var(--spacing) * 3);\n }\n .pl-4 {\n padding-left: calc(var(--spacing) * 4);\n }\n .pl-8 {\n padding-left: calc(var(--spacing) * 8);\n }\n .pl-10 {\n padding-left: calc(var(--spacing) * 10);\n }\n .text-center {\n text-align: center;\n }\n .text-left {\n text-align: left;\n }\n .text-2xl {\n font-size: var(--text-2xl);\n line-height: var(--tw-leading, var(--text-2xl--line-height));\n }\n .text-base {\n font-size: var(--text-base);\n line-height: var(--tw-leading, var(--text-base--line-height));\n }\n .text-lg {\n font-size: var(--text-lg);\n line-height: var(--tw-leading, var(--text-lg--line-height));\n }\n .text-sm {\n font-size: var(--text-sm);\n line-height: var(--tw-leading, var(--text-sm--line-height));\n }\n .text-xs {\n font-size: var(--text-xs);\n line-height: var(--tw-leading, var(--text-xs--line-height));\n }\n .leading-5 {\n --tw-leading: calc(var(--spacing) * 5);\n line-height: calc(var(--spacing) * 5);\n }\n .leading-relaxed {\n --tw-leading: var(--leading-relaxed);\n line-height: var(--leading-relaxed);\n }\n .font-bold {\n --tw-font-weight: var(--font-weight-bold);\n font-weight: var(--font-weight-bold);\n }\n .font-medium {\n --tw-font-weight: var(--font-weight-medium);\n font-weight: var(--font-weight-medium);\n }\n .font-normal {\n --tw-font-weight: var(--font-weight-normal);\n font-weight: var(--font-weight-normal);\n }\n .font-semibold {\n --tw-font-weight: var(--font-weight-semibold);\n font-weight: var(--font-weight-semibold);\n }\n .whitespace-pre-line {\n white-space: pre-line;\n }\n .text-blue-600 {\n color: var(--color-blue-600);\n }\n .text-gray-400 {\n color: var(--color-gray-400);\n }\n .text-gray-500 {\n color: var(--color-gray-500);\n }\n .text-gray-600 {\n color: var(--color-gray-600);\n }\n .text-gray-700 {\n color: var(--color-gray-700);\n }\n .text-gray-800 {\n color: var(--color-gray-800);\n }\n .text-gray-900 {\n color: var(--color-gray-900);\n }\n .text-white {\n color: var(--color-white);\n }\n .text-yellow-500 {\n color: var(--color-yellow-500);\n }\n .uppercase {\n text-transform: uppercase;\n }\n .italic {\n font-style: italic;\n }\n .no-underline\\! {\n text-decoration-line: none !important;\n }\n .underline {\n text-decoration-line: underline;\n }\n .opacity-0 {\n opacity: 0%;\n }\n .opacity-25 {\n opacity: 25%;\n }\n .opacity-50 {\n opacity: 50%;\n }\n .opacity-75 {\n opacity: 75%;\n }\n .opacity-100 {\n opacity: 100%;\n }\n .shadow-lg {\n --tw-shadow: 0 10px 15px -3px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 4px 6px -4px var(--tw-shadow-color, rgb(0 0 0 / 0.1));\n box-shadow:\n var(--tw-inset-shadow),\n var(--tw-inset-ring-shadow),\n var(--tw-ring-offset-shadow),\n var(--tw-ring-shadow),\n var(--tw-shadow);\n }\n .shadow-md {\n --tw-shadow: 0 4px 6px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 2px 4px -2px var(--tw-shadow-color, rgb(0 0 0 / 0.1));\n box-shadow:\n var(--tw-inset-shadow),\n var(--tw-inset-ring-shadow),\n var(--tw-ring-offset-shadow),\n var(--tw-ring-shadow),\n var(--tw-shadow);\n }\n .shadow-sm {\n --tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1));\n box-shadow:\n var(--tw-inset-shadow),\n var(--tw-inset-ring-shadow),\n var(--tw-ring-offset-shadow),\n var(--tw-ring-shadow),\n var(--tw-shadow);\n }\n .shadow-xl {\n --tw-shadow: 0 20px 25px -5px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 8px 10px -6px var(--tw-shadow-color, rgb(0 0 0 / 0.1));\n box-shadow:\n var(--tw-inset-shadow),\n var(--tw-inset-ring-shadow),\n var(--tw-ring-offset-shadow),\n var(--tw-ring-shadow),\n var(--tw-shadow);\n }\n .ring-1 {\n --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);\n box-shadow:\n var(--tw-inset-shadow),\n var(--tw-inset-ring-shadow),\n var(--tw-ring-offset-shadow),\n var(--tw-ring-shadow),\n var(--tw-shadow);\n }\n .ring-2 {\n --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);\n box-shadow:\n var(--tw-inset-shadow),\n var(--tw-inset-ring-shadow),\n var(--tw-ring-offset-shadow),\n var(--tw-ring-shadow),\n var(--tw-shadow);\n }\n .ring-black\\/5 {\n --tw-ring-color: color-mix(in srgb, #000 5%, transparent);\n @supports (color: color-mix(in lab, red, red)) {\n --tw-ring-color: color-mix(in oklab, var(--color-black) 5%, transparent);\n }\n }\n .ring-gray-100 {\n --tw-ring-color: var(--color-gray-100);\n }\n .blur {\n --tw-blur: blur(8px);\n filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);\n }\n .filter {\n filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);\n }\n .transition-all {\n transition-property: all;\n transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));\n transition-duration: var(--tw-duration, var(--default-transition-duration));\n }\n .transition-colors {\n transition-property:\n color,\n background-color,\n border-color,\n outline-color,\n text-decoration-color,\n fill,\n stroke,\n --tw-gradient-from,\n --tw-gradient-via,\n --tw-gradient-to;\n transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));\n transition-duration: var(--tw-duration, var(--default-transition-duration));\n }\n .transition-opacity {\n transition-property: opacity;\n transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));\n transition-duration: var(--tw-duration, var(--default-transition-duration));\n }\n .duration-200 {\n --tw-duration: 200ms;\n transition-duration: 200ms;\n }\n .shimmer-color-gray-300\\/30 {\n --alpha: 100%;\n --alpha: calc(30 * 1%);\n --shimmer-color: oklch(87.2% 0.01 258.338);\n @supports (color: color-mix(in lab, red, red)) {\n --shimmer-color: color-mix(in oklab, var(--color-gray-300) var(--alpha, 100%), transparent);\n }\n }\n .shimmer-color-gray-400\\/20 {\n --alpha: 100%;\n --alpha: calc(20 * 1%);\n --shimmer-color: oklch(70.7% 0.022 261.325);\n @supports (color: color-mix(in lab, red, red)) {\n --shimmer-color: color-mix(in oklab, var(--color-gray-400) var(--alpha, 100%), transparent);\n }\n }\n .outline-none {\n --tw-outline-style: none;\n outline-style: none;\n }\n .select-none {\n -webkit-user-select: none;\n user-select: none;\n }\n .\\[--shimmer-x\\:60\\] {\n --shimmer-x: 60;\n }\n .\\[--shimmer-y\\:0\\] {\n --shimmer-y: 0;\n }\n .\\[--shimmer-y\\:20\\] {\n --shimmer-y: 20;\n }\n .\\[--shimmer-y\\:40\\] {\n --shimmer-y: 40;\n }\n .\\[--shimmer-y\\:60\\] {\n --shimmer-y: 60;\n }\n .\\[--shimmer-y\\:80\\] {\n --shimmer-y: 80;\n }\n .group-hover\\:opacity-100 {\n &:is(:where(.group):hover *) {\n @media (hover: hover) {\n opacity: 100%;\n }\n }\n }\n .hover\\:border-gray-300 {\n &:hover {\n @media (hover: hover) {\n border-color: var(--color-gray-300);\n }\n }\n }\n .hover\\:border-gray-400 {\n &:hover {\n @media (hover: hover) {\n border-color: var(--color-gray-400);\n }\n }\n }\n .hover\\:bg-gray-50 {\n &:hover {\n @media (hover: hover) {\n background-color: var(--color-gray-50);\n }\n }\n }\n .hover\\:bg-gray-100 {\n &:hover {\n @media (hover: hover) {\n background-color: var(--color-gray-100);\n }\n }\n }\n .hover\\:bg-gray-800 {\n &:hover {\n @media (hover: hover) {\n background-color: var(--color-gray-800);\n }\n }\n }\n .hover\\:text-black {\n &:hover {\n @media (hover: hover) {\n color: var(--color-black);\n }\n }\n }\n .hover\\:text-gray-700 {\n &:hover {\n @media (hover: hover) {\n color: var(--color-gray-700);\n }\n }\n }\n .hover\\:text-red-500 {\n &:hover {\n @media (hover: hover) {\n color: var(--color-red-500);\n }\n }\n }\n .focus\\:ring-0 {\n &:focus {\n --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);\n box-shadow:\n var(--tw-inset-shadow),\n var(--tw-inset-ring-shadow),\n var(--tw-ring-offset-shadow),\n var(--tw-ring-shadow),\n var(--tw-shadow);\n }\n }\n .focus\\:outline-none {\n &:focus {\n --tw-outline-style: none;\n outline-style: none;\n }\n }\n .focus-visible\\:ring-2 {\n &:focus-visible {\n --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);\n box-shadow:\n var(--tw-inset-shadow),\n var(--tw-inset-ring-shadow),\n var(--tw-ring-offset-shadow),\n var(--tw-ring-shadow),\n var(--tw-shadow);\n }\n }\n .focus-visible\\:ring-white\\/75 {\n &:focus-visible {\n --tw-ring-color: color-mix(in srgb, #fff 75%, transparent);\n @supports (color: color-mix(in lab, red, red)) {\n --tw-ring-color: color-mix(in oklab, var(--color-white) 75%, transparent);\n }\n }\n }\n .focus-visible\\:ring-offset-2 {\n &:focus-visible {\n --tw-ring-offset-width: 2px;\n --tw-ring-offset-shadow: var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);\n }\n }\n .focus-visible\\:ring-offset-blue-300 {\n &:focus-visible {\n --tw-ring-offset-color: var(--color-blue-300);\n }\n }\n .active\\:scale-95 {\n &:active {\n --tw-scale-x: 95%;\n --tw-scale-y: 95%;\n --tw-scale-z: 95%;\n scale: var(--tw-scale-x) var(--tw-scale-y);\n }\n }\n .data-closed\\:opacity-0 {\n &[data-closed] {\n opacity: 0%;\n }\n }\n .sm\\:grid-cols-2 {\n @media (width >= 40rem) {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n }\n }\n .sm\\:text-sm {\n @media (width >= 40rem) {\n font-size: var(--text-sm);\n line-height: var(--tw-leading, var(--text-sm--line-height));\n }\n }\n .md\\:min-w-md {\n @media (width >= 48rem) {\n min-width: var(--container-md);\n }\n }\n .md\\:grid-cols-3 {\n @media (width >= 48rem) {\n grid-template-columns: repeat(3, minmax(0, 1fr));\n }\n }\n}\n@property --shimmer-track-height { syntax: "<length>"; inherits: true; initial-value: 200px; }\n@property --shimmer-angle { syntax: "<angle>"; inherits: true; initial-value: 15deg; }\n@layer base {\n *,\n ::after,\n ::before,\n ::backdrop,\n ::file-selector-button {\n border-color: var(--color-gray-200, currentcolor);\n }\n}\n.editor-paragraph {\n margin-bottom: 0px !important;\n}\n@property --tw-space-y-reverse { syntax: "*"; inherits: false; initial-value: 0; }\n@property --tw-divide-y-reverse { syntax: "*"; inherits: false; initial-value: 0; }\n@property --tw-border-style { syntax: "*"; inherits: false; initial-value: solid; }\n@property --tw-gradient-position { syntax: "*"; inherits: false; }\n@property --tw-gradient-from { syntax: "<color>"; inherits: false; initial-value: #0000; }\n@property --tw-gradient-via { syntax: "<color>"; inherits: false; initial-value: #0000; }\n@property --tw-gradient-to { syntax: "<color>"; inherits: false; initial-value: #0000; }\n@property --tw-gradient-stops { syntax: "*"; inherits: false; }\n@property --tw-gradient-via-stops { syntax: "*"; inherits: false; }\n@property --tw-gradient-from-position { syntax: "<length-percentage>"; inherits: false; initial-value: 0%; }\n@property --tw-gradient-via-position { syntax: "<length-percentage>"; inherits: false; initial-value: 50%; }\n@property --tw-gradient-to-position { syntax: "<length-percentage>"; inherits: false; initial-value: 100%; }\n@property --tw-leading { syntax: "*"; inherits: false; }\n@property --tw-font-weight { syntax: "*"; inherits: false; }\n@property --tw-shadow { syntax: "*"; inherits: false; initial-value: 0 0 #0000; }\n@property --tw-shadow-color { syntax: "*"; inherits: false; }\n@property --tw-shadow-alpha { syntax: "<percentage>"; inherits: false; initial-value: 100%; }\n@property --tw-inset-shadow { syntax: "*"; inherits: false; initial-value: 0 0 #0000; }\n@property --tw-inset-shadow-color { syntax: "*"; inherits: false; }\n@property --tw-inset-shadow-alpha { syntax: "<percentage>"; inherits: false; initial-value: 100%; }\n@property --tw-ring-color { syntax: "*"; inherits: false; }\n@property --tw-ring-shadow { syntax: "*"; inherits: false; initial-value: 0 0 #0000; }\n@property --tw-inset-ring-color { syntax: "*"; inherits: false; }\n@property --tw-inset-ring-shadow { syntax: "*"; inherits: false; initial-value: 0 0 #0000; }\n@property --tw-ring-inset { syntax: "*"; inherits: false; }\n@property --tw-ring-offset-width { syntax: "<length>"; inherits: false; initial-value: 0px; }\n@property --tw-ring-offset-color { syntax: "*"; inherits: false; initial-value: #fff; }\n@property --tw-ring-offset-shadow { syntax: "*"; inherits: false; initial-value: 0 0 #0000; }\n@property --tw-blur { syntax: "*"; inherits: false; }\n@property --tw-brightness { syntax: "*"; inherits: false; }\n@property --tw-contrast { syntax: "*"; inherits: false; }\n@property --tw-grayscale { syntax: "*"; inherits: false; }\n@property --tw-hue-rotate { syntax: "*"; inherits: false; }\n@property --tw-invert { syntax: "*"; inherits: false; }\n@property --tw-opacity { syntax: "*"; inherits: false; }\n@property --tw-saturate { syntax: "*"; inherits: false; }\n@property --tw-sepia { syntax: "*"; inherits: false; }\n@property --tw-drop-shadow { syntax: "*"; inherits: false; }\n@property --tw-drop-shadow-color { syntax: "*"; inherits: false; }\n@property --tw-drop-shadow-alpha { syntax: "<percentage>"; inherits: false; initial-value: 100%; }\n@property --tw-drop-shadow-size { syntax: "*"; inherits: false; }\n@property --tw-duration { syntax: "*"; inherits: false; }\n@property --tw-scale-x { syntax: "*"; inherits: false; initial-value: 1; }\n@property --tw-scale-y { syntax: "*"; inherits: false; initial-value: 1; }\n@property --tw-scale-z { syntax: "*"; inherits: false; initial-value: 1; }\n@keyframes spin {\n to {\n transform: rotate(360deg);\n }\n}\n@keyframes tw-shimmer {\n from {\n background-position: 100% 0;\n }\n}\n@layer properties {\n @supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {\n :root,\n :host {\n --shimmer-track-height: 200px;\n --shimmer-angle: 15deg;\n }\n *,\n ::before,\n ::after,\n ::backdrop {\n --tw-space-y-reverse: 0;\n --tw-divide-y-reverse: 0;\n --tw-border-style: solid;\n --tw-gradient-position: initial;\n --tw-gradient-from: #0000;\n --tw-gradient-via: #0000;\n --tw-gradient-to: #0000;\n --tw-gradient-stops: initial;\n --tw-gradient-via-stops: initial;\n --tw-gradient-from-position: 0%;\n --tw-gradient-via-position: 50%;\n --tw-gradient-to-position: 100%;\n --tw-leading: initial;\n --tw-font-weight: initial;\n --tw-shadow: 0 0 #0000;\n --tw-shadow-color: initial;\n --tw-shadow-alpha: 100%;\n --tw-inset-shadow: 0 0 #0000;\n --tw-inset-shadow-color: initial;\n --tw-inset-shadow-alpha: 100%;\n --tw-ring-color: initial;\n --tw-ring-shadow: 0 0 #0000;\n --tw-inset-ring-color: initial;\n --tw-inset-ring-shadow: 0 0 #0000;\n --tw-ring-inset: initial;\n --tw-ring-offset-width: 0px;\n --tw-ring-offset-color: #fff;\n --tw-ring-offset-shadow: 0 0 #0000;\n --tw-blur: initial;\n --tw-brightness: initial;\n --tw-contrast: initial;\n --tw-grayscale: initial;\n --tw-hue-rotate: initial;\n --tw-invert: initial;\n --tw-opacity: initial;\n --tw-saturate: initial;\n --tw-sepia: initial;\n --tw-drop-shadow: initial;\n --tw-drop-shadow-color: initial;\n --tw-drop-shadow-alpha: 100%;\n --tw-drop-shadow-size: initial;\n --tw-duration: initial;\n --tw-scale-x: 1;\n --tw-scale-y: 1;\n --tw-scale-z: 1;\n }\n }\n}\n');
25
+
26
+ // src/components/ChatSearch.tsx
27
+ import {
28
+ useEffect as useEffect4,
29
+ useState as useState3
30
+ } from "react";
31
+
32
+ // src/lib/useNlWeb.ts
33
+ import { useState, useCallback, useEffect, useRef } from "react";
34
+
35
+ // src/lib/parseSchema.ts
36
+ import { z } from "zod";
37
+
38
+ // src/lib/util.ts
39
+ function abridgedCount(n) {
40
+ if (n < 1e3) {
41
+ return n.toString();
42
+ }
43
+ if (n < 1e6) {
44
+ const divided2 = n / 1e3;
45
+ const formatted2 = divided2 % 1 === 0 ? divided2.toString() : divided2.toFixed(1);
46
+ return formatted2 + "K";
47
+ }
48
+ if (n < 1e9) {
49
+ const divided2 = n / 1e6;
50
+ const formatted2 = divided2 % 1 === 0 ? divided2.toString() : divided2.toFixed(1);
51
+ return formatted2 + "M";
52
+ }
53
+ const divided = n / 1e9;
54
+ const formatted = divided % 1 === 0 ? divided.toString() : divided.toFixed(1);
55
+ return formatted + "B";
56
+ }
57
+ function filterTruthy(arr) {
58
+ return arr.filter(Boolean);
59
+ }
60
+ function intersperse(arr, sep) {
61
+ if (arr.length === 0) return [];
62
+ return arr.slice(1).reduce((acc, item) => acc.concat([sep, item]), [arr[0]]);
63
+ }
64
+ function shortQuantity(value) {
65
+ const parsed = tryParseNumber(value);
66
+ return typeof parsed === "number" && !isNaN(parsed) ? abridgedCount(parsed) : String(value);
67
+ }
68
+ function tryParseNumber(value) {
69
+ try {
70
+ return typeof value === "string" ? parseFloat(value) : value;
71
+ } catch {
72
+ return value;
73
+ }
74
+ }
75
+
76
+ // src/lib/parseSchema.ts
77
+ var ImageObjectSchema = z.object({
78
+ "@type": z.literal("ImageObject").optional(),
79
+ "@id": z.string(),
80
+ url: z.string().optional(),
81
+ contentUrl: z.string().optional()
82
+ }).passthrough();
83
+ var ImageSchema = z.union([
84
+ z.string(),
85
+ ImageObjectSchema,
86
+ z.array(z.union([z.string(), ImageObjectSchema]))
87
+ ]);
88
+ var PersonSchema = z.object({
89
+ "@type": z.literal("Person").optional(),
90
+ name: z.string(),
91
+ url: z.string().optional()
92
+ }).passthrough();
93
+ var OrganizationSchema = z.object({
94
+ "@type": z.literal("Organization").optional(),
95
+ name: z.string(),
96
+ url: z.string().optional()
97
+ }).passthrough();
98
+ var RatingSchema = z.object({
99
+ "@type": z.literal("Rating").optional(),
100
+ ratingValue: z.union([z.string(), z.number()]),
101
+ bestRating: z.union([z.string(), z.number()]).optional(),
102
+ worstRating: z.union([z.string(), z.number()]).optional()
103
+ }).passthrough();
104
+ var AggregateRatingSchema = z.object({
105
+ "@type": z.literal("AggregateRating").optional(),
106
+ ratingValue: z.union([z.string(), z.number()]),
107
+ bestRating: z.union([z.string(), z.number()]).optional(),
108
+ worstRating: z.union([z.string(), z.number()]).optional(),
109
+ ratingCount: z.union([z.string(), z.number()]).optional(),
110
+ reviewCount: z.union([z.string(), z.number()]).optional()
111
+ }).passthrough();
112
+ var ReviewSchema = z.object({
113
+ "@type": z.literal("Review").optional(),
114
+ datePublished: z.string().optional(),
115
+ reviewBody: z.string().optional(),
116
+ reviewRating: RatingSchema.optional(),
117
+ author: z.union([PersonSchema, OrganizationSchema, z.string()]).optional()
118
+ }).passthrough();
119
+ var VideoObjectSchema = z.object({
120
+ "@type": z.literal("VideoObject").optional(),
121
+ name: z.string().optional(),
122
+ description: z.string().optional(),
123
+ thumbnailUrl: z.string().optional(),
124
+ uploadDate: z.string().optional(),
125
+ contentUrl: z.string().optional(),
126
+ embedUrl: z.string().optional(),
127
+ duration: z.string().optional()
128
+ }).passthrough();
129
+ function resultTypeIs(result, type) {
130
+ if (Array.isArray(result["@type"])) {
131
+ return result["@type"].includes(type);
132
+ }
133
+ return result["@type"] == type;
134
+ }
135
+ function getThumbnailCandidates(result) {
136
+ function filter(candidates) {
137
+ return deduplicate(filterTruthy(candidates));
138
+ }
139
+ if (isArticleResult(result) && typeof result.thumbnailUrl === "string") {
140
+ const thumbnailUrl = result.thumbnailUrl ? result.thumbnailUrl.startsWith("http") ? result.thumbnailUrl : `https://${result.site}${result.thumbnailUrl}` : null;
141
+ return filter([thumbnailUrl, getImageUrl(result.image)]);
142
+ }
143
+ if (isMovieResult(result)) {
144
+ return filter([getImageUrl(result.image), result.trailer?.thumbnailUrl]);
145
+ }
146
+ return filter([getImageUrl(result.image)]);
147
+ }
148
+ function deduplicate(array) {
149
+ const seen = /* @__PURE__ */ new Set();
150
+ return array.filter((item) => {
151
+ if (seen.has(item)) {
152
+ return false;
153
+ }
154
+ seen.add(item);
155
+ return true;
156
+ });
157
+ }
158
+ function getImageUrl(image) {
159
+ if (!image) {
160
+ return null;
161
+ }
162
+ if (typeof image === "string") {
163
+ return image;
164
+ }
165
+ if (Array.isArray(image)) {
166
+ if (image.length === 0) {
167
+ return null;
168
+ }
169
+ const firstImage = image[0];
170
+ if (typeof firstImage === "string") {
171
+ return firstImage;
172
+ }
173
+ return firstImage.contentUrl || firstImage.url || null;
174
+ }
175
+ return image.contentUrl || image.url || image["@id"];
176
+ }
177
+ var RecipeSchema = z.object({
178
+ "@type": z.union([
179
+ z.literal("Recipe"),
180
+ z.array(z.string()).refine((arr) => arr.includes("Recipe"), {
181
+ message: "Array must contain 'Recipe'"
182
+ })
183
+ ]),
184
+ "@id": z.string(),
185
+ score: z.number(),
186
+ site: z.string(),
187
+ name: z.string().optional(),
188
+ recipeIngredient: z.union([z.array(z.string()), z.string()]).optional(),
189
+ recipeInstructions: z.any().optional(),
190
+ recipeYield: z.any().optional(),
191
+ cookTime: z.string().optional(),
192
+ prepTime: z.string().optional(),
193
+ totalTime: z.string().optional(),
194
+ description: z.string().optional(),
195
+ image: ImageSchema.optional()
196
+ }).passthrough();
197
+ var ArticleSchema = z.object({
198
+ "@type": z.union([
199
+ z.literal("Article"),
200
+ z.literal("NewsArticle"),
201
+ z.literal("BlogPosting"),
202
+ z.array(z.string()).refine(
203
+ (arr) => arr.includes("Article") || arr.includes("NewsArticle") || arr.includes("BlogPosting"),
204
+ {
205
+ message: "Array must contain 'Article', 'NewsArticle', or 'BlogPosting'"
206
+ }
207
+ )
208
+ ]),
209
+ "@id": z.string(),
210
+ score: z.number(),
211
+ site: z.string(),
212
+ headline: z.string().optional(),
213
+ description: z.string().optional(),
214
+ image: ImageSchema.optional(),
215
+ author: z.any().optional(),
216
+ publisher: z.any().optional(),
217
+ datePublished: z.string().optional(),
218
+ dateModified: z.string().optional(),
219
+ thumbnailUrl: z.string().optional()
220
+ }).passthrough();
221
+ function isArticleResult(result) {
222
+ return resultTypeIs(result, "Article") || resultTypeIs(result, "NewsArticle") || resultTypeIs(result, "BlogPosting");
223
+ }
224
+ var MovieSchema = z.object({
225
+ "@type": z.union([
226
+ z.literal("Movie"),
227
+ z.array(z.string()).refine((arr) => arr.includes("Movie"), {
228
+ message: "Array must contain 'Movie'"
229
+ })
230
+ ]),
231
+ "@id": z.string(),
232
+ score: z.number(),
233
+ site: z.string(),
234
+ name: z.string().optional(),
235
+ description: z.string().optional(),
236
+ image: ImageSchema.optional(),
237
+ url: z.string().optional(),
238
+ genre: z.array(z.string()).optional(),
239
+ datePublished: z.string().optional(),
240
+ director: z.union([PersonSchema, z.array(PersonSchema)]).optional(),
241
+ actor: z.union([PersonSchema, z.array(PersonSchema)]).optional(),
242
+ aggregateRating: AggregateRatingSchema.optional(),
243
+ duration: z.string().optional(),
244
+ contentRating: z.string().optional(),
245
+ productionCompany: z.union([OrganizationSchema, z.array(OrganizationSchema)]).optional(),
246
+ trailer: VideoObjectSchema.optional(),
247
+ review: z.union([ReviewSchema, z.array(ReviewSchema)]).optional(),
248
+ sameAs: z.array(z.string()).optional(),
249
+ // Additional schema.org/Movie properties
250
+ countryOfOrigin: z.any().optional(),
251
+ musicBy: z.union([PersonSchema, z.array(PersonSchema)]).optional(),
252
+ producer: z.union([
253
+ PersonSchema,
254
+ OrganizationSchema,
255
+ z.array(z.union([PersonSchema, OrganizationSchema]))
256
+ ]).optional()
257
+ }).passthrough();
258
+ function isMovieResult(result) {
259
+ return resultTypeIs(result, "Movie");
260
+ }
261
+ var SummarySchema = z.object({
262
+ "@type": z.literal("Summary"),
263
+ text: z.string()
264
+ }).passthrough();
265
+ function parseSchema(data) {
266
+ const recipeResult = RecipeSchema.safeParse(data);
267
+ if (recipeResult.success) {
268
+ return recipeResult.data;
269
+ }
270
+ const movieResult = MovieSchema.safeParse(data);
271
+ if (movieResult.success) {
272
+ return movieResult.data;
273
+ }
274
+ const articleResult = ArticleSchema.safeParse(data);
275
+ if (articleResult.success) {
276
+ return articleResult.data;
277
+ }
278
+ const summaryResult = SummarySchema.safeParse(data);
279
+ if (summaryResult.success) {
280
+ return summaryResult.data;
281
+ } else {
282
+ console.log("failed to parse", data);
283
+ }
284
+ return null;
285
+ }
286
+
287
+ // src/lib/useNlWeb.ts
288
+ function convertParamsToRequest(params, site, numRetrievalResults = 50, maxResults = 9) {
289
+ const v054Request = {
290
+ query: {
291
+ text: params.query,
292
+ site,
293
+ max_results: maxResults,
294
+ num_results: numRetrievalResults
295
+ },
296
+ prefer: {
297
+ streaming: true,
298
+ response_format: "conv_search",
299
+ mode: "list, summarize"
300
+ },
301
+ meta: {
302
+ api_version: "0.54",
303
+ start_num: params.resultOffset || 0
304
+ }
305
+ };
306
+ if (params.userId) {
307
+ v054Request.meta.user = {
308
+ id: params.userId
309
+ };
310
+ }
311
+ if (params.remember) {
312
+ v054Request.meta.remember = true;
313
+ }
314
+ if (params.conversationHistory && params.conversationHistory.length > 0) {
315
+ v054Request.context = {
316
+ "@type": "ConversationalContext",
317
+ prev: params.conversationHistory.slice(-5)
318
+ // Last 5 queries
319
+ };
320
+ }
321
+ return v054Request;
322
+ }
323
+ function useNlWeb(config) {
324
+ const { endpoint, site, maxResults = 9, numRetrievalResults = 50 } = config;
325
+ const [state, setState] = useState({
326
+ results: [],
327
+ query: null,
328
+ error: null,
329
+ rawLogs: [],
330
+ streamingIndex: -1,
331
+ loading: false
332
+ });
333
+ const abortControllerRef = useRef(null);
334
+ const sortResultsByScore = useCallback(
335
+ (results) => {
336
+ return [...results].sort((a, b) => {
337
+ const scoreA = a.score || 0;
338
+ const scoreB = b.score || 0;
339
+ return scoreB - scoreA;
340
+ });
341
+ },
342
+ []
343
+ );
344
+ const handleStreamingResponse = useCallback(
345
+ async (response) => {
346
+ const reader = response.body?.getReader();
347
+ if (!reader) {
348
+ throw new Error("Response body is not readable");
349
+ }
350
+ const decoder = new TextDecoder();
351
+ let buffer = "";
352
+ const accumulatedResults = [];
353
+ let summary = "";
354
+ let decontextualizedQuery = "";
355
+ const debugLogs = [];
356
+ try {
357
+ while (true) {
358
+ const { value, done } = await reader.read();
359
+ if (done) break;
360
+ buffer += decoder.decode(value, { stream: true });
361
+ const lines = buffer.split("\n");
362
+ buffer = lines.pop() || "";
363
+ for (const line of lines) {
364
+ if (!line.trim() || !line.startsWith("data: ")) continue;
365
+ try {
366
+ const dataStr = line.slice(6);
367
+ const data = JSON.parse(dataStr);
368
+ debugLogs.push(data);
369
+ if (data._meta) {
370
+ if (data._meta.response_type === "Failure" && data.error) {
371
+ const error = data.error;
372
+ throw new Error(`Error (${error.code}): ${error.message}`);
373
+ } else if (data._meta.decontextualized_query) {
374
+ decontextualizedQuery = data._meta.decontextualized_query;
375
+ setState((prev) => ({
376
+ ...prev,
377
+ rawLogs: debugLogs,
378
+ decontextualizedQuery
379
+ }));
380
+ }
381
+ }
382
+ if (data.results && Array.isArray(data.results)) {
383
+ data.results.forEach((result) => {
384
+ const parsed = parseSchema(result);
385
+ if (parsed) {
386
+ if (parsed["@type"] == "Summary") {
387
+ summary = parsed.text;
388
+ } else {
389
+ const exists = accumulatedResults.some(
390
+ (item) => item["@id"] == parsed["@id"]
391
+ );
392
+ if (!exists) {
393
+ accumulatedResults.push(parsed);
394
+ }
395
+ }
396
+ }
397
+ });
398
+ const sortedResults = sortResultsByScore(accumulatedResults);
399
+ setState((prev) => ({
400
+ ...prev,
401
+ rawLogs: debugLogs,
402
+ summary,
403
+ results: sortedResults,
404
+ decontextualizedQuery
405
+ }));
406
+ }
407
+ if (data.structuredData && Array.isArray(data.structuredData)) {
408
+ data.structuredData.forEach((result) => {
409
+ const exists = accumulatedResults.some(
410
+ (item) => item.url && item.url === result.url || item.name && item.name === result.name
411
+ );
412
+ if (!exists) {
413
+ accumulatedResults.push(result);
414
+ }
415
+ });
416
+ const sortedResults = sortResultsByScore(accumulatedResults);
417
+ setState((prev) => ({
418
+ ...prev,
419
+ rawLogs: debugLogs,
420
+ results: sortedResults,
421
+ decontextualizedQuery
422
+ }));
423
+ }
424
+ } catch (err) {
425
+ console.error("Error parsing SSE line:", err, "Line:", line);
426
+ }
427
+ }
428
+ }
429
+ return {
430
+ results: sortResultsByScore(accumulatedResults),
431
+ summary,
432
+ decontextualizedQuery,
433
+ rawLogs: debugLogs
434
+ };
435
+ } finally {
436
+ reader.releaseLock();
437
+ }
438
+ },
439
+ [sortResultsByScore]
440
+ );
441
+ const search = useCallback(
442
+ async (params) => {
443
+ if (abortControllerRef.current) {
444
+ abortControllerRef.current.abort();
445
+ }
446
+ const abortController = new AbortController();
447
+ abortControllerRef.current = abortController;
448
+ const query = params.query;
449
+ const streamingIndex = params.conversationHistory ? params.conversationHistory.length : 0;
450
+ setState({
451
+ results: [],
452
+ query,
453
+ error: null,
454
+ rawLogs: [],
455
+ streamingIndex,
456
+ resultOffset: params.resultOffset || 0,
457
+ loading: true
458
+ });
459
+ try {
460
+ const v054Request = convertParamsToRequest(
461
+ params,
462
+ site,
463
+ numRetrievalResults,
464
+ maxResults
465
+ );
466
+ const response = await fetch(endpoint, {
467
+ method: "POST",
468
+ headers: {
469
+ "Content-Type": "application/json",
470
+ Accept: "text/event-stream"
471
+ },
472
+ body: JSON.stringify(v054Request),
473
+ signal: abortController.signal
474
+ });
475
+ if (!response.ok) {
476
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
477
+ }
478
+ const results = await handleStreamingResponse(response);
479
+ setState({
480
+ results: results.results,
481
+ summary: results.summary,
482
+ decontextualizedQuery: results.decontextualizedQuery,
483
+ error: null,
484
+ query,
485
+ resultOffset: params.resultOffset,
486
+ streamingIndex,
487
+ rawLogs: results.rawLogs,
488
+ loading: false
489
+ });
490
+ return results;
491
+ } catch (error) {
492
+ if (error.name !== "AbortError") {
493
+ const errorMessage = error.message || "An error occurred during search";
494
+ setState({
495
+ results: [],
496
+ query: null,
497
+ error: errorMessage,
498
+ rawLogs: [],
499
+ streamingIndex,
500
+ loading: false
501
+ });
502
+ throw error;
503
+ }
504
+ return {
505
+ results: [],
506
+ rawLogs: []
507
+ };
508
+ }
509
+ },
510
+ [endpoint, site, maxResults, handleStreamingResponse]
511
+ );
512
+ const cancelSearch = useCallback(() => {
513
+ if (abortControllerRef.current) {
514
+ abortControllerRef.current.abort();
515
+ abortControllerRef.current = null;
516
+ setState((prev) => ({
517
+ ...prev,
518
+ query: null
519
+ }));
520
+ }
521
+ }, []);
522
+ const clearResults = useCallback(() => {
523
+ setState({
524
+ results: [],
525
+ query: null,
526
+ error: null,
527
+ streamingIndex: -1,
528
+ loading: false
529
+ });
530
+ }, []);
531
+ useEffect(() => {
532
+ clearResults();
533
+ }, [site]);
534
+ return {
535
+ ...state,
536
+ search,
537
+ cancelSearch,
538
+ clearResults
539
+ };
540
+ }
541
+
542
+ // src/components/ChatSearch.tsx
543
+ import { Dialog, DialogPanel, Button as Button2 } from "@headlessui/react";
544
+ import { XMarkIcon, NewspaperIcon } from "@heroicons/react/24/solid";
545
+ import { clsx as clsx2 } from "clsx";
546
+
547
+ // src/components/SearchQuery.tsx
548
+ import { useState as useState2, useEffect as useEffect2, useCallback as useCallback2, useRef as useRef2 } from "react";
549
+ import { Button } from "@headlessui/react";
550
+ import { MagnifyingGlassIcon, ArrowRightIcon } from "@heroicons/react/24/solid";
551
+ import { clsx } from "clsx";
552
+ import { LexicalComposer } from "@lexical/react/LexicalComposer";
553
+ import { PlainTextPlugin } from "@lexical/react/LexicalPlainTextPlugin";
554
+ import { ContentEditable } from "@lexical/react/LexicalContentEditable";
555
+ import { HistoryPlugin } from "@lexical/react/LexicalHistoryPlugin";
556
+ import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
557
+ import { useLexicalIsTextContentEmpty } from "@lexical/react/useLexicalIsTextContentEmpty";
558
+ import {
559
+ $getRoot,
560
+ COMMAND_PRIORITY_HIGH,
561
+ KEY_ENTER_COMMAND,
562
+ $createParagraphNode,
563
+ $createTextNode
564
+ } from "lexical";
565
+ import { jsx, jsxs } from "react/jsx-runtime";
566
+ function EnterKeyPlugin({
567
+ onSubmit,
568
+ disabled
569
+ }) {
570
+ const [editor] = useLexicalComposerContext();
571
+ useEffect2(() => {
572
+ return editor.registerCommand(
573
+ KEY_ENTER_COMMAND,
574
+ (event) => {
575
+ if (disabled) {
576
+ event.preventDefault();
577
+ return true;
578
+ }
579
+ if (event.shiftKey) {
580
+ return false;
581
+ } else {
582
+ event.preventDefault();
583
+ onSubmit();
584
+ return true;
585
+ }
586
+ },
587
+ COMMAND_PRIORITY_HIGH
588
+ );
589
+ }, [editor, onSubmit, disabled]);
590
+ return null;
591
+ }
592
+ function InitialValuePlugin({ initQuery }) {
593
+ const [editor] = useLexicalComposerContext();
594
+ useEffect2(() => {
595
+ if (initQuery) {
596
+ editor.update(() => {
597
+ const root = $getRoot();
598
+ root.clear();
599
+ const paragraph = $createParagraphNode();
600
+ paragraph.append($createTextNode(initQuery));
601
+ root.append(paragraph);
602
+ });
603
+ }
604
+ }, [editor, initQuery]);
605
+ return null;
606
+ }
607
+ function FocusPlugin({
608
+ onFocusChange
609
+ }) {
610
+ const [editor] = useLexicalComposerContext();
611
+ useEffect2(() => {
612
+ return editor.registerRootListener((rootElement, prevRootElement) => {
613
+ if (prevRootElement !== null) {
614
+ prevRootElement.removeEventListener("focus", () => onFocusChange(true));
615
+ prevRootElement.removeEventListener("blur", () => onFocusChange(false));
616
+ }
617
+ if (rootElement !== null) {
618
+ rootElement.addEventListener("focus", () => onFocusChange(true));
619
+ rootElement.addEventListener("blur", () => onFocusChange(false));
620
+ }
621
+ });
622
+ }, [editor, onFocusChange]);
623
+ return null;
624
+ }
625
+ function EditorStatePlugin({
626
+ editorRef,
627
+ onEmptyChange,
628
+ onMultilineChange
629
+ }) {
630
+ const [editor] = useLexicalComposerContext();
631
+ const isEmpty = useLexicalIsTextContentEmpty(editor);
632
+ const initialHeightRef = useRef2(null);
633
+ const isMultilineRef = useRef2(false);
634
+ useEffect2(() => {
635
+ editorRef.current = editor;
636
+ return () => {
637
+ editorRef.current = null;
638
+ };
639
+ }, [editor, editorRef]);
640
+ useEffect2(() => {
641
+ onEmptyChange(isEmpty);
642
+ if (isEmpty && isMultilineRef.current) {
643
+ isMultilineRef.current = false;
644
+ onMultilineChange(false);
645
+ }
646
+ }, [isEmpty, onEmptyChange, onMultilineChange]);
647
+ useEffect2(() => {
648
+ const rootElement = editor.getRootElement();
649
+ if (!rootElement) return;
650
+ if (initialHeightRef.current === null) {
651
+ initialHeightRef.current = rootElement.offsetHeight;
652
+ }
653
+ const resizeObserver = new ResizeObserver((entries) => {
654
+ for (const entry of entries) {
655
+ const currentHeight = entry.contentRect.height;
656
+ if (initialHeightRef.current !== null && currentHeight >= initialHeightRef.current) {
657
+ if (!isMultilineRef.current) {
658
+ isMultilineRef.current = true;
659
+ onMultilineChange(true);
660
+ }
661
+ }
662
+ }
663
+ });
664
+ resizeObserver.observe(rootElement);
665
+ return () => {
666
+ resizeObserver.disconnect();
667
+ };
668
+ }, [editor, onMultilineChange]);
669
+ return null;
670
+ }
671
+ function SearchQuery({
672
+ initQuery,
673
+ className,
674
+ inputClassName,
675
+ loading,
676
+ handleSearch,
677
+ placeholder = "Ask anything..."
678
+ }) {
679
+ const [isFocused, setIsFocused] = useState2(false);
680
+ const [isMultiline, setIsMultiline] = useState2(false);
681
+ const [hasText, setHasText] = useState2(!!initQuery);
682
+ const editorRef = useRef2(null);
683
+ const initialConfig = {
684
+ namespace: "SearchQuery",
685
+ theme: {
686
+ paragraph: "editor-paragraph",
687
+ text: {
688
+ bold: "editor-text-bold",
689
+ italic: "editor-text-italic",
690
+ underline: "editor-text-underline"
691
+ }
692
+ },
693
+ onError: (error) => {
694
+ console.error(error);
695
+ }
696
+ };
697
+ const handleSubmit = useCallback2(async () => {
698
+ if (!editorRef.current || loading) return;
699
+ const query = editorRef.current.getEditorState().read(() => {
700
+ const root = $getRoot();
701
+ return root.getTextContent();
702
+ });
703
+ if (query.trim()) {
704
+ await handleSearch(query);
705
+ }
706
+ }, [loading, handleSearch]);
707
+ const handleEmptyChange = useCallback2((isEmpty) => {
708
+ setHasText(!isEmpty);
709
+ }, []);
710
+ const handleMultilineChange = useCallback2((isMultiline2) => {
711
+ setIsMultiline(isMultiline2);
712
+ }, []);
713
+ return /* @__PURE__ */ jsx(LexicalComposer, { initialConfig, children: /* @__PURE__ */ jsxs(
714
+ "div",
715
+ {
716
+ className: clsx(
717
+ `flex relative rounded-lg border transition-all duration-200`,
718
+ isFocused ? "border-gray-400 shadow-md ring-2 ring-gray-100" : "border-gray-200 hover:border-gray-300",
719
+ isMultiline ? "flex-col items-stretch gap-0" : "flex-row items-center gap-3",
720
+ className || "bg-white"
721
+ ),
722
+ children: [
723
+ /* @__PURE__ */ jsx(
724
+ "div",
725
+ {
726
+ className: clsx(
727
+ "absolute left-3.5 pointer-events-none z-10",
728
+ isMultiline ? "hidden" : ""
729
+ ),
730
+ children: /* @__PURE__ */ jsx(MagnifyingGlassIcon, { className: "size-4 text-gray-400" })
731
+ }
732
+ ),
733
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 relative", children: [
734
+ /* @__PURE__ */ jsx(
735
+ PlainTextPlugin,
736
+ {
737
+ contentEditable: /* @__PURE__ */ jsx(
738
+ ContentEditable,
739
+ {
740
+ className: clsx(
741
+ "outline-none flex-1 m-0 rounded-md text-base px-4 py-3 text-gray-900",
742
+ isMultiline ? "pl-4" : "pl-10 pr-12",
743
+ inputClassName,
744
+ loading && "opacity-50 cursor-not-allowed"
745
+ ),
746
+ "aria-placeholder": placeholder,
747
+ placeholder: /* @__PURE__ */ jsx(
748
+ "div",
749
+ {
750
+ className: clsx(
751
+ "absolute top-3 text-base text-gray-400 pointer-events-none select-none",
752
+ isMultiline ? "left-4" : "left-10 truncate right-10"
753
+ ),
754
+ children: placeholder
755
+ }
756
+ )
757
+ }
758
+ ),
759
+ ErrorBoundary: () => null
760
+ }
761
+ ),
762
+ /* @__PURE__ */ jsx(HistoryPlugin, {}),
763
+ /* @__PURE__ */ jsx(EnterKeyPlugin, { onSubmit: handleSubmit, disabled: loading }),
764
+ /* @__PURE__ */ jsx(InitialValuePlugin, { initQuery }),
765
+ /* @__PURE__ */ jsx(FocusPlugin, { onFocusChange: setIsFocused }),
766
+ /* @__PURE__ */ jsx(
767
+ EditorStatePlugin,
768
+ {
769
+ editorRef,
770
+ onEmptyChange: handleEmptyChange,
771
+ onMultilineChange: handleMultilineChange
772
+ }
773
+ )
774
+ ] }),
775
+ /* @__PURE__ */ jsx(
776
+ "div",
777
+ {
778
+ className: clsx(
779
+ isMultiline ? "relative flex justify-end p-2" : "absolute right-2 "
780
+ ),
781
+ children: /* @__PURE__ */ jsx(
782
+ Button,
783
+ {
784
+ type: "button",
785
+ onClick: handleSubmit,
786
+ disabled: !hasText || loading,
787
+ className: `p-2 rounded-md transition-all duration-200 ${hasText && !loading ? "bg-black text-white hover:bg-gray-800 active:scale-95" : "bg-gray-100 text-gray-400 cursor-not-allowed"}`,
788
+ "aria-label": "Submit search",
789
+ children: loading ? /* @__PURE__ */ jsxs(
790
+ "svg",
791
+ {
792
+ className: "w-4 h-4 animate-spin",
793
+ fill: "none",
794
+ viewBox: "0 0 24 24",
795
+ children: [
796
+ /* @__PURE__ */ jsx(
797
+ "circle",
798
+ {
799
+ className: "opacity-25",
800
+ cx: "12",
801
+ cy: "12",
802
+ r: "10",
803
+ stroke: "currentColor",
804
+ strokeWidth: "4"
805
+ }
806
+ ),
807
+ /* @__PURE__ */ jsx(
808
+ "path",
809
+ {
810
+ className: "opacity-75",
811
+ fill: "currentColor",
812
+ 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"
813
+ }
814
+ )
815
+ ]
816
+ }
817
+ ) : /* @__PURE__ */ jsx(ArrowRightIcon, { className: "size-4" })
818
+ }
819
+ )
820
+ }
821
+ )
822
+ ]
823
+ }
824
+ ) });
825
+ }
826
+
827
+ // src/lib/useAutoScroll.ts
828
+ import { useEffect as useEffect3, useRef as useRef3, useCallback as useCallback3 } from "react";
829
+ var useAutoScroll = (dependency) => {
830
+ const containerRef = useRef3(null);
831
+ const anchorRef = useRef3(null);
832
+ const shouldScrollRef = useRef3(true);
833
+ const scrollToAnchor = useCallback3(() => {
834
+ if (anchorRef.current) {
835
+ anchorRef.current.scrollIntoView({
836
+ behavior: "smooth",
837
+ block: "center"
838
+ });
839
+ }
840
+ }, []);
841
+ useEffect3(() => {
842
+ if (shouldScrollRef.current) {
843
+ scrollToAnchor();
844
+ }
845
+ }, dependency);
846
+ return { containerRef, anchorRef };
847
+ };
848
+
849
+ // src/components/ChatSearch.tsx
850
+ import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
851
+ function decodeHtmlEntities(text) {
852
+ return text.replace(
853
+ /&#x([0-9a-fA-F]+);/g,
854
+ (_, hex) => String.fromCharCode(parseInt(hex, 16))
855
+ ).replace(/&#(\d+);/g, (_, dec) => String.fromCharCode(parseInt(dec, 10))).replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&apos;/g, "'").replace(/&nbsp;/g, " ");
856
+ }
857
+ function ResultCardSkeleton() {
858
+ return /* @__PURE__ */ jsx2("div", { className: "block w-full transition-all overflow-hidden shimmer-container", children: /* @__PURE__ */ jsxs2("div", { className: "flex flex-col gap-3", children: [
859
+ /* @__PURE__ */ jsx2("div", { className: "h-36 w-full shimmer shimmer-bg shimmer-color-gray-300/30 [--shimmer-x:60] [--shimmer-y:0] bg-gray-100 rounded" }),
860
+ /* @__PURE__ */ jsxs2("div", { className: "flex-1 min-w-0 space-y-2", children: [
861
+ /* @__PURE__ */ jsx2("div", { className: "h-4 bg-gray-100 rounded w-3/4" }),
862
+ /* @__PURE__ */ jsxs2("div", { className: "space-y-1", children: [
863
+ /* @__PURE__ */ jsx2("div", { className: "h-3 bg-gray-100 rounded w-full" }),
864
+ /* @__PURE__ */ jsx2("div", { className: "h-3 bg-gray-100 rounded w-5/6" }),
865
+ /* @__PURE__ */ jsx2("div", { className: "h-3 bg-gray-100 rounded w-5/6" })
866
+ ] }),
867
+ /* @__PURE__ */ jsx2("div", { className: "h-2 bg-gray-100 rounded w-1/6" })
868
+ ] })
869
+ ] }) });
870
+ }
871
+ function ResultCard({ result }) {
872
+ const srcs = getThumbnailCandidates(result);
873
+ return /* @__PURE__ */ jsx2(
874
+ "a",
875
+ {
876
+ href: result.url || result.grounding || "#",
877
+ target: "_blank",
878
+ rel: "noopener noreferrer",
879
+ className: "block no-underline! transition-all duration-200 overflow-hidden",
880
+ children: /* @__PURE__ */ jsxs2("div", { className: "flex flex-col gap-3", children: [
881
+ /* @__PURE__ */ jsx2(
882
+ Thumbnail,
883
+ {
884
+ srcs,
885
+ alt: decodeHtmlEntities(
886
+ result.name || result.title || "Result image"
887
+ )
888
+ }
889
+ ),
890
+ /* @__PURE__ */ jsxs2("div", { className: "flex-1 min-w-0", children: [
891
+ /* @__PURE__ */ jsx2("h3", { className: "font-medium text-gray-900 text-sm mb-1 line-clamp-2", children: decodeHtmlEntities(
892
+ result.name || result.title || "Untitled"
893
+ ) }),
894
+ /* @__PURE__ */ jsx2(ResultCardDetails, { result }),
895
+ result.description && /* @__PURE__ */ jsx2("p", { className: "text-xs text-gray-600 line-clamp-3 mb-2", children: typeof result.description == "string" ? decodeHtmlEntities(result.description) : "" }),
896
+ result.site && /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-1 text-xs text-gray-500", children: [
897
+ /* @__PURE__ */ jsx2(
898
+ "svg",
899
+ {
900
+ className: "w-3 h-3",
901
+ fill: "none",
902
+ stroke: "currentColor",
903
+ viewBox: "0 0 24 24",
904
+ children: /* @__PURE__ */ jsx2(
905
+ "path",
906
+ {
907
+ strokeLinecap: "round",
908
+ strokeLinejoin: "round",
909
+ strokeWidth: 2,
910
+ d: "M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9a9 9 0 01-9-9m9 9c1.657 0 3-4.03 3-9s-1.343-9-3-9m0 18c-1.657 0-3-4.03-3-9s1.343-9 3-9m-9 9a9 9 0 019-9"
911
+ }
912
+ )
913
+ }
914
+ ),
915
+ /* @__PURE__ */ jsx2("span", { className: "truncate", children: result.site })
916
+ ] })
917
+ ] })
918
+ ] })
919
+ }
920
+ );
921
+ }
922
+ function ResultCardDetails({ result }) {
923
+ if (isMovieResult(result)) {
924
+ return /* @__PURE__ */ jsxs2(Fragment, { children: [
925
+ result.director && /* @__PURE__ */ jsx2("div", { className: "text-xs text-gray-500 font-semibold", children: Array.isArray(result.director) ? result.director.map((d) => d.name).join(", ") : result.director.name }),
926
+ /* @__PURE__ */ jsx2("div", { className: "flex items-center gap-1", children: intersperse(
927
+ [
928
+ result.aggregateRating?.ratingValue ? /* @__PURE__ */ jsxs2("span", { className: "flex items-center gap-1", children: [
929
+ /* @__PURE__ */ jsx2("span", { className: "text-yellow-500", children: "\u2605" }),
930
+ /* @__PURE__ */ jsx2("span", { className: "text-xs", children: typeof result.aggregateRating.ratingValue === "number" ? result.aggregateRating.ratingValue.toFixed(1) : result.aggregateRating.ratingValue })
931
+ ] }) : null,
932
+ result.aggregateRating?.ratingCount ? /* @__PURE__ */ jsxs2("span", { className: "text-xs text-gray-500", children: [
933
+ shortQuantity(result.aggregateRating.ratingCount),
934
+ " review",
935
+ result.aggregateRating.ratingCount != 1 ? "s" : ""
936
+ ] }) : null
937
+ ].filter(Boolean),
938
+ /* @__PURE__ */ jsx2("span", { className: "text-xs text-gray-400", children: "|" })
939
+ ) })
940
+ ] });
941
+ }
942
+ return null;
943
+ }
944
+ function Thumbnail({
945
+ srcs,
946
+ className,
947
+ ...rest
948
+ }) {
949
+ const [srcIndex, setSrcIndex] = useState3(0);
950
+ function advance() {
951
+ setSrcIndex((srcIndex2) => {
952
+ const nextIndex = srcIndex2 + 1;
953
+ return Math.min(nextIndex, srcs.length);
954
+ });
955
+ }
956
+ const srcsExhausted = srcIndex >= srcs.length;
957
+ return /* @__PURE__ */ jsxs2(
958
+ "div",
959
+ {
960
+ className: clsx2(
961
+ "relative text-xs flex-shrink-0 h-36 rounded",
962
+ srcsExhausted && "bg-gray-100 flex items-center justify-center",
963
+ className
964
+ ),
965
+ children: [
966
+ srcsExhausted ? /* @__PURE__ */ jsx2(NewspaperIcon, { className: "absolute size-5 text-gray-400" }) : null,
967
+ /* @__PURE__ */ jsx2(
968
+ "img",
969
+ {
970
+ src: srcs[srcIndex],
971
+ className: clsx2(
972
+ "w-full! h-full! object-cover! rounded!",
973
+ srcsExhausted && "invisible"
974
+ ),
975
+ ...rest,
976
+ onError: advance
977
+ }
978
+ )
979
+ ]
980
+ }
981
+ );
982
+ }
983
+ function SummarySkeleton() {
984
+ return /* @__PURE__ */ jsxs2("div", { className: "space-y-3 w-full md:min-w-md shimmer-container", children: [
985
+ /* @__PURE__ */ jsx2("div", { className: "h-4 shimmer shimmer-bg shimmer-color-gray-400/20 [--shimmer-x:60] [--shimmer-y:0] bg-gray-200 rounded-md w-full" }),
986
+ /* @__PURE__ */ jsx2("div", { className: "h-4 shimmer shimmer-bg shimmer-color-gray-400/20 [--shimmer-x:60] [--shimmer-y:20] bg-gray-200 rounded-md w-11/12" }),
987
+ /* @__PURE__ */ jsx2("div", { className: "h-4 shimmer shimmer-bg shimmer-color-gray-400/20 [--shimmer-x:60] [--shimmer-y:40] bg-gray-200 rounded-md w-full" }),
988
+ /* @__PURE__ */ jsx2("div", { className: "h-4 shimmer shimmer-bg shimmer-color-gray-400/20 [--shimmer-x:60] [--shimmer-y:60] bg-gray-200 rounded-md w-10/12" }),
989
+ /* @__PURE__ */ jsx2("div", { className: "h-4 shimmer shimmer-bg shimmer-color-gray-400/20 [--shimmer-x:60] [--shimmer-y:80] bg-gray-200 rounded-md w-full" })
990
+ ] });
991
+ }
992
+ function SummaryCard({ summary }) {
993
+ if (summary) {
994
+ return /* @__PURE__ */ jsx2("div", { className: "text-lg text-gray-800 leading-relaxed", children: summary });
995
+ }
996
+ return /* @__PURE__ */ jsx2(SummarySkeleton, {});
997
+ }
998
+ function SearchingFor({
999
+ query,
1000
+ streaming
1001
+ }) {
1002
+ return /* @__PURE__ */ jsx2(
1003
+ "div",
1004
+ {
1005
+ className: clsx2(
1006
+ "text-gray-500 gap-1 flex items-center text-sm pb-2 px-2",
1007
+ streaming && "shimmer"
1008
+ ),
1009
+ children: query ? /* @__PURE__ */ jsxs2("span", { className: "text-gray-800 overflow-ellipse", children: [
1010
+ "Searching for: ",
1011
+ query
1012
+ ] }, query) : "Working on it"
1013
+ }
1014
+ );
1015
+ }
1016
+ function PageButton({
1017
+ page,
1018
+ onClick,
1019
+ activePage,
1020
+ disabled = true
1021
+ }) {
1022
+ return /* @__PURE__ */ jsx2(
1023
+ Button2,
1024
+ {
1025
+ disabled,
1026
+ onClick,
1027
+ className: clsx2(
1028
+ "text-sm p-2 size-6 rounded-md transition-colors duration-200 hover:bg-gray-100 flex items-center justify-center",
1029
+ page == activePage ? "text-gray-700" : "text-gray-400",
1030
+ disabled ? "opacity-50 pointer-events-none" : ""
1031
+ ),
1032
+ children: page + 1
1033
+ }
1034
+ );
1035
+ }
1036
+ function AssistantMessage({
1037
+ summary,
1038
+ results,
1039
+ loading,
1040
+ addResults,
1041
+ followUpQuery,
1042
+ config,
1043
+ anchorRef
1044
+ }) {
1045
+ const nlweb = useNlWeb(config);
1046
+ const maxResults = config.maxResults || 50;
1047
+ const numRetrievalResults = config.numRetrievalResults || 50;
1048
+ const pagesAvailable = Math.floor(numRetrievalResults / maxResults);
1049
+ const [page, setPage] = useState3(0);
1050
+ const pageRange = Array.from(
1051
+ { length: pagesAvailable + 1 },
1052
+ (_, index) => index
1053
+ );
1054
+ async function viewMoreResults(pageNumber) {
1055
+ setPage(pageNumber);
1056
+ const offsetToFetch = pageNumber * maxResults;
1057
+ if (results.length <= offsetToFetch) {
1058
+ const response = await nlweb.search({
1059
+ query: followUpQuery,
1060
+ conversationHistory: [],
1061
+ resultOffset: offsetToFetch
1062
+ });
1063
+ await addResults(response.results);
1064
+ }
1065
+ }
1066
+ const pageOffset = page * maxResults;
1067
+ const isStreamingPage = pageOffset == nlweb.resultOffset;
1068
+ const resultsOfPage = isStreamingPage ? nlweb.results : results.slice(pageOffset, (page + 1) * maxResults);
1069
+ const skeletonCount = Math.max(0, 9 - resultsOfPage.length);
1070
+ return /* @__PURE__ */ jsx2("div", { className: "flex justify-start", children: /* @__PURE__ */ jsxs2("div", { className: "max-w-3xl flex-1 bg-gray-50 p-6 rounded-lg", children: [
1071
+ /* @__PURE__ */ jsxs2("div", { className: "space-y-4 mb-2", children: [
1072
+ /* @__PURE__ */ jsx2(SummaryCard, { summary }),
1073
+ /* @__PURE__ */ jsx2("div", { ref: anchorRef }),
1074
+ /* @__PURE__ */ jsxs2("div", { className: "grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-6", children: [
1075
+ resultsOfPage.map((r, idx) => /* @__PURE__ */ jsx2(ResultCard, { result: r }, r.url || r.name || idx)),
1076
+ (loading || nlweb.loading && isStreamingPage) && Array.from({ length: skeletonCount }).map((_, idx) => /* @__PURE__ */ jsx2(ResultCardSkeleton, {}, `skeleton-${idx}`))
1077
+ ] })
1078
+ ] }),
1079
+ /* @__PURE__ */ jsx2("div", { className: "flex mt-4 -mx-2 items-center gap-2", children: pageRange.map(
1080
+ (p) => results.length >= p * maxResults && (p + 1) * maxResults < numRetrievalResults && /* @__PURE__ */ jsx2(
1081
+ PageButton,
1082
+ {
1083
+ onClick: () => viewMoreResults(p),
1084
+ page: p,
1085
+ activePage: page,
1086
+ disabled: loading
1087
+ },
1088
+ p
1089
+ )
1090
+ ) })
1091
+ ] }) });
1092
+ }
1093
+ function QueryMessage({ query }) {
1094
+ return /* @__PURE__ */ jsx2("div", { className: "flex justify-end mb-6", children: /* @__PURE__ */ jsx2("div", { className: "max-w-2xl", children: /* @__PURE__ */ jsx2("div", { className: "bg-gray-900 text-white rounded-2xl rounded-tr-sm px-4 py-3 shadow-sm", children: /* @__PURE__ */ jsx2("p", { className: "text-sm leading-relaxed whitespace-pre-line", children: query }) }) }) });
1095
+ }
1096
+ function ChatEntry({
1097
+ index,
1098
+ query,
1099
+ loading,
1100
+ decontextualizedQuery,
1101
+ summary,
1102
+ results,
1103
+ config,
1104
+ addResults,
1105
+ anchorRef
1106
+ }) {
1107
+ return /* @__PURE__ */ jsxs2("div", { children: [
1108
+ index > 0 ? /* @__PURE__ */ jsx2(QueryMessage, { query }) : null,
1109
+ index > 0 ? /* @__PURE__ */ jsx2(SearchingFor, { streaming: loading, query: decontextualizedQuery }) : null,
1110
+ results.length > 0 || loading ? /* @__PURE__ */ jsx2(
1111
+ AssistantMessage,
1112
+ {
1113
+ anchorRef,
1114
+ summary,
1115
+ results,
1116
+ loading,
1117
+ followUpQuery: decontextualizedQuery || query,
1118
+ addResults,
1119
+ config
1120
+ }
1121
+ ) : /* @__PURE__ */ jsx2("div", { className: "flex max-w-3xl text-base text-gray-500 justify-start bg-gray-50 p-6 rounded-lg", children: "No results found" })
1122
+ ] }, `${query}-${index}`);
1123
+ }
1124
+ function ChatResults({
1125
+ nlweb,
1126
+ config,
1127
+ searches,
1128
+ addResults,
1129
+ anchorRef
1130
+ }) {
1131
+ const combinedStreamedResults = searches.length - 1 == nlweb.streamingIndex && searches[nlweb.streamingIndex]?.response ? [
1132
+ ...nlweb.results,
1133
+ ...searches[nlweb.streamingIndex].response.results.slice(
1134
+ nlweb.results.length
1135
+ )
1136
+ ] : nlweb.results;
1137
+ return /* @__PURE__ */ jsxs2("div", { className: "space-y-4 py-6", children: [
1138
+ searches.map(
1139
+ (r, idx) => idx != nlweb.streamingIndex && /* @__PURE__ */ jsx2(
1140
+ ChatEntry,
1141
+ {
1142
+ loading: false,
1143
+ index: idx,
1144
+ query: r.query,
1145
+ results: r.response.results,
1146
+ summary: r.response.summary,
1147
+ decontextualizedQuery: r.response.decontextualizedQuery,
1148
+ config,
1149
+ addResults: (results) => addResults(r.id || "", results)
1150
+ },
1151
+ `${r.query}-${idx}`
1152
+ )
1153
+ ),
1154
+ nlweb.query && /* @__PURE__ */ jsx2(
1155
+ ChatEntry,
1156
+ {
1157
+ index: nlweb.streamingIndex,
1158
+ query: nlweb.query,
1159
+ results: combinedStreamedResults,
1160
+ summary: nlweb.summary,
1161
+ decontextualizedQuery: nlweb.decontextualizedQuery,
1162
+ loading: nlweb.loading,
1163
+ anchorRef,
1164
+ config,
1165
+ addResults: (r) => addResults(searches[nlweb.streamingIndex].id || "", r)
1166
+ },
1167
+ `${nlweb.query}-${nlweb.streamingIndex}`
1168
+ )
1169
+ ] });
1170
+ }
1171
+ function ChatSearch({
1172
+ searches,
1173
+ addSearch,
1174
+ addResults,
1175
+ startSession,
1176
+ endSession,
1177
+ nlweb,
1178
+ config,
1179
+ children,
1180
+ sidebar,
1181
+ sessionId = "NLWEB_DEFAULT_SESSION"
1182
+ }) {
1183
+ const { anchorRef } = useAutoScroll([nlweb.query]);
1184
+ const [searchOpen, setSearchOpen] = useState3(searches.length > 0);
1185
+ function closeSearch() {
1186
+ setSearchOpen(false);
1187
+ if (endSession) {
1188
+ endSession();
1189
+ }
1190
+ }
1191
+ async function handleSearch(query, isRoot) {
1192
+ let response;
1193
+ let sId = sessionId;
1194
+ if (isRoot) {
1195
+ sId = await startSession(query) || sessionId;
1196
+ setSearchOpen(true);
1197
+ response = await nlweb.search({
1198
+ query
1199
+ });
1200
+ } else {
1201
+ response = await nlweb.search({
1202
+ query,
1203
+ conversationHistory: searches.map((r) => r.query)
1204
+ });
1205
+ }
1206
+ if (sId) {
1207
+ const search = { query, response, sessionId: sId };
1208
+ await addSearch(search);
1209
+ }
1210
+ }
1211
+ useEffect4(() => {
1212
+ if (searches.length > 0 && !searchOpen) {
1213
+ setSearchOpen(true);
1214
+ }
1215
+ }, [searches]);
1216
+ const rootQuery = searches.length > 0 ? searches[0].query : nlweb.query;
1217
+ return /* @__PURE__ */ jsxs2("div", { children: [
1218
+ /* @__PURE__ */ jsx2("div", { className: "mb-6 min-w-50 max-h-12 relative z-30", children: /* @__PURE__ */ jsx2(
1219
+ SearchQuery,
1220
+ {
1221
+ loading: nlweb.loading,
1222
+ handleSearch: (q) => handleSearch(q, true)
1223
+ }
1224
+ ) }),
1225
+ /* @__PURE__ */ jsx2(
1226
+ Dialog,
1227
+ {
1228
+ className: "relative z-50",
1229
+ open: searchOpen,
1230
+ onClose: closeSearch,
1231
+ children: /* @__PURE__ */ jsxs2("div", { className: "fixed bg-white inset-0 w-screen h-screen overflow-hidden", children: [
1232
+ /* @__PURE__ */ jsx2(
1233
+ Button2,
1234
+ {
1235
+ onClick: closeSearch,
1236
+ className: "z-50 text-gray-500 hover:text-black absolute right-6 top-4",
1237
+ children: /* @__PURE__ */ jsx2(XMarkIcon, { className: "size-5" })
1238
+ }
1239
+ ),
1240
+ /* @__PURE__ */ jsxs2(
1241
+ DialogPanel,
1242
+ {
1243
+ className: "w-full h-screen flex items-stretch overflow-hidden",
1244
+ children: [
1245
+ sidebar,
1246
+ /* @__PURE__ */ jsxs2("div", { className: "flex-1 flex flex-col overflow-hidden", children: [
1247
+ children,
1248
+ /* @__PURE__ */ jsxs2("div", { className: "relative flex-1 overflow-hidden flex flex-col", children: [
1249
+ /* @__PURE__ */ jsx2("div", { className: "flex-1 overflow-y-auto p-4 pt-16 pb-24", children: /* @__PURE__ */ jsxs2("div", { className: "max-w-7xl mx-auto", children: [
1250
+ /* @__PURE__ */ jsx2("div", { className: "mb-6 max-w-xl mx-auto", children: /* @__PURE__ */ jsx2(
1251
+ SearchQuery,
1252
+ {
1253
+ className: "bg-gray-50",
1254
+ loading: nlweb.loading,
1255
+ handleSearch: (q) => handleSearch(q, true),
1256
+ initQuery: rootQuery
1257
+ },
1258
+ rootQuery || "empty-search"
1259
+ ) }),
1260
+ /* @__PURE__ */ jsx2(
1261
+ ChatResults,
1262
+ {
1263
+ nlweb,
1264
+ searches,
1265
+ addResults,
1266
+ config,
1267
+ anchorRef
1268
+ }
1269
+ )
1270
+ ] }) }),
1271
+ /* @__PURE__ */ jsx2("div", { className: "absolute pointer-events-none bottom-0 top-[calc(100%_-_100px)] bg-gradient-to-b from-transparent to-white left-0 right-0" }),
1272
+ /* @__PURE__ */ jsx2("div", { className: "absolute bottom-8 left-4 right-4", children: /* @__PURE__ */ jsx2("div", { className: "max-w-xl mx-auto", children: /* @__PURE__ */ jsx2(
1273
+ SearchQuery,
1274
+ {
1275
+ loading: nlweb.loading,
1276
+ handleSearch: (q) => handleSearch(q, false),
1277
+ inputClassName: "max-h-[60vh] overflow-y-auto",
1278
+ className: "shadow-xl bg-white",
1279
+ placeholder: "Enter a follow up query"
1280
+ },
1281
+ nlweb.query
1282
+ ) }) })
1283
+ ] })
1284
+ ] })
1285
+ ]
1286
+ }
1287
+ )
1288
+ ] })
1289
+ }
1290
+ )
1291
+ ] });
1292
+ }
1293
+
1294
+ // src/components/HistorySidebar.tsx
1295
+ import { Button as Button3 } from "@headlessui/react";
1296
+ import {
1297
+ XMarkIcon as XMarkIcon2,
1298
+ PencilSquareIcon,
1299
+ Bars3Icon
1300
+ } from "@heroicons/react/24/solid";
1301
+ import { Transition } from "@headlessui/react";
1302
+ import { useState as useState4 } from "react";
1303
+ import { clsx as clsx3 } from "clsx";
1304
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
1305
+ function SiteBadge({ site }) {
1306
+ const siteParts = site.split(".");
1307
+ return /* @__PURE__ */ jsx3("div", { className: "px-3 py-2 flex gap-2 items-center text-xs font-medium text-gray-400 uppercase", children: siteParts[0] });
1308
+ }
1309
+ function SessionButton({
1310
+ session,
1311
+ onSelect,
1312
+ onDelete,
1313
+ selected = false
1314
+ }) {
1315
+ return /* @__PURE__ */ jsxs3(
1316
+ "div",
1317
+ {
1318
+ className: clsx3(
1319
+ "flex group hover:bg-gray-100 rounded-md overflow-hidden",
1320
+ selected ? "bg-gray-200/50" : ""
1321
+ ),
1322
+ children: [
1323
+ /* @__PURE__ */ jsx3(
1324
+ Button3,
1325
+ {
1326
+ onClick: onSelect,
1327
+ className: "w-full text-left px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 rounded-md truncate",
1328
+ children: session.query
1329
+ }
1330
+ ),
1331
+ /* @__PURE__ */ jsx3(
1332
+ Button3,
1333
+ {
1334
+ onClick: onDelete,
1335
+ className: "opacity-0 group-hover:opacity-100 px-2 flex items-center justify-center text-gray-400 hover:text-red-500 rounded-md transition-all",
1336
+ children: /* @__PURE__ */ jsx3(XMarkIcon2, { className: "size-4" })
1337
+ }
1338
+ )
1339
+ ]
1340
+ }
1341
+ );
1342
+ }
1343
+ function HistorySidebar({
1344
+ selected,
1345
+ sessions,
1346
+ onSelect,
1347
+ onDelete,
1348
+ onCreate
1349
+ }) {
1350
+ const [isOpen, setIsOpen] = useState4(sessions.length > 1);
1351
+ const groupedSessions = sessions.reduce((acc, session) => {
1352
+ const site = session.backend.site;
1353
+ if (!acc[site]) {
1354
+ acc[site] = [];
1355
+ }
1356
+ acc[site].push(session);
1357
+ return acc;
1358
+ }, {});
1359
+ return /* @__PURE__ */ jsx3(
1360
+ "div",
1361
+ {
1362
+ className: clsx3(
1363
+ "flex-1 flex flex-col relative z-50 transition-all",
1364
+ isOpen ? "max-w-70" : "max-w-12"
1365
+ ),
1366
+ children: /* @__PURE__ */ jsxs3("div", { className: "flex flex-col flex-1 overflow-hidden", children: [
1367
+ /* @__PURE__ */ jsx3(
1368
+ "div",
1369
+ {
1370
+ className: clsx3(
1371
+ "absolute right-2 top-0 h-12 flex items-center",
1372
+ !isOpen ? "left-2 flex justify-center" : ""
1373
+ ),
1374
+ children: /* @__PURE__ */ jsx3(
1375
+ Button3,
1376
+ {
1377
+ onClick: () => setIsOpen(!isOpen),
1378
+ className: "text-gray-400 hover:text-gray-700 transition-colors rounded-lg w-7 h-7 hover:bg-gray-50 flex items-center justify-center",
1379
+ children: /* @__PURE__ */ jsx3(Bars3Icon, { className: "size-5" })
1380
+ }
1381
+ )
1382
+ }
1383
+ ),
1384
+ /* @__PURE__ */ jsxs3(
1385
+ "div",
1386
+ {
1387
+ className: clsx3(
1388
+ "border-r min-w-64 bg-gray-50 overflow-y-auto",
1389
+ "flex-1 data-closed:opacity-0"
1390
+ ),
1391
+ children: [
1392
+ /* @__PURE__ */ jsx3(
1393
+ "div",
1394
+ {
1395
+ className: clsx3(
1396
+ "h-12 items-center font-medium p-4 px-5 pb-0 pointer-events-none text-xs text-gray-500 transition-opacity",
1397
+ isOpen ? "opacity-100" : "opacity-0"
1398
+ ),
1399
+ children: "Chats"
1400
+ }
1401
+ ),
1402
+ /* @__PURE__ */ jsx3("div", { className: "px-2", children: /* @__PURE__ */ jsxs3(
1403
+ Button3,
1404
+ {
1405
+ onClick: onCreate,
1406
+ className: clsx3(
1407
+ "flex items-center gap-2 text-left px-2 py-2 text-sm text-gray-700 hover:bg-gray-100 rounded-md truncate",
1408
+ isOpen ? "w-full" : ""
1409
+ ),
1410
+ children: [
1411
+ /* @__PURE__ */ jsx3(PencilSquareIcon, { className: "size-4 text-gray-400" }),
1412
+ /* @__PURE__ */ jsx3(Transition, { show: isOpen, children: /* @__PURE__ */ jsx3("div", { children: "New Chat" }) })
1413
+ ]
1414
+ }
1415
+ ) }),
1416
+ /* @__PURE__ */ jsx3(Transition, { show: isOpen, children: /* @__PURE__ */ jsxs3("div", { className: "divide-y", children: [
1417
+ Object.entries(groupedSessions).map(([site, siteSessions]) => /* @__PURE__ */ jsxs3("div", { className: "p-2", children: [
1418
+ /* @__PURE__ */ jsx3(SiteBadge, { site }),
1419
+ /* @__PURE__ */ jsx3("div", { className: "space-y-1", children: siteSessions.map((session) => /* @__PURE__ */ jsx3(
1420
+ SessionButton,
1421
+ {
1422
+ selected: session.sessionId == selected,
1423
+ session,
1424
+ onSelect: () => onSelect(session),
1425
+ onDelete: () => onDelete(session.sessionId)
1426
+ },
1427
+ session.sessionId
1428
+ )) })
1429
+ ] }, site)),
1430
+ sessions.length === 0 && /* @__PURE__ */ jsx3("div", { className: "px-3 py-8 text-sm text-gray-500 text-center", children: "No search history yet" })
1431
+ ] }) })
1432
+ ]
1433
+ }
1434
+ )
1435
+ ] })
1436
+ }
1437
+ );
1438
+ }
1439
+
1440
+ // src/components/DebugTools.tsx
1441
+ import { BugAntIcon } from "@heroicons/react/24/solid";
1442
+ import { useState as useState5 } from "react";
1443
+ import {
1444
+ Dialog as Dialog2,
1445
+ DialogPanel as DialogPanel2,
1446
+ Button as Button4,
1447
+ Tab,
1448
+ TabGroup,
1449
+ TabList,
1450
+ TabPanel,
1451
+ TabPanels
1452
+ } from "@headlessui/react";
1453
+ import { clsx as clsx4 } from "clsx";
1454
+ import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
1455
+ function translateResultsToNlWebRequests(streamingState, results, config) {
1456
+ const turns = [];
1457
+ for (let i = 0; i < results.length; i++) {
1458
+ const result = results[i];
1459
+ const params = {
1460
+ query: result.query,
1461
+ conversationHistory: i > 0 ? results.slice(0, i).map((r) => r.query) : void 0
1462
+ };
1463
+ const request = convertParamsToRequest(
1464
+ params,
1465
+ config.site,
1466
+ config.numRetrievalResults,
1467
+ config.maxResults
1468
+ );
1469
+ turns.push({
1470
+ request,
1471
+ response: result.response.rawLogs || []
1472
+ });
1473
+ }
1474
+ if (streamingState.query) {
1475
+ const streamingParams = {
1476
+ query: streamingState.query,
1477
+ conversationHistory: results.length > 0 ? results.map((r) => r.query) : void 0
1478
+ };
1479
+ const streamingRequest = convertParamsToRequest(
1480
+ streamingParams,
1481
+ config.site,
1482
+ config.numRetrievalResults,
1483
+ config.maxResults
1484
+ );
1485
+ turns.push({
1486
+ request: streamingRequest,
1487
+ response: streamingState.rawLogs || []
1488
+ });
1489
+ }
1490
+ return turns;
1491
+ }
1492
+ function CodeBlock({ code }) {
1493
+ const [copied, setCopied] = useState5(false);
1494
+ const handleCopy = async () => {
1495
+ try {
1496
+ await navigator.clipboard.writeText(code);
1497
+ setCopied(true);
1498
+ setTimeout(() => setCopied(false), 2e3);
1499
+ } catch (err) {
1500
+ console.error("Failed to copy:", err);
1501
+ }
1502
+ };
1503
+ return /* @__PURE__ */ jsxs4("div", { className: "relative overflow-hidden", children: [
1504
+ /* @__PURE__ */ jsx4(
1505
+ Button4,
1506
+ {
1507
+ onClick: handleCopy,
1508
+ className: "absolute top-0 right-0 px-3 py-1.5 text-xs font-medium text-gray-600 bg-white border border-gray-300 rounded hover:bg-gray-50 hover:border-gray-400 transition-colors",
1509
+ children: copied ? "Copied!" : "Copy"
1510
+ }
1511
+ ),
1512
+ /* @__PURE__ */ jsx4("pre", { className: "overflow-auto text-gray-500 text-sm pr-20", children: code })
1513
+ ] });
1514
+ }
1515
+ function MessagesDialog({
1516
+ isOpen,
1517
+ onClose,
1518
+ searches,
1519
+ streamingState,
1520
+ config
1521
+ }) {
1522
+ return /* @__PURE__ */ jsxs4(Dialog2, { open: isOpen, onClose, className: "relative z-50", children: [
1523
+ /* @__PURE__ */ jsx4("div", { className: "fixed inset-0 bg-black/30", "aria-hidden": "true" }),
1524
+ /* @__PURE__ */ jsx4("div", { className: "fixed inset-0 flex items-center justify-center p-4", children: /* @__PURE__ */ jsx4(DialogPanel2, { className: "flex flex-col bg-white rounded-lg shadow-xl max-w-3xl w-full max-h-[80vh] overflow-hidden flex flex-col", children: /* @__PURE__ */ jsxs4(TabGroup, { className: "flex flex-col flex-1 overflow-hidden", children: [
1525
+ /* @__PURE__ */ jsx4(TabList, { className: "flex border-b bg-gray-50", children: /* @__PURE__ */ jsx4(
1526
+ Tab,
1527
+ {
1528
+ className: ({ selected }) => clsx4(
1529
+ "px-6 py-3 text-sm font-medium outline-none transition-colors",
1530
+ selected ? "border-b-2 border-gray-900 text-gray-900" : "text-gray-500 hover:text-gray-700"
1531
+ ),
1532
+ children: "Raw Messages"
1533
+ }
1534
+ ) }),
1535
+ /* @__PURE__ */ jsx4(TabPanels, { className: "flex-1 overflow-y-auto", children: /* @__PURE__ */ jsx4(TabPanel, { className: "p-6", children: /* @__PURE__ */ jsx4(
1536
+ CodeBlock,
1537
+ {
1538
+ code: JSON.stringify(
1539
+ translateResultsToNlWebRequests(
1540
+ streamingState,
1541
+ searches,
1542
+ config
1543
+ ),
1544
+ null,
1545
+ 2
1546
+ )
1547
+ }
1548
+ ) }) })
1549
+ ] }) }) })
1550
+ ] });
1551
+ }
1552
+ function DebugTool({
1553
+ searches,
1554
+ streamingState,
1555
+ config
1556
+ }) {
1557
+ const [messagesOpen, setMessagesOpen] = useState5(false);
1558
+ return /* @__PURE__ */ jsxs4(Fragment2, { children: [
1559
+ /* @__PURE__ */ jsxs4(
1560
+ Button4,
1561
+ {
1562
+ onClick: () => setMessagesOpen(true),
1563
+ className: "flex bg-white items-center gap-2 ml-auto text-gray-600 hover:bg-gray-100 p-3 py-2 rounded-md text-sm",
1564
+ children: [
1565
+ /* @__PURE__ */ jsx4(BugAntIcon, { className: "size-4 text-gray-500" }),
1566
+ "JSON"
1567
+ ]
1568
+ }
1569
+ ),
1570
+ /* @__PURE__ */ jsx4(
1571
+ MessagesDialog,
1572
+ {
1573
+ isOpen: messagesOpen,
1574
+ onClose: () => setMessagesOpen(false),
1575
+ searches,
1576
+ streamingState,
1577
+ config
1578
+ }
1579
+ )
1580
+ ] });
1581
+ }
1582
+
1583
+ // src/components/SiteDropdown.tsx
1584
+ import { useState as useState6 } from "react";
1585
+ import {
1586
+ Combobox,
1587
+ ComboboxButton,
1588
+ ComboboxInput,
1589
+ ComboboxOption,
1590
+ ComboboxOptions
1591
+ } from "@headlessui/react";
1592
+ import {
1593
+ CheckIcon,
1594
+ GlobeAltIcon,
1595
+ ChevronUpDownIcon,
1596
+ StarIcon
1597
+ } from "@heroicons/react/20/solid";
1598
+ import { Fragment as Fragment3, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1599
+ function SiteDropdown({
1600
+ sites,
1601
+ selected,
1602
+ onSelect,
1603
+ placeholder = "Select a site..."
1604
+ }) {
1605
+ const [query, setQuery] = useState6("");
1606
+ const filteredSites = query === "" ? sites : sites.filter((site) => {
1607
+ return site.url.toLowerCase().includes(query.toLowerCase());
1608
+ });
1609
+ return /* @__PURE__ */ jsx5(Combobox, { value: selected.url, onChange: onSelect, children: /* @__PURE__ */ jsxs5("div", { className: "relative w-full", children: [
1610
+ /* @__PURE__ */ jsxs5("div", { className: "relative flex items-center w-full cursor-default overflow-hidden rounded-lg bg-white text-left border focus:outline-none focus-visible:ring-2 focus-visible:ring-white/75 focus-visible:ring-offset-2 focus-visible:ring-offset-blue-300 sm:text-sm", children: [
1611
+ /* @__PURE__ */ jsx5(
1612
+ ComboboxInput,
1613
+ {
1614
+ className: "w-full pl-8 rounded-md text-sm border-none py-2 pl-3 pr-10 text-sm leading-5 text-gray-900 focus:ring-0",
1615
+ displayValue: (site) => site || "",
1616
+ onChange: (event) => setQuery(event.target.value),
1617
+ placeholder
1618
+ }
1619
+ ),
1620
+ /* @__PURE__ */ jsx5(ComboboxButton, { className: "absolute inset-y-0 left-0 flex items-center pl-2", children: /* @__PURE__ */ jsx5(GlobeAltIcon, { className: "text-gray-400 size-4" }) }),
1621
+ /* @__PURE__ */ jsx5(ComboboxButton, { className: "absolute inset-y-0 right-0 flex items-center pr-2", children: /* @__PURE__ */ jsx5(ChevronUpDownIcon, { className: "text-gray-400 size-4" }) })
1622
+ ] }),
1623
+ /* @__PURE__ */ jsxs5(ComboboxOptions, { className: "absolute mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black/5 focus:outline-none sm:text-sm z-10", children: [
1624
+ filteredSites.map((site) => /* @__PURE__ */ jsx5(
1625
+ ComboboxOption,
1626
+ {
1627
+ className: ({ focus }) => `relative text-sm cursor-default select-none py-2 pl-10 pr-4 ${focus ? "bg-blue-600 text-white" : "text-gray-900"}`,
1628
+ value: site.url,
1629
+ children: ({ selected: selected2, focus }) => /* @__PURE__ */ jsxs5(Fragment3, { children: [
1630
+ /* @__PURE__ */ jsx5(
1631
+ "span",
1632
+ {
1633
+ className: `block truncate ${selected2 ? "font-medium" : "font-normal"}`,
1634
+ children: site.url
1635
+ }
1636
+ ),
1637
+ selected2 ? /* @__PURE__ */ jsx5(
1638
+ "span",
1639
+ {
1640
+ className: `absolute inset-y-0 left-0 flex items-center pl-3 ${focus ? "text-white" : "text-blue-600"}`,
1641
+ children: /* @__PURE__ */ jsx5(CheckIcon, { className: "h-5 w-5", "aria-hidden": "true" })
1642
+ }
1643
+ ) : null,
1644
+ site.featured ? /* @__PURE__ */ jsx5(
1645
+ "span",
1646
+ {
1647
+ className: `absolute inset-y-0 right-0 flex items-center pr-3 ${focus ? "text-white" : "text-blue-600"}`,
1648
+ children: /* @__PURE__ */ jsx5(StarIcon, { className: "size-5 text-yellow-500" })
1649
+ }
1650
+ ) : null
1651
+ ] })
1652
+ },
1653
+ site.url
1654
+ )),
1655
+ query !== "" && !sites.some(
1656
+ (site) => site.url.toLowerCase() === query.toLowerCase()
1657
+ ) && /* @__PURE__ */ jsx5(
1658
+ ComboboxOption,
1659
+ {
1660
+ className: ({ focus }) => `relative text-sm cursor-default select-none py-2 pl-10 pr-4 ${focus ? "bg-blue-600 text-white" : "text-gray-900"}`,
1661
+ value: query,
1662
+ children: ({ focus }) => /* @__PURE__ */ jsxs5(
1663
+ "span",
1664
+ {
1665
+ className: `block truncate ${focus ? "font-medium" : "font-normal"}`,
1666
+ children: [
1667
+ 'Use "',
1668
+ query,
1669
+ '"'
1670
+ ]
1671
+ }
1672
+ )
1673
+ }
1674
+ )
1675
+ ] })
1676
+ ] }) });
1677
+ }
1678
+
1679
+ // src/lib/useHistory.ts
1680
+ import { useLiveQuery } from "dexie-react-hooks";
1681
+
1682
+ // src/lib/db.ts
1683
+ import { Dexie } from "dexie";
1684
+ var db = new Dexie("ChatHistory");
1685
+ db.version(1).stores({
1686
+ messages: "++id, sessionId",
1687
+ // primary key "id" (for the runtime!),
1688
+ sessions: "sessionId, created, updated"
1689
+ });
1690
+
1691
+ // src/lib/useHistory.ts
1692
+ function useSearchSessions() {
1693
+ const sessions = useLiveQuery(
1694
+ () => db.sessions.orderBy("updated").reverse().toArray(),
1695
+ []
1696
+ ) ?? [];
1697
+ async function startSession(sessionId, query, backend) {
1698
+ await db.transaction("rw", db.messages, db.sessions, async () => {
1699
+ const duplicates = sessions.filter((s) => s.query == query);
1700
+ for (const s of duplicates) {
1701
+ await db.messages.where("sessionId").equals(s.sessionId).delete();
1702
+ db.sessions.delete(s.sessionId);
1703
+ }
1704
+ const created = /* @__PURE__ */ new Date();
1705
+ const updated = /* @__PURE__ */ new Date();
1706
+ await db.sessions.put({ sessionId, query, backend, created, updated });
1707
+ });
1708
+ }
1709
+ async function deleteSession(sessionId) {
1710
+ await db.transaction("rw", db.messages, db.sessions, async () => {
1711
+ await db.messages.where("sessionId").equals(sessionId).delete();
1712
+ db.sessions.delete(sessionId);
1713
+ });
1714
+ }
1715
+ return {
1716
+ sessions,
1717
+ startSession,
1718
+ deleteSession
1719
+ };
1720
+ }
1721
+ function useSearchSession(sessionId) {
1722
+ const queryResults = useLiveQuery(async () => {
1723
+ if (!sessionId) return [];
1724
+ return await db.messages.where("sessionId").equals(sessionId).sortBy("id");
1725
+ }, [sessionId]) ?? [];
1726
+ async function addSearch(result) {
1727
+ await db.transaction("rw", db.messages, db.sessions, async () => {
1728
+ db.sessions.update(result.sessionId, {
1729
+ updated: /* @__PURE__ */ new Date()
1730
+ });
1731
+ db.messages.add(result);
1732
+ });
1733
+ }
1734
+ async function addResults(id, results) {
1735
+ await db.transaction("rw", db.messages, db.sessions, async () => {
1736
+ const currMessage = await db.messages.get(id);
1737
+ if (currMessage) {
1738
+ db.sessions.update(currMessage.sessionId, {
1739
+ updated: /* @__PURE__ */ new Date()
1740
+ });
1741
+ db.messages.update(id, {
1742
+ ...currMessage,
1743
+ response: {
1744
+ ...currMessage.response,
1745
+ results: [...currMessage.response.results, ...results]
1746
+ }
1747
+ });
1748
+ }
1749
+ });
1750
+ }
1751
+ return {
1752
+ searches: queryResults,
1753
+ addSearch,
1754
+ addResults
1755
+ };
1756
+ }
1757
+ export {
1758
+ ChatSearch,
1759
+ DebugTool,
1760
+ HistorySidebar,
1761
+ SiteDropdown,
1762
+ useNlWeb,
1763
+ useSearchSession,
1764
+ useSearchSessions
1765
+ };
1766
+ //# sourceMappingURL=index.mjs.map