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