@retrivora-ai/rag-engine 1.7.9 → 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 (47) hide show
  1. package/dist/DocumentChunker-Dh9TvmGG.d.mts +45 -0
  2. package/dist/DocumentChunker-Dh9TvmGG.d.ts +45 -0
  3. package/dist/{index-wCRqMtdX.d.mts → ILLMProvider-BfRgI1Xh.d.mts} +59 -1
  4. package/dist/{index-wCRqMtdX.d.ts → ILLMProvider-BfRgI1Xh.d.ts} +59 -1
  5. package/dist/MultiTablePostgresProvider-YY7LPNJK.mjs +8 -0
  6. package/dist/{chunk-UHGYLTTY.mjs → chunk-BFYLQYQU.mjs} +846 -458
  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 +1033 -622
  11. package/dist/handlers/index.mjs +1 -1
  12. package/dist/{index-BDqz2_Yu.d.ts → index-1Z4GuYBi.d.ts} +7 -1
  13. package/dist/{index-DhsG2o5q.d.mts → 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 +507 -352
  17. package/dist/index.mjs +427 -253
  18. package/dist/server.d.mts +35 -5
  19. package/dist/server.d.ts +35 -5
  20. package/dist/server.js +1183 -795
  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/DynamicChart.tsx +10 -35
  25. package/src/components/MessageBubble.tsx +223 -74
  26. package/src/components/ProductCard.tsx +2 -2
  27. package/src/components/VisualizationRenderer.tsx +347 -247
  28. package/src/config/RagConfig.ts +5 -0
  29. package/src/config/serverConfig.ts +3 -1
  30. package/src/core/Pipeline.ts +70 -209
  31. package/src/core/ProviderRegistry.ts +2 -2
  32. package/src/core/VectorPlugin.ts +9 -0
  33. package/src/handlers/index.ts +23 -7
  34. package/src/hooks/useRagChat.ts +2 -2
  35. package/src/llm/LLMFactory.ts +54 -2
  36. package/src/llm/providers/AnthropicProvider.ts +12 -8
  37. package/src/llm/providers/GeminiProvider.ts +188 -143
  38. package/src/llm/providers/OllamaProvider.ts +7 -3
  39. package/src/llm/providers/OpenAIProvider.ts +12 -8
  40. package/src/types/chat.ts +6 -0
  41. package/src/types/index.ts +81 -0
  42. package/src/utils/SchemaMapper.ts +129 -0
  43. package/src/utils/UITransformer.ts +524 -194
  44. package/dist/DocumentChunker-Bmscbh-X.d.ts +0 -93
  45. package/dist/DocumentChunker-DCuxrOdM.d.mts +0 -93
  46. package/dist/PostgreSQLProvider-BMOETDZA.mjs +0 -8
  47. 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);
@@ -267,46 +266,23 @@ function DynamicChart({
267
266
  const finalXKey = String(resolvedXKey);
268
267
  const finalDataKeys = resolvedDataKeys.map(String).filter((k) => k !== "undefined");
269
268
  const pieDataKey = finalDataKeys[0] || "value";
270
- const stockAwareColor = (entry, index) => {
271
- var _a, _b, _c;
272
- const stockStatus = String(
273
- (_c = (_b = (_a = entry.stockStatus) != null ? _a : entry.status) != null ? _b : entry.availability) != null ? _c : ""
274
- ).toLowerCase();
275
- const inStockCount = typeof entry.inStockCount === "number" ? entry.inStockCount : void 0;
276
- const outOfStockCount = typeof entry.outOfStockCount === "number" ? entry.outOfStockCount : void 0;
277
- const inStockFlag = entry.inStock;
278
- if (stockStatus.includes("in stock")) return "#10b981";
279
- if (stockStatus.includes("out of stock")) return "#f97316";
280
- if (typeof inStockFlag === "string" && inStockFlag.toLowerCase() === "true") return "#10b981";
281
- if (inStockFlag === 1) return "#10b981";
282
- if (inStockFlag === 0) return "#f97316";
283
- if (typeof inStockCount === "number" || typeof outOfStockCount === "number") {
284
- const inCount = inStockCount != null ? inStockCount : 0;
285
- const outCount = outOfStockCount != null ? outOfStockCount : 0;
286
- if (inCount > outCount) return "#10b981";
287
- if (outCount > inCount) return "#f97316";
288
- }
289
- return colors[index % colors.length];
290
- };
291
269
  const tooltipLabelMap = {
292
- value: "Value",
270
+ value: "Total Items",
293
271
  count: "Count",
294
- inStock: "In stock",
295
- inStockCount: "In stock",
296
- outOfStockCount: "Out of stock",
297
- stockStatus: "Stock status",
298
- category: "Category",
299
- label: "Label",
300
- name: "Name"
272
+ inStockCount: "In Stock",
273
+ outOfStockCount: "Out of Stock",
274
+ stockStatus: "Status"
301
275
  };
302
276
  const renderTooltip = (row, label) => {
303
277
  var _a, _b;
304
278
  if (!row) return null;
305
- 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]) => {
306
280
  var _a2;
307
281
  if (value === null || value === void 0 || key === finalXKey) return null;
308
282
  if (typeof value === "object") return null;
309
- 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)));
283
+ const forbiddenKeys = ["fill", "color", "payload", "percent", "stroke", "inStock"];
284
+ if (forbiddenKeys.includes(key)) return null;
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)));
310
286
  }));
311
287
  };
312
288
  const getTooltipRow = (payload) => {
@@ -318,7 +294,7 @@ function DynamicChart({
318
294
  return row;
319
295
  };
320
296
  if (type === "pie") {
321
- 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(
322
298
  Pie,
323
299
  {
324
300
  data: sanitizedData,
@@ -330,10 +306,10 @@ function DynamicChart({
330
306
  label: false,
331
307
  labelLine: false
332
308
  },
333
- sanitizedData.map((entry, index) => /* @__PURE__ */ React5.createElement(Cell, { key: `cell-${index}`, fill: stockAwareColor(entry, index) }))
334
- ), /* @__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" })))));
335
311
  }
336
- 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(
337
313
  Bar,
338
314
  {
339
315
  key,
@@ -342,7 +318,7 @@ function DynamicChart({
342
318
  radius: [4, 4, 0, 0],
343
319
  barSize: isTiny ? Math.max(10, 28 / data.length) : isCompact ? Math.max(14, 42 / data.length) : Math.max(20, 60 / data.length)
344
320
  }
345
- ))) : /* @__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(
346
322
  Line,
347
323
  {
348
324
  key,
@@ -357,175 +333,251 @@ function DynamicChart({
357
333
  }
358
334
 
359
335
  // src/components/VisualizationRenderer.tsx
360
- 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
+ }
361
392
  function VisualizationRenderer({
362
393
  data,
363
- className = ""
394
+ className = "",
395
+ primaryColor,
396
+ onAddToCart
364
397
  }) {
398
+ var _a;
365
399
  if (!data) {
366
- 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");
367
401
  }
368
- 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));
369
403
  }
370
- function renderVisualization(type, data) {
371
- switch (type) {
372
- case "pie_chart":
373
- return renderPieChart(data);
374
- case "bar_chart":
375
- return renderBarChart(data);
376
- case "line_chart":
377
- return renderLineChart(data);
378
- case "table":
379
- return renderTable(data);
380
- case "product_carousel":
381
- return renderProductCarousel(data);
382
- case "text":
383
- default:
384
- 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." } });
385
427
  }
386
428
  }
387
- function renderPieChart(data) {
388
- if (!Array.isArray(data)) {
389
- 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");
390
432
  }
391
- 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(
392
- "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,
393
439
  {
394
- key: idx,
395
- 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
396
448
  },
397
- /* @__PURE__ */ React.createElement(
398
- "div",
399
- {
400
- className: "w-3 h-3 rounded-full",
401
- style: { backgroundColor: generateColor(idx) }
402
- }
403
- ),
404
- /* @__PURE__ */ React.createElement("span", { className: "text-sm font-medium text-gray-700 dark:text-gray-300" }, item.label, ": ", item.value),
405
- item.inStock !== void 0 && /* @__PURE__ */ React.createElement(
406
- "span",
407
- {
408
- className: `text-xs px-2 py-1 rounded ${item.inStock ? "bg-green-100 text-green-800" : "bg-red-100 text-red-800"}`
409
- },
410
- item.inStock ? "In Stock" : "Out of Stock"
411
- )
412
- ))));
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" }))));
413
451
  }
414
- function renderBarChart(data) {
415
- if (!Array.isArray(data)) {
416
- 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");
417
455
  }
418
- 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(
419
- "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,
420
464
  {
421
- className: "h-full rounded-full transition-all",
422
- style: {
423
- width: `${item.value / Math.max(...data.map((d) => d.value)) * 100}%`,
424
- backgroundColor: generateColor(idx)
425
- }
465
+ dataKey: "category",
466
+ tick: { fontSize: 11, fill: "#64748b" },
467
+ tickLine: false,
468
+ axisLine: { stroke: "#cbd5e1" }
426
469
  }
427
- )), item.inStock !== void 0 && /* @__PURE__ */ React.createElement(
428
- "span",
429
- {
430
- className: `text-xs px-2 py-1 rounded inline-block ${item.inStock ? "bg-green-100 text-green-800" : "bg-red-100 text-red-800"}`
431
- },
432
- item.inStock ? "In Stock" : "Out of Stock"
433
- )))));
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] }))));
434
471
  }
435
- function renderLineChart(data) {
436
- if (!Array.isArray(data)) {
437
- 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");
438
475
  }
439
- 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(
440
- "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,
441
486
  {
442
- key: idx,
443
- className: "flex justify-between items-center p-2 bg-gray-50 dark:bg-gray-700 rounded"
444
- },
445
- /* @__PURE__ */ React.createElement("span", { className: "text-sm text-gray-600 dark:text-gray-400" }, point.label || point.timestamp),
446
- /* @__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
+ }
447
503
  ))));
448
504
  }
449
- function renderTable(data) {
450
- if (!data.columns || !data.rows) {
451
- 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");
452
508
  }
453
- 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(
454
529
  "th",
455
530
  {
456
531
  key: idx,
457
- 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"
458
533
  },
459
534
  col
460
- )))), /* @__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(
461
536
  "tr",
462
537
  {
463
538
  key: rowIdx,
464
- 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"
465
540
  },
466
- row.map((cell, cellIdx) => /* @__PURE__ */ React.createElement(
541
+ row.map((cell, cellIdx) => /* @__PURE__ */ React5.createElement(
467
542
  "td",
468
543
  {
469
544
  key: cellIdx,
470
- 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"
471
546
  },
472
547
  String(cell)
473
548
  ))
474
- )))));
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" : "")));
475
550
  }
476
- function renderProductCarousel(data) {
551
+ function CarouselView({
552
+ data,
553
+ onAddToCart,
554
+ primaryColor
555
+ }) {
477
556
  if (!Array.isArray(data)) {
478
- 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");
479
558
  }
480
- 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(
481
- "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,
482
571
  {
483
- key: product.id,
484
- className: "flex-shrink-0 w-64 bg-white dark:bg-gray-800 rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow"
485
- },
486
- product.image && /* @__PURE__ */ React.createElement("div", { className: "relative w-full h-40 bg-gray-200" }, /* @__PURE__ */ React.createElement(
487
- Image2,
488
- {
489
- src: product.image,
490
- alt: product.name,
491
- fill: true,
492
- className: "object-cover",
493
- sizes: "(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
494
- }
495
- )),
496
- /* @__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(
497
- "span",
498
- {
499
- 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"}`
500
- },
501
- product.inStock ? "In Stock" : "Out of Stock"
502
- )))
503
- ))));
504
- }
505
- function renderText(data) {
506
- const content = (data == null ? void 0 : data.content) || String(data);
507
- 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
+ ));
508
577
  }
509
- function generateColor(index) {
510
- const colors = [
511
- "#3B82F6",
512
- // blue
513
- "#10B981",
514
- // green
515
- "#F59E0B",
516
- // amber
517
- "#EF4444",
518
- // red
519
- "#8B5CF6",
520
- // purple
521
- "#EC4899",
522
- // pink
523
- "#06B6D4",
524
- // cyan
525
- "#F97316"
526
- // orange
527
- ];
528
- 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));
529
581
  }
530
582
 
531
583
  // src/components/MessageBubble.tsx
@@ -609,41 +661,51 @@ function stripStructuredUiLabels(raw) {
609
661
  );
610
662
  }
611
663
  function extractBareJsonObject(raw) {
612
- const start = raw.indexOf("{");
664
+ let start = raw.indexOf("{");
613
665
  if (start === -1) return null;
614
- let depth = 0;
615
- let inString = false;
616
- let escape = false;
617
- for (let i = start; i < raw.length; i += 1) {
618
- const ch = raw[i];
619
- if (escape) {
620
- escape = false;
621
- continue;
622
- }
623
- if (ch === "\\" && inString) {
624
- escape = true;
625
- continue;
626
- }
627
- if (ch === '"') {
628
- inString = !inString;
629
- continue;
630
- }
631
- if (inString) continue;
632
- if (ch === "{") depth += 1;
633
- if (ch === "}") {
634
- depth -= 1;
635
- if (depth === 0) {
636
- 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
+ }
637
698
  }
638
699
  }
700
+ start = raw.indexOf("{", start + 1);
639
701
  }
640
- return null;
702
+ return bestJson;
641
703
  }
642
704
  function extractStructuredPayload(raw) {
643
705
  var _a, _b;
644
706
  let working = raw;
645
707
  const fenceRegex = /```(?:[a-z]+)?\s*([\s\S]*?)```/gi;
646
- for (const match of raw.matchAll(fenceRegex)) {
708
+ for (const match of Array.from(raw.matchAll(fenceRegex))) {
647
709
  const body = (_b = (_a = match[1]) == null ? void 0 : _a.trim()) != null ? _b : "";
648
710
  if (!looksLikeStructuredPayload(body)) continue;
649
711
  working = working.replace(match[0], "");
@@ -667,19 +729,28 @@ function extractStructuredPayload(raw) {
667
729
  }
668
730
  function isLikelyUiOnlyMessage(text) {
669
731
  const trimmed = text.trim();
670
- 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);
671
733
  }
672
734
  function resolveImage(data) {
673
735
  const isPlaceholder = (val) => typeof val === "string" && (val === "..." || val === "" || val.toLowerCase() === "null" || val.toLowerCase() === "undefined");
674
- for (const key of ["image", "img", "thumbnail"]) {
736
+ for (const key of ["image", "img", "thumbnail", "images", "product_image", "Image", "Thumbnail"]) {
675
737
  const v = data[key];
676
738
  if (typeof v === "string" && v && !isPlaceholder(v)) return v;
677
- }
678
- if (Array.isArray(data.images) && typeof data.images[0] === "string" && !isPlaceholder(data.images[0])) {
679
- return data.images[0];
739
+ if (Array.isArray(v) && typeof v[0] === "string" && v[0] && !isPlaceholder(v[0])) return v[0];
680
740
  }
681
741
  return void 0;
682
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
+ }
683
754
  function normaliseChild(children) {
684
755
  if (typeof children === "object" && children !== null && !Array.isArray(children) && !React6.isValidElement(children)) {
685
756
  return JSON.stringify(children);
@@ -757,6 +828,13 @@ function DataTable({ config, viewportSize = "large" }) {
757
828
  }
758
829
  function normalizeTabularRows(rows, columns) {
759
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
+ };
760
838
  return rows.map((row) => {
761
839
  if (Array.isArray(row)) {
762
840
  const keys = Array.isArray(columns) && columns.length > 0 ? columns : row.map((_, index) => `column_${index + 1}`);
@@ -766,7 +844,36 @@ function normalizeTabularRows(rows, columns) {
766
844
  }, {});
767
845
  }
768
846
  if (row && typeof row === "object") {
769
- 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;
770
877
  }
771
878
  return { value: row };
772
879
  }).filter((row) => Object.keys(row).length > 0);
@@ -809,12 +916,15 @@ function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAd
809
916
  config2.view = "chart";
810
917
  config2.chartType = config2.chartType || "bar";
811
918
  }
812
- if (!config2.view) {
813
- if (config2.type === "products" || Array.isArray(config2.items) || hasProductLikeData) {
919
+ if (!config2.view || ["pie", "bar", "line", "pie_chart", "bar_chart", "line_chart"].includes(String(config2.view))) {
920
+ const viewStr = String(config2.view || "").toLowerCase();
921
+ if (viewStr.includes("carousel") || config2.type === "products" || Array.isArray(config2.items) || hasProductLikeData) {
814
922
  config2.view = "carousel";
815
- } else if (hasAggregationLikeData || typeof config2.type === "string" && ["pie", "bar", "line"].includes(config2.type) || typeof config2.chartType === "string" && ["pie", "bar", "line"].includes(config2.chartType)) {
923
+ } else if (viewStr.includes("chart") || viewStr.includes("pie") || viewStr.includes("bar") || viewStr.includes("line") || hasAggregationLikeData || typeof config2.type === "string" && ["pie", "bar", "line"].includes(config2.type) || typeof config2.chartType === "string" && ["pie", "bar", "line"].includes(config2.chartType)) {
816
924
  config2.view = "chart";
817
- config2.chartType = config2.chartType || config2.type || "bar";
925
+ config2.chartType = config2.chartType || viewStr.replace("_chart", "") || config2.type || "bar";
926
+ } else if (viewStr.includes("table")) {
927
+ config2.view = "table";
818
928
  } else {
819
929
  config2.view = "table";
820
930
  }
@@ -853,13 +963,13 @@ function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAd
853
963
  accentColor,
854
964
  viewportSize
855
965
  }
856
- ), hasInsights && /* @__PURE__ */ React6.createElement("div", { className: "mt-4 flex flex-wrap gap-2 px-2" }, (_a = config.insights) == null ? void 0 : _a.map((insight) => /* @__PURE__ */ React6.createElement(
966
+ ), hasInsights && /* @__PURE__ */ React6.createElement("div", { className: "mt-4 flex flex-wrap gap-2 px-2" }, (_a = config.insights) == null ? void 0 : _a.map((insight, i) => /* @__PURE__ */ React6.createElement(
857
967
  "span",
858
968
  {
859
- key: insight,
969
+ key: `insight-${i}`,
860
970
  className: `rounded-full border border-emerald-200 bg-emerald-50 ${isCompact ? "px-2.5 py-1 text-[10px]" : "px-3 py-1 text-[11px]"} font-medium text-emerald-700 dark:border-emerald-500/20 dark:bg-emerald-500/10 dark:text-emerald-300`
861
971
  },
862
- insight
972
+ String(insight)
863
973
  ))));
864
974
  case "carousel":
865
975
  return /* @__PURE__ */ React6.createElement("div", { className: "my-4" }, config.title && /* @__PURE__ */ React6.createElement("h4", { className: `${isCompact ? "mb-1.5 text-[11px]" : "mb-2 text-xs"} font-semibold text-slate-500` }, config.title), hasDescription && /* @__PURE__ */ React6.createElement("p", { className: `mb-3 ${isCompact ? "text-xs" : "text-sm"} text-slate-500 dark:text-white/60` }, config.description), /* @__PURE__ */ React6.createElement(ProductCarousel, { products: config.data.map((item) => {
@@ -895,43 +1005,58 @@ function MessageBubble({
895
1005
  const isCompact = viewportSize === "compact";
896
1006
  const isMedium = viewportSize === "medium";
897
1007
  const [showSources, setShowSources] = React6.useState(false);
898
- const hasStructuredProductBlock = React6.useMemo(
899
- () => /```(?: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),
900
- [message.content]
901
- );
902
1008
  const structuredContent = React6.useMemo(
903
1009
  () => isUser ? { payload: null, text: message.content } : extractStructuredPayload(message.content),
904
1010
  [isUser, message.content]
905
1011
  );
906
- 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);
907
1023
  const productsFromSources = React6.useMemo(() => {
908
1024
  if (isUser || !sources) return [];
909
1025
  return sources.filter((s) => {
910
1026
  var _a;
911
1027
  const m = (_a = s.metadata) != null ? _a : {};
912
- 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";
913
1034
  }).map((s) => {
914
- var _a, _b, _c, _d;
1035
+ var _a, _b;
915
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"]);
916
1040
  return {
917
1041
  id: s.id,
918
- name: (_d = (_c = (_b = m.name) != null ? _b : m.title) != null ? _c : s.content.split("\n")[0]) != null ? _d : "Unknown Product",
919
- brand: m.brand,
920
- price: m.price,
1042
+ name: (_b = name != null ? name : s.content.split("\n")[0]) != null ? _b : "Unknown Product",
1043
+ brand,
1044
+ price,
921
1045
  image: resolveImage(m),
922
- link: m.link,
1046
+ link: getMetadataValue(m, ["link", "url", "product_url"]),
923
1047
  description: s.content
924
1048
  };
925
1049
  });
926
1050
  }, [sources, isUser]);
927
1051
  const { productsFromContent, cleanContent } = React6.useMemo(() => {
1052
+ var _a, _b;
928
1053
  if (isUser) {
929
1054
  return { productsFromContent: [], cleanContent: message.content };
930
1055
  }
931
1056
  const jsonRegex = /```(?:json|ui)?\s*([\s\S]*?)\s*```/g;
932
1057
  const products = [];
933
1058
  let content = structuredContent.text;
934
- 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);
935
1060
  for (const candidate of payloadCandidates) {
936
1061
  try {
937
1062
  const data = JSON.parse(sanitizeJson(candidate));
@@ -939,9 +1064,9 @@ function MessageBubble({
939
1064
  if ((data.type === "products" || data.view === "carousel" || data.view === "table" || Array.isArray(data.products)) && itemSet) {
940
1065
  products.push(
941
1066
  ...itemSet.map((item) => {
942
- var _a;
1067
+ var _a2;
943
1068
  return __spreadProps(__spreadValues({}, item), {
944
- image: (_a = item.image) != null ? _a : resolveImage(item)
1069
+ image: (_a2 = item.image) != null ? _a2 : resolveImage(item)
945
1070
  });
946
1071
  })
947
1072
  );
@@ -950,15 +1075,15 @@ function MessageBubble({
950
1075
  }
951
1076
  }
952
1077
  if (content.includes('"type": "products"') || content.includes('"type":"products"')) {
953
- for (const match of content.matchAll(jsonRegex)) {
1078
+ for (const match of Array.from(content.matchAll(jsonRegex))) {
954
1079
  try {
955
1080
  const data = JSON.parse(sanitizeJson(match[1]));
956
1081
  if (data.type === "products" && Array.isArray(data.items)) {
957
1082
  products.push(
958
1083
  ...data.items.map((item) => {
959
- var _a;
1084
+ var _a2;
960
1085
  return __spreadProps(__spreadValues({}, item), {
961
- image: (_a = item.image) != null ? _a : resolveImage(item)
1086
+ image: (_a2 = item.image) != null ? _a2 : resolveImage(item)
962
1087
  });
963
1088
  })
964
1089
  );
@@ -968,8 +1093,41 @@ function MessageBubble({
968
1093
  }
969
1094
  }
970
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
+ }
971
1129
  return { productsFromContent: products, cleanContent: content.trim() };
972
- }, [message.content, isUser, structuredContent]);
1130
+ }, [message.content, isUser, structuredContent, productsFromSources]);
973
1131
  const allProducts = React6.useMemo(() => {
974
1132
  if (productsFromContent.length > 0) return productsFromContent;
975
1133
  const seen = /* @__PURE__ */ new Set();
@@ -1166,8 +1324,24 @@ ${match.trim()}
1166
1324
  onAddToCart,
1167
1325
  viewportSize
1168
1326
  }
1169
- ), !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" }))
1170
- ), !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(
1171
1345
  ProductCarousel,
1172
1346
  {
1173
1347
  products: allProducts,
@@ -1371,7 +1545,7 @@ function useRagChat(projectId, options = {}) {
1371
1545
  const complete = buffer.slice(0, lastBoundary + 2);
1372
1546
  buffer = buffer.slice(lastBoundary + 2);
1373
1547
  for (const frame of parseSseChunk(complete)) {
1374
- if (frame.type === "text") {
1548
+ if (frame.type === "text" && frame.text) {
1375
1549
  assistantContent += frame.text;
1376
1550
  } else if (frame.type === "metadata") {
1377
1551
  sources = (_a = frame.sources) != null ? _a : [];
@@ -1393,7 +1567,7 @@ function useRagChat(projectId, options = {}) {
1393
1567
  }
1394
1568
  if (buffer.trim()) {
1395
1569
  for (const frame of parseSseChunk(buffer)) {
1396
- if (frame.type === "text") assistantContent += frame.text;
1570
+ if (frame.type === "text" && frame.text) assistantContent += frame.text;
1397
1571
  else if (frame.type === "metadata") sources = (_b = frame.sources) != null ? _b : [];
1398
1572
  }
1399
1573
  }