@retrivora-ai/rag-engine 1.8.0 → 1.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/DocumentChunker-Dh9TvmGG.d.mts +45 -0
  2. package/dist/DocumentChunker-Dh9TvmGG.d.ts +45 -0
  3. package/dist/{index-DPsQodME.d.mts → ILLMProvider-BfRgI1Xh.d.mts} +58 -1
  4. package/dist/{index-DPsQodME.d.ts → ILLMProvider-BfRgI1Xh.d.ts} +58 -1
  5. package/dist/MultiTablePostgresProvider-YY7LPNJK.mjs +8 -0
  6. package/dist/{chunk-PV3MFHWU.mjs → chunk-BFYLQYQU.mjs} +808 -439
  7. package/dist/chunk-R3RGUMHE.mjs +218 -0
  8. package/dist/handlers/index.d.mts +2 -2
  9. package/dist/handlers/index.d.ts +2 -2
  10. package/dist/handlers/index.js +995 -603
  11. package/dist/handlers/index.mjs +1 -1
  12. package/dist/{index-Bb2yEopi.d.mts → index-1Z4GuYBi.d.ts} +7 -1
  13. package/dist/{index-CkbTzj9J.d.ts → index-BV0z5mb6.d.mts} +7 -1
  14. package/dist/index.d.mts +3 -3
  15. package/dist/index.d.ts +3 -3
  16. package/dist/index.js +491 -316
  17. package/dist/index.mjs +411 -217
  18. package/dist/server.d.mts +35 -5
  19. package/dist/server.d.ts +35 -5
  20. package/dist/server.js +1146 -777
  21. package/dist/server.mjs +163 -176
  22. package/package.json +4 -2
  23. package/src/app/page.tsx +1 -2
  24. package/src/components/MessageBubble.tsx +211 -69
  25. package/src/components/ProductCard.tsx +2 -2
  26. package/src/components/VisualizationRenderer.tsx +347 -247
  27. package/src/config/RagConfig.ts +5 -0
  28. package/src/config/serverConfig.ts +3 -1
  29. package/src/core/Pipeline.ts +65 -206
  30. package/src/core/ProviderRegistry.ts +2 -2
  31. package/src/core/VectorPlugin.ts +9 -0
  32. package/src/handlers/index.ts +23 -7
  33. package/src/hooks/useRagChat.ts +2 -2
  34. package/src/llm/LLMFactory.ts +54 -2
  35. package/src/llm/providers/AnthropicProvider.ts +12 -8
  36. package/src/llm/providers/GeminiProvider.ts +188 -143
  37. package/src/llm/providers/OllamaProvider.ts +7 -3
  38. package/src/llm/providers/OpenAIProvider.ts +12 -8
  39. package/src/types/chat.ts +6 -0
  40. package/src/types/index.ts +80 -0
  41. package/src/utils/SchemaMapper.ts +129 -0
  42. package/src/utils/UITransformer.ts +491 -181
  43. package/dist/DocumentChunker-C1GEEosY.d.ts +0 -93
  44. package/dist/DocumentChunker-CFEiRopR.d.mts +0 -93
  45. package/dist/PostgreSQLProvider-BMOETDZA.mjs +0 -8
  46. package/dist/chunk-FLOSGE6A.mjs +0 -202
package/dist/index.mjs CHANGED
@@ -31,41 +31,40 @@ import {
31
31
  import React6 from "react";
32
32
  import { Bot, User, ChevronDown as ChevronDown2, ChevronRight as ChevronRight2 } from "lucide-react";
33
33
  import ReactMarkdown from "react-markdown";
34
- import remarkGfm from "remark-gfm";
35
34
 
36
35
  // src/components/SourceCard.tsx
37
- import React2 from "react";
36
+ import React from "react";
38
37
  import { ChevronDown, ChevronUp } from "lucide-react";
39
38
  function SourceCard({ source, index }) {
40
39
  var _a, _b;
41
- const [expanded, setExpanded] = React2.useState(false);
40
+ const [expanded, setExpanded] = React.useState(false);
42
41
  const scorePercent = Math.round(source.score * 100);
43
42
  const scoreColor = source.score >= 0.8 ? "text-emerald-400" : source.score >= 0.6 ? "text-amber-400" : "text-rose-400";
44
43
  const scoreBg = source.score >= 0.8 ? "bg-emerald-50 border-emerald-100 dark:bg-emerald-400/10 dark:border-emerald-400/20" : source.score >= 0.6 ? "bg-amber-50 border-amber-100 dark:bg-amber-400/10 dark:border-amber-400/20" : "bg-rose-50 border-rose-100 dark:bg-rose-400/10 dark:border-rose-400/20";
45
44
  const preview = source.content.slice(0, 120);
46
45
  const hasMore = source.content.length > 120;
47
- return /* @__PURE__ */ React2.createElement(
46
+ return /* @__PURE__ */ React.createElement(
48
47
  "div",
49
48
  {
50
49
  className: `rounded-xl border backdrop-blur-sm p-3 transition-all duration-200 hover:scale-[1.01] cursor-pointer ${scoreBg}`,
51
50
  onClick: () => setExpanded((e) => !e)
52
51
  },
53
- /* @__PURE__ */ React2.createElement("div", { className: "flex items-start justify-between gap-2" }, /* @__PURE__ */ React2.createElement("div", { className: "flex items-center gap-2 min-w-0" }, /* @__PURE__ */ React2.createElement("span", { className: "flex-shrink-0 w-5 h-5 rounded-full bg-slate-200 dark:bg-white/10 flex items-center justify-center text-[10px] font-bold text-slate-500 dark:text-white/60" }, index + 1), /* @__PURE__ */ React2.createElement("span", { className: "text-xs text-slate-500 dark:text-white/50 truncate font-mono" }, (_b = (_a = source.metadata) == null ? void 0 : _a.docId) != null ? _b : source.id)), /* @__PURE__ */ React2.createElement("span", { className: `flex-shrink-0 text-xs font-semibold ${scoreColor} tabular-nums` }, scorePercent, "%")),
54
- /* @__PURE__ */ React2.createElement("p", { className: "mt-2 text-xs text-slate-600 dark:text-white/70 leading-relaxed" }, expanded ? source.content : `${preview}${hasMore && !expanded ? "\u2026" : ""}`),
55
- hasMore && /* @__PURE__ */ React2.createElement("button", { className: "mt-1 text-[10px] text-indigo-400 hover:text-indigo-300 transition-colors flex items-center gap-0.5" }, expanded ? /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(ChevronUp, { className: "w-3 h-3" }), " Show less") : /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(ChevronDown, { className: "w-3 h-3" }), " Show more"))
52
+ /* @__PURE__ */ React.createElement("div", { className: "flex items-start justify-between gap-2" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2 min-w-0" }, /* @__PURE__ */ React.createElement("span", { className: "flex-shrink-0 w-5 h-5 rounded-full bg-slate-200 dark:bg-white/10 flex items-center justify-center text-[10px] font-bold text-slate-500 dark:text-white/60" }, index + 1), /* @__PURE__ */ React.createElement("span", { className: "text-xs text-slate-500 dark:text-white/50 truncate font-mono" }, (_b = (_a = source.metadata) == null ? void 0 : _a.docId) != null ? _b : source.id)), /* @__PURE__ */ React.createElement("span", { className: `flex-shrink-0 text-xs font-semibold ${scoreColor} tabular-nums` }, scorePercent, "%")),
53
+ /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-xs text-slate-600 dark:text-white/70 leading-relaxed" }, expanded ? source.content : `${preview}${hasMore && !expanded ? "\u2026" : ""}`),
54
+ hasMore && /* @__PURE__ */ React.createElement("button", { className: "mt-1 text-[10px] text-indigo-400 hover:text-indigo-300 transition-colors flex items-center gap-0.5" }, expanded ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(ChevronUp, { className: "w-3 h-3" }), " Show less") : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(ChevronDown, { className: "w-3 h-3" }), " Show more"))
56
55
  );
57
56
  }
58
57
 
59
58
  // src/components/ProductCarousel.tsx
60
- import React4, { useRef, useState, useEffect } from "react";
59
+ import React3, { useRef, useState, useEffect } from "react";
61
60
  import { ChevronLeft, ChevronRight } from "lucide-react";
62
61
 
63
62
  // src/components/ProductCard.tsx
64
- import React3 from "react";
63
+ import React2 from "react";
65
64
  import Image from "next/image";
66
65
  import { ShoppingCart, ExternalLink } from "lucide-react";
67
66
  function ProductCard({ product, primaryColor = "#6366f1", onAddToCart }) {
68
- return /* @__PURE__ */ React3.createElement("div", { className: "flex-shrink-0 w-full bg-white dark:bg-white/5 border border-slate-200 dark:border-white/10 rounded-xl overflow-hidden shadow-sm hover:shadow-md transition-all duration-300 group" }, /* @__PURE__ */ React3.createElement("div", { className: "relative h-40 w-full bg-slate-100 dark:bg-white/5 flex items-center justify-center overflow-hidden" }, product.image ? /* @__PURE__ */ React3.createElement(
67
+ return /* @__PURE__ */ React2.createElement("div", { className: "flex-shrink-0 w-full bg-white dark:bg-white/5 border border-slate-200 dark:border-white/10 rounded-xl overflow-hidden shadow-sm hover:shadow-md transition-all duration-300 group" }, /* @__PURE__ */ React2.createElement("div", { className: "relative h-40 w-full bg-slate-100 dark:bg-white/5 flex items-center justify-center overflow-hidden" }, product.image ? /* @__PURE__ */ React2.createElement(
69
68
  Image,
70
69
  {
71
70
  src: product.image,
@@ -74,16 +73,16 @@ function ProductCard({ product, primaryColor = "#6366f1", onAddToCart }) {
74
73
  unoptimized: true,
75
74
  className: "object-cover group-hover:scale-105 transition-transform duration-500"
76
75
  }
77
- ) : /* @__PURE__ */ React3.createElement("div", { className: "text-slate-400 dark:text-white/20" }, /* @__PURE__ */ React3.createElement(ShoppingCart, { className: "w-12 h-12" })), product.brand && /* @__PURE__ */ React3.createElement("div", { className: "absolute top-2 left-2 px-2 py-1 bg-white/90 dark:bg-black/60 backdrop-blur-md rounded-md text-[10px] font-bold uppercase tracking-wider text-slate-700 dark:text-white/90 shadow-sm" }, product.brand)), /* @__PURE__ */ React3.createElement("div", { className: "p-4 flex flex-col gap-2" }, /* @__PURE__ */ React3.createElement("div", { className: "flex justify-between items-start gap-2" }, /* @__PURE__ */ React3.createElement("h3", { className: "text-sm font-semibold text-slate-800 dark:text-white/90 line-clamp-1" }, product.name), product.price && /* @__PURE__ */ React3.createElement("span", { className: "text-sm font-bold", style: { color: primaryColor } }, typeof product.price === "number" ? `$${product.price}` : product.price)), product.description && /* @__PURE__ */ React3.createElement("p", { className: "text-[11px] text-slate-500 dark:text-white/50 line-clamp-2 leading-relaxed" }, product.description), /* @__PURE__ */ React3.createElement("div", { className: "mt-2 flex gap-2" }, /* @__PURE__ */ React3.createElement(
76
+ ) : /* @__PURE__ */ React2.createElement("div", { className: "text-slate-400 dark:text-white/20" }, /* @__PURE__ */ React2.createElement(ShoppingCart, { className: "w-12 h-12" })), product.brand && /* @__PURE__ */ React2.createElement("div", { className: "absolute top-2 left-2 px-2 py-1 bg-white/90 dark:bg-black/60 backdrop-blur-md rounded-md text-[10px] font-bold uppercase tracking-wider text-slate-700 dark:text-white/90 shadow-sm" }, product.brand)), /* @__PURE__ */ React2.createElement("div", { className: "p-4 flex flex-col gap-2" }, /* @__PURE__ */ React2.createElement("div", { className: "flex justify-between items-start gap-2" }, /* @__PURE__ */ React2.createElement("h3", { className: "text-sm font-semibold text-slate-800 dark:text-white/90 line-clamp-1" }, product.name), product.price && /* @__PURE__ */ React2.createElement("span", { className: "text-sm font-bold", style: { color: primaryColor } }, typeof product.price === "number" ? `$${product.price}` : product.price)), product.description && /* @__PURE__ */ React2.createElement("p", { className: "text-[11px] text-slate-500 dark:text-white/50 line-clamp-2 leading-relaxed" }, product.description), /* @__PURE__ */ React2.createElement("div", { className: "mt-2 flex gap-2" }, /* @__PURE__ */ React2.createElement(
78
77
  "button",
79
78
  {
80
79
  onClick: () => onAddToCart == null ? void 0 : onAddToCart(product),
81
80
  className: "flex-1 py-2 px-3 rounded-lg text-[11px] font-medium text-white shadow-sm hover:opacity-90 transition-opacity flex items-center justify-center gap-1.5",
82
81
  style: { background: primaryColor }
83
82
  },
84
- /* @__PURE__ */ React3.createElement(ShoppingCart, { className: "w-3 h-3" }),
83
+ /* @__PURE__ */ React2.createElement(ShoppingCart, { className: "w-3 h-3" }),
85
84
  "Add to Cart"
86
- ), product.link && /* @__PURE__ */ React3.createElement(
85
+ ), product.link && /* @__PURE__ */ React2.createElement(
87
86
  "a",
88
87
  {
89
88
  href: product.link,
@@ -91,7 +90,7 @@ function ProductCard({ product, primaryColor = "#6366f1", onAddToCart }) {
91
90
  rel: "noopener noreferrer",
92
91
  className: "p-2 rounded-lg bg-slate-100 dark:bg-white/10 text-slate-600 dark:text-white/70 hover:bg-slate-200 dark:hover:bg-white/20 transition-colors"
93
92
  },
94
- /* @__PURE__ */ React3.createElement(ExternalLink, { className: "w-3.5 h-3.5" })
93
+ /* @__PURE__ */ React2.createElement(ExternalLink, { className: "w-3.5 h-3.5" })
95
94
  ))));
96
95
  }
97
96
 
@@ -123,9 +122,9 @@ function ProductCarousel({ products, primaryColor = "#6366f1", onAddToCart }) {
123
122
  };
124
123
  if (!products || products.length === 0) return null;
125
124
  if (products.length === 1) {
126
- return /* @__PURE__ */ React4.createElement("div", { className: "my-4 w-[min(88%,18rem)] min-w-0 max-w-full animate-fade-in-up" }, /* @__PURE__ */ React4.createElement(ProductCard, { product: products[0], primaryColor, onAddToCart }));
125
+ return /* @__PURE__ */ React3.createElement("div", { className: "my-4 w-[min(88%,18rem)] min-w-0 max-w-full animate-fade-in-up" }, /* @__PURE__ */ React3.createElement(ProductCard, { product: products[0], primaryColor, onAddToCart }));
127
126
  }
128
- return /* @__PURE__ */ React4.createElement("div", { className: "relative w-full my-4 group/carousel animate-fade-in-up" }, canScroll && /* @__PURE__ */ React4.createElement(React4.Fragment, null, /* @__PURE__ */ React4.createElement("div", { className: "absolute -left-4 top-1/2 -translate-y-1/2 z-20 opacity-100 md:opacity-0 md:group-hover/carousel:opacity-100 transition-opacity" }, /* @__PURE__ */ React4.createElement(
127
+ return /* @__PURE__ */ React3.createElement("div", { className: "relative w-full my-4 group/carousel animate-fade-in-up" }, canScroll && /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement("div", { className: "absolute -left-4 top-1/2 -translate-y-1/2 z-20 opacity-100 md:opacity-0 md:group-hover/carousel:opacity-100 transition-opacity" }, /* @__PURE__ */ React3.createElement(
129
128
  "button",
130
129
  {
131
130
  onClick: (e) => {
@@ -135,8 +134,8 @@ function ProductCarousel({ products, primaryColor = "#6366f1", onAddToCart }) {
135
134
  className: "p-2 rounded-full bg-white dark:bg-slate-800 shadow-xl border border-slate-200 dark:border-white/10 text-slate-600 dark:text-white/80 hover:text-indigo-500 dark:hover:text-indigo-400 transition-all hover:scale-110 active:scale-95",
136
135
  "aria-label": "Scroll left"
137
136
  },
138
- /* @__PURE__ */ React4.createElement(ChevronLeft, { className: "w-5 h-5" })
139
- )), /* @__PURE__ */ React4.createElement("div", { className: "absolute -right-4 top-1/2 -translate-y-1/2 z-20 opacity-100 md:opacity-0 md:group-hover/carousel:opacity-100 transition-opacity" }, /* @__PURE__ */ React4.createElement(
137
+ /* @__PURE__ */ React3.createElement(ChevronLeft, { className: "w-5 h-5" })
138
+ )), /* @__PURE__ */ React3.createElement("div", { className: "absolute -right-4 top-1/2 -translate-y-1/2 z-20 opacity-100 md:opacity-0 md:group-hover/carousel:opacity-100 transition-opacity" }, /* @__PURE__ */ React3.createElement(
140
139
  "button",
141
140
  {
142
141
  onClick: (e) => {
@@ -146,27 +145,27 @@ function ProductCarousel({ products, primaryColor = "#6366f1", onAddToCart }) {
146
145
  className: "p-2 rounded-full bg-white dark:bg-slate-800 shadow-xl border border-slate-200 dark:border-white/10 text-slate-600 dark:text-white/80 hover:text-indigo-500 dark:hover:text-indigo-400 transition-all hover:scale-110 active:scale-95",
147
146
  "aria-label": "Scroll right"
148
147
  },
149
- /* @__PURE__ */ React4.createElement(ChevronRight, { className: "w-5 h-5" })
150
- ))), /* @__PURE__ */ React4.createElement(
148
+ /* @__PURE__ */ React3.createElement(ChevronRight, { className: "w-5 h-5" })
149
+ ))), /* @__PURE__ */ React3.createElement(
151
150
  "div",
152
151
  {
153
152
  ref: scrollRef,
154
153
  className: "flex gap-4 overflow-x-auto pb-4 px-1 scrollbar-hide snap-x snap-mandatory",
155
154
  style: { scrollbarWidth: "none", msOverflowStyle: "none" }
156
155
  },
157
- products.map((product) => /* @__PURE__ */ React4.createElement(
156
+ products.map((product) => /* @__PURE__ */ React3.createElement(
158
157
  "div",
159
158
  {
160
159
  key: product.id,
161
160
  className: "snap-start flex-shrink-0 w-[min(88%,18rem)] min-w-[11rem] max-w-full"
162
161
  },
163
- /* @__PURE__ */ React4.createElement(ProductCard, { product, primaryColor, onAddToCart })
162
+ /* @__PURE__ */ React3.createElement(ProductCard, { product, primaryColor, onAddToCart })
164
163
  ))
165
164
  ));
166
165
  }
167
166
 
168
167
  // src/components/DynamicChart.tsx
169
- import React5 from "react";
168
+ import React4 from "react";
170
169
  import {
171
170
  BarChart,
172
171
  Bar,
@@ -191,9 +190,9 @@ function DynamicChart({
191
190
  }) {
192
191
  const { type, data } = config;
193
192
  const colors = config.colors || [primaryColor, accentColor, ...DEFAULT_COLORS];
194
- const containerRef = React5.useRef(null);
195
- const [containerWidth, setContainerWidth] = React5.useState(0);
196
- React5.useEffect(() => {
193
+ const containerRef = React4.useRef(null);
194
+ const [containerWidth, setContainerWidth] = React4.useState(0);
195
+ React4.useEffect(() => {
197
196
  const element = containerRef.current;
198
197
  if (!element || typeof ResizeObserver === "undefined") return;
199
198
  const updateWidth = (width) => {
@@ -219,7 +218,7 @@ function DynamicChart({
219
218
  return text.length > maxLength ? `${text.slice(0, maxLength)}\u2026` : text;
220
219
  };
221
220
  const chartMargin = isTiny ? { top: 8, right: 4, left: -28, bottom: 0 } : isCompact ? { top: 10, right: 6, left: -24, bottom: 0 } : { top: 10, right: 10, left: -20, bottom: 0 };
222
- const sanitizedData = React5.useMemo(() => {
221
+ const sanitizedData = React4.useMemo(() => {
223
222
  if (!data) return [];
224
223
  return data.map((item) => {
225
224
  const newItem = {};
@@ -252,7 +251,7 @@ function DynamicChart({
252
251
  });
253
252
  }, [data]);
254
253
  if (!data || data.length === 0) {
255
- return /* @__PURE__ */ React5.createElement("div", { className: "p-4 text-sm text-slate-500" }, "No data available for chart.");
254
+ return /* @__PURE__ */ React4.createElement("div", { className: "p-4 text-sm text-slate-500" }, "No data available for chart.");
256
255
  }
257
256
  const firstItem = sanitizedData[0];
258
257
  const allKeys = Object.keys(firstItem);
@@ -277,13 +276,13 @@ function DynamicChart({
277
276
  const renderTooltip = (row, label) => {
278
277
  var _a, _b;
279
278
  if (!row) return null;
280
- return /* @__PURE__ */ React5.createElement("div", { className: "rounded-xl border border-slate-200 bg-white px-3 py-2 text-xs shadow-xl dark:border-white/10 dark:bg-slate-950" }, /* @__PURE__ */ React5.createElement("p", { className: "mb-2 font-semibold text-slate-800 dark:text-white/90" }, String((_b = (_a = row[finalXKey]) != null ? _a : label) != null ? _b : "Details")), Object.entries(row).map(([key, value]) => {
279
+ return /* @__PURE__ */ React4.createElement("div", { className: "rounded-xl border border-slate-200 bg-white px-3 py-2 text-xs shadow-xl dark:border-white/10 dark:bg-slate-950" }, /* @__PURE__ */ React4.createElement("p", { className: "mb-2 font-semibold text-slate-800 dark:text-white/90" }, String((_b = (_a = row[finalXKey]) != null ? _a : label) != null ? _b : "Details")), Object.entries(row).map(([key, value]) => {
281
280
  var _a2;
282
281
  if (value === null || value === void 0 || key === finalXKey) return null;
283
282
  if (typeof value === "object") return null;
284
283
  const forbiddenKeys = ["fill", "color", "payload", "percent", "stroke", "inStock"];
285
284
  if (forbiddenKeys.includes(key)) return null;
286
- return /* @__PURE__ */ React5.createElement("div", { key, className: "flex items-center justify-between gap-3 text-slate-600 dark:text-white/70" }, /* @__PURE__ */ React5.createElement("span", null, (_a2 = tooltipLabelMap[key]) != null ? _a2 : key), /* @__PURE__ */ React5.createElement("span", { className: "font-medium text-slate-900 dark:text-white/90" }, String(value)));
285
+ return /* @__PURE__ */ React4.createElement("div", { key, className: "flex items-center justify-between gap-3 text-slate-600 dark:text-white/70" }, /* @__PURE__ */ React4.createElement("span", null, (_a2 = tooltipLabelMap[key]) != null ? _a2 : key), /* @__PURE__ */ React4.createElement("span", { className: "font-medium text-slate-900 dark:text-white/90" }, String(value)));
287
286
  }));
288
287
  };
289
288
  const getTooltipRow = (payload) => {
@@ -295,7 +294,7 @@ function DynamicChart({
295
294
  return row;
296
295
  };
297
296
  if (type === "pie") {
298
- return /* @__PURE__ */ React5.createElement("div", { ref: containerRef, className: "w-full min-w-0 mt-4 mb-2 select-none overflow-hidden" }, /* @__PURE__ */ React5.createElement("div", { className: "mx-auto w-full max-w-full aspect-square" }, /* @__PURE__ */ React5.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React5.createElement(PieChart, null, /* @__PURE__ */ React5.createElement(
297
+ return /* @__PURE__ */ React4.createElement("div", { ref: containerRef, className: "w-full min-w-0 mt-4 mb-2 select-none overflow-hidden" }, /* @__PURE__ */ React4.createElement("div", { className: "mx-auto w-full max-w-full aspect-square" }, /* @__PURE__ */ React4.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React4.createElement(PieChart, null, /* @__PURE__ */ React4.createElement(
299
298
  Pie,
300
299
  {
301
300
  data: sanitizedData,
@@ -307,10 +306,10 @@ function DynamicChart({
307
306
  label: false,
308
307
  labelLine: false
309
308
  },
310
- sanitizedData.map((_entry, index) => /* @__PURE__ */ React5.createElement(Cell, { key: `cell-${index}`, fill: colors[index % colors.length] }))
311
- ), /* @__PURE__ */ React5.createElement(Tooltip, { content: (props) => renderTooltip(getTooltipRow(props.payload), typeof props.label === "string" ? props.label : void 0) }), showLegend && !isCompact && /* @__PURE__ */ React5.createElement(Legend, { wrapperStyle: { fontSize: 12 }, verticalAlign: "bottom" })))));
309
+ sanitizedData.map((_entry, index) => /* @__PURE__ */ React4.createElement(Cell, { key: `cell-${index}`, fill: colors[index % colors.length] }))
310
+ ), /* @__PURE__ */ React4.createElement(Tooltip, { content: (props) => renderTooltip(getTooltipRow(props.payload), typeof props.label === "string" ? props.label : void 0) }), showLegend && !isCompact && /* @__PURE__ */ React4.createElement(Legend, { wrapperStyle: { fontSize: 12 }, verticalAlign: "bottom" })))));
312
311
  }
313
- return /* @__PURE__ */ React5.createElement("div", { ref: containerRef, className: "w-full min-w-0 mt-4 mb-2 select-none overflow-hidden" }, /* @__PURE__ */ React5.createElement("div", { className: "w-full aspect-[4/3]" }, /* @__PURE__ */ React5.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, type === "bar" ? /* @__PURE__ */ React5.createElement(BarChart, { data: sanitizedData, margin: chartMargin }, /* @__PURE__ */ React5.createElement(CartesianGrid, { strokeDasharray: "3 3", vertical: false, stroke: "#e2e8f0" }), /* @__PURE__ */ React5.createElement(XAxis, { dataKey: finalXKey, tick: axisTick, tickFormatter: truncateAxisLabel, tickLine: false, axisLine: { stroke: "#cbd5e1" }, interval: 0, minTickGap: isTiny ? 2 : isCompact ? 6 : 12 }), /* @__PURE__ */ React5.createElement(YAxis, { tick: axisTick, tickLine: false, axisLine: false }), /* @__PURE__ */ React5.createElement(Tooltip, { content: (props) => renderTooltip(getTooltipRow(props.payload), typeof props.label === "string" ? props.label : void 0), cursor: { fill: "#f1f5f9" } }), showLegend && /* @__PURE__ */ React5.createElement(Legend, { wrapperStyle: { fontSize: isCompact ? 10 : 12 } }), finalDataKeys.map((key, index) => /* @__PURE__ */ React5.createElement(
312
+ return /* @__PURE__ */ React4.createElement("div", { ref: containerRef, className: "w-full min-w-0 mt-4 mb-2 select-none overflow-hidden" }, /* @__PURE__ */ React4.createElement("div", { className: "w-full aspect-[4/3]" }, /* @__PURE__ */ React4.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, type === "bar" ? /* @__PURE__ */ React4.createElement(BarChart, { data: sanitizedData, margin: chartMargin }, /* @__PURE__ */ React4.createElement(CartesianGrid, { strokeDasharray: "3 3", vertical: false, stroke: "#e2e8f0" }), /* @__PURE__ */ React4.createElement(XAxis, { dataKey: finalXKey, tick: axisTick, tickFormatter: truncateAxisLabel, tickLine: false, axisLine: { stroke: "#cbd5e1" }, interval: 0, minTickGap: isTiny ? 2 : isCompact ? 6 : 12 }), /* @__PURE__ */ React4.createElement(YAxis, { tick: axisTick, tickLine: false, axisLine: false }), /* @__PURE__ */ React4.createElement(Tooltip, { content: (props) => renderTooltip(getTooltipRow(props.payload), typeof props.label === "string" ? props.label : void 0), cursor: { fill: "#f1f5f9" } }), showLegend && /* @__PURE__ */ React4.createElement(Legend, { wrapperStyle: { fontSize: isCompact ? 10 : 12 } }), finalDataKeys.map((key, index) => /* @__PURE__ */ React4.createElement(
314
313
  Bar,
315
314
  {
316
315
  key,
@@ -319,7 +318,7 @@ function DynamicChart({
319
318
  radius: [4, 4, 0, 0],
320
319
  barSize: isTiny ? Math.max(10, 28 / data.length) : isCompact ? Math.max(14, 42 / data.length) : Math.max(20, 60 / data.length)
321
320
  }
322
- ))) : /* @__PURE__ */ React5.createElement(LineChart, { data: sanitizedData, margin: chartMargin }, /* @__PURE__ */ React5.createElement(CartesianGrid, { strokeDasharray: "3 3", vertical: false, stroke: "#e2e8f0" }), /* @__PURE__ */ React5.createElement(XAxis, { dataKey: finalXKey, tick: axisTick, tickFormatter: truncateAxisLabel, tickLine: false, axisLine: { stroke: "#cbd5e1" }, interval: 0, minTickGap: isTiny ? 2 : isCompact ? 6 : 12 }), /* @__PURE__ */ React5.createElement(YAxis, { tick: axisTick, tickLine: false, axisLine: false }), /* @__PURE__ */ React5.createElement(Tooltip, { content: (props) => renderTooltip(getTooltipRow(props.payload), typeof props.label === "string" ? props.label : void 0) }), showLegend && /* @__PURE__ */ React5.createElement(Legend, { wrapperStyle: { fontSize: isCompact ? 10 : 12 } }), finalDataKeys.map((key, index) => /* @__PURE__ */ React5.createElement(
321
+ ))) : /* @__PURE__ */ React4.createElement(LineChart, { data: sanitizedData, margin: chartMargin }, /* @__PURE__ */ React4.createElement(CartesianGrid, { strokeDasharray: "3 3", vertical: false, stroke: "#e2e8f0" }), /* @__PURE__ */ React4.createElement(XAxis, { dataKey: finalXKey, tick: axisTick, tickFormatter: truncateAxisLabel, tickLine: false, axisLine: { stroke: "#cbd5e1" }, interval: 0, minTickGap: isTiny ? 2 : isCompact ? 6 : 12 }), /* @__PURE__ */ React4.createElement(YAxis, { tick: axisTick, tickLine: false, axisLine: false }), /* @__PURE__ */ React4.createElement(Tooltip, { content: (props) => renderTooltip(getTooltipRow(props.payload), typeof props.label === "string" ? props.label : void 0) }), showLegend && /* @__PURE__ */ React4.createElement(Legend, { wrapperStyle: { fontSize: isCompact ? 10 : 12 } }), finalDataKeys.map((key, index) => /* @__PURE__ */ React4.createElement(
323
322
  Line,
324
323
  {
325
324
  key,
@@ -334,175 +333,251 @@ function DynamicChart({
334
333
  }
335
334
 
336
335
  // src/components/VisualizationRenderer.tsx
337
- import Image2 from "next/image";
336
+ import React5 from "react";
337
+ import {
338
+ PieChart as PieChart2,
339
+ Pie as Pie2,
340
+ Cell as Cell2,
341
+ BarChart as BarChart2,
342
+ Bar as Bar2,
343
+ LineChart as LineChart2,
344
+ Line as Line2,
345
+ XAxis as XAxis2,
346
+ YAxis as YAxis2,
347
+ CartesianGrid as CartesianGrid2,
348
+ Tooltip as Tooltip2,
349
+ Legend as Legend2,
350
+ ResponsiveContainer as ResponsiveContainer2,
351
+ RadarChart,
352
+ Radar,
353
+ PolarGrid,
354
+ PolarAngleAxis,
355
+ PolarRadiusAxis
356
+ } from "recharts";
357
+ var CHART_COLORS = [
358
+ "#6366f1",
359
+ // indigo
360
+ "#10b981",
361
+ // emerald
362
+ "#f59e0b",
363
+ // amber
364
+ "#ef4444",
365
+ // red
366
+ "#8b5cf6",
367
+ // purple
368
+ "#ec4899",
369
+ // pink
370
+ "#06b6d4",
371
+ // cyan
372
+ "#f97316"
373
+ // orange
374
+ ];
375
+ function getColor(index) {
376
+ return CHART_COLORS[index % CHART_COLORS.length];
377
+ }
378
+ function CustomTooltip({
379
+ active,
380
+ payload,
381
+ label
382
+ }) {
383
+ if (!active || !payload || payload.length === 0) return null;
384
+ return /* @__PURE__ */ React5.createElement("div", { className: "rounded-xl border border-slate-200 bg-white px-3 py-2 text-xs shadow-xl dark:border-white/10 dark:bg-slate-950" }, label && /* @__PURE__ */ React5.createElement("p", { className: "mb-2 font-semibold text-slate-800 dark:text-white/90" }, label), payload.map((entry, i) => /* @__PURE__ */ React5.createElement("div", { key: i, className: "flex items-center justify-between gap-3 text-slate-600 dark:text-white/70" }, /* @__PURE__ */ React5.createElement("span", { className: "flex items-center gap-1.5" }, entry.color && /* @__PURE__ */ React5.createElement(
385
+ "span",
386
+ {
387
+ className: "inline-block w-2 h-2 rounded-full flex-shrink-0",
388
+ style: { background: entry.color }
389
+ }
390
+ ), entry.name), /* @__PURE__ */ React5.createElement("span", { className: "font-medium text-slate-900 dark:text-white/90" }, entry.value))));
391
+ }
338
392
  function VisualizationRenderer({
339
393
  data,
340
- className = ""
394
+ className = "",
395
+ primaryColor,
396
+ onAddToCart
341
397
  }) {
398
+ var _a;
342
399
  if (!data) {
343
- return /* @__PURE__ */ React.createElement("div", { className: `p-4 text-gray-500 ${className}` }, "No data available for visualization");
400
+ return /* @__PURE__ */ React5.createElement("div", { className: `p-4 text-gray-500 ${className}` }, "No data available for visualization");
344
401
  }
345
- return /* @__PURE__ */ React.createElement("div", { className: `space-y-4 ${className}` }, /* @__PURE__ */ React.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React.createElement("h3", { className: "text-lg font-semibold text-gray-900 dark:text-white" }, data.title), data.description && /* @__PURE__ */ React.createElement("p", { className: "text-sm text-gray-600 dark:text-gray-400" }, data.description)), renderVisualization(data.type, data.data));
402
+ return /* @__PURE__ */ React5.createElement("div", { className: `space-y-4 ${className}` }, /* @__PURE__ */ React5.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ React5.createElement("h3", { className: "text-sm font-semibold text-slate-900 dark:text-white" }, data.title), data.description && data.description !== ((_a = data.data) == null ? void 0 : _a.content) && /* @__PURE__ */ React5.createElement("p", { className: "text-xs text-slate-500 dark:text-slate-400" }, data.description)), renderVisualization(data.type, data.data, onAddToCart, primaryColor));
346
403
  }
347
- function renderVisualization(type, data) {
348
- switch (type) {
349
- case "pie_chart":
350
- return renderPieChart(data);
351
- case "bar_chart":
352
- return renderBarChart(data);
353
- case "line_chart":
354
- return renderLineChart(data);
355
- case "table":
356
- return renderTable(data);
357
- case "product_carousel":
358
- return renderProductCarousel(data);
359
- case "text":
360
- default:
361
- return renderText(data);
404
+ function renderVisualization(type, data, onAddToCart, primaryColor) {
405
+ try {
406
+ switch (type) {
407
+ case "pie_chart":
408
+ return /* @__PURE__ */ React5.createElement(PieChartView, { data });
409
+ case "bar_chart":
410
+ return /* @__PURE__ */ React5.createElement(BarChartView, { data, primaryColor });
411
+ case "line_chart":
412
+ return /* @__PURE__ */ React5.createElement(LineChartView, { data, primaryColor });
413
+ case "radar_chart":
414
+ return /* @__PURE__ */ React5.createElement(RadarChartView, { data });
415
+ case "table":
416
+ return /* @__PURE__ */ React5.createElement(TableView, { data });
417
+ case "product_carousel":
418
+ case "carousel":
419
+ return /* @__PURE__ */ React5.createElement(CarouselView, { data, onAddToCart, primaryColor });
420
+ case "text":
421
+ default:
422
+ return /* @__PURE__ */ React5.createElement(TextView, { data });
423
+ }
424
+ } catch (error) {
425
+ console.warn("[VisualizationRenderer] Rendering failed, falling back to text:", error);
426
+ return /* @__PURE__ */ React5.createElement(TextView, { data: { content: "Unable to render the requested visualization." } });
362
427
  }
363
428
  }
364
- function renderPieChart(data) {
365
- if (!Array.isArray(data)) {
366
- return /* @__PURE__ */ React.createElement("div", { className: "text-red-500" }, "Invalid pie chart data");
429
+ function PieChartView({ data }) {
430
+ if (!Array.isArray(data) || data.length === 0) {
431
+ return /* @__PURE__ */ React5.createElement("div", { className: "text-xs text-red-500" }, "Invalid pie chart data");
367
432
  }
368
- return /* @__PURE__ */ React.createElement("div", { className: "bg-white dark:bg-gray-800 p-4 rounded-lg shadow" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-2" }, data.map((item, idx) => /* @__PURE__ */ React.createElement(
369
- "div",
433
+ const chartData = data.map((item) => ({
434
+ name: item.label,
435
+ value: item.value
436
+ }));
437
+ return /* @__PURE__ */ React5.createElement("div", { className: "bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 p-4 shadow-sm" }, /* @__PURE__ */ React5.createElement(ResponsiveContainer2, { width: "100%", height: 280 }, /* @__PURE__ */ React5.createElement(PieChart2, null, /* @__PURE__ */ React5.createElement(
438
+ Pie2,
370
439
  {
371
- key: idx,
372
- className: "flex items-center gap-2 p-2 bg-gray-50 dark:bg-gray-700 rounded"
440
+ data: chartData,
441
+ dataKey: "value",
442
+ nameKey: "name",
443
+ cx: "50%",
444
+ cy: "50%",
445
+ outerRadius: "68%",
446
+ label: false,
447
+ labelLine: false
373
448
  },
374
- /* @__PURE__ */ React.createElement(
375
- "div",
376
- {
377
- className: "w-3 h-3 rounded-full",
378
- style: { backgroundColor: generateColor(idx) }
379
- }
380
- ),
381
- /* @__PURE__ */ React.createElement("span", { className: "text-sm font-medium text-gray-700 dark:text-gray-300" }, item.label, ": ", item.value),
382
- item.inStock !== void 0 && /* @__PURE__ */ React.createElement(
383
- "span",
384
- {
385
- className: `text-xs px-2 py-1 rounded ${item.inStock ? "bg-green-100 text-green-800" : "bg-red-100 text-red-800"}`
386
- },
387
- item.inStock ? "In Stock" : "Out of Stock"
388
- )
389
- ))));
449
+ chartData.map((_entry, index) => /* @__PURE__ */ React5.createElement(Cell2, { key: `cell-${index}`, fill: getColor(index) }))
450
+ ), /* @__PURE__ */ React5.createElement(Tooltip2, { content: /* @__PURE__ */ React5.createElement(CustomTooltip, null) }), /* @__PURE__ */ React5.createElement(Legend2, { wrapperStyle: { fontSize: 12 }, verticalAlign: "bottom" }))));
390
451
  }
391
- function renderBarChart(data) {
392
- if (!Array.isArray(data)) {
393
- return /* @__PURE__ */ React.createElement("div", { className: "text-red-500" }, "Invalid bar chart data");
452
+ function BarChartView({ data, primaryColor }) {
453
+ if (!Array.isArray(data) || data.length === 0) {
454
+ return /* @__PURE__ */ React5.createElement("div", { className: "text-xs text-red-500" }, "Invalid bar chart data");
394
455
  }
395
- return /* @__PURE__ */ React.createElement("div", { className: "bg-white dark:bg-gray-800 p-4 rounded-lg shadow overflow-x-auto" }, /* @__PURE__ */ React.createElement("div", { className: "space-y-3" }, data.map((item, idx) => /* @__PURE__ */ React.createElement("div", { key: idx, className: "space-y-1" }, /* @__PURE__ */ React.createElement("div", { className: "flex justify-between items-center" }, /* @__PURE__ */ React.createElement("span", { className: "text-sm font-medium text-gray-700 dark:text-gray-300" }, item.category), /* @__PURE__ */ React.createElement("span", { className: "text-sm text-gray-600 dark:text-gray-400" }, item.value)), /* @__PURE__ */ React.createElement("div", { className: "w-full bg-gray-200 dark:bg-gray-700 rounded-full h-2" }, /* @__PURE__ */ React.createElement(
396
- "div",
456
+ const chartData = data.map((item) => __spreadValues(__spreadValues({
457
+ category: item.category,
458
+ value: item.value
459
+ }, item.inStockCount !== void 0 && { inStockCount: item.inStockCount }), item.outOfStockCount !== void 0 && { outOfStockCount: item.outOfStockCount }));
460
+ const barColor = primaryColor != null ? primaryColor : getColor(0);
461
+ const hasStock = chartData.some((d) => "inStockCount" in d);
462
+ return /* @__PURE__ */ React5.createElement("div", { className: "bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 p-4 shadow-sm overflow-x-auto" }, /* @__PURE__ */ React5.createElement(ResponsiveContainer2, { width: "100%", height: 260 }, /* @__PURE__ */ React5.createElement(BarChart2, { data: chartData, margin: { top: 10, right: 10, left: -20, bottom: 0 } }, /* @__PURE__ */ React5.createElement(CartesianGrid2, { strokeDasharray: "3 3", vertical: false, stroke: "#e2e8f0" }), /* @__PURE__ */ React5.createElement(
463
+ XAxis2,
397
464
  {
398
- className: "h-full rounded-full transition-all",
399
- style: {
400
- width: `${item.value / Math.max(...data.map((d) => d.value)) * 100}%`,
401
- backgroundColor: generateColor(idx)
402
- }
465
+ dataKey: "category",
466
+ tick: { fontSize: 11, fill: "#64748b" },
467
+ tickLine: false,
468
+ axisLine: { stroke: "#cbd5e1" }
403
469
  }
404
- )), item.inStock !== void 0 && /* @__PURE__ */ React.createElement(
405
- "span",
406
- {
407
- className: `text-xs px-2 py-1 rounded inline-block ${item.inStock ? "bg-green-100 text-green-800" : "bg-red-100 text-red-800"}`
408
- },
409
- item.inStock ? "In Stock" : "Out of Stock"
410
- )))));
470
+ ), /* @__PURE__ */ React5.createElement(YAxis2, { tick: { fontSize: 11, fill: "#64748b" }, tickLine: false, axisLine: false }), /* @__PURE__ */ React5.createElement(Tooltip2, { content: /* @__PURE__ */ React5.createElement(CustomTooltip, null), cursor: { fill: "#f1f5f9" } }), /* @__PURE__ */ React5.createElement(Legend2, { wrapperStyle: { fontSize: 12 } }), hasStock ? /* @__PURE__ */ React5.createElement(React5.Fragment, null, /* @__PURE__ */ React5.createElement(Bar2, { dataKey: "inStockCount", name: "In Stock", fill: getColor(1), radius: [4, 4, 0, 0] }), /* @__PURE__ */ React5.createElement(Bar2, { dataKey: "outOfStockCount", name: "Out of Stock", fill: getColor(3), radius: [4, 4, 0, 0] })) : /* @__PURE__ */ React5.createElement(Bar2, { dataKey: "value", name: "Value", fill: barColor, radius: [4, 4, 0, 0] }))));
411
471
  }
412
- function renderLineChart(data) {
413
- if (!Array.isArray(data)) {
414
- return /* @__PURE__ */ React.createElement("div", { className: "text-red-500" }, "Invalid line chart data");
472
+ function LineChartView({ data, primaryColor }) {
473
+ if (!Array.isArray(data) || data.length === 0) {
474
+ return /* @__PURE__ */ React5.createElement("div", { className: "text-xs text-red-500" }, "Invalid line chart data");
415
475
  }
416
- return /* @__PURE__ */ React.createElement("div", { className: "bg-white dark:bg-gray-800 p-4 rounded-lg shadow overflow-x-auto" }, /* @__PURE__ */ React.createElement("div", { className: "space-y-2" }, data.map((point, idx) => /* @__PURE__ */ React.createElement(
417
- "div",
476
+ const chartData = data.map((point) => {
477
+ var _a;
478
+ return {
479
+ label: String((_a = point.label) != null ? _a : point.timestamp),
480
+ value: Number(point.value)
481
+ };
482
+ });
483
+ const lineColor = primaryColor != null ? primaryColor : getColor(0);
484
+ return /* @__PURE__ */ React5.createElement("div", { className: "bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 p-4 shadow-sm overflow-x-auto" }, /* @__PURE__ */ React5.createElement(ResponsiveContainer2, { width: "100%", height: 260 }, /* @__PURE__ */ React5.createElement(LineChart2, { data: chartData, margin: { top: 10, right: 10, left: -20, bottom: 0 } }, /* @__PURE__ */ React5.createElement(CartesianGrid2, { strokeDasharray: "3 3", vertical: false, stroke: "#e2e8f0" }), /* @__PURE__ */ React5.createElement(
485
+ XAxis2,
418
486
  {
419
- key: idx,
420
- className: "flex justify-between items-center p-2 bg-gray-50 dark:bg-gray-700 rounded"
421
- },
422
- /* @__PURE__ */ React.createElement("span", { className: "text-sm text-gray-600 dark:text-gray-400" }, point.label || point.timestamp),
423
- /* @__PURE__ */ React.createElement("span", { className: "text-sm font-semibold text-gray-900 dark:text-white" }, typeof point.value === "number" ? point.value.toFixed(2) : point.value)
487
+ dataKey: "label",
488
+ tick: { fontSize: 11, fill: "#64748b" },
489
+ tickLine: false,
490
+ axisLine: { stroke: "#cbd5e1" }
491
+ }
492
+ ), /* @__PURE__ */ React5.createElement(YAxis2, { tick: { fontSize: 11, fill: "#64748b" }, tickLine: false, axisLine: false }), /* @__PURE__ */ React5.createElement(Tooltip2, { content: /* @__PURE__ */ React5.createElement(CustomTooltip, null) }), /* @__PURE__ */ React5.createElement(
493
+ Line2,
494
+ {
495
+ type: "monotone",
496
+ dataKey: "value",
497
+ name: "Value",
498
+ stroke: lineColor,
499
+ strokeWidth: 2.5,
500
+ dot: { r: 3.5, strokeWidth: 2, fill: lineColor },
501
+ activeDot: { r: 5 }
502
+ }
424
503
  ))));
425
504
  }
426
- function renderTable(data) {
427
- if (!data.columns || !data.rows) {
428
- return /* @__PURE__ */ React.createElement("div", { className: "text-red-500" }, "Invalid table data");
505
+ function RadarChartView({ data }) {
506
+ if (!Array.isArray(data) || data.length === 0) {
507
+ return /* @__PURE__ */ React5.createElement("div", { className: "text-xs text-red-500" }, "Invalid radar chart data");
429
508
  }
430
- return /* @__PURE__ */ React.createElement("div", { className: "bg-white dark:bg-gray-800 rounded-lg shadow overflow-x-auto" }, /* @__PURE__ */ React.createElement("table", { className: "w-full text-sm" }, /* @__PURE__ */ React.createElement("thead", { className: "bg-gray-100 dark:bg-gray-700 border-b border-gray-200 dark:border-gray-600" }, /* @__PURE__ */ React.createElement("tr", null, data.columns.map((col, idx) => /* @__PURE__ */ React.createElement(
509
+ const productNames = Array.from(
510
+ new Set(data.flatMap((item) => Object.keys(item).filter((key) => key !== "attribute")))
511
+ );
512
+ return /* @__PURE__ */ React5.createElement("div", { className: "bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 p-4 shadow-sm" }, /* @__PURE__ */ React5.createElement(ResponsiveContainer2, { width: "100%", height: 300 }, /* @__PURE__ */ React5.createElement(RadarChart, { cx: "50%", cy: "50%", outerRadius: "80%", data }, /* @__PURE__ */ React5.createElement(PolarGrid, { stroke: "#e2e8f0" }), /* @__PURE__ */ React5.createElement(PolarAngleAxis, { dataKey: "attribute", tick: { fontSize: 11, fill: "#64748b" } }), /* @__PURE__ */ React5.createElement(PolarRadiusAxis, { tick: { fontSize: 10, fill: "#64748b" } }), productNames.map((productName, index) => /* @__PURE__ */ React5.createElement(
513
+ Radar,
514
+ {
515
+ key: productName,
516
+ name: productName,
517
+ dataKey: productName,
518
+ stroke: getColor(index),
519
+ fill: getColor(index),
520
+ fillOpacity: 0.4
521
+ }
522
+ )), /* @__PURE__ */ React5.createElement(Tooltip2, { content: /* @__PURE__ */ React5.createElement(CustomTooltip, null) }), /* @__PURE__ */ React5.createElement(Legend2, { wrapperStyle: { fontSize: 12 } }))));
523
+ }
524
+ function TableView({ data }) {
525
+ if (!(data == null ? void 0 : data.columns) || !(data == null ? void 0 : data.rows)) {
526
+ return /* @__PURE__ */ React5.createElement("div", { className: "text-xs text-red-500" }, "Invalid table data");
527
+ }
528
+ return /* @__PURE__ */ React5.createElement("div", { className: "bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 shadow-sm overflow-hidden" }, /* @__PURE__ */ React5.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React5.createElement("table", { className: "w-full text-sm border-collapse" }, /* @__PURE__ */ React5.createElement("thead", { className: "bg-slate-50 dark:bg-white/5 border-b border-slate-200 dark:border-white/10" }, /* @__PURE__ */ React5.createElement("tr", null, data.columns.map((col, idx) => /* @__PURE__ */ React5.createElement(
431
529
  "th",
432
530
  {
433
531
  key: idx,
434
- className: "px-4 py-2 text-left font-semibold text-gray-900 dark:text-white"
532
+ className: "px-4 py-3 text-left text-xs font-semibold text-slate-700 dark:text-white/90 whitespace-nowrap uppercase tracking-wider"
435
533
  },
436
534
  col
437
- )))), /* @__PURE__ */ React.createElement("tbody", { className: "divide-y divide-gray-200 dark:divide-gray-600" }, data.rows.map((row, rowIdx) => /* @__PURE__ */ React.createElement(
535
+ )))), /* @__PURE__ */ React5.createElement("tbody", { className: "divide-y divide-slate-100 dark:divide-white/5" }, data.rows.map((row, rowIdx) => /* @__PURE__ */ React5.createElement(
438
536
  "tr",
439
537
  {
440
538
  key: rowIdx,
441
- className: "hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors"
539
+ className: "hover:bg-slate-50/60 dark:hover:bg-white/5 transition-colors"
442
540
  },
443
- row.map((cell, cellIdx) => /* @__PURE__ */ React.createElement(
541
+ row.map((cell, cellIdx) => /* @__PURE__ */ React5.createElement(
444
542
  "td",
445
543
  {
446
544
  key: cellIdx,
447
- className: "px-4 py-2 text-gray-700 dark:text-gray-300"
545
+ className: "px-4 py-3 text-slate-600 dark:text-white/70 text-sm"
448
546
  },
449
547
  String(cell)
450
548
  ))
451
- )))));
549
+ ))))), /* @__PURE__ */ React5.createElement("div", { className: "px-4 py-2 bg-slate-50 dark:bg-white/5 border-t border-slate-100 dark:border-white/5" }, /* @__PURE__ */ React5.createElement("p", { className: "text-[11px] text-slate-400 dark:text-white/30" }, data.rows.length, " row", data.rows.length !== 1 ? "s" : "")));
452
550
  }
453
- function renderProductCarousel(data) {
551
+ function CarouselView({
552
+ data,
553
+ onAddToCart,
554
+ primaryColor
555
+ }) {
454
556
  if (!Array.isArray(data)) {
455
- return /* @__PURE__ */ React.createElement("div", { className: "text-red-500" }, "Invalid carousel data");
557
+ return /* @__PURE__ */ React5.createElement("div", { className: "text-xs text-red-500" }, "Invalid carousel data");
456
558
  }
457
- return /* @__PURE__ */ React.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React.createElement("div", { className: "flex gap-4 pb-4" }, data.map((product) => /* @__PURE__ */ React.createElement(
458
- "div",
559
+ const products = data.map((product) => ({
560
+ id: product.id,
561
+ name: product.name,
562
+ brand: product.brand,
563
+ price: product.price,
564
+ image: product.image,
565
+ link: typeof product.link === "string" ? product.link : void 0,
566
+ description: product.description,
567
+ inStock: product.inStock
568
+ }));
569
+ return /* @__PURE__ */ React5.createElement("div", { className: "w-full" }, /* @__PURE__ */ React5.createElement(
570
+ ProductCarousel,
459
571
  {
460
- key: product.id,
461
- className: "flex-shrink-0 w-64 bg-white dark:bg-gray-800 rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow"
462
- },
463
- product.image && /* @__PURE__ */ React.createElement("div", { className: "relative w-full h-40 bg-gray-200" }, /* @__PURE__ */ React.createElement(
464
- Image2,
465
- {
466
- src: product.image,
467
- alt: product.name,
468
- fill: true,
469
- className: "object-cover",
470
- sizes: "(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
471
- }
472
- )),
473
- /* @__PURE__ */ React.createElement("div", { className: "p-4 space-y-2" }, /* @__PURE__ */ React.createElement("h4", { className: "font-semibold text-gray-900 dark:text-white line-clamp-2" }, product.name), product.brand && /* @__PURE__ */ React.createElement("p", { className: "text-xs text-gray-500 dark:text-gray-400" }, "Brand: ", product.brand), product.price && /* @__PURE__ */ React.createElement("p", { className: "text-lg font-bold text-gray-900 dark:text-white" }, "$", typeof product.price === "number" ? product.price.toFixed(2) : product.price), product.description && /* @__PURE__ */ React.createElement("p", { className: "text-sm text-gray-600 dark:text-gray-400 line-clamp-2" }, product.description), /* @__PURE__ */ React.createElement("div", { className: "flex gap-2 pt-2" }, /* @__PURE__ */ React.createElement(
474
- "span",
475
- {
476
- className: `text-xs px-2 py-1 rounded flex-1 text-center ${product.inStock ? "bg-green-100 text-green-800" : "bg-red-100 text-red-800"}`
477
- },
478
- product.inStock ? "In Stock" : "Out of Stock"
479
- )))
480
- ))));
481
- }
482
- function renderText(data) {
483
- const content = (data == null ? void 0 : data.content) || String(data);
484
- return /* @__PURE__ */ React.createElement("div", { className: "bg-white dark:bg-gray-800 p-4 rounded-lg shadow prose dark:prose-invert max-w-none" }, /* @__PURE__ */ React.createElement("p", { className: "text-gray-700 dark:text-gray-300 whitespace-pre-wrap" }, content));
572
+ products,
573
+ primaryColor: primaryColor != null ? primaryColor : "#6366f1",
574
+ onAddToCart: (p) => onAddToCart == null ? void 0 : onAddToCart(p)
575
+ }
576
+ ));
485
577
  }
486
- function generateColor(index) {
487
- const colors = [
488
- "#3B82F6",
489
- // blue
490
- "#10B981",
491
- // green
492
- "#F59E0B",
493
- // amber
494
- "#EF4444",
495
- // red
496
- "#8B5CF6",
497
- // purple
498
- "#EC4899",
499
- // pink
500
- "#06B6D4",
501
- // cyan
502
- "#F97316"
503
- // orange
504
- ];
505
- return colors[index % colors.length];
578
+ function TextView({ data }) {
579
+ const content = (data == null ? void 0 : data.content) || String(data != null ? data : "");
580
+ return /* @__PURE__ */ React5.createElement("div", { className: "bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 p-4 shadow-sm" }, /* @__PURE__ */ React5.createElement("p", { className: "text-sm text-slate-700 dark:text-slate-300 whitespace-pre-wrap leading-relaxed" }, content));
506
581
  }
507
582
 
508
583
  // src/components/MessageBubble.tsx
@@ -586,41 +661,51 @@ function stripStructuredUiLabels(raw) {
586
661
  );
587
662
  }
588
663
  function extractBareJsonObject(raw) {
589
- const start = raw.indexOf("{");
664
+ let start = raw.indexOf("{");
590
665
  if (start === -1) return null;
591
- let depth = 0;
592
- let inString = false;
593
- let escape = false;
594
- for (let i = start; i < raw.length; i += 1) {
595
- const ch = raw[i];
596
- if (escape) {
597
- escape = false;
598
- continue;
599
- }
600
- if (ch === "\\" && inString) {
601
- escape = true;
602
- continue;
603
- }
604
- if (ch === '"') {
605
- inString = !inString;
606
- continue;
607
- }
608
- if (inString) continue;
609
- if (ch === "{") depth += 1;
610
- if (ch === "}") {
611
- depth -= 1;
612
- if (depth === 0) {
613
- return raw.slice(start, i + 1);
666
+ let bestJson = null;
667
+ let maxLen = 0;
668
+ while (start !== -1) {
669
+ let depth = 0;
670
+ let inString = false;
671
+ let escape = false;
672
+ for (let i = start; i < raw.length; i += 1) {
673
+ const ch = raw[i];
674
+ if (escape) {
675
+ escape = false;
676
+ continue;
677
+ }
678
+ if (ch === "\\" && inString) {
679
+ escape = true;
680
+ continue;
681
+ }
682
+ if (ch === '"') {
683
+ inString = !inString;
684
+ continue;
685
+ }
686
+ if (inString) continue;
687
+ if (ch === "{") depth += 1;
688
+ else if (ch === "}") {
689
+ depth -= 1;
690
+ if (depth === 0) {
691
+ const candidate = raw.slice(start, i + 1);
692
+ if (candidate.length > maxLen) {
693
+ maxLen = candidate.length;
694
+ bestJson = candidate;
695
+ }
696
+ break;
697
+ }
614
698
  }
615
699
  }
700
+ start = raw.indexOf("{", start + 1);
616
701
  }
617
- return null;
702
+ return bestJson;
618
703
  }
619
704
  function extractStructuredPayload(raw) {
620
705
  var _a, _b;
621
706
  let working = raw;
622
707
  const fenceRegex = /```(?:[a-z]+)?\s*([\s\S]*?)```/gi;
623
- for (const match of raw.matchAll(fenceRegex)) {
708
+ for (const match of Array.from(raw.matchAll(fenceRegex))) {
624
709
  const body = (_b = (_a = match[1]) == null ? void 0 : _a.trim()) != null ? _b : "";
625
710
  if (!looksLikeStructuredPayload(body)) continue;
626
711
  working = working.replace(match[0], "");
@@ -644,19 +729,28 @@ function extractStructuredPayload(raw) {
644
729
  }
645
730
  function isLikelyUiOnlyMessage(text) {
646
731
  const trimmed = text.trim();
647
- return trimmed.length === 0 || /^(chart data|table data|visualization|visual representation|product catalog|product recommendations|catalog summary)\s*:?\s*$/i.test(trimmed);
732
+ return trimmed.length === 0 || trimmed.length < 30 || /^(chart data|table data|visualization|visual representation|product catalog|product recommendations|catalog summary|availability|inventory status|details)\s*:?\s*$/i.test(trimmed);
648
733
  }
649
734
  function resolveImage(data) {
650
735
  const isPlaceholder = (val) => typeof val === "string" && (val === "..." || val === "" || val.toLowerCase() === "null" || val.toLowerCase() === "undefined");
651
- for (const key of ["image", "img", "thumbnail"]) {
736
+ for (const key of ["image", "img", "thumbnail", "images", "product_image", "Image", "Thumbnail"]) {
652
737
  const v = data[key];
653
738
  if (typeof v === "string" && v && !isPlaceholder(v)) return v;
654
- }
655
- if (Array.isArray(data.images) && typeof data.images[0] === "string" && !isPlaceholder(data.images[0])) {
656
- return data.images[0];
739
+ if (Array.isArray(v) && typeof v[0] === "string" && v[0] && !isPlaceholder(v[0])) return v[0];
657
740
  }
658
741
  return void 0;
659
742
  }
743
+ function getMetadataValue(meta, keys) {
744
+ const searchKeys = keys.map((k) => k.toLowerCase());
745
+ const metaKeys = Object.keys(meta);
746
+ const foundKey = metaKeys.find((k) => searchKeys.includes(k.toLowerCase()));
747
+ if (foundKey) return meta[foundKey];
748
+ const partialKey = metaKeys.find((k) => {
749
+ const kLow = k.toLowerCase();
750
+ return searchKeys.some((sk) => kLow.includes(sk) || sk.includes(kLow));
751
+ });
752
+ return partialKey ? meta[partialKey] : void 0;
753
+ }
660
754
  function normaliseChild(children) {
661
755
  if (typeof children === "object" && children !== null && !Array.isArray(children) && !React6.isValidElement(children)) {
662
756
  return JSON.stringify(children);
@@ -734,6 +828,13 @@ function DataTable({ config, viewportSize = "large" }) {
734
828
  }
735
829
  function normalizeTabularRows(rows, columns) {
736
830
  if (!Array.isArray(rows)) return [];
831
+ const SYNONYMS = {
832
+ name: ["product", "item", "title", "label", "heading", "product name", "item name"],
833
+ price: ["cost", "amount", "msrp", "price", "rate", "value", "price_usd"],
834
+ brand: ["manufacturer", "vendor", "make", "company", "brand_name", "supplier"],
835
+ image: ["imageUrl", "thumbnail", "img", "url", "photo", "picture", "media"],
836
+ stock: ["inventory", "quantity", "count", "availability", "stock_level", "in stock", "status"]
837
+ };
737
838
  return rows.map((row) => {
738
839
  if (Array.isArray(row)) {
739
840
  const keys = Array.isArray(columns) && columns.length > 0 ? columns : row.map((_, index) => `column_${index + 1}`);
@@ -743,7 +844,36 @@ function normalizeTabularRows(rows, columns) {
743
844
  }, {});
744
845
  }
745
846
  if (row && typeof row === "object") {
746
- return row;
847
+ const result = __spreadValues({}, row);
848
+ if (Array.isArray(columns)) {
849
+ columns.forEach((col) => {
850
+ if (result[col] !== void 0) return;
851
+ const rowKeys = Object.keys(row);
852
+ const colLower = col.toLowerCase().replace(/_/g, " ");
853
+ const foundKey = rowKeys.find((rk) => {
854
+ const rkLower = rk.toLowerCase().replace(/_/g, " ");
855
+ return rkLower === colLower || rkLower.includes(colLower) || colLower.includes(rkLower);
856
+ });
857
+ if (foundKey) {
858
+ result[col] = row[foundKey];
859
+ return;
860
+ }
861
+ for (const [target, aliases] of Object.entries(SYNONYMS)) {
862
+ const isMatch = target === colLower || aliases.some((a) => colLower.includes(a) || a.includes(colLower));
863
+ if (isMatch) {
864
+ const mappedKey = rowKeys.find((rk) => {
865
+ const rkL = rk.toLowerCase();
866
+ return rkL === target || aliases.some((a) => rkL.includes(a) || a.includes(rkL));
867
+ });
868
+ if (mappedKey) {
869
+ result[col] = row[mappedKey];
870
+ break;
871
+ }
872
+ }
873
+ }
874
+ });
875
+ }
876
+ return result;
747
877
  }
748
878
  return { value: row };
749
879
  }).filter((row) => Object.keys(row).length > 0);
@@ -875,43 +1005,58 @@ function MessageBubble({
875
1005
  const isCompact = viewportSize === "compact";
876
1006
  const isMedium = viewportSize === "medium";
877
1007
  const [showSources, setShowSources] = React6.useState(false);
878
- const hasStructuredProductBlock = React6.useMemo(
879
- () => /```(?:ui|json|chart)\s*[\s\S]*?"?(?:view|type|data|products)"?\s*:\s*"?(?:carousel|products|table)"?/i.test(message.content) || /\{[\s\S]*?"?(?:view|type|data|products)"?\s*:\s*"?(?:carousel|products)"?[\s\S]*\}/i.test(message.content) && looksLikeStructuredPayload(message.content),
880
- [message.content]
881
- );
882
1008
  const structuredContent = React6.useMemo(
883
1009
  () => isUser ? { payload: null, text: message.content } : extractStructuredPayload(message.content),
884
1010
  [isUser, message.content]
885
1011
  );
886
- const shouldRenderStructuredOnly = !isUser && Boolean(structuredContent.payload) && isLikelyUiOnlyMessage(structuredContent.text);
1012
+ const hasRichUI = React6.useMemo(() => {
1013
+ if (message.uiTransformation) {
1014
+ const type = message.uiTransformation.type;
1015
+ if (type && !["text", "table"].includes(type)) return true;
1016
+ }
1017
+ if (structuredContent.payload) {
1018
+ return /"?(?:view|type|chartType)"?\s*:\s*"?(?:chart|carousel|pie|bar|line|product_carousel)"?/i.test(structuredContent.payload);
1019
+ }
1020
+ return false;
1021
+ }, [message.uiTransformation, structuredContent.payload]);
1022
+ const shouldRenderStructuredOnly = !isUser && hasRichUI && isLikelyUiOnlyMessage(structuredContent.text);
887
1023
  const productsFromSources = React6.useMemo(() => {
888
1024
  if (isUser || !sources) return [];
889
1025
  return sources.filter((s) => {
890
1026
  var _a;
891
1027
  const m = (_a = s.metadata) != null ? _a : {};
892
- return m.price || m.image || m.img || m.thumbnail || m.images || m.brand || m.type === "product";
1028
+ const keys = Object.keys(m).map((k) => k.toLowerCase());
1029
+ const hasProductKey = keys.some(
1030
+ (k) => ["price", "image", "img", "thumbnail", "images", "brand", "product", "sku", "category", "model", "cost"].includes(k)
1031
+ );
1032
+ const hasPricePattern = /\$\s*\d+/.test(s.content);
1033
+ return hasProductKey || hasPricePattern || m.type === "product";
893
1034
  }).map((s) => {
894
- var _a, _b, _c, _d;
1035
+ var _a, _b;
895
1036
  const m = (_a = s.metadata) != null ? _a : {};
1037
+ const name = getMetadataValue(m, ["name", "product", "title", "label", "item"]);
1038
+ const brand = getMetadataValue(m, ["brand", "manufacturer", "vendor", "make"]);
1039
+ const price = getMetadataValue(m, ["price", "cost", "amount", "msrp", "rate"]);
896
1040
  return {
897
1041
  id: s.id,
898
- name: (_d = (_c = (_b = m.name) != null ? _b : m.title) != null ? _c : s.content.split("\n")[0]) != null ? _d : "Unknown Product",
899
- brand: m.brand,
900
- price: m.price,
1042
+ name: (_b = name != null ? name : s.content.split("\n")[0]) != null ? _b : "Unknown Product",
1043
+ brand,
1044
+ price,
901
1045
  image: resolveImage(m),
902
- link: m.link,
1046
+ link: getMetadataValue(m, ["link", "url", "product_url"]),
903
1047
  description: s.content
904
1048
  };
905
1049
  });
906
1050
  }, [sources, isUser]);
907
1051
  const { productsFromContent, cleanContent } = React6.useMemo(() => {
1052
+ var _a, _b;
908
1053
  if (isUser) {
909
1054
  return { productsFromContent: [], cleanContent: message.content };
910
1055
  }
911
1056
  const jsonRegex = /```(?:json|ui)?\s*([\s\S]*?)\s*```/g;
912
1057
  const products = [];
913
1058
  let content = structuredContent.text;
914
- const payloadCandidates = [structuredContent.payload, ...content.matchAll(jsonRegex).map((match) => match[1])].filter(Boolean);
1059
+ const payloadCandidates = [structuredContent.payload, ...Array.from(content.matchAll(jsonRegex)).map((match) => match[1])].filter(Boolean);
915
1060
  for (const candidate of payloadCandidates) {
916
1061
  try {
917
1062
  const data = JSON.parse(sanitizeJson(candidate));
@@ -919,9 +1064,9 @@ function MessageBubble({
919
1064
  if ((data.type === "products" || data.view === "carousel" || data.view === "table" || Array.isArray(data.products)) && itemSet) {
920
1065
  products.push(
921
1066
  ...itemSet.map((item) => {
922
- var _a;
1067
+ var _a2;
923
1068
  return __spreadProps(__spreadValues({}, item), {
924
- image: (_a = item.image) != null ? _a : resolveImage(item)
1069
+ image: (_a2 = item.image) != null ? _a2 : resolveImage(item)
925
1070
  });
926
1071
  })
927
1072
  );
@@ -930,15 +1075,15 @@ function MessageBubble({
930
1075
  }
931
1076
  }
932
1077
  if (content.includes('"type": "products"') || content.includes('"type":"products"')) {
933
- for (const match of content.matchAll(jsonRegex)) {
1078
+ for (const match of Array.from(content.matchAll(jsonRegex))) {
934
1079
  try {
935
1080
  const data = JSON.parse(sanitizeJson(match[1]));
936
1081
  if (data.type === "products" && Array.isArray(data.items)) {
937
1082
  products.push(
938
1083
  ...data.items.map((item) => {
939
- var _a;
1084
+ var _a2;
940
1085
  return __spreadProps(__spreadValues({}, item), {
941
- image: (_a = item.image) != null ? _a : resolveImage(item)
1086
+ image: (_a2 = item.image) != null ? _a2 : resolveImage(item)
942
1087
  });
943
1088
  })
944
1089
  );
@@ -948,8 +1093,41 @@ function MessageBubble({
948
1093
  }
949
1094
  }
950
1095
  }
1096
+ const bulletRegex = /(?:[•*-]\s*)?([^•\n\-\$*–—\(]+?)(?:\s*\(?Price\s*[:\-–—]?\s*)?(?:\s*[:\-–—]\s*|\s+)\$?([\d,.]+)(?:\s*USD)?/gi;
1097
+ const matches = Array.from(content.matchAll(bulletRegex));
1098
+ if (matches.length >= 2) {
1099
+ const normalize = (s) => s.toLowerCase().replace(/[^a-z0-9]/g, "");
1100
+ for (const match of matches) {
1101
+ let name = ((_a = match[1]) == null ? void 0 : _a.trim()) || "";
1102
+ name = name.replace(/\s*\(?Price\s*$/i, "").replace(/[:\-–—]\s*$/g, "").trim();
1103
+ let price = ((_b = match[2]) == null ? void 0 : _b.trim()) || "";
1104
+ price = price.replace(/[,\s]+$/, "").trim();
1105
+ if (name && price) {
1106
+ const newProduct = {
1107
+ id: `text-prod-${name}-${price}`,
1108
+ name,
1109
+ price: `$${price}`
1110
+ };
1111
+ const normName = normalize(name);
1112
+ const sourceMatch = productsFromSources.find((s) => {
1113
+ const sn = normalize(s.name);
1114
+ return sn.includes(normName) || normName.includes(sn);
1115
+ });
1116
+ if (sourceMatch) {
1117
+ newProduct.image = sourceMatch.image;
1118
+ newProduct.brand = sourceMatch.brand;
1119
+ newProduct.link = sourceMatch.link;
1120
+ newProduct.description = sourceMatch.description;
1121
+ }
1122
+ products.push(newProduct);
1123
+ content = content.replace(match[0], `
1124
+ **${name}** \u2014 $${price}`);
1125
+ }
1126
+ }
1127
+ content = content.replace(/\n{3,}/g, "\n\n").trim();
1128
+ }
951
1129
  return { productsFromContent: products, cleanContent: content.trim() };
952
- }, [message.content, isUser, structuredContent]);
1130
+ }, [message.content, isUser, structuredContent, productsFromSources]);
953
1131
  const allProducts = React6.useMemo(() => {
954
1132
  if (productsFromContent.length > 0) return productsFromContent;
955
1133
  const seen = /* @__PURE__ */ new Set();
@@ -1146,8 +1324,24 @@ ${match.trim()}
1146
1324
  onAddToCart,
1147
1325
  viewportSize
1148
1326
  }
1149
- ), !shouldRenderStructuredOnly && /* @__PURE__ */ React6.createElement(ReactMarkdown, { remarkPlugins: [remarkGfm], components: markdownComponents }, processedMarkdown), !isUser && !structuredContent.payload && !!message.uiTransformation && /* @__PURE__ */ React6.createElement("div", { className: "mt-4 border-t border-slate-100 dark:border-white/5 pt-4" }, /* @__PURE__ */ React6.createElement(VisualizationRenderer, { data: message.uiTransformation })), isStreaming && message.content && /* @__PURE__ */ React6.createElement("span", { className: "inline-block w-1.5 h-3.5 ml-1 bg-emerald-500 animate-pulse align-middle" }))
1150
- ), !isUser && !hasStructuredProductBlock && allProducts.length > 0 && /* @__PURE__ */ React6.createElement("div", { className: "w-full mt-1" }, /* @__PURE__ */ React6.createElement(
1327
+ ), !shouldRenderStructuredOnly && /* @__PURE__ */ React6.createElement(ReactMarkdown, { components: markdownComponents }, processedMarkdown), isStreaming && message.content && /* @__PURE__ */ React6.createElement("span", { className: "inline-block w-1.5 h-3.5 ml-1 bg-emerald-500 animate-pulse align-middle" }))
1328
+ ), (() => {
1329
+ var _a, _b;
1330
+ if (isUser || structuredContent.payload || !message.uiTransformation) return null;
1331
+ const ui = message.uiTransformation;
1332
+ const textContent = (_b = (_a = ui.data) == null ? void 0 : _a.content) != null ? _b : "";
1333
+ const shouldShow = ui.type === "table" && allProducts.length === 0 || !["text", "table"].includes(ui.type) || ui.type === "text" && !hasRichUI && allProducts.length === 0 && textContent && !processedMarkdown.toLowerCase().includes(textContent.trim().toLowerCase());
1334
+ if (!shouldShow) return null;
1335
+ return /* @__PURE__ */ React6.createElement("div", { className: "w-full mt-3" }, /* @__PURE__ */ React6.createElement(
1336
+ VisualizationRenderer,
1337
+ {
1338
+ data: ui,
1339
+ primaryColor,
1340
+ onAddToCart,
1341
+ className: "rounded-3xl border border-slate-200/60 dark:border-white/10 bg-white/90 dark:bg-slate-900/80 p-4"
1342
+ }
1343
+ ));
1344
+ })(), !isUser && !hasRichUI && allProducts.length > 0 && /* @__PURE__ */ React6.createElement("div", { className: "w-full mt-1" }, /* @__PURE__ */ React6.createElement(
1151
1345
  ProductCarousel,
1152
1346
  {
1153
1347
  products: allProducts,
@@ -1351,7 +1545,7 @@ function useRagChat(projectId, options = {}) {
1351
1545
  const complete = buffer.slice(0, lastBoundary + 2);
1352
1546
  buffer = buffer.slice(lastBoundary + 2);
1353
1547
  for (const frame of parseSseChunk(complete)) {
1354
- if (frame.type === "text") {
1548
+ if (frame.type === "text" && frame.text) {
1355
1549
  assistantContent += frame.text;
1356
1550
  } else if (frame.type === "metadata") {
1357
1551
  sources = (_a = frame.sources) != null ? _a : [];
@@ -1373,7 +1567,7 @@ function useRagChat(projectId, options = {}) {
1373
1567
  }
1374
1568
  if (buffer.trim()) {
1375
1569
  for (const frame of parseSseChunk(buffer)) {
1376
- if (frame.type === "text") assistantContent += frame.text;
1570
+ if (frame.type === "text" && frame.text) assistantContent += frame.text;
1377
1571
  else if (frame.type === "metadata") sources = (_b = frame.sources) != null ? _b : [];
1378
1572
  }
1379
1573
  }