@brainfish-ai/components 0.20.6 → 0.21.0

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.
Files changed (51) hide show
  1. package/dist/badge.d.ts +1 -1
  2. package/dist/chat-search.d.ts +12 -1
  3. package/dist/convos.d.ts +1 -1
  4. package/dist/esm/chunks/{ChatSearch.Bad4C07E.js → ChatSearch.CNS7ZOgA.js} +13 -3
  5. package/dist/esm/chunks/ChatSearch.CNS7ZOgA.js.map +1 -0
  6. package/dist/esm/chunks/FormattedMessage.ieoe4jPf.js.map +1 -1
  7. package/dist/esm/chunks/{chart.DGCX9VIn.js → chart.CTzWtshH.js} +40 -61
  8. package/dist/esm/chunks/chart.CTzWtshH.js.map +1 -0
  9. package/dist/esm/chunks/combobox.DNYCWyub.js.map +1 -1
  10. package/dist/esm/chunks/font-picker.DX1mER3H.js.map +1 -1
  11. package/dist/esm/chunks/hooks.CKojclfe.js.map +1 -1
  12. package/dist/esm/chunks/review-list.jCfkezEr.js +72 -0
  13. package/dist/esm/chunks/review-list.jCfkezEr.js.map +1 -0
  14. package/dist/esm/chunks/status-badge.DkPNh30S.js +952 -0
  15. package/dist/esm/chunks/status-badge.DkPNh30S.js.map +1 -0
  16. package/dist/esm/components/chart-area-linear.js +1 -1
  17. package/dist/esm/components/chart-radial-stacked.js +1 -1
  18. package/dist/esm/components/chat-search.js +1 -1
  19. package/dist/esm/components/convos.js +6 -30
  20. package/dist/esm/components/convos.js.map +1 -1
  21. package/dist/esm/components/ui/alert-dialog.js +22 -54
  22. package/dist/esm/components/ui/alert-dialog.js.map +1 -1
  23. package/dist/esm/components/ui/alert.js +47 -64
  24. package/dist/esm/components/ui/alert.js.map +1 -1
  25. package/dist/esm/components/ui/badge.js +1 -1
  26. package/dist/esm/components/ui/badge.js.map +1 -1
  27. package/dist/esm/components/ui/calendar.js +1 -6
  28. package/dist/esm/components/ui/calendar.js.map +1 -1
  29. package/dist/esm/components/ui/command.js +19 -42
  30. package/dist/esm/components/ui/command.js.map +1 -1
  31. package/dist/esm/components/ui/dialog.js +13 -40
  32. package/dist/esm/components/ui/dialog.js.map +1 -1
  33. package/dist/esm/components/ui/dropdown-menu.js +5 -25
  34. package/dist/esm/components/ui/dropdown-menu.js.map +1 -1
  35. package/dist/esm/components/ui/item.js +8 -46
  36. package/dist/esm/components/ui/item.js.map +1 -1
  37. package/dist/esm/components/ui/sheet.js +17 -53
  38. package/dist/esm/components/ui/sheet.js.map +1 -1
  39. package/dist/esm/global.css +1 -1
  40. package/dist/esm/index.js +2 -1
  41. package/dist/esm/index.js.map +1 -1
  42. package/dist/esm/scenes/knowledge-review/review-list.js +2 -0
  43. package/dist/esm/scenes/knowledge-review/review-list.js.map +1 -0
  44. package/dist/index.d.ts +65 -11
  45. package/dist/item.d.ts +9 -9
  46. package/dist/review-list.d.ts +47 -0
  47. package/dist/scenes/knowledge-review/review-list.d.ts +2 -0
  48. package/dist/stats.html +1 -1
  49. package/package.json +9 -5
  50. package/dist/esm/chunks/ChatSearch.Bad4C07E.js.map +0 -1
  51. package/dist/esm/chunks/chart.DGCX9VIn.js.map +0 -1
@@ -31,9 +31,7 @@ const ChartContainer = React.forwardRef(({ id, className, children, config, ...p
31
31
  });
32
32
  ChartContainer.displayName = "Chart";
33
33
  const ChartStyle = ({ id, config }) => {
34
- const colorConfig = Object.entries(config).filter(
35
- ([, config2]) => config2.theme || config2.color
36
- );
34
+ const colorConfig = Object.entries(config).filter(([, config2]) => config2.theme || config2.color);
37
35
  if (!colorConfig.length) {
38
36
  return null;
39
37
  }
@@ -88,15 +86,7 @@ const ChartTooltipContent = React.forwardRef(
88
86
  return null;
89
87
  }
90
88
  return /* @__PURE__ */ React.createElement("div", { className: cn("font-medium", labelClassName) }, value);
91
- }, [
92
- label,
93
- labelFormatter,
94
- payload,
95
- hideLabel,
96
- labelClassName,
97
- config,
98
- labelKey
99
- ]);
89
+ }, [label, labelFormatter, payload, hideLabel, labelClassName, config, labelKey]);
100
90
  if (!active || !payload?.length) {
101
91
  return null;
102
92
  }
@@ -127,15 +117,12 @@ const ChartTooltipContent = React.forwardRef(
127
117
  formatter && item?.value !== void 0 && item.name ? formatter(item.value, item.name, item, index, item.payload) : /* @__PURE__ */ React.createElement(React.Fragment, null, itemConfig?.icon ? /* @__PURE__ */ React.createElement(itemConfig.icon, null) : !hideIndicator && /* @__PURE__ */ React.createElement(
128
118
  "div",
129
119
  {
130
- className: cn(
131
- "shrink-0 rounded-[2px] border-[--color-border] bg-[--color-bg]",
132
- {
133
- "h-2.5 w-2.5": indicator === "dot",
134
- "w-1": indicator === "line",
135
- "w-0 border-[1.5px] border-dashed bg-transparent": indicator === "dashed",
136
- "my-0.5": nestLabel && indicator === "dashed"
137
- }
138
- ),
120
+ className: cn("shrink-0 rounded-[2px] border-[--color-border] bg-[--color-bg]", {
121
+ "h-2.5 w-2.5": indicator === "dot",
122
+ "w-1": indicator === "line",
123
+ "w-0 border-[1.5px] border-dashed bg-transparent": indicator === "dashed",
124
+ "my-0.5": nestLabel && indicator === "dashed"
125
+ }),
139
126
  style: {
140
127
  "--color-bg": indicatorColor,
141
128
  "--color-border": indicatorColor
@@ -158,48 +145,40 @@ const ChartTooltipContent = React.forwardRef(
158
145
  }
159
146
  );
160
147
  ChartTooltipContent.displayName = "ChartTooltip";
161
- const ChartLegendContent = React.forwardRef(
162
- ({ className, hideIcon = false, payload, verticalAlign = "bottom", nameKey }, ref) => {
163
- const { config } = useChart();
164
- if (!payload?.length) {
165
- return null;
166
- }
167
- return /* @__PURE__ */ React.createElement(
168
- "div",
169
- {
170
- ref,
171
- className: cn(
172
- "flex items-center justify-center gap-4",
173
- verticalAlign === "top" ? "pb-3" : "pt-3",
174
- className
175
- )
176
- },
177
- payload.filter((item) => item.type !== "none").map((item) => {
178
- const key = `${nameKey || item.dataKey || "value"}`;
179
- const itemConfig = getPayloadConfigFromPayload(config, item, key);
180
- return /* @__PURE__ */ React.createElement(
148
+ const ChartLegendContent = React.forwardRef(({ className, hideIcon = false, payload, verticalAlign = "bottom", nameKey }, ref) => {
149
+ const { config } = useChart();
150
+ if (!payload?.length) {
151
+ return null;
152
+ }
153
+ return /* @__PURE__ */ React.createElement(
154
+ "div",
155
+ {
156
+ ref,
157
+ className: cn("flex items-center justify-center gap-4", verticalAlign === "top" ? "pb-3" : "pt-3", className)
158
+ },
159
+ payload.filter((item) => item.type !== "none").map((item) => {
160
+ const key = `${nameKey || item.dataKey || "value"}`;
161
+ const itemConfig = getPayloadConfigFromPayload(config, item, key);
162
+ return /* @__PURE__ */ React.createElement(
163
+ "div",
164
+ {
165
+ key: item.value,
166
+ className: cn("flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground")
167
+ },
168
+ itemConfig?.icon && !hideIcon ? /* @__PURE__ */ React.createElement(itemConfig.icon, null) : /* @__PURE__ */ React.createElement(
181
169
  "div",
182
170
  {
183
- key: item.value,
184
- className: cn(
185
- "flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground"
186
- )
187
- },
188
- itemConfig?.icon && !hideIcon ? /* @__PURE__ */ React.createElement(itemConfig.icon, null) : /* @__PURE__ */ React.createElement(
189
- "div",
190
- {
191
- className: "h-2 w-2 shrink-0 rounded-[2px]",
192
- style: {
193
- backgroundColor: item.color
194
- }
171
+ className: "h-2 w-2 shrink-0 rounded-[2px]",
172
+ style: {
173
+ backgroundColor: item.color
195
174
  }
196
- ),
197
- itemConfig?.label
198
- );
199
- })
200
- );
201
- }
202
- );
175
+ }
176
+ ),
177
+ itemConfig?.label
178
+ );
179
+ })
180
+ );
181
+ });
203
182
  ChartLegendContent.displayName = "ChartLegend";
204
183
  function getPayloadConfigFromPayload(config, payload, key) {
205
184
  if (typeof payload !== "object" || payload === null) {
@@ -216,4 +195,4 @@ function getPayloadConfigFromPayload(config, payload, key) {
216
195
  }
217
196
 
218
197
  export { ChartContainer as C, ChartTooltip as a, ChartTooltipContent as b };
219
- //# sourceMappingURL=chart.DGCX9VIn.js.map
198
+ //# sourceMappingURL=chart.CTzWtshH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chart.CTzWtshH.js","sources":["../../../src/components/ui/chart.tsx"],"sourcesContent":["import * as React from 'react';\nimport * as RechartsPrimitive from 'recharts';\n\nimport { cn } from '@/lib/utils';\n\n// Format: { THEME_NAME: CSS_SELECTOR }\nconst THEMES = { light: '', dark: '.dark' } as const;\n\nexport type ChartConfig = {\n [k in string]: {\n label?: React.ReactNode;\n icon?: React.ComponentType;\n } & ({ color?: string; theme?: never } | { color?: never; theme: Record<keyof typeof THEMES, string> });\n};\n\ntype ChartContextProps = {\n config: ChartConfig;\n};\n\nconst ChartContext = React.createContext<ChartContextProps | null>(null);\n\nfunction useChart() {\n const context = React.useContext(ChartContext);\n\n if (!context) {\n throw new Error('useChart must be used within a <ChartContainer />');\n }\n\n return context;\n}\n\nconst ChartContainer = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<'div'> & {\n config: ChartConfig;\n children: React.ComponentProps<typeof RechartsPrimitive.ResponsiveContainer>['children'];\n }\n>(({ id, className, children, config, ...props }, ref) => {\n const uniqueId = React.useId();\n const chartId = `chart-${id || uniqueId.replace(/:/g, '')}`;\n\n return (\n <ChartContext.Provider value={{ config }}>\n <div\n data-chart={chartId}\n ref={ref}\n className={cn(\n \"flex aspect-video justify-center text-xs [&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-none [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-sector]:outline-none [&_.recharts-surface]:outline-none\",\n className,\n )}\n {...props}\n >\n <ChartStyle id={chartId} config={config} />\n <RechartsPrimitive.ResponsiveContainer>{children}</RechartsPrimitive.ResponsiveContainer>\n </div>\n </ChartContext.Provider>\n );\n});\nChartContainer.displayName = 'Chart';\n\nconst ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {\n const colorConfig = Object.entries(config).filter(([, config]) => config.theme || config.color);\n\n if (!colorConfig.length) {\n return null;\n }\n\n return (\n <style\n dangerouslySetInnerHTML={{\n __html: Object.entries(THEMES)\n .map(\n ([theme, prefix]) => `\n${prefix} [data-chart=${id}] {\n${colorConfig\n .map(([key, itemConfig]) => {\n const color = itemConfig.theme?.[theme as keyof typeof itemConfig.theme] || itemConfig.color;\n\n return color ? ` --color-${key}: ${color};` : null;\n })\n .join('\\n')}\n}\n`,\n )\n .join('\\n'),\n }}\n />\n );\n};\n\nconst ChartTooltip = RechartsPrimitive.Tooltip;\n\nconst ChartTooltipContent = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<typeof RechartsPrimitive.Tooltip> &\n React.ComponentProps<'div'> & {\n hideLabel?: boolean;\n hideIndicator?: boolean;\n indicator?: 'line' | 'dot' | 'dashed';\n nameKey?: string;\n labelKey?: string;\n }\n>(\n (\n {\n active,\n payload,\n className,\n indicator = 'dot',\n hideLabel = false,\n hideIndicator = false,\n label,\n labelFormatter,\n labelClassName,\n formatter,\n color,\n nameKey,\n labelKey,\n },\n ref,\n ) => {\n const { config } = useChart();\n\n const tooltipLabel = React.useMemo(() => {\n if (hideLabel || !payload?.length) {\n return null;\n }\n\n const [item] = payload;\n const key = `${labelKey || item?.dataKey || item?.name || 'value'}`;\n const itemConfig = getPayloadConfigFromPayload(config, item, key);\n const value =\n !labelKey && typeof label === 'string'\n ? config[label as keyof typeof config]?.label || label\n : itemConfig?.label;\n\n if (labelFormatter) {\n return <div className={cn('font-medium', labelClassName)}>{labelFormatter(value, payload)}</div>;\n }\n\n if (!value) {\n return null;\n }\n\n return <div className={cn('font-medium', labelClassName)}>{value}</div>;\n }, [label, labelFormatter, payload, hideLabel, labelClassName, config, labelKey]);\n\n if (!active || !payload?.length) {\n return null;\n }\n\n const nestLabel = payload.length === 1 && indicator !== 'dot';\n\n return (\n <div\n ref={ref}\n className={cn(\n 'grid min-w-[8rem] items-start gap-1.5 rounded-lg border border-border/50 bg-background px-2.5 py-1.5 text-xs shadow-xl',\n className,\n )}\n >\n {!nestLabel ? tooltipLabel : null}\n <div className=\"grid gap-1.5\">\n {payload\n .filter((item) => item.type !== 'none')\n .map((item, index) => {\n const key = `${nameKey || item.name || item.dataKey || 'value'}`;\n const itemConfig = getPayloadConfigFromPayload(config, item, key);\n const indicatorColor = color || item.payload.fill || item.color;\n\n return (\n <div\n key={item.dataKey}\n className={cn(\n 'flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground',\n indicator === 'dot' && 'items-center',\n )}\n >\n {formatter && item?.value !== undefined && item.name ? (\n formatter(item.value, item.name, item, index, item.payload)\n ) : (\n <>\n {itemConfig?.icon ? (\n <itemConfig.icon />\n ) : (\n !hideIndicator && (\n <div\n className={cn('shrink-0 rounded-[2px] border-[--color-border] bg-[--color-bg]', {\n 'h-2.5 w-2.5': indicator === 'dot',\n 'w-1': indicator === 'line',\n 'w-0 border-[1.5px] border-dashed bg-transparent': indicator === 'dashed',\n 'my-0.5': nestLabel && indicator === 'dashed',\n })}\n style={\n {\n '--color-bg': indicatorColor,\n '--color-border': indicatorColor,\n } as React.CSSProperties\n }\n />\n )\n )}\n <div\n className={cn(\n 'flex flex-1 justify-between leading-none',\n nestLabel ? 'items-end' : 'items-center',\n )}\n >\n <div className=\"grid gap-1.5\">\n {nestLabel ? tooltipLabel : null}\n <span className=\"text-muted-foreground\">{itemConfig?.label || item.name}</span>\n </div>\n {item.value && (\n <span className=\"font-mono font-medium tabular-nums text-foreground\">\n {item.value.toLocaleString()}\n </span>\n )}\n </div>\n </>\n )}\n </div>\n );\n })}\n </div>\n </div>\n );\n },\n);\nChartTooltipContent.displayName = 'ChartTooltip';\n\nconst ChartLegend = RechartsPrimitive.Legend;\n\nconst ChartLegendContent = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<'div'> &\n Pick<RechartsPrimitive.LegendProps, 'payload' | 'verticalAlign'> & {\n hideIcon?: boolean;\n nameKey?: string;\n }\n>(({ className, hideIcon = false, payload, verticalAlign = 'bottom', nameKey }, ref) => {\n const { config } = useChart();\n\n if (!payload?.length) {\n return null;\n }\n\n return (\n <div\n ref={ref}\n className={cn('flex items-center justify-center gap-4', verticalAlign === 'top' ? 'pb-3' : 'pt-3', className)}\n >\n {payload\n .filter((item) => item.type !== 'none')\n .map((item) => {\n const key = `${nameKey || item.dataKey || 'value'}`;\n const itemConfig = getPayloadConfigFromPayload(config, item, key);\n\n return (\n <div\n key={item.value}\n className={cn('flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground')}\n >\n {itemConfig?.icon && !hideIcon ? (\n <itemConfig.icon />\n ) : (\n <div\n className=\"h-2 w-2 shrink-0 rounded-[2px]\"\n style={{\n backgroundColor: item.color,\n }}\n />\n )}\n {itemConfig?.label}\n </div>\n );\n })}\n </div>\n );\n});\nChartLegendContent.displayName = 'ChartLegend';\n\n// Helper to extract item config from a payload.\nfunction getPayloadConfigFromPayload(config: ChartConfig, payload: unknown, key: string) {\n if (typeof payload !== 'object' || payload === null) {\n return undefined;\n }\n\n const payloadPayload =\n 'payload' in payload && typeof payload.payload === 'object' && payload.payload !== null\n ? payload.payload\n : undefined;\n\n let configLabelKey: string = key;\n\n if (key in payload && typeof payload[key as keyof typeof payload] === 'string') {\n configLabelKey = payload[key as keyof typeof payload] as string;\n } else if (\n payloadPayload &&\n key in payloadPayload &&\n typeof payloadPayload[key as keyof typeof payloadPayload] === 'string'\n ) {\n configLabelKey = payloadPayload[key as keyof typeof payloadPayload] as string;\n }\n\n return configLabelKey in config ? config[configLabelKey] : config[key as keyof typeof config];\n}\n\nexport { ChartContainer, ChartTooltip, ChartTooltipContent, ChartLegend, ChartLegendContent, ChartStyle };\n"],"names":["config"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"combobox.DNYCWyub.js","sources":["../../../src/components/combobox/combobox.tsx"],"sourcesContent":["import * as React from 'react';\nimport { Check, CaretUpDown } from '@phosphor-icons/react';\n\nimport { cn } from '@/lib/utils';\nimport { Button } from '@/components/ui/button';\nimport { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from '@/components/ui/command';\nimport { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';\n\nexport interface ComboboxItem {\n value: string;\n label: string;\n icon?: React.ReactNode;\n /** Additional data that can be passed to custom renderers */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n data?: any;\n}\n\nexport interface ComboboxRenderItemProps<T extends ComboboxItem = ComboboxItem> {\n item: T;\n isSelected: boolean;\n onSelect: () => void;\n}\n\nexport interface ComboboxProps<T extends ComboboxItem = ComboboxItem> {\n options: T[];\n defaultValueLabel: string;\n placeholder: string;\n noResultsLabel: string;\n className?: string;\n dropdownWidth?: number;\n value?: string;\n /** Whether the combobox is disabled */\n disabled?: boolean;\n /** Callback on open state change */\n onOpenChange?: (open: boolean) => void;\n /** Callback on value change */\n onChange?: (value: string) => void;\n /** Custom render function for each item in the dropdown */\n renderItem?: (props: ComboboxRenderItemProps<T>) => React.ReactNode;\n /** Custom render function for the selected value in the trigger button */\n renderValue?: (item: T) => React.ReactNode;\n /** Custom render function for the entire trigger button content */\n renderTrigger?: (props: { selectedItem: T | undefined; isOpen: boolean }) => React.ReactNode;\n}\n\nexport function Combobox<T extends ComboboxItem = ComboboxItem>({\n options,\n defaultValueLabel,\n placeholder,\n noResultsLabel,\n className,\n onOpenChange,\n onChange,\n dropdownWidth,\n value: controlledValue,\n disabled = false,\n renderItem,\n renderValue,\n renderTrigger,\n}: ComboboxProps<T>) {\n const [open, setOpen] = React.useState(false);\n const [internalValue, setInternalValue] = React.useState('');\n\n // Use controlled value if provided, otherwise use internal state\n const value = controlledValue !== undefined ? controlledValue : internalValue;\n const triggerRef = React.useRef<HTMLButtonElement>(null);\n const [triggerWidth, setTriggerWidth] = React.useState<number | null>(null);\n\n const selectedItem = options.find((option) => option.value === value);\n\n // Update width when popover opens\n const handleOpenChange = (isOpen: boolean) => {\n if (isOpen && triggerRef.current) {\n setTriggerWidth(dropdownWidth ? dropdownWidth : triggerRef.current.offsetWidth);\n }\n setOpen(isOpen);\n onOpenChange?.(isOpen);\n };\n\n const handleSelect = (selectedValue: string) => {\n const newValue = selectedValue === value ? '' : selectedValue;\n\n // Update internal state only if not controlled\n if (controlledValue === undefined) {\n setInternalValue(newValue);\n }\n\n setOpen(false);\n onChange?.(newValue);\n };\n\n // Default trigger content\n const defaultTriggerContent = selectedItem\n ? renderValue\n ? renderValue(selectedItem)\n : selectedItem.label\n : defaultValueLabel;\n\n return (\n <Popover open={open} onOpenChange={handleOpenChange}>\n <PopoverTrigger asChild>\n <Button\n ref={triggerRef}\n variant=\"outline\"\n role=\"combobox\"\n aria-expanded={open}\n disabled={disabled}\n className={cn('w-full justify-between', className)}\n >\n {renderTrigger ? renderTrigger({ selectedItem, isOpen: open }) : defaultTriggerContent}\n <CaretUpDown className=\"opacity-50\" />\n </Button>\n </PopoverTrigger>\n <PopoverContent\n className=\"p-0\"\n style={{\n width: triggerWidth ? `${triggerWidth}px` : 'auto',\n }}\n align=\"start\"\n >\n <Command>\n <CommandInput placeholder={placeholder} />\n <CommandList>\n <CommandEmpty>{noResultsLabel}</CommandEmpty>\n <CommandGroup>\n {options.map((option) => {\n const isSelected = value === option.value;\n\n if (renderItem) {\n return (\n <React.Fragment key={option.value}>\n {renderItem({\n item: option,\n isSelected,\n onSelect: () => handleSelect(option.value),\n })}\n </React.Fragment>\n );\n }\n\n return (\n <CommandItem\n key={option.value}\n value={option.label}\n onSelect={(currentLabel) => {\n const selectedOption = options.find((opt) => opt.label === currentLabel);\n\n handleSelect(selectedOption?.value || '');\n }}\n >\n {option.label}\n <Check className={cn('ml-auto', isSelected ? 'opacity-100' : 'opacity-0')} />\n </CommandItem>\n );\n })}\n </CommandGroup>\n </CommandList>\n </Command>\n </PopoverContent>\n </Popover>\n );\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"combobox.DNYCWyub.js","sources":["../../../src/components/combobox/combobox.tsx"],"sourcesContent":["import * as React from 'react';\nimport { Check, CaretUpDown } from '@phosphor-icons/react';\n\nimport { cn } from '@/lib/utils';\nimport { Button } from '@/components/ui/button';\nimport { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from '@/components/ui/command';\nimport { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';\n\nexport interface ComboboxItem {\n value: string;\n label: string;\n icon?: React.ReactNode;\n /** Additional data that can be passed to custom renderers */\n\n data?: any;\n}\n\nexport interface ComboboxRenderItemProps<T extends ComboboxItem = ComboboxItem> {\n item: T;\n isSelected: boolean;\n onSelect: () => void;\n}\n\nexport interface ComboboxProps<T extends ComboboxItem = ComboboxItem> {\n options: T[];\n defaultValueLabel: string;\n placeholder: string;\n noResultsLabel: string;\n className?: string;\n dropdownWidth?: number;\n value?: string;\n /** Whether the combobox is disabled */\n disabled?: boolean;\n /** Callback on open state change */\n onOpenChange?: (open: boolean) => void;\n /** Callback on value change */\n onChange?: (value: string) => void;\n /** Custom render function for each item in the dropdown */\n renderItem?: (props: ComboboxRenderItemProps<T>) => React.ReactNode;\n /** Custom render function for the selected value in the trigger button */\n renderValue?: (item: T) => React.ReactNode;\n /** Custom render function for the entire trigger button content */\n renderTrigger?: (props: { selectedItem: T | undefined; isOpen: boolean }) => React.ReactNode;\n}\n\nexport function Combobox<T extends ComboboxItem = ComboboxItem>({\n options,\n defaultValueLabel,\n placeholder,\n noResultsLabel,\n className,\n onOpenChange,\n onChange,\n dropdownWidth,\n value: controlledValue,\n disabled = false,\n renderItem,\n renderValue,\n renderTrigger,\n}: ComboboxProps<T>) {\n const [open, setOpen] = React.useState(false);\n const [internalValue, setInternalValue] = React.useState('');\n\n // Use controlled value if provided, otherwise use internal state\n const value = controlledValue !== undefined ? controlledValue : internalValue;\n const triggerRef = React.useRef<HTMLButtonElement>(null);\n const [triggerWidth, setTriggerWidth] = React.useState<number | null>(null);\n\n const selectedItem = options.find((option) => option.value === value);\n\n // Update width when popover opens\n const handleOpenChange = (isOpen: boolean) => {\n if (isOpen && triggerRef.current) {\n setTriggerWidth(dropdownWidth ? dropdownWidth : triggerRef.current.offsetWidth);\n }\n setOpen(isOpen);\n onOpenChange?.(isOpen);\n };\n\n const handleSelect = (selectedValue: string) => {\n const newValue = selectedValue === value ? '' : selectedValue;\n\n // Update internal state only if not controlled\n if (controlledValue === undefined) {\n setInternalValue(newValue);\n }\n\n setOpen(false);\n onChange?.(newValue);\n };\n\n // Default trigger content\n const defaultTriggerContent = selectedItem\n ? renderValue\n ? renderValue(selectedItem)\n : selectedItem.label\n : defaultValueLabel;\n\n return (\n <Popover open={open} onOpenChange={handleOpenChange}>\n <PopoverTrigger asChild>\n <Button\n ref={triggerRef}\n variant=\"outline\"\n role=\"combobox\"\n aria-expanded={open}\n disabled={disabled}\n className={cn('w-full justify-between', className)}\n >\n {renderTrigger ? renderTrigger({ selectedItem, isOpen: open }) : defaultTriggerContent}\n <CaretUpDown className=\"opacity-50\" />\n </Button>\n </PopoverTrigger>\n <PopoverContent\n className=\"p-0\"\n style={{\n width: triggerWidth ? `${triggerWidth}px` : 'auto',\n }}\n align=\"start\"\n >\n <Command>\n <CommandInput placeholder={placeholder} />\n <CommandList>\n <CommandEmpty>{noResultsLabel}</CommandEmpty>\n <CommandGroup>\n {options.map((option) => {\n const isSelected = value === option.value;\n\n if (renderItem) {\n return (\n <React.Fragment key={option.value}>\n {renderItem({\n item: option,\n isSelected,\n onSelect: () => handleSelect(option.value),\n })}\n </React.Fragment>\n );\n }\n\n return (\n <CommandItem\n key={option.value}\n value={option.label}\n onSelect={(currentLabel) => {\n const selectedOption = options.find((opt) => opt.label === currentLabel);\n\n handleSelect(selectedOption?.value || '');\n }}\n >\n {option.label}\n <Check className={cn('ml-auto', isSelected ? 'opacity-100' : 'opacity-0')} />\n </CommandItem>\n );\n })}\n </CommandGroup>\n </CommandList>\n </Command>\n </PopoverContent>\n </Popover>\n );\n}\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"font-picker.DX1mER3H.js","sources":["../../../src/components/font-picker/font-utils.ts","../../../src/components/font-picker/use-font-options.ts","../../../src/components/font-picker/font-picker-item.tsx","../../../src/components/font-picker/font-picker.tsx"],"sourcesContent":["import { FontOption } from './types';\n\n// Cache keys for localStorage\nconst FONT_CACHE_KEY = 'brainfish-google-fonts-cache';\nconst FONT_CACHE_TIMESTAMP_KEY = 'brainfish-google-fonts-cache-timestamp';\nconst FONT_CACHE_DURATION_MS = 24 * 60 * 60 * 1000; // 24 hours\n\n// Cache for loaded font stylesheets to prevent duplicate loads\nconst loadedFonts = new Set<string>();\n\n/**\n * Get cached fonts from localStorage\n */\nexport const getCachedFonts = (): FontOption[] | null => {\n try {\n const timestamp = localStorage.getItem(FONT_CACHE_TIMESTAMP_KEY);\n if (!timestamp) return null;\n\n const cacheAge = Date.now() - parseInt(timestamp, 10);\n if (cacheAge > FONT_CACHE_DURATION_MS) {\n localStorage.removeItem(FONT_CACHE_KEY);\n localStorage.removeItem(FONT_CACHE_TIMESTAMP_KEY);\n\n return null;\n }\n\n const cached = localStorage.getItem(FONT_CACHE_KEY);\n if (!cached) return null;\n\n return JSON.parse(cached) as FontOption[];\n } catch {\n return null;\n }\n};\n\n/**\n * Save fonts to localStorage cache\n */\nexport const setCachedFonts = (fonts: FontOption[]): void => {\n try {\n localStorage.setItem(FONT_CACHE_KEY, JSON.stringify(fonts));\n localStorage.setItem(FONT_CACHE_TIMESTAMP_KEY, Date.now().toString());\n } catch {\n // localStorage might be full or unavailable, ignore\n }\n};\n\n/**\n * Load a Google Font stylesheet dynamically\n */\nexport const loadFont = (fontFamily: string): void => {\n if (loadedFonts.has(fontFamily)) return;\n\n const link = document.createElement('link');\n link.href = `https://fonts.googleapis.com/css2?family=${fontFamily.replace(/ /g, '+')}&display=swap`;\n link.rel = 'stylesheet';\n document.head.appendChild(link);\n loadedFonts.add(fontFamily);\n};\n\n/**\n * Clear the font cache from localStorage\n * Useful for forcing a fresh fetch of fonts\n */\nexport const clearFontCache = (): void => {\n try {\n localStorage.removeItem(FONT_CACHE_KEY);\n localStorage.removeItem(FONT_CACHE_TIMESTAMP_KEY);\n } catch {\n // localStorage might be unavailable, ignore\n }\n};\n","import * as React from 'react';\n\nimport { FontOption } from './types';\nimport { getCachedFonts, setCachedFonts, loadFont } from './font-utils';\n\nexport interface UseFontOptionsProps {\n apiKey: string;\n maxFonts?: number;\n onError?: (error: Error) => void;\n}\n\nexport interface UseFontOptionsReturn {\n fonts: FontOption[];\n isLoading: boolean;\n loadFont: typeof loadFont;\n}\n\n/**\n * Hook to fetch and manage Google Fonts options\n * Handles caching, loading state, and font stylesheet loading\n */\nexport function useFontOptions({ apiKey, maxFonts = 500, onError }: UseFontOptionsProps): UseFontOptionsReturn {\n const [fonts, setFonts] = React.useState<FontOption[]>([]);\n const [isLoading, setIsLoading] = React.useState(true);\n\n // Stabilize onError callback reference to prevent effect re-runs when\n // consumers pass inline functions (which create new references on every render)\n const onErrorRef = React.useRef(onError);\n React.useEffect(() => {\n onErrorRef.current = onError;\n }, [onError]);\n\n React.useEffect(() => {\n const fetchFonts = async () => {\n // Check cache first\n const cachedFonts = getCachedFonts();\n if (cachedFonts && cachedFonts.length > 0) {\n setFonts(cachedFonts.slice(0, maxFonts));\n setIsLoading(false);\n\n return;\n }\n\n try {\n const response = await fetch(`https://www.googleapis.com/webfonts/v1/webfonts?key=${apiKey}&sort=popularity`);\n\n if (!response.ok) {\n throw new Error(`Failed to fetch fonts: ${response.statusText}`);\n }\n\n const data = await response.json();\n\n const fontOptions: FontOption[] = data.items.map((fontItem: { family: string; category: string; variants: string[] }) => ({\n value: fontItem.family,\n label: fontItem.family,\n category: fontItem.category,\n variants: fontItem.variants,\n }));\n\n // Cache all fonts, but only display up to maxFonts\n setCachedFonts(fontOptions);\n setFonts(fontOptions.slice(0, maxFonts));\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to fetch fonts');\n // eslint-disable-next-line no-console\n console.error('FontPicker: Failed to fetch Google Fonts', error);\n onErrorRef.current?.(error);\n } finally {\n setIsLoading(false);\n }\n };\n\n void fetchFonts();\n }, [apiKey, maxFonts]);\n\n return { fonts, isLoading, loadFont };\n}\n","import * as React from 'react';\nimport { Check } from '@phosphor-icons/react';\n\nimport { FontOption } from './types';\nimport { loadFont } from './font-utils';\n\nimport { cn } from '@/lib/utils';\nimport { CommandItem } from '@/components/ui/command';\n\nexport interface FontPickerItemProps {\n font: FontOption;\n isSelected: boolean;\n onSelect: () => void;\n}\n\n/**\n * Individual font item in the dropdown list\n * Loads font on mount for preview using Intersection Observer\n * Memoized to prevent unnecessary re-renders\n */\nexport const FontPickerItem = React.memo(function FontPickerItem({ font, isSelected, onSelect }: FontPickerItemProps) {\n const itemRef = React.useRef<HTMLDivElement>(null);\n const [isVisible, setIsVisible] = React.useState(false);\n\n // Use Intersection Observer to lazy-load fonts\n React.useEffect(() => {\n const observer = new IntersectionObserver(\n ([entry]) => {\n if (entry.isIntersecting) {\n setIsVisible(true);\n observer.disconnect();\n }\n },\n { threshold: 0.1 },\n );\n\n if (itemRef.current) {\n observer.observe(itemRef.current);\n }\n\n return () => observer.disconnect();\n }, []);\n\n // Load font when visible\n React.useEffect(() => {\n if (isVisible) {\n loadFont(font.value);\n }\n }, [isVisible, font.value]);\n\n // Build accessible label\n const ariaLabel = `Select ${font.label} font${font.category ? `, ${font.category} category` : ''}`;\n\n return (\n <CommandItem\n ref={itemRef}\n value={font.label}\n onSelect={onSelect}\n className=\"cursor-pointer\"\n aria-label={ariaLabel}\n aria-selected={isSelected}\n >\n <span style={{ fontFamily: isVisible ? font.value : undefined }} className=\"flex-1 truncate\">\n {font.label}\n </span>\n {font.category && <span className=\"text-xs text-muted-foreground capitalize\">{font.category}</span>}\n <Check className={cn('ml-2 h-4 w-4', isSelected ? 'opacity-100' : 'opacity-0')} />\n </CommandItem>\n );\n});\n","import * as React from 'react';\nimport { SpinnerGap } from '@phosphor-icons/react';\n\nimport { useFontOptions } from './use-font-options';\nimport { FontPickerItem } from './font-picker-item';\n\nimport { Combobox } from '@/components/combobox/combobox';\n\nexport interface FontPickerProps {\n /** Currently selected font family */\n value?: string;\n /** Callback when font selection changes */\n onChange?: (fontFamily: string) => void;\n /** Google Fonts API key (required) */\n apiKey: string;\n /** Placeholder text when no font is selected */\n placeholder?: string;\n /** Text shown in search input */\n searchPlaceholder?: string;\n /** Text shown when no fonts match the search */\n noResultsText?: string;\n /** Text shown while fonts are loading */\n loadingText?: string;\n /** Whether the picker is disabled */\n disabled?: boolean;\n /** Additional CSS classes for the trigger button */\n className?: string;\n /** Width of the dropdown (defaults to trigger width) */\n dropdownWidth?: number;\n /** Maximum number of fonts to display (for performance) */\n maxFonts?: number;\n /** Callback when an error occurs fetching fonts */\n onError?: (error: Error) => void;\n}\n\n/**\n * FontPicker - A searchable dropdown for selecting Google Fonts\n *\n * @example\n * ```tsx\n * const [font, setFont] = useState('Inter');\n *\n * <FontPicker\n * apiKey=\"YOUR_API_KEY\"\n * value={font}\n * onChange={setFont}\n * placeholder=\"Select a font\"\n * />\n * ```\n */\nexport function FontPicker({\n value,\n onChange,\n apiKey,\n placeholder = 'Select a font...',\n searchPlaceholder = 'Search fonts...',\n noResultsText = 'No fonts found.',\n loadingText = 'Loading fonts...',\n disabled = false,\n className,\n dropdownWidth,\n maxFonts = 500,\n onError,\n}: FontPickerProps) {\n const { fonts, isLoading, loadFont } = useFontOptions({ apiKey, maxFonts, onError });\n\n // Load font stylesheet when selected\n React.useEffect(() => {\n if (value) {\n loadFont(value);\n }\n }, [value, loadFont]);\n\n // fonts already have value/label which Combobox requires\n const options = fonts;\n\n return (\n <Combobox\n options={options}\n value={value}\n onChange={onChange}\n defaultValueLabel={placeholder}\n placeholder={searchPlaceholder}\n noResultsLabel={noResultsText}\n disabled={disabled || isLoading}\n className={className}\n dropdownWidth={dropdownWidth}\n renderTrigger={({ selectedItem }) => {\n if (isLoading) {\n return (\n <span className=\"flex items-center gap-2 text-muted-foreground\">\n <SpinnerGap className=\"h-4 w-4 animate-spin\" />\n {loadingText}\n </span>\n );\n }\n\n if (selectedItem) {\n return <span style={{ fontFamily: selectedItem.value }}>{selectedItem.label}</span>;\n }\n\n return <span className=\"text-muted-foreground\">{placeholder}</span>;\n }}\n renderItem={({ item, isSelected, onSelect }) => (\n <FontPickerItem font={item} isSelected={isSelected} onSelect={onSelect} />\n )}\n />\n );\n}\n\nexport default FontPicker;\n"],"names":["FontPickerItem"],"mappings":"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"}
1
+ {"version":3,"file":"font-picker.DX1mER3H.js","sources":["../../../src/components/font-picker/font-utils.ts","../../../src/components/font-picker/use-font-options.ts","../../../src/components/font-picker/font-picker-item.tsx","../../../src/components/font-picker/font-picker.tsx"],"sourcesContent":["import { FontOption } from './types';\n\n// Cache keys for localStorage\nconst FONT_CACHE_KEY = 'brainfish-google-fonts-cache';\nconst FONT_CACHE_TIMESTAMP_KEY = 'brainfish-google-fonts-cache-timestamp';\nconst FONT_CACHE_DURATION_MS = 24 * 60 * 60 * 1000; // 24 hours\n\n// Cache for loaded font stylesheets to prevent duplicate loads\nconst loadedFonts = new Set<string>();\n\n/**\n * Get cached fonts from localStorage\n */\nexport const getCachedFonts = (): FontOption[] | null => {\n try {\n const timestamp = localStorage.getItem(FONT_CACHE_TIMESTAMP_KEY);\n if (!timestamp) return null;\n\n const cacheAge = Date.now() - parseInt(timestamp, 10);\n if (cacheAge > FONT_CACHE_DURATION_MS) {\n localStorage.removeItem(FONT_CACHE_KEY);\n localStorage.removeItem(FONT_CACHE_TIMESTAMP_KEY);\n\n return null;\n }\n\n const cached = localStorage.getItem(FONT_CACHE_KEY);\n if (!cached) return null;\n\n return JSON.parse(cached) as FontOption[];\n } catch {\n return null;\n }\n};\n\n/**\n * Save fonts to localStorage cache\n */\nexport const setCachedFonts = (fonts: FontOption[]): void => {\n try {\n localStorage.setItem(FONT_CACHE_KEY, JSON.stringify(fonts));\n localStorage.setItem(FONT_CACHE_TIMESTAMP_KEY, Date.now().toString());\n } catch {\n // localStorage might be full or unavailable, ignore\n }\n};\n\n/**\n * Load a Google Font stylesheet dynamically\n */\nexport const loadFont = (fontFamily: string): void => {\n if (loadedFonts.has(fontFamily)) return;\n\n const link = document.createElement('link');\n link.href = `https://fonts.googleapis.com/css2?family=${fontFamily.replace(/ /g, '+')}&display=swap`;\n link.rel = 'stylesheet';\n document.head.appendChild(link);\n loadedFonts.add(fontFamily);\n};\n\n/**\n * Clear the font cache from localStorage\n * Useful for forcing a fresh fetch of fonts\n */\nexport const clearFontCache = (): void => {\n try {\n localStorage.removeItem(FONT_CACHE_KEY);\n localStorage.removeItem(FONT_CACHE_TIMESTAMP_KEY);\n } catch {\n // localStorage might be unavailable, ignore\n }\n};\n","import * as React from 'react';\n\nimport { FontOption } from './types';\nimport { getCachedFonts, setCachedFonts, loadFont } from './font-utils';\n\nexport interface UseFontOptionsProps {\n apiKey: string;\n maxFonts?: number;\n onError?: (error: Error) => void;\n}\n\nexport interface UseFontOptionsReturn {\n fonts: FontOption[];\n isLoading: boolean;\n loadFont: typeof loadFont;\n}\n\n/**\n * Hook to fetch and manage Google Fonts options\n * Handles caching, loading state, and font stylesheet loading\n */\nexport function useFontOptions({ apiKey, maxFonts = 500, onError }: UseFontOptionsProps): UseFontOptionsReturn {\n const [fonts, setFonts] = React.useState<FontOption[]>([]);\n const [isLoading, setIsLoading] = React.useState(true);\n\n // Stabilize onError callback reference to prevent effect re-runs when\n // consumers pass inline functions (which create new references on every render)\n const onErrorRef = React.useRef(onError);\n React.useEffect(() => {\n onErrorRef.current = onError;\n }, [onError]);\n\n React.useEffect(() => {\n const fetchFonts = async () => {\n // Check cache first\n const cachedFonts = getCachedFonts();\n if (cachedFonts && cachedFonts.length > 0) {\n setFonts(cachedFonts.slice(0, maxFonts));\n setIsLoading(false);\n\n return;\n }\n\n try {\n const response = await fetch(`https://www.googleapis.com/webfonts/v1/webfonts?key=${apiKey}&sort=popularity`);\n\n if (!response.ok) {\n throw new Error(`Failed to fetch fonts: ${response.statusText}`);\n }\n\n const data = await response.json();\n\n const fontOptions: FontOption[] = data.items.map((fontItem: { family: string; category: string; variants: string[] }) => ({\n value: fontItem.family,\n label: fontItem.family,\n category: fontItem.category,\n variants: fontItem.variants,\n }));\n\n // Cache all fonts, but only display up to maxFonts\n setCachedFonts(fontOptions);\n setFonts(fontOptions.slice(0, maxFonts));\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to fetch fonts');\n\n console.error('FontPicker: Failed to fetch Google Fonts', error);\n onErrorRef.current?.(error);\n } finally {\n setIsLoading(false);\n }\n };\n\n void fetchFonts();\n }, [apiKey, maxFonts]);\n\n return { fonts, isLoading, loadFont };\n}\n","import * as React from 'react';\nimport { Check } from '@phosphor-icons/react';\n\nimport { FontOption } from './types';\nimport { loadFont } from './font-utils';\n\nimport { cn } from '@/lib/utils';\nimport { CommandItem } from '@/components/ui/command';\n\nexport interface FontPickerItemProps {\n font: FontOption;\n isSelected: boolean;\n onSelect: () => void;\n}\n\n/**\n * Individual font item in the dropdown list\n * Loads font on mount for preview using Intersection Observer\n * Memoized to prevent unnecessary re-renders\n */\nexport const FontPickerItem = React.memo(function FontPickerItem({ font, isSelected, onSelect }: FontPickerItemProps) {\n const itemRef = React.useRef<HTMLDivElement>(null);\n const [isVisible, setIsVisible] = React.useState(false);\n\n // Use Intersection Observer to lazy-load fonts\n React.useEffect(() => {\n const observer = new IntersectionObserver(\n ([entry]) => {\n if (entry.isIntersecting) {\n setIsVisible(true);\n observer.disconnect();\n }\n },\n { threshold: 0.1 },\n );\n\n if (itemRef.current) {\n observer.observe(itemRef.current);\n }\n\n return () => observer.disconnect();\n }, []);\n\n // Load font when visible\n React.useEffect(() => {\n if (isVisible) {\n loadFont(font.value);\n }\n }, [isVisible, font.value]);\n\n // Build accessible label\n const ariaLabel = `Select ${font.label} font${font.category ? `, ${font.category} category` : ''}`;\n\n return (\n <CommandItem\n ref={itemRef}\n value={font.label}\n onSelect={onSelect}\n className=\"cursor-pointer\"\n aria-label={ariaLabel}\n aria-selected={isSelected}\n >\n <span style={{ fontFamily: isVisible ? font.value : undefined }} className=\"flex-1 truncate\">\n {font.label}\n </span>\n {font.category && <span className=\"text-xs text-muted-foreground capitalize\">{font.category}</span>}\n <Check className={cn('ml-2 h-4 w-4', isSelected ? 'opacity-100' : 'opacity-0')} />\n </CommandItem>\n );\n});\n","import * as React from 'react';\nimport { SpinnerGap } from '@phosphor-icons/react';\n\nimport { useFontOptions } from './use-font-options';\nimport { FontPickerItem } from './font-picker-item';\n\nimport { Combobox } from '@/components/combobox/combobox';\n\nexport interface FontPickerProps {\n /** Currently selected font family */\n value?: string;\n /** Callback when font selection changes */\n onChange?: (fontFamily: string) => void;\n /** Google Fonts API key (required) */\n apiKey: string;\n /** Placeholder text when no font is selected */\n placeholder?: string;\n /** Text shown in search input */\n searchPlaceholder?: string;\n /** Text shown when no fonts match the search */\n noResultsText?: string;\n /** Text shown while fonts are loading */\n loadingText?: string;\n /** Whether the picker is disabled */\n disabled?: boolean;\n /** Additional CSS classes for the trigger button */\n className?: string;\n /** Width of the dropdown (defaults to trigger width) */\n dropdownWidth?: number;\n /** Maximum number of fonts to display (for performance) */\n maxFonts?: number;\n /** Callback when an error occurs fetching fonts */\n onError?: (error: Error) => void;\n}\n\n/**\n * FontPicker - A searchable dropdown for selecting Google Fonts\n *\n * @example\n * ```tsx\n * const [font, setFont] = useState('Inter');\n *\n * <FontPicker\n * apiKey=\"YOUR_API_KEY\"\n * value={font}\n * onChange={setFont}\n * placeholder=\"Select a font\"\n * />\n * ```\n */\nexport function FontPicker({\n value,\n onChange,\n apiKey,\n placeholder = 'Select a font...',\n searchPlaceholder = 'Search fonts...',\n noResultsText = 'No fonts found.',\n loadingText = 'Loading fonts...',\n disabled = false,\n className,\n dropdownWidth,\n maxFonts = 500,\n onError,\n}: FontPickerProps) {\n const { fonts, isLoading, loadFont } = useFontOptions({ apiKey, maxFonts, onError });\n\n // Load font stylesheet when selected\n React.useEffect(() => {\n if (value) {\n loadFont(value);\n }\n }, [value, loadFont]);\n\n // fonts already have value/label which Combobox requires\n const options = fonts;\n\n return (\n <Combobox\n options={options}\n value={value}\n onChange={onChange}\n defaultValueLabel={placeholder}\n placeholder={searchPlaceholder}\n noResultsLabel={noResultsText}\n disabled={disabled || isLoading}\n className={className}\n dropdownWidth={dropdownWidth}\n renderTrigger={({ selectedItem }) => {\n if (isLoading) {\n return (\n <span className=\"flex items-center gap-2 text-muted-foreground\">\n <SpinnerGap className=\"h-4 w-4 animate-spin\" />\n {loadingText}\n </span>\n );\n }\n\n if (selectedItem) {\n return <span style={{ fontFamily: selectedItem.value }}>{selectedItem.label}</span>;\n }\n\n return <span className=\"text-muted-foreground\">{placeholder}</span>;\n }}\n renderItem={({ item, isSelected, onSelect }) => (\n <FontPickerItem font={item} isSelected={isSelected} onSelect={onSelect} />\n )}\n />\n );\n}\n\nexport default FontPicker;\n"],"names":["FontPickerItem"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"hooks.CKojclfe.js","sources":["../../../src/lib/fetchClient.ts","../../../src/lib/api.ts","../../../src/lib/hooks.ts"],"sourcesContent":["interface RetryOptions {\n maxRetries?: number;\n baseDelay?: number;\n shouldRetry?: (error: Error, attempt: number) => boolean | Promise<boolean>;\n}\n\ninterface FallbackConfig {\n emptyResponse?: string;\n errorResponse?: string;\n}\n\ninterface RequestConfig<T> extends Omit<RequestInit, 'body'> {\n retry?: RetryOptions;\n body?: T;\n isStream?: boolean;\n fallback?: FallbackConfig;\n}\n\ninterface APIError extends Error {\n status?: number;\n data?: unknown;\n}\n\nconst defaultRetryOptions: Required<RetryOptions> = {\n maxRetries: 3,\n baseDelay: 1000,\n shouldRetry: (error: Error) => {\n // By default, retry on network errors and 5xx server errors\n if (error instanceof TypeError) {\n return true; // Network errors\n }\n\n const apiError = error as APIError;\n\n return apiError.status ? apiError.status >= 500 : false;\n },\n};\n\nconst defaultFallbackConfig: Required<FallbackConfig> = {\n emptyResponse: \"I apologize, but I couldn't generate an answer at this time. Please try again.\",\n errorResponse: 'I encountered an error while processing your request. Please try again.',\n};\n\nlet globalRetryOptions: Required<RetryOptions> = { ...defaultRetryOptions };\nlet globalFallbackConfig: Required<FallbackConfig> = { ...defaultFallbackConfig };\n\nexport function configureRetry(options: RetryOptions): void {\n globalRetryOptions = {\n ...defaultRetryOptions,\n ...options,\n };\n}\n\nexport function configureFallback(config: FallbackConfig): void {\n globalFallbackConfig = {\n ...defaultFallbackConfig,\n ...config,\n };\n}\n\nasync function delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport async function makeRequest<TResponse, TBody = unknown>(url: string | URL, config: RequestConfig<TBody> = {}): Promise<TResponse> {\n const { retry = {}, headers = {}, body, isStream = false, fallback = {}, ...fetchConfig } = config;\n const retryOptions = {\n ...globalRetryOptions,\n ...retry,\n };\n const fallbackOptions = {\n ...globalFallbackConfig,\n ...fallback,\n };\n\n let attempt = 1;\n // eslint-disable-next-line no-constant-condition\n while (true) {\n try {\n const response = await fetch(url, {\n ...fetchConfig,\n headers: {\n 'Content-Type': 'application/json',\n ...headers,\n },\n body: body ? JSON.stringify(body) : undefined,\n });\n\n if (response.ok) {\n // For streaming responses, return the response object directly\n if (isStream) {\n if (!response.ok) {\n throw new Error('Failed to fetch answer');\n }\n\n return response as unknown as TResponse;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const data = await response.json();\n // Check for empty response in JSON\n if (!data || (typeof data === 'object' && Object.keys(data).length === 0)) {\n const emptyError = new Error(fallbackOptions.emptyResponse);\n emptyError.name = 'EmptyResponseError';\n throw emptyError;\n }\n\n return data as TResponse;\n }\n\n const error = new Error(fallbackOptions.errorResponse) as APIError;\n error.status = response.status;\n error.data = await response.json().catch(() => undefined);\n throw error;\n } catch (error) {\n const shouldAttemptRetry = await Promise.resolve(retryOptions.shouldRetry(error as Error, attempt));\n\n if (attempt >= retryOptions.maxRetries || !shouldAttemptRetry) {\n if (error instanceof Error && error.name === 'EmptyResponseError') {\n return { message: error.message } as TResponse;\n }\n throw error;\n }\n\n console.warn(`Request failed, attempt ${attempt} of ${retryOptions.maxRetries}. Retrying...`);\n\n // Exponential backoff with jitter to prevent thundering herd\n const jitter = Math.random() * 0.3 + 0.85; // Random between 0.85-1.15\n await delay(retryOptions.baseDelay * Math.pow(2, attempt - 1) * jitter);\n attempt++;\n }\n }\n}\n","import { createId } from '@paralleldrive/cuid2';\n\nimport { makeRequest } from './fetchClient';\n\nimport {\n SearchResponse,\n FeedbackResponse,\n APIFeedbackResponse,\n SearchResult,\n ActionInputs,\n ClientExecutionResult,\n SearchQueryAttributes,\n} from '@/components/chat-search/types';\n\n// Retry configurations for different endpoints\nconst searchRetryConfig = {\n maxRetries: 3,\n baseDelay: 300, // Start with 300ms delay\n shouldRetry: (error: Error) => {\n if (error instanceof TypeError) {\n return true; // Network errors\n }\n const apiError = error as { status?: number };\n // Retry on server errors (5xx) and rate limits (429)\n\n return apiError.status ? apiError.status >= 500 || apiError.status === 429 : false;\n },\n};\n\nconst answerRetryConfig = {\n maxRetries: 3,\n baseDelay: 500, // Start with 500ms delay for streaming responses\n shouldRetry: (error: Error) => {\n if (error instanceof TypeError) {\n return true; // Network errors\n }\n const apiError = error as { status?: number };\n // Only retry on server errors, not on client errors\n\n return apiError.status ? apiError.status >= 500 : false;\n },\n};\n\ninterface APISearchResponse {\n data: Array<{\n id: string;\n title: string;\n url: string;\n }>;\n searchQuery: {\n id: string;\n };\n isAgenticResponse?: boolean;\n}\n\nexport async function searchApi({\n endpoint,\n query,\n collectionId,\n headers,\n conversationId,\n attributes,\n secretAttributes,\n allowedRegions,\n source,\n}: {\n endpoint: string;\n query: string;\n collectionId?: string;\n headers?: Record<string, string>;\n conversationId?: string;\n attributes?: SearchQueryAttributes;\n secretAttributes?: string;\n allowedRegions?: string[];\n source?: string;\n}): Promise<SearchResponse> {\n const payload = await makeRequest<\n APISearchResponse,\n {\n query: string;\n limit: number;\n collectionId?: string;\n conversationId?: string;\n source?: string;\n }\n >(endpoint, {\n method: 'POST',\n headers,\n retry: searchRetryConfig,\n body: {\n query,\n limit: 5,\n ...(collectionId && collectionId !== 'all' && { collectionId }),\n ...(conversationId && { conversationId }),\n ...(attributes && { attributes }),\n ...(secretAttributes && { secretAttributes }),\n ...(allowedRegions && { allowedRegions }),\n ...(source && { source }),\n },\n });\n\n return {\n results: payload?.data,\n searchQueryId: payload?.searchQuery?.id,\n isAgenticResponse: payload?.isAgenticResponse,\n };\n}\n\nexport async function fetchAnswer({\n endpoint,\n searchQueryId,\n headers,\n conversationId,\n}: {\n endpoint: string;\n searchQueryId: string;\n headers?: Record<string, string>;\n conversationId?: string;\n}): Promise<Response> {\n return makeRequest<\n Response,\n {\n searchQueryId: string;\n conversationId?: string;\n }\n >(endpoint, {\n method: 'POST',\n headers,\n retry: answerRetryConfig,\n isStream: true,\n fallback: {\n emptyResponse: \"I apologize, but I couldn't generate an answer for your query. Please try rephrasing your question.\",\n errorResponse: 'I encountered an issue while generating the answer. Please try again in a moment.',\n },\n body: {\n searchQueryId,\n ...(conversationId && { conversationId }),\n },\n });\n}\n\nexport async function fetchAnswerStream({\n endpoint,\n headers,\n body,\n}: {\n endpoint: string;\n headers?: Record<string, string>;\n body:\n | {\n type: 'generate-for-query';\n conversationId?: string;\n searchQueryId: string;\n profileId?: string;\n context?: { type: 'text'; text: string }[];\n }\n | {\n type: 'invoke-action';\n conversationId?: string;\n searchQueryId: string;\n searchIntentId: string;\n actionId: string;\n parameters: ActionInputs;\n profileId?: string;\n }\n | {\n type: 'client-execution-result';\n conversationId?: string;\n searchQueryId: string;\n searchIntentId: string;\n actionId: string;\n results: ClientExecutionResult;\n profileId?: string;\n };\n}): Promise<Response> {\n return makeRequest<Response, typeof body>(endpoint, {\n method: 'POST',\n headers,\n retry: answerRetryConfig,\n isStream: true,\n fallback: {\n emptyResponse: \"I apologize, but I couldn't generate an answer for your query. Please try rephrasing your question.\",\n errorResponse: 'I encountered an issue while generating the answer. Please try again in a moment.',\n },\n body,\n });\n}\n\n// Helper function to generate conversation ID\nexport function createConversationId(): string {\n return createId();\n}\n\nexport async function sendFeedback({\n endpoint,\n searchQueryId,\n response,\n headers,\n conversationId,\n}: {\n endpoint: string;\n searchQueryId: string;\n response: FeedbackResponse;\n headers?: Record<string, string>;\n conversationId?: string;\n}): Promise<void> {\n await makeRequest<\n void,\n {\n searchQueryId: string;\n response: FeedbackResponse;\n conversationId?: string;\n }\n >(endpoint, {\n method: 'POST',\n headers,\n body: {\n searchQueryId,\n response,\n ...(conversationId && { conversationId }),\n },\n });\n}\n\nexport async function sendFeedbackReason({\n endpoint,\n searchQueryId,\n reason,\n headers,\n}: {\n endpoint: string;\n searchQueryId: string;\n reason: string;\n headers?: Record<string, string>;\n conversationId?: string;\n}): Promise<void> {\n await makeRequest<\n void,\n {\n searchQueryId: string;\n reason: string;\n conversationId?: string;\n }\n >(endpoint, {\n method: 'POST',\n headers,\n body: {\n searchQueryId,\n reason,\n },\n });\n}\n\nexport interface ConversationResponse {\n data: Array<{\n question: string;\n answer: string;\n searchQueryId: string;\n searchResults?: SearchResult[];\n searchIntentId?: string;\n feedback?: APIFeedbackResponse;\n }>;\n}\n\nexport async function loadConversation({\n endpoint,\n conversationId,\n headers,\n signal,\n}: {\n endpoint: string;\n conversationId: string;\n headers?: Record<string, string>;\n signal?: AbortSignal;\n}): Promise<{ data: ConversationResponse['data'] }> {\n const payload = await makeRequest<\n ConversationResponse,\n {\n conversationId: string;\n }\n >(endpoint, {\n method: 'POST',\n headers,\n body: { conversationId },\n signal,\n });\n\n return { data: payload.data };\n}\n\ninterface FollowUpQuestionsResponse {\n questions: string[];\n}\n\nexport async function fetchFollowUpQuestions({\n endpoint,\n searchQueryId,\n headers,\n conversationId,\n}: {\n endpoint: string;\n searchQueryId: string;\n headers?: Record<string, string>;\n conversationId?: string;\n}): Promise<string[]> {\n const data = await makeRequest<\n FollowUpQuestionsResponse,\n {\n searchQueryId: string;\n conversationId?: string;\n }\n >(endpoint, {\n method: 'POST',\n headers,\n body: {\n searchQueryId,\n ...(conversationId && { conversationId }),\n },\n });\n\n return data.questions;\n}\n\ninterface AutocompleteResponse {\n response: {\n results: Array<{\n id: string;\n question: string;\n }>;\n };\n}\n\nexport async function fetchAutocomplete({\n endpoint,\n query,\n collectionId,\n headers,\n conversationId,\n allowedRegions,\n}: {\n endpoint: string;\n query: string;\n collectionId?: string;\n headers?: Record<string, string>;\n conversationId?: string;\n allowedRegions?: string[];\n}): Promise<AutocompleteResponse> {\n const data = await makeRequest<\n AutocompleteResponse,\n {\n query: string;\n collectionId?: string;\n conversationId?: string;\n allowedRegions?: string[];\n }\n >(endpoint, {\n method: 'POST',\n headers,\n retry: searchRetryConfig,\n body: {\n query,\n ...(collectionId && collectionId !== 'all' && { collectionId }),\n ...(conversationId && { conversationId }),\n ...(allowedRegions && { allowedRegions }),\n },\n });\n\n return data;\n}\n","import { useState, useEffect, useCallback } from 'react';\n\nimport { fetchAutocomplete } from './api';\n\nimport type { Suggestion } from '@/components/chat-search/types';\n\nexport function useDebounce<T>(value: T, delay: number): T {\n const [debouncedValue, setDebouncedValue] = useState<T>(value);\n\n useEffect(() => {\n const handler = setTimeout(() => {\n setDebouncedValue(value);\n }, delay);\n\n return () => {\n clearTimeout(handler);\n };\n }, [value, delay]);\n\n return debouncedValue;\n}\n\nexport function useAutocomplete({\n query,\n endpoint,\n allowedRegions,\n collectionId,\n headers,\n conversationId,\n delay = 300,\n}: {\n query: string;\n endpoint: string;\n allowedRegions?: string[];\n collectionId?: string;\n headers?: Record<string, string>;\n conversationId?: string;\n delay?: number;\n}) {\n const [suggestions, setSuggestions] = useState<Suggestion[]>([]);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<string>();\n\n const debouncedQuery = useDebounce(query, delay);\n\n const fetchSuggestions = useCallback(async () => {\n if (!debouncedQuery.trim()) {\n setSuggestions([]);\n\n return;\n }\n\n setIsLoading(true);\n setError(undefined);\n\n try {\n const payload = await fetchAutocomplete({\n endpoint,\n query: debouncedQuery,\n collectionId,\n headers,\n conversationId,\n allowedRegions,\n });\n if (payload?.response?.results) {\n // move suggestions that don't prefix with the debounced question to the bottom\n const lowerCaseQuery = debouncedQuery.toLowerCase();\n const sortedSuggestions = payload.response.results.sort((a: { question: string }, b: { question: string }) => {\n const aStartsWithQuery = a.question.toLowerCase().startsWith(lowerCaseQuery);\n const bStartsWithQuery = b.question.toLowerCase().startsWith(lowerCaseQuery);\n\n if (aStartsWithQuery && !bStartsWithQuery) return -1;\n if (!aStartsWithQuery && bStartsWithQuery) return 1;\n\n return 0; // preserve original order for items in same category\n });\n\n setSuggestions(sortedSuggestions);\n } else {\n setSuggestions([]);\n }\n } catch (err) {\n console.error('Error fetching autocomplete suggestions:', err);\n setError(err instanceof Error ? err.message : 'Failed to fetch suggestions');\n setSuggestions([]);\n } finally {\n setIsLoading(false);\n }\n }, [debouncedQuery, endpoint, collectionId, headers, conversationId, allowedRegions]);\n\n useEffect(() => {\n void fetchSuggestions();\n }, [fetchSuggestions]);\n\n return { suggestions, isLoading, error };\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"hooks.CKojclfe.js","sources":["../../../src/lib/fetchClient.ts","../../../src/lib/api.ts","../../../src/lib/hooks.ts"],"sourcesContent":["interface RetryOptions {\n maxRetries?: number;\n baseDelay?: number;\n shouldRetry?: (error: Error, attempt: number) => boolean | Promise<boolean>;\n}\n\ninterface FallbackConfig {\n emptyResponse?: string;\n errorResponse?: string;\n}\n\ninterface RequestConfig<T> extends Omit<RequestInit, 'body'> {\n retry?: RetryOptions;\n body?: T;\n isStream?: boolean;\n fallback?: FallbackConfig;\n}\n\ninterface APIError extends Error {\n status?: number;\n data?: unknown;\n}\n\nconst defaultRetryOptions: Required<RetryOptions> = {\n maxRetries: 3,\n baseDelay: 1000,\n shouldRetry: (error: Error) => {\n // By default, retry on network errors and 5xx server errors\n if (error instanceof TypeError) {\n return true; // Network errors\n }\n\n const apiError = error as APIError;\n\n return apiError.status ? apiError.status >= 500 : false;\n },\n};\n\nconst defaultFallbackConfig: Required<FallbackConfig> = {\n emptyResponse: \"I apologize, but I couldn't generate an answer at this time. Please try again.\",\n errorResponse: 'I encountered an error while processing your request. Please try again.',\n};\n\nlet globalRetryOptions: Required<RetryOptions> = { ...defaultRetryOptions };\nlet globalFallbackConfig: Required<FallbackConfig> = { ...defaultFallbackConfig };\n\nexport function configureRetry(options: RetryOptions): void {\n globalRetryOptions = {\n ...defaultRetryOptions,\n ...options,\n };\n}\n\nexport function configureFallback(config: FallbackConfig): void {\n globalFallbackConfig = {\n ...defaultFallbackConfig,\n ...config,\n };\n}\n\nasync function delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport async function makeRequest<TResponse, TBody = unknown>(url: string | URL, config: RequestConfig<TBody> = {}): Promise<TResponse> {\n const { retry = {}, headers = {}, body, isStream = false, fallback = {}, ...fetchConfig } = config;\n const retryOptions = {\n ...globalRetryOptions,\n ...retry,\n };\n const fallbackOptions = {\n ...globalFallbackConfig,\n ...fallback,\n };\n\n let attempt = 1;\n\n while (true) {\n try {\n const response = await fetch(url, {\n ...fetchConfig,\n headers: {\n 'Content-Type': 'application/json',\n ...headers,\n },\n body: body ? JSON.stringify(body) : undefined,\n });\n\n if (response.ok) {\n // For streaming responses, return the response object directly\n if (isStream) {\n if (!response.ok) {\n throw new Error('Failed to fetch answer');\n }\n\n return response as unknown as TResponse;\n }\n\n const data = await response.json();\n // Check for empty response in JSON\n if (!data || (typeof data === 'object' && Object.keys(data).length === 0)) {\n const emptyError = new Error(fallbackOptions.emptyResponse);\n emptyError.name = 'EmptyResponseError';\n throw emptyError;\n }\n\n return data as TResponse;\n }\n\n const error = new Error(fallbackOptions.errorResponse) as APIError;\n error.status = response.status;\n error.data = await response.json().catch(() => undefined);\n throw error;\n } catch (error) {\n const shouldAttemptRetry = await Promise.resolve(retryOptions.shouldRetry(error as Error, attempt));\n\n if (attempt >= retryOptions.maxRetries || !shouldAttemptRetry) {\n if (error instanceof Error && error.name === 'EmptyResponseError') {\n return { message: error.message } as TResponse;\n }\n throw error;\n }\n\n console.warn(`Request failed, attempt ${attempt} of ${retryOptions.maxRetries}. Retrying...`);\n\n // Exponential backoff with jitter to prevent thundering herd\n const jitter = Math.random() * 0.3 + 0.85; // Random between 0.85-1.15\n await delay(retryOptions.baseDelay * Math.pow(2, attempt - 1) * jitter);\n attempt++;\n }\n }\n}\n","import { createId } from '@paralleldrive/cuid2';\n\nimport { makeRequest } from './fetchClient';\n\nimport {\n SearchResponse,\n FeedbackResponse,\n APIFeedbackResponse,\n SearchResult,\n ActionInputs,\n ClientExecutionResult,\n SearchQueryAttributes,\n} from '@/components/chat-search/types';\n\n// Retry configurations for different endpoints\nconst searchRetryConfig = {\n maxRetries: 3,\n baseDelay: 300, // Start with 300ms delay\n shouldRetry: (error: Error) => {\n if (error instanceof TypeError) {\n return true; // Network errors\n }\n const apiError = error as { status?: number };\n // Retry on server errors (5xx) and rate limits (429)\n\n return apiError.status ? apiError.status >= 500 || apiError.status === 429 : false;\n },\n};\n\nconst answerRetryConfig = {\n maxRetries: 3,\n baseDelay: 500, // Start with 500ms delay for streaming responses\n shouldRetry: (error: Error) => {\n if (error instanceof TypeError) {\n return true; // Network errors\n }\n const apiError = error as { status?: number };\n // Only retry on server errors, not on client errors\n\n return apiError.status ? apiError.status >= 500 : false;\n },\n};\n\ninterface APISearchResponse {\n data: Array<{\n id: string;\n title: string;\n url: string;\n }>;\n searchQuery: {\n id: string;\n };\n isAgenticResponse?: boolean;\n}\n\nexport async function searchApi({\n endpoint,\n query,\n collectionId,\n headers,\n conversationId,\n attributes,\n secretAttributes,\n allowedRegions,\n source,\n}: {\n endpoint: string;\n query: string;\n collectionId?: string;\n headers?: Record<string, string>;\n conversationId?: string;\n attributes?: SearchQueryAttributes;\n secretAttributes?: string;\n allowedRegions?: string[];\n source?: string;\n}): Promise<SearchResponse> {\n const payload = await makeRequest<\n APISearchResponse,\n {\n query: string;\n limit: number;\n collectionId?: string;\n conversationId?: string;\n source?: string;\n }\n >(endpoint, {\n method: 'POST',\n headers,\n retry: searchRetryConfig,\n body: {\n query,\n limit: 5,\n ...(collectionId && collectionId !== 'all' && { collectionId }),\n ...(conversationId && { conversationId }),\n ...(attributes && { attributes }),\n ...(secretAttributes && { secretAttributes }),\n ...(allowedRegions && { allowedRegions }),\n ...(source && { source }),\n },\n });\n\n return {\n results: payload?.data,\n searchQueryId: payload?.searchQuery?.id,\n isAgenticResponse: payload?.isAgenticResponse,\n };\n}\n\nexport async function fetchAnswer({\n endpoint,\n searchQueryId,\n headers,\n conversationId,\n}: {\n endpoint: string;\n searchQueryId: string;\n headers?: Record<string, string>;\n conversationId?: string;\n}): Promise<Response> {\n return makeRequest<\n Response,\n {\n searchQueryId: string;\n conversationId?: string;\n }\n >(endpoint, {\n method: 'POST',\n headers,\n retry: answerRetryConfig,\n isStream: true,\n fallback: {\n emptyResponse: \"I apologize, but I couldn't generate an answer for your query. Please try rephrasing your question.\",\n errorResponse: 'I encountered an issue while generating the answer. Please try again in a moment.',\n },\n body: {\n searchQueryId,\n ...(conversationId && { conversationId }),\n },\n });\n}\n\nexport async function fetchAnswerStream({\n endpoint,\n headers,\n body,\n}: {\n endpoint: string;\n headers?: Record<string, string>;\n body:\n | {\n type: 'generate-for-query';\n conversationId?: string;\n searchQueryId: string;\n profileId?: string;\n context?: { type: 'text'; text: string }[];\n }\n | {\n type: 'invoke-action';\n conversationId?: string;\n searchQueryId: string;\n searchIntentId: string;\n actionId: string;\n parameters: ActionInputs;\n profileId?: string;\n }\n | {\n type: 'client-execution-result';\n conversationId?: string;\n searchQueryId: string;\n searchIntentId: string;\n actionId: string;\n results: ClientExecutionResult;\n profileId?: string;\n };\n}): Promise<Response> {\n return makeRequest<Response, typeof body>(endpoint, {\n method: 'POST',\n headers,\n retry: answerRetryConfig,\n isStream: true,\n fallback: {\n emptyResponse: \"I apologize, but I couldn't generate an answer for your query. Please try rephrasing your question.\",\n errorResponse: 'I encountered an issue while generating the answer. Please try again in a moment.',\n },\n body,\n });\n}\n\n// Helper function to generate conversation ID\nexport function createConversationId(): string {\n return createId();\n}\n\nexport async function sendFeedback({\n endpoint,\n searchQueryId,\n response,\n headers,\n conversationId,\n}: {\n endpoint: string;\n searchQueryId: string;\n response: FeedbackResponse;\n headers?: Record<string, string>;\n conversationId?: string;\n}): Promise<void> {\n await makeRequest<\n void,\n {\n searchQueryId: string;\n response: FeedbackResponse;\n conversationId?: string;\n }\n >(endpoint, {\n method: 'POST',\n headers,\n body: {\n searchQueryId,\n response,\n ...(conversationId && { conversationId }),\n },\n });\n}\n\nexport async function sendFeedbackReason({\n endpoint,\n searchQueryId,\n reason,\n headers,\n}: {\n endpoint: string;\n searchQueryId: string;\n reason: string;\n headers?: Record<string, string>;\n conversationId?: string;\n}): Promise<void> {\n await makeRequest<\n void,\n {\n searchQueryId: string;\n reason: string;\n conversationId?: string;\n }\n >(endpoint, {\n method: 'POST',\n headers,\n body: {\n searchQueryId,\n reason,\n },\n });\n}\n\nexport interface ConversationResponse {\n data: Array<{\n question: string;\n answer: string;\n searchQueryId: string;\n searchResults?: SearchResult[];\n searchIntentId?: string;\n feedback?: APIFeedbackResponse;\n }>;\n}\n\nexport async function loadConversation({\n endpoint,\n conversationId,\n headers,\n signal,\n}: {\n endpoint: string;\n conversationId: string;\n headers?: Record<string, string>;\n signal?: AbortSignal;\n}): Promise<{ data: ConversationResponse['data'] }> {\n const payload = await makeRequest<\n ConversationResponse,\n {\n conversationId: string;\n }\n >(endpoint, {\n method: 'POST',\n headers,\n body: { conversationId },\n signal,\n });\n\n return { data: payload.data };\n}\n\ninterface FollowUpQuestionsResponse {\n questions: string[];\n}\n\nexport async function fetchFollowUpQuestions({\n endpoint,\n searchQueryId,\n headers,\n conversationId,\n}: {\n endpoint: string;\n searchQueryId: string;\n headers?: Record<string, string>;\n conversationId?: string;\n}): Promise<string[]> {\n const data = await makeRequest<\n FollowUpQuestionsResponse,\n {\n searchQueryId: string;\n conversationId?: string;\n }\n >(endpoint, {\n method: 'POST',\n headers,\n body: {\n searchQueryId,\n ...(conversationId && { conversationId }),\n },\n });\n\n return data.questions;\n}\n\ninterface AutocompleteResponse {\n response: {\n results: Array<{\n id: string;\n question: string;\n }>;\n };\n}\n\nexport async function fetchAutocomplete({\n endpoint,\n query,\n collectionId,\n headers,\n conversationId,\n allowedRegions,\n}: {\n endpoint: string;\n query: string;\n collectionId?: string;\n headers?: Record<string, string>;\n conversationId?: string;\n allowedRegions?: string[];\n}): Promise<AutocompleteResponse> {\n const data = await makeRequest<\n AutocompleteResponse,\n {\n query: string;\n collectionId?: string;\n conversationId?: string;\n allowedRegions?: string[];\n }\n >(endpoint, {\n method: 'POST',\n headers,\n retry: searchRetryConfig,\n body: {\n query,\n ...(collectionId && collectionId !== 'all' && { collectionId }),\n ...(conversationId && { conversationId }),\n ...(allowedRegions && { allowedRegions }),\n },\n });\n\n return data;\n}\n","import { useState, useEffect, useCallback } from 'react';\n\nimport { fetchAutocomplete } from './api';\n\nimport type { Suggestion } from '@/components/chat-search/types';\n\nexport function useDebounce<T>(value: T, delay: number): T {\n const [debouncedValue, setDebouncedValue] = useState<T>(value);\n\n useEffect(() => {\n const handler = setTimeout(() => {\n setDebouncedValue(value);\n }, delay);\n\n return () => {\n clearTimeout(handler);\n };\n }, [value, delay]);\n\n return debouncedValue;\n}\n\nexport function useAutocomplete({\n query,\n endpoint,\n allowedRegions,\n collectionId,\n headers,\n conversationId,\n delay = 300,\n}: {\n query: string;\n endpoint: string;\n allowedRegions?: string[];\n collectionId?: string;\n headers?: Record<string, string>;\n conversationId?: string;\n delay?: number;\n}) {\n const [suggestions, setSuggestions] = useState<Suggestion[]>([]);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<string>();\n\n const debouncedQuery = useDebounce(query, delay);\n\n const fetchSuggestions = useCallback(async () => {\n if (!debouncedQuery.trim()) {\n setSuggestions([]);\n\n return;\n }\n\n setIsLoading(true);\n setError(undefined);\n\n try {\n const payload = await fetchAutocomplete({\n endpoint,\n query: debouncedQuery,\n collectionId,\n headers,\n conversationId,\n allowedRegions,\n });\n if (payload?.response?.results) {\n // move suggestions that don't prefix with the debounced question to the bottom\n const lowerCaseQuery = debouncedQuery.toLowerCase();\n const sortedSuggestions = payload.response.results.sort((a: { question: string }, b: { question: string }) => {\n const aStartsWithQuery = a.question.toLowerCase().startsWith(lowerCaseQuery);\n const bStartsWithQuery = b.question.toLowerCase().startsWith(lowerCaseQuery);\n\n if (aStartsWithQuery && !bStartsWithQuery) return -1;\n if (!aStartsWithQuery && bStartsWithQuery) return 1;\n\n return 0; // preserve original order for items in same category\n });\n\n setSuggestions(sortedSuggestions);\n } else {\n setSuggestions([]);\n }\n } catch (err) {\n console.error('Error fetching autocomplete suggestions:', err);\n setError(err instanceof Error ? err.message : 'Failed to fetch suggestions');\n setSuggestions([]);\n } finally {\n setIsLoading(false);\n }\n }, [debouncedQuery, endpoint, collectionId, headers, conversationId, allowedRegions]);\n\n useEffect(() => {\n void fetchSuggestions();\n }, [fetchSuggestions]);\n\n return { suggestions, isLoading, error };\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,72 @@
1
+ import * as React from 'react';
2
+ import React__default, { useState, useCallback, useMemo, createContext, useContext, forwardRef } from 'react';
3
+ import { Circle } from '@phosphor-icons/react';
4
+ import { c as cn } from './utils.Cwtlq8dh.js';
5
+ import { f as formatDistance, S as StatusBadge } from './status-badge.DkPNh30S.js';
6
+
7
+ function EditCount({ count }) {
8
+ return /* @__PURE__ */ React__default.createElement("span", { className: "inline-flex items-center gap-0.5" }, /* @__PURE__ */ React__default.createElement(Circle, { size: 8, weight: "fill", "aria-hidden": "true", className: "text-blue" }), /* @__PURE__ */ React__default.createElement("span", { className: "text-xs leading-4 text-subtle" }, count, " ", count === 1 ? "edit" : "edits"));
9
+ }
10
+
11
+ const ReviewListItem = React.forwardRef(function ReviewListItem2({ title, timestamp, icon, description, isNew, editCount, isSelected = false, className, ...props }, ref) {
12
+ let status;
13
+ if (isNew) {
14
+ status = /* @__PURE__ */ React.createElement(StatusBadge, { variant: "success" }, "New");
15
+ } else if (editCount) {
16
+ status = /* @__PURE__ */ React.createElement(EditCount, { count: editCount });
17
+ }
18
+ return /* @__PURE__ */ React.createElement(
19
+ "button",
20
+ {
21
+ ...props,
22
+ ref,
23
+ type: "button",
24
+ "aria-pressed": isSelected,
25
+ className: cn(
26
+ "relative flex w-full flex-col gap-1 bg-surface rounded p-4 cursor-pointer transition-colors text-left border border-transparent",
27
+ isSelected ? [
28
+ "shadow-[0px_1px_3px_0px_rgba(95,95,95,0.26)]",
29
+ 'after:content-[""] after:block after:absolute after:right-[-2px] after:top-1/2 after:-translate-y-1/2 after:w-1 after:h-14 after:rounded-sm after:bg-primary'
30
+ ] : "hover:bg-dark-100 hover:border-dark-300",
31
+ className
32
+ )
33
+ },
34
+ /* @__PURE__ */ React.createElement("span", { className: "flex items-center justify-between w-full gap-2" }, /* @__PURE__ */ React.createElement("span", { className: cn("text-sm text-default truncate min-w-0 flex-1", isSelected ? "font-bold" : "font-normal") }, title), timestamp && /* @__PURE__ */ React.createElement("span", { className: "text-xs leading-4 text-subtlest shrink-0" }, formatDistance(timestamp))),
35
+ (icon || description || status) && /* @__PURE__ */ React.createElement("span", { className: "flex items-center gap-1 leading-4" }, icon && /* @__PURE__ */ React.createElement("span", { className: "shrink-0 flex items-center" }, icon), description && /* @__PURE__ */ React.createElement("span", { className: "text-xs text-subtle" }, description), status && /* @__PURE__ */ React.createElement("span", { className: "shrink-0 ml-1 flex items-center" }, status))
36
+ );
37
+ });
38
+
39
+ const ReviewsContext = createContext(null);
40
+ function ReviewsSelectionProvider({ children, defaultSelectedItem = null }) {
41
+ const [selectedItem, setSelectedItem] = useState(defaultSelectedItem);
42
+ const handleSelect = useCallback((item) => setSelectedItem(item), []);
43
+ const value = useMemo(
44
+ () => ({ selectedItem, setSelectedItem: handleSelect }),
45
+ [selectedItem, handleSelect]
46
+ );
47
+ return /* @__PURE__ */ React__default.createElement(ReviewsContext.Provider, { value }, children);
48
+ }
49
+ function useReviewsSelection() {
50
+ const ctx = useContext(ReviewsContext);
51
+ if (!ctx) {
52
+ throw new Error("useReviewsSelection must be used within ReviewsSelectionProvider");
53
+ }
54
+ return ctx;
55
+ }
56
+
57
+ const ReviewList = forwardRef(({ items, className, ...props }, ref) => {
58
+ const { selectedItem, setSelectedItem } = useReviewsSelection();
59
+ return /* @__PURE__ */ React__default.createElement(
60
+ "ul",
61
+ {
62
+ ref,
63
+ className: cn("bg-muted border border-dark-300 rounded-lg p-2 space-y-2 list-none", className),
64
+ ...props
65
+ },
66
+ items.map((item) => /* @__PURE__ */ React__default.createElement("li", { key: item.id }, /* @__PURE__ */ React__default.createElement(ReviewListItem, { ...item, isSelected: selectedItem?.id === item.id, onClick: () => setSelectedItem(item) })))
67
+ );
68
+ });
69
+ ReviewList.displayName = "ReviewList";
70
+
71
+ export { ReviewListItem as R, ReviewList as a, ReviewsSelectionProvider as b, useReviewsSelection as u };
72
+ //# sourceMappingURL=review-list.jCfkezEr.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-list.jCfkezEr.js","sources":["../../../src/scenes/knowledge-review/review-list/edit-count.tsx","../../../src/scenes/knowledge-review/review-list/review-list-item.tsx","../../../src/scenes/knowledge-review/context.tsx","../../../src/scenes/knowledge-review/review-list/review-list.tsx"],"sourcesContent":["import React from 'react';\nimport { Circle } from '@phosphor-icons/react';\n\nexport function EditCount({ count }: { count: number }) {\n return (\n <span className=\"inline-flex items-center gap-0.5\">\n <Circle size={8} weight=\"fill\" aria-hidden=\"true\" className=\"text-blue\" />\n <span className=\"text-xs leading-4 text-subtle\">\n {count} {count === 1 ? 'edit' : 'edits'}\n </span>\n </span>\n );\n}\n","import * as React from 'react';\n\nimport { EditCount } from './edit-count';\n\nimport { cn } from '@/lib/utils';\nimport { formatDistance } from '@/lib/formatDate';\nimport { StatusBadge } from '@/components/convos/status-badge';\n\nexport interface ReviewListItemProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n /** Unique identifier for the knowledge article */\n id: string;\n /** Primary text displayed in the first row */\n title: string;\n /** Content rendered in the top-right corner (e.g. relative timestamp) */\n timestamp: Date;\n /** Icon rendered at the start of the second row */\n icon?: React.ReactNode;\n /** Text or content next to the icon in the second row */\n description?: React.ReactNode;\n /** Whether this item is new */\n isNew?: boolean;\n /** Number of edits for this item */\n editCount?: number;\n /** Whether this item is currently selected */\n isSelected?: boolean;\n}\n\nexport const ReviewListItem = React.forwardRef<HTMLButtonElement, ReviewListItemProps>(function ReviewListItem(\n { title, timestamp, icon, description, isNew, editCount, isSelected = false, className, ...props },\n ref,\n) {\n let status: React.ReactNode | undefined;\n if (isNew) {\n status = <StatusBadge variant=\"success\">New</StatusBadge>;\n } else if (editCount) {\n status = <EditCount count={editCount} />;\n }\n\n return (\n <button\n {...props}\n ref={ref}\n type=\"button\"\n aria-pressed={isSelected}\n className={cn(\n 'relative flex w-full flex-col gap-1 bg-surface rounded p-4 cursor-pointer transition-colors text-left border border-transparent',\n isSelected\n ? [\n 'shadow-[0px_1px_3px_0px_rgba(95,95,95,0.26)]',\n 'after:content-[\"\"] after:block after:absolute after:right-[-2px] after:top-1/2 after:-translate-y-1/2 after:w-1 after:h-14 after:rounded-sm after:bg-primary',\n ]\n : 'hover:bg-dark-100 hover:border-dark-300',\n className,\n )}\n >\n {/* Row 1: Title + Timestamp */}\n <span className=\"flex items-center justify-between w-full gap-2\">\n <span className={cn('text-sm text-default truncate min-w-0 flex-1', isSelected ? 'font-bold' : 'font-normal')}>\n {title}\n </span>\n {timestamp && <span className=\"text-xs leading-4 text-subtlest shrink-0\">{formatDistance(timestamp)}</span>}\n </span>\n\n {/* Row 2: Icon + Description + Status */}\n {(icon || description || status) && (\n <span className=\"flex items-center gap-1 leading-4\">\n {icon && <span className=\"shrink-0 flex items-center\">{icon}</span>}\n {description && <span className=\"text-xs text-subtle\">{description}</span>}\n {status && <span className=\"shrink-0 ml-1 flex items-center\">{status}</span>}\n </span>\n )}\n </button>\n );\n});\n","import React, { createContext, useCallback, useContext, useMemo, useState } from 'react';\n\nimport type { ReviewListItemProps } from './review-list/review-list-item';\n\ntype ReviewsSelectionValue = {\n selectedItem: ReviewListItemProps | null;\n setSelectedItem: (item: ReviewListItemProps | null) => void;\n};\n\nconst ReviewsContext = createContext<ReviewsSelectionValue | null>(null);\n\ntype ReviewsProviderProps = {\n children: React.ReactNode;\n /** Optional initial selection (e.g. from server or mock data) */\n defaultSelectedItem?: ReviewListItemProps | null;\n};\n\nexport function ReviewsSelectionProvider({ children, defaultSelectedItem = null }: ReviewsProviderProps) {\n const [selectedItem, setSelectedItem] = useState<ReviewListItemProps | null>(defaultSelectedItem);\n const handleSelect = useCallback((item: ReviewListItemProps | null) => setSelectedItem(item), []);\n const value = useMemo<ReviewsSelectionValue>(\n () => ({ selectedItem, setSelectedItem: handleSelect }),\n [selectedItem, handleSelect],\n );\n\n return <ReviewsContext.Provider value={value}>{children}</ReviewsContext.Provider>;\n}\n\nexport function useReviewsSelection(): ReviewsSelectionValue {\n const ctx = useContext(ReviewsContext);\n if (!ctx) {\n throw new Error('useReviewsSelection must be used within ReviewsSelectionProvider');\n }\n\n return ctx;\n}\n","import React, { forwardRef } from 'react';\n\nimport { ReviewListItem, ReviewListItemProps } from './review-list-item';\nimport { useReviewsSelection } from '../context';\n\nimport { cn } from '@/lib/utils';\n\ninterface ReviewListProps {\n items: ReviewListItemProps[];\n className?: string;\n}\n\nexport const ReviewList = forwardRef<HTMLUListElement, ReviewListProps>(({ items, className, ...props }, ref) => {\n const { selectedItem, setSelectedItem } = useReviewsSelection();\n\n return (\n <ul\n ref={ref}\n className={cn('bg-muted border border-dark-300 rounded-lg p-2 space-y-2 list-none', className)}\n {...props}\n >\n {items.map((item) => (\n <li key={item.id}>\n <ReviewListItem {...item} isSelected={selectedItem?.id === item.id} onClick={() => setSelectedItem(item)} />\n </li>\n ))}\n </ul>\n );\n});\nReviewList.displayName = 'ReviewList';\n"],"names":["React","ReviewListItem"],"mappings":"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"}