@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.
- package/dist/DocumentChunker-Dh9TvmGG.d.mts +45 -0
- package/dist/DocumentChunker-Dh9TvmGG.d.ts +45 -0
- package/dist/{index-wCRqMtdX.d.mts → ILLMProvider-BfRgI1Xh.d.mts} +59 -1
- package/dist/{index-wCRqMtdX.d.ts → ILLMProvider-BfRgI1Xh.d.ts} +59 -1
- package/dist/MultiTablePostgresProvider-YY7LPNJK.mjs +8 -0
- package/dist/{chunk-UHGYLTTY.mjs → chunk-BFYLQYQU.mjs} +846 -458
- package/dist/chunk-R3RGUMHE.mjs +218 -0
- package/dist/handlers/index.d.mts +2 -2
- package/dist/handlers/index.d.ts +2 -2
- package/dist/handlers/index.js +1033 -622
- package/dist/handlers/index.mjs +1 -1
- package/dist/{index-BDqz2_Yu.d.ts → index-1Z4GuYBi.d.ts} +7 -1
- package/dist/{index-DhsG2o5q.d.mts → index-BV0z5mb6.d.mts} +7 -1
- package/dist/index.d.mts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +507 -352
- package/dist/index.mjs +427 -253
- package/dist/server.d.mts +35 -5
- package/dist/server.d.ts +35 -5
- package/dist/server.js +1183 -795
- package/dist/server.mjs +163 -176
- package/package.json +4 -2
- package/src/app/page.tsx +1 -2
- package/src/components/DynamicChart.tsx +10 -35
- package/src/components/MessageBubble.tsx +223 -74
- package/src/components/ProductCard.tsx +2 -2
- package/src/components/VisualizationRenderer.tsx +347 -247
- package/src/config/RagConfig.ts +5 -0
- package/src/config/serverConfig.ts +3 -1
- package/src/core/Pipeline.ts +70 -209
- package/src/core/ProviderRegistry.ts +2 -2
- package/src/core/VectorPlugin.ts +9 -0
- package/src/handlers/index.ts +23 -7
- package/src/hooks/useRagChat.ts +2 -2
- package/src/llm/LLMFactory.ts +54 -2
- package/src/llm/providers/AnthropicProvider.ts +12 -8
- package/src/llm/providers/GeminiProvider.ts +188 -143
- package/src/llm/providers/OllamaProvider.ts +7 -3
- package/src/llm/providers/OpenAIProvider.ts +12 -8
- package/src/types/chat.ts +6 -0
- package/src/types/index.ts +81 -0
- package/src/utils/SchemaMapper.ts +129 -0
- package/src/utils/UITransformer.ts +524 -194
- package/dist/DocumentChunker-Bmscbh-X.d.ts +0 -93
- package/dist/DocumentChunker-DCuxrOdM.d.mts +0 -93
- package/dist/PostgreSQLProvider-BMOETDZA.mjs +0 -8
- 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
|
|
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] =
|
|
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__ */
|
|
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__ */
|
|
54
|
-
/* @__PURE__ */
|
|
55
|
-
hasMore && /* @__PURE__ */
|
|
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
|
|
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
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
83
|
+
/* @__PURE__ */ React2.createElement(ShoppingCart, { className: "w-3 h-3" }),
|
|
85
84
|
"Add to Cart"
|
|
86
|
-
), product.link && /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
139
|
-
)), /* @__PURE__ */
|
|
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__ */
|
|
150
|
-
))), /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
162
|
+
/* @__PURE__ */ React3.createElement(ProductCard, { product, primaryColor, onAddToCart })
|
|
164
163
|
))
|
|
165
164
|
));
|
|
166
165
|
}
|
|
167
166
|
|
|
168
167
|
// src/components/DynamicChart.tsx
|
|
169
|
-
import
|
|
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 =
|
|
195
|
-
const [containerWidth, setContainerWidth] =
|
|
196
|
-
|
|
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 =
|
|
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__ */
|
|
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: "
|
|
270
|
+
value: "Total Items",
|
|
293
271
|
count: "Count",
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
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__ */
|
|
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
|
-
|
|
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__ */
|
|
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((
|
|
334
|
-
), /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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
|
|
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__ */
|
|
400
|
+
return /* @__PURE__ */ React5.createElement("div", { className: `p-4 text-gray-500 ${className}` }, "No data available for visualization");
|
|
367
401
|
}
|
|
368
|
-
return /* @__PURE__ */
|
|
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
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
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
|
|
388
|
-
if (!Array.isArray(data)) {
|
|
389
|
-
return /* @__PURE__ */
|
|
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
|
-
|
|
392
|
-
|
|
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
|
-
|
|
395
|
-
|
|
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__ */
|
|
398
|
-
|
|
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
|
|
415
|
-
if (!Array.isArray(data)) {
|
|
416
|
-
return /* @__PURE__ */
|
|
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
|
-
|
|
419
|
-
|
|
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
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
}
|
|
465
|
+
dataKey: "category",
|
|
466
|
+
tick: { fontSize: 11, fill: "#64748b" },
|
|
467
|
+
tickLine: false,
|
|
468
|
+
axisLine: { stroke: "#cbd5e1" }
|
|
426
469
|
}
|
|
427
|
-
)),
|
|
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
|
|
436
|
-
if (!Array.isArray(data)) {
|
|
437
|
-
return /* @__PURE__ */
|
|
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
|
-
|
|
440
|
-
|
|
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
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
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
|
|
450
|
-
if (!data
|
|
451
|
-
return /* @__PURE__ */
|
|
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
|
-
|
|
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-
|
|
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__ */
|
|
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-
|
|
539
|
+
className: "hover:bg-slate-50/60 dark:hover:bg-white/5 transition-colors"
|
|
465
540
|
},
|
|
466
|
-
row.map((cell, cellIdx) => /* @__PURE__ */
|
|
541
|
+
row.map((cell, cellIdx) => /* @__PURE__ */ React5.createElement(
|
|
467
542
|
"td",
|
|
468
543
|
{
|
|
469
544
|
key: cellIdx,
|
|
470
|
-
className: "px-4 py-
|
|
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
|
|
551
|
+
function CarouselView({
|
|
552
|
+
data,
|
|
553
|
+
onAddToCart,
|
|
554
|
+
primaryColor
|
|
555
|
+
}) {
|
|
477
556
|
if (!Array.isArray(data)) {
|
|
478
|
-
return /* @__PURE__ */
|
|
557
|
+
return /* @__PURE__ */ React5.createElement("div", { className: "text-xs text-red-500" }, "Invalid carousel data");
|
|
479
558
|
}
|
|
480
|
-
|
|
481
|
-
|
|
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
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
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
|
|
510
|
-
const
|
|
511
|
-
|
|
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
|
-
|
|
664
|
+
let start = raw.indexOf("{");
|
|
613
665
|
if (start === -1) return null;
|
|
614
|
-
let
|
|
615
|
-
let
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
if (
|
|
636
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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: (
|
|
919
|
-
brand
|
|
920
|
-
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
|
|
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
|
|
1067
|
+
var _a2;
|
|
943
1068
|
return __spreadProps(__spreadValues({}, item), {
|
|
944
|
-
image: (
|
|
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
|
|
1084
|
+
var _a2;
|
|
960
1085
|
return __spreadProps(__spreadValues({}, item), {
|
|
961
|
-
image: (
|
|
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, {
|
|
1170
|
-
),
|
|
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
|
}
|