@retrivora-ai/rag-engine 1.5.5 → 1.5.7
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/{chunk-Q4MDH6C4.mjs → chunk-ID2Q4CF7.mjs} +2 -0
- package/dist/handlers/index.js +2 -0
- package/dist/handlers/index.mjs +1 -1
- package/dist/index.d.mts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +118 -40
- package/dist/index.mjs +118 -40
- package/dist/server.js +2 -0
- package/dist/server.mjs +1 -1
- package/package.json +1 -1
- package/src/components/ChatWindow.tsx +26 -0
- package/src/components/DynamicChart.tsx +32 -16
- package/src/components/MessageBubble.tsx +112 -27
- package/src/components/ProductCarousel.tsx +2 -2
- package/src/core/Pipeline.ts +2 -0
- package/src/types/props.ts +3 -0
|
@@ -2298,6 +2298,8 @@ When the user asks for a visual representation, comparison, distribution, breakd
|
|
|
2298
2298
|
- Never use ASCII art, markdown tables as the primary answer, or text-only fake charts when a UI block is requested.
|
|
2299
2299
|
- Put any short natural-language explanation before or after the \`\`\`ui\`\`\` block, but keep it concise.
|
|
2300
2300
|
- Return valid JSON inside the \`\`\`ui\`\`\` block.
|
|
2301
|
+
- If the user explicitly asks for a pie chart, return "view": "chart" with "chartType": "pie", not a table fallback.
|
|
2302
|
+
- Do not prefix the JSON with labels like "Table View", "Alternative", or explanatory text inside the code block.
|
|
2301
2303
|
|
|
2302
2304
|
5. EXAMPLE
|
|
2303
2305
|
User: "provide me the distribution of products across categories and highlight which ones are in stock in a pie chart"
|
package/dist/handlers/index.js
CHANGED
|
@@ -3861,6 +3861,8 @@ When the user asks for a visual representation, comparison, distribution, breakd
|
|
|
3861
3861
|
- Never use ASCII art, markdown tables as the primary answer, or text-only fake charts when a UI block is requested.
|
|
3862
3862
|
- Put any short natural-language explanation before or after the \`\`\`ui\`\`\` block, but keep it concise.
|
|
3863
3863
|
- Return valid JSON inside the \`\`\`ui\`\`\` block.
|
|
3864
|
+
- If the user explicitly asks for a pie chart, return "view": "chart" with "chartType": "pie", not a table fallback.
|
|
3865
|
+
- Do not prefix the JSON with labels like "Table View", "Alternative", or explanatory text inside the code block.
|
|
3864
3866
|
|
|
3865
3867
|
5. EXAMPLE
|
|
3866
3868
|
User: "provide me the distribution of products across categories and highlight which ones are in stock in a pie chart"
|
package/dist/handlers/index.mjs
CHANGED
package/dist/index.d.mts
CHANGED
|
@@ -3,6 +3,7 @@ import { P as Product, U as UIConfig, R as RagMessage, V as VectorMatch, a as Us
|
|
|
3
3
|
export { C as ChatMessage, c as ChatOptions, d as ChatResponse, E as EmbeddingConfig, e as EmbeddingProvider, I as IngestDocument, L as LLMConfig, f as LLMProvider, g as RAGConfig, h as RagConfig, i as UpsertDocument, j as VectorDBConfig, k as VectorDBProvider } from './index-Cti1u0y1.mjs';
|
|
4
4
|
export { C as Chunk, a as ChunkOptions, E as EmbedOptions, I as ILLMProvider } from './DocumentChunker-D1dg5iCi.mjs';
|
|
5
5
|
|
|
6
|
+
type ChatViewportSize = 'compact' | 'medium' | 'large';
|
|
6
7
|
interface ChatWindowProps {
|
|
7
8
|
/** Additional className for the wrapper div */
|
|
8
9
|
className?: string;
|
|
@@ -38,6 +39,7 @@ interface MessageBubbleProps {
|
|
|
38
39
|
primaryColor: string;
|
|
39
40
|
accentColor: string;
|
|
40
41
|
onAddToCart?: (product: Product) => void;
|
|
42
|
+
viewportSize?: ChatViewportSize;
|
|
41
43
|
}
|
|
42
44
|
interface ProductCardProps {
|
|
43
45
|
product: Product;
|
|
@@ -79,7 +81,7 @@ declare function ChatWindow({ className, style, onClose, showClose, onResizeStar
|
|
|
79
81
|
|
|
80
82
|
declare function DocumentUpload({ namespace, onUploadComplete, className }: DocumentUploadProps): React.JSX.Element;
|
|
81
83
|
|
|
82
|
-
declare function MessageBubble({ message, sources, isStreaming, primaryColor, accentColor, onAddToCart, }: MessageBubbleProps): React.JSX.Element;
|
|
84
|
+
declare function MessageBubble({ message, sources, isStreaming, primaryColor, accentColor, onAddToCart, viewportSize, }: MessageBubbleProps): React.JSX.Element;
|
|
83
85
|
|
|
84
86
|
declare function SourceCard({ source, index }: SourceCardProps): React.JSX.Element;
|
|
85
87
|
|
package/dist/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { P as Product, U as UIConfig, R as RagMessage, V as VectorMatch, a as Us
|
|
|
3
3
|
export { C as ChatMessage, c as ChatOptions, d as ChatResponse, E as EmbeddingConfig, e as EmbeddingProvider, I as IngestDocument, L as LLMConfig, f as LLMProvider, g as RAGConfig, h as RagConfig, i as UpsertDocument, j as VectorDBConfig, k as VectorDBProvider } from './index-Cti1u0y1.js';
|
|
4
4
|
export { C as Chunk, a as ChunkOptions, E as EmbedOptions, I as ILLMProvider } from './DocumentChunker-BXOUMKoP.js';
|
|
5
5
|
|
|
6
|
+
type ChatViewportSize = 'compact' | 'medium' | 'large';
|
|
6
7
|
interface ChatWindowProps {
|
|
7
8
|
/** Additional className for the wrapper div */
|
|
8
9
|
className?: string;
|
|
@@ -38,6 +39,7 @@ interface MessageBubbleProps {
|
|
|
38
39
|
primaryColor: string;
|
|
39
40
|
accentColor: string;
|
|
40
41
|
onAddToCart?: (product: Product) => void;
|
|
42
|
+
viewportSize?: ChatViewportSize;
|
|
41
43
|
}
|
|
42
44
|
interface ProductCardProps {
|
|
43
45
|
product: Product;
|
|
@@ -79,7 +81,7 @@ declare function ChatWindow({ className, style, onClose, showClose, onResizeStar
|
|
|
79
81
|
|
|
80
82
|
declare function DocumentUpload({ namespace, onUploadComplete, className }: DocumentUploadProps): React.JSX.Element;
|
|
81
83
|
|
|
82
|
-
declare function MessageBubble({ message, sources, isStreaming, primaryColor, accentColor, onAddToCart, }: MessageBubbleProps): React.JSX.Element;
|
|
84
|
+
declare function MessageBubble({ message, sources, isStreaming, primaryColor, accentColor, onAddToCart, viewportSize, }: MessageBubbleProps): React.JSX.Element;
|
|
83
85
|
|
|
84
86
|
declare function SourceCard({ source, index }: SourceCardProps): React.JSX.Element;
|
|
85
87
|
|
package/dist/index.js
CHANGED
|
@@ -174,7 +174,7 @@ function ProductCarousel({ products, primaryColor = "#6366f1", onAddToCart }) {
|
|
|
174
174
|
};
|
|
175
175
|
if (!products || products.length === 0) return null;
|
|
176
176
|
if (products.length === 1) {
|
|
177
|
-
return /* @__PURE__ */ import_react3.default.createElement("div", { className: "my-4 w-[
|
|
177
|
+
return /* @__PURE__ */ import_react3.default.createElement("div", { className: "my-4 w-[min(88%,18rem)] min-w-0 max-w-full animate-fade-in-up" }, /* @__PURE__ */ import_react3.default.createElement(ProductCard, { product: products[0], primaryColor, onAddToCart }));
|
|
178
178
|
}
|
|
179
179
|
return /* @__PURE__ */ import_react3.default.createElement("div", { className: "relative w-full my-4 group/carousel animate-fade-in-up" }, canScroll && /* @__PURE__ */ import_react3.default.createElement(import_react3.default.Fragment, null, /* @__PURE__ */ import_react3.default.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__ */ import_react3.default.createElement(
|
|
180
180
|
"button",
|
|
@@ -209,7 +209,7 @@ function ProductCarousel({ products, primaryColor = "#6366f1", onAddToCart }) {
|
|
|
209
209
|
"div",
|
|
210
210
|
{
|
|
211
211
|
key: product.id,
|
|
212
|
-
className: "snap-start flex-shrink-0 w-[
|
|
212
|
+
className: "snap-start flex-shrink-0 w-[min(88%,18rem)] min-w-[11rem] max-w-full"
|
|
213
213
|
},
|
|
214
214
|
/* @__PURE__ */ import_react3.default.createElement(ProductCard, { product, primaryColor, onAddToCart })
|
|
215
215
|
))
|
|
@@ -220,9 +220,20 @@ function ProductCarousel({ products, primaryColor = "#6366f1", onAddToCart }) {
|
|
|
220
220
|
var import_react4 = __toESM(require("react"));
|
|
221
221
|
var import_recharts = require("recharts");
|
|
222
222
|
var DEFAULT_COLORS = ["#6366f1", "#10b981", "#f59e0b", "#ef4444", "#8b5cf6", "#ec4899"];
|
|
223
|
-
function DynamicChart({
|
|
223
|
+
function DynamicChart({
|
|
224
|
+
config,
|
|
225
|
+
primaryColor = "#6366f1",
|
|
226
|
+
accentColor = "#8b5cf6",
|
|
227
|
+
viewportSize = "large"
|
|
228
|
+
}) {
|
|
224
229
|
const { type, data } = config;
|
|
225
230
|
const colors = config.colors || [primaryColor, accentColor, ...DEFAULT_COLORS];
|
|
231
|
+
const isCompact = viewportSize === "compact";
|
|
232
|
+
const isMedium = viewportSize === "medium";
|
|
233
|
+
const chartHeightClass = isCompact ? "h-56 min-h-[220px]" : isMedium ? "h-64 min-h-[250px]" : "h-72 min-h-[280px]";
|
|
234
|
+
const pieOuterRadius = isCompact ? 56 : isMedium ? 68 : 80;
|
|
235
|
+
const pieLabel = ({ name, percent }) => isCompact ? `${name}` : `${name} ${((percent != null ? percent : 0) * 100).toFixed(0)}%`;
|
|
236
|
+
const axisTick = { fontSize: isCompact ? 10 : 12, fill: "#64748b" };
|
|
226
237
|
const sanitizedData = import_react4.default.useMemo(() => {
|
|
227
238
|
if (!data) return [];
|
|
228
239
|
return data.map((item) => {
|
|
@@ -322,7 +333,7 @@ function DynamicChart({ config, primaryColor = "#6366f1", accentColor = "#8b5cf6
|
|
|
322
333
|
return row;
|
|
323
334
|
};
|
|
324
335
|
if (type === "pie") {
|
|
325
|
-
return /* @__PURE__ */ import_react4.default.createElement("div", { className:
|
|
336
|
+
return /* @__PURE__ */ import_react4.default.createElement("div", { className: `w-full ${chartHeightClass} mt-4 mb-2 select-none overflow-hidden` }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.ResponsiveContainer, { width: "99%", height: "100%" }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.PieChart, null, /* @__PURE__ */ import_react4.default.createElement(
|
|
326
337
|
import_recharts.Pie,
|
|
327
338
|
{
|
|
328
339
|
data: sanitizedData,
|
|
@@ -330,31 +341,31 @@ function DynamicChart({ config, primaryColor = "#6366f1", accentColor = "#8b5cf6
|
|
|
330
341
|
nameKey: finalXKey,
|
|
331
342
|
cx: "50%",
|
|
332
343
|
cy: "50%",
|
|
333
|
-
outerRadius:
|
|
334
|
-
label:
|
|
344
|
+
outerRadius: pieOuterRadius,
|
|
345
|
+
label: pieLabel
|
|
335
346
|
},
|
|
336
347
|
sanitizedData.map((entry, index) => /* @__PURE__ */ import_react4.default.createElement(import_recharts.Cell, { key: `cell-${index}`, fill: stockAwareColor(entry, index) }))
|
|
337
|
-
), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Tooltip, { content: (props) => renderTooltip(getTooltipRow(props.payload), typeof props.label === "string" ? props.label : void 0) }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Legend,
|
|
348
|
+
), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Tooltip, { content: (props) => renderTooltip(getTooltipRow(props.payload), typeof props.label === "string" ? props.label : void 0) }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Legend, { wrapperStyle: { fontSize: isCompact ? 10 : 12 } }))));
|
|
338
349
|
}
|
|
339
|
-
return /* @__PURE__ */ import_react4.default.createElement("div", { className:
|
|
350
|
+
return /* @__PURE__ */ import_react4.default.createElement("div", { className: `w-full ${chartHeightClass} mt-4 mb-2 select-none overflow-hidden` }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.ResponsiveContainer, { width: "99%", height: "100%" }, type === "bar" ? /* @__PURE__ */ import_react4.default.createElement(import_recharts.BarChart, { data: sanitizedData, margin: { top: 10, right: 10, left: -20, bottom: 0 } }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.CartesianGrid, { strokeDasharray: "3 3", vertical: false, stroke: "#e2e8f0" }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.XAxis, { dataKey: finalXKey, tick: axisTick, tickLine: false, axisLine: { stroke: "#cbd5e1" } }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.YAxis, { tick: axisTick, tickLine: false, axisLine: false }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Tooltip, { content: (props) => renderTooltip(getTooltipRow(props.payload), typeof props.label === "string" ? props.label : void 0), cursor: { fill: "#f1f5f9" } }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Legend, { wrapperStyle: { fontSize: isCompact ? 10 : 12 } }), finalDataKeys.map((key, index) => /* @__PURE__ */ import_react4.default.createElement(
|
|
340
351
|
import_recharts.Bar,
|
|
341
352
|
{
|
|
342
353
|
key,
|
|
343
354
|
dataKey: key,
|
|
344
355
|
fill: colors[index % colors.length],
|
|
345
356
|
radius: [4, 4, 0, 0],
|
|
346
|
-
barSize: Math.max(20, 60 / data.length)
|
|
357
|
+
barSize: isCompact ? Math.max(14, 42 / data.length) : Math.max(20, 60 / data.length)
|
|
347
358
|
}
|
|
348
|
-
))) : /* @__PURE__ */ import_react4.default.createElement(import_recharts.LineChart, { data: sanitizedData, margin: { top: 10, right: 10, left: -20, bottom: 0 } }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.CartesianGrid, { strokeDasharray: "3 3", vertical: false, stroke: "#e2e8f0" }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.XAxis, { dataKey: finalXKey, tick:
|
|
359
|
+
))) : /* @__PURE__ */ import_react4.default.createElement(import_recharts.LineChart, { data: sanitizedData, margin: { top: 10, right: 10, left: -20, bottom: 0 } }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.CartesianGrid, { strokeDasharray: "3 3", vertical: false, stroke: "#e2e8f0" }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.XAxis, { dataKey: finalXKey, tick: axisTick, tickLine: false, axisLine: { stroke: "#cbd5e1" } }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.YAxis, { tick: axisTick, tickLine: false, axisLine: false }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Tooltip, { content: (props) => renderTooltip(getTooltipRow(props.payload), typeof props.label === "string" ? props.label : void 0) }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Legend, { wrapperStyle: { fontSize: isCompact ? 10 : 12 } }), finalDataKeys.map((key, index) => /* @__PURE__ */ import_react4.default.createElement(
|
|
349
360
|
import_recharts.Line,
|
|
350
361
|
{
|
|
351
362
|
key,
|
|
352
363
|
type: "monotone",
|
|
353
364
|
dataKey: key,
|
|
354
365
|
stroke: colors[index % colors.length],
|
|
355
|
-
strokeWidth: 3,
|
|
356
|
-
dot: { r: 4, strokeWidth: 2 },
|
|
357
|
-
activeDot: { r: 6 }
|
|
366
|
+
strokeWidth: isCompact ? 2 : 3,
|
|
367
|
+
dot: { r: isCompact ? 3 : 4, strokeWidth: 2 },
|
|
368
|
+
activeDot: { r: isCompact ? 5 : 6 }
|
|
358
369
|
}
|
|
359
370
|
)))));
|
|
360
371
|
}
|
|
@@ -413,6 +424,14 @@ function sanitizeJson(raw) {
|
|
|
413
424
|
}
|
|
414
425
|
return s;
|
|
415
426
|
}
|
|
427
|
+
function extractLikelyJsonBlock(raw) {
|
|
428
|
+
const trimmed = raw.trim();
|
|
429
|
+
const objectStart = trimmed.indexOf("{");
|
|
430
|
+
const arrayStart = trimmed.indexOf("[");
|
|
431
|
+
const starts = [objectStart, arrayStart].filter((index) => index >= 0);
|
|
432
|
+
if (starts.length === 0) return trimmed;
|
|
433
|
+
return trimmed.slice(Math.min(...starts));
|
|
434
|
+
}
|
|
416
435
|
function resolveImage(data) {
|
|
417
436
|
for (const key of ["image", "img", "thumbnail"]) {
|
|
418
437
|
const v = data[key];
|
|
@@ -429,7 +448,9 @@ function normaliseChild(children) {
|
|
|
429
448
|
}
|
|
430
449
|
return children;
|
|
431
450
|
}
|
|
432
|
-
function DataTable({ config }) {
|
|
451
|
+
function DataTable({ config, viewportSize = "large" }) {
|
|
452
|
+
const isCompact = viewportSize === "compact";
|
|
453
|
+
const isMedium = viewportSize === "medium";
|
|
433
454
|
const keys = import_react5.default.useMemo(() => {
|
|
434
455
|
if (Array.isArray(config.columns) && config.columns.length) {
|
|
435
456
|
return config.columns;
|
|
@@ -451,11 +472,11 @@ function DataTable({ config }) {
|
|
|
451
472
|
if (typeof val === "boolean") return val ? "\u2713" : "\u2717";
|
|
452
473
|
return String(val);
|
|
453
474
|
};
|
|
454
|
-
return /* @__PURE__ */ import_react5.default.createElement("div", { className: "my-4 rounded-xl border border-slate-200 dark:border-white/10 shadow-sm overflow-hidden" }, config.title && /* @__PURE__ */ import_react5.default.createElement("div", { className: "px-4 py-2.5 bg-slate-50 dark:bg-white/5 border-b border-slate-200 dark:border-white/10" }, /* @__PURE__ */ import_react5.default.createElement("p", { className: "text-xs font-semibold text-slate-600 dark:text-white/70 uppercase tracking-wide" }, config.title)), /* @__PURE__ */ import_react5.default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ import_react5.default.createElement("table", { className:
|
|
475
|
+
return /* @__PURE__ */ import_react5.default.createElement("div", { className: "my-4 rounded-xl border border-slate-200 dark:border-white/10 shadow-sm overflow-hidden" }, config.title && /* @__PURE__ */ import_react5.default.createElement("div", { className: "px-4 py-2.5 bg-slate-50 dark:bg-white/5 border-b border-slate-200 dark:border-white/10" }, /* @__PURE__ */ import_react5.default.createElement("p", { className: "text-xs font-semibold text-slate-600 dark:text-white/70 uppercase tracking-wide" }, config.title)), /* @__PURE__ */ import_react5.default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ import_react5.default.createElement("table", { className: `w-full text-left border-collapse ${isCompact ? "min-w-[240px] text-xs" : isMedium ? "min-w-[280px] text-[13px]" : "min-w-[320px] text-sm"}` }, /* @__PURE__ */ import_react5.default.createElement("thead", { className: "bg-slate-50 dark:bg-white/5 border-b border-slate-200 dark:border-white/10" }, /* @__PURE__ */ import_react5.default.createElement("tr", null, keys.map((k) => /* @__PURE__ */ import_react5.default.createElement(
|
|
455
476
|
"th",
|
|
456
477
|
{
|
|
457
478
|
key: k,
|
|
458
|
-
className: "px-4 py-3 font-semibold text-slate-700 dark:text-white/90 whitespace-nowrap
|
|
479
|
+
className: `${isCompact ? "px-2.5 py-2" : "px-4 py-3"} font-semibold text-slate-700 dark:text-white/90 whitespace-nowrap`
|
|
459
480
|
},
|
|
460
481
|
k
|
|
461
482
|
)))), /* @__PURE__ */ import_react5.default.createElement("tbody", null, config.data.map((row, ri) => /* @__PURE__ */ import_react5.default.createElement(
|
|
@@ -468,34 +489,63 @@ function DataTable({ config }) {
|
|
|
468
489
|
"td",
|
|
469
490
|
{
|
|
470
491
|
key: k,
|
|
471
|
-
className: "px-4 py-3 text-slate-600 dark:text-white/70 whitespace-nowrap
|
|
492
|
+
className: `${isCompact ? "px-2.5 py-2" : "px-4 py-3"} text-slate-600 dark:text-white/70 whitespace-nowrap`
|
|
472
493
|
},
|
|
473
494
|
formatCell(row[k])
|
|
474
495
|
))
|
|
475
496
|
))))), /* @__PURE__ */ import_react5.default.createElement("div", { className: "px-4 py-2 bg-slate-50 dark:bg-white/5 border-t border-slate-100 dark:border-white/5" }, /* @__PURE__ */ import_react5.default.createElement("p", { className: "text-[11px] text-slate-400 dark:text-white/30" }, config.data.length, " row", config.data.length !== 1 ? "s" : "")));
|
|
476
497
|
}
|
|
477
|
-
function
|
|
498
|
+
function normalizeTabularRows(rows, columns) {
|
|
499
|
+
if (!Array.isArray(rows)) return [];
|
|
500
|
+
return rows.map((row) => {
|
|
501
|
+
if (Array.isArray(row)) {
|
|
502
|
+
const keys = Array.isArray(columns) && columns.length > 0 ? columns : row.map((_, index) => `column_${index + 1}`);
|
|
503
|
+
return keys.reduce((acc, key, index) => {
|
|
504
|
+
acc[key] = row[index];
|
|
505
|
+
return acc;
|
|
506
|
+
}, {});
|
|
507
|
+
}
|
|
508
|
+
if (row && typeof row === "object") {
|
|
509
|
+
return row;
|
|
510
|
+
}
|
|
511
|
+
return { value: row };
|
|
512
|
+
}).filter((row) => Object.keys(row).length > 0);
|
|
513
|
+
}
|
|
514
|
+
function resolveStructuredRows(config) {
|
|
515
|
+
if (Array.isArray(config.data) && config.data.length > 0) return config.data;
|
|
516
|
+
if (Array.isArray(config.rows) && config.rows.length > 0) return config.rows;
|
|
517
|
+
if (Array.isArray(config.items) && config.items.length > 0) return config.items;
|
|
518
|
+
if (Array.isArray(config.products) && config.products.length > 0) return config.products;
|
|
519
|
+
if (Array.isArray(config.results) && config.results.length > 0) return config.results;
|
|
520
|
+
return [];
|
|
521
|
+
}
|
|
522
|
+
function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAddToCart, viewportSize = "large" }) {
|
|
478
523
|
var _a;
|
|
479
524
|
const result = import_react5.default.useMemo(() => {
|
|
480
525
|
if (isStreaming) return { loading: true };
|
|
481
526
|
try {
|
|
482
527
|
const clean = rawContent.replace(/^```[a-z]*\s*/i, "").replace(/```\s*$/, "").trim();
|
|
483
|
-
const parsed = JSON.parse(sanitizeJson(clean));
|
|
528
|
+
const parsed = JSON.parse(sanitizeJson(extractLikelyJsonBlock(clean)));
|
|
484
529
|
const config2 = __spreadValues({}, parsed);
|
|
485
530
|
if (!config2.view) {
|
|
486
531
|
if (config2.type === "products" || Array.isArray(config2.items)) {
|
|
487
532
|
config2.view = "carousel";
|
|
488
533
|
config2.data = config2.data || config2.items || [];
|
|
489
|
-
} else if (["pie", "bar", "line"].includes(config2.type) || ["pie", "bar", "line"].includes(config2.chartType)) {
|
|
534
|
+
} else if (typeof config2.type === "string" && ["pie", "bar", "line"].includes(config2.type) || typeof config2.chartType === "string" && ["pie", "bar", "line"].includes(config2.chartType)) {
|
|
535
|
+
const resolvedChartType = config2.chartType === "pie" || config2.chartType === "bar" || config2.chartType === "line" ? config2.chartType : config2.type === "pie" || config2.type === "bar" || config2.type === "line" ? config2.type : "bar";
|
|
490
536
|
config2.view = "chart";
|
|
491
|
-
config2.chartType =
|
|
537
|
+
config2.chartType = resolvedChartType;
|
|
492
538
|
config2.data = config2.data || [];
|
|
493
539
|
} else {
|
|
494
540
|
config2.view = "table";
|
|
495
541
|
config2.data = Array.isArray(config2.data) ? config2.data : Array.isArray(config2) ? config2 : [];
|
|
496
542
|
}
|
|
497
543
|
}
|
|
498
|
-
|
|
544
|
+
const resolvedRows = resolveStructuredRows(config2);
|
|
545
|
+
const normalizedConfig = __spreadProps(__spreadValues({}, config2), {
|
|
546
|
+
data: normalizeTabularRows(resolvedRows, config2.columns)
|
|
547
|
+
});
|
|
548
|
+
return { config: normalizedConfig };
|
|
499
549
|
} catch (err) {
|
|
500
550
|
return { error: String(err) };
|
|
501
551
|
}
|
|
@@ -507,11 +557,12 @@ function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAd
|
|
|
507
557
|
return /* @__PURE__ */ import_react5.default.createElement("pre", { className: "p-4 my-2 bg-slate-900 text-slate-100 rounded-lg text-[10px] overflow-auto max-h-40" }, /* @__PURE__ */ import_react5.default.createElement("code", null, rawContent));
|
|
508
558
|
}
|
|
509
559
|
const { config } = result;
|
|
560
|
+
const isCompact = viewportSize === "compact";
|
|
510
561
|
const hasInsights = Array.isArray(config.insights) && config.insights.length > 0;
|
|
511
562
|
const hasDescription = typeof config.description === "string" && config.description.trim().length > 0;
|
|
512
563
|
switch (config.view) {
|
|
513
564
|
case "chart":
|
|
514
|
-
return /* @__PURE__ */ import_react5.default.createElement("div", { className: "my-6 p-4 bg-white dark:bg-slate-900 rounded-2xl border border-slate-200 dark:border-white/10 shadow-sm overflow-hidden
|
|
565
|
+
return /* @__PURE__ */ import_react5.default.createElement("div", { className: `${isCompact ? "my-4 p-3" : "my-6 p-4"} bg-white dark:bg-slate-900 rounded-2xl border border-slate-200 dark:border-white/10 shadow-sm overflow-hidden` }, config.title && /* @__PURE__ */ import_react5.default.createElement("h4", { className: `${isCompact ? "text-[11px] mb-3" : "text-xs mb-4"} font-semibold text-slate-500 px-2` }, config.title), hasDescription && /* @__PURE__ */ import_react5.default.createElement("p", { className: `px-2 ${isCompact ? "text-xs" : "text-sm"} text-slate-500 dark:text-white/60` }, config.description), /* @__PURE__ */ import_react5.default.createElement(
|
|
515
566
|
DynamicChart,
|
|
516
567
|
{
|
|
517
568
|
config: {
|
|
@@ -522,25 +573,26 @@ function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAd
|
|
|
522
573
|
colors: config.colors
|
|
523
574
|
},
|
|
524
575
|
primaryColor,
|
|
525
|
-
accentColor
|
|
576
|
+
accentColor,
|
|
577
|
+
viewportSize
|
|
526
578
|
}
|
|
527
579
|
), hasInsights && /* @__PURE__ */ import_react5.default.createElement("div", { className: "mt-4 flex flex-wrap gap-2 px-2" }, (_a = config.insights) == null ? void 0 : _a.map((insight) => /* @__PURE__ */ import_react5.default.createElement(
|
|
528
580
|
"span",
|
|
529
581
|
{
|
|
530
582
|
key: insight,
|
|
531
|
-
className:
|
|
583
|
+
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`
|
|
532
584
|
},
|
|
533
585
|
insight
|
|
534
586
|
))));
|
|
535
587
|
case "carousel":
|
|
536
|
-
return /* @__PURE__ */ import_react5.default.createElement("div", { className: "my-4" }, config.title && /* @__PURE__ */ import_react5.default.createElement("h4", { className: "mb-2 text-xs font-semibold text-slate-500
|
|
588
|
+
return /* @__PURE__ */ import_react5.default.createElement("div", { className: "my-4" }, config.title && /* @__PURE__ */ import_react5.default.createElement("h4", { className: `${isCompact ? "mb-1.5 text-[11px]" : "mb-2 text-xs"} font-semibold text-slate-500` }, config.title), hasDescription && /* @__PURE__ */ import_react5.default.createElement("p", { className: `mb-3 ${isCompact ? "text-xs" : "text-sm"} text-slate-500 dark:text-white/60` }, config.description), /* @__PURE__ */ import_react5.default.createElement(ProductCarousel, { products: config.data.map((item) => {
|
|
537
589
|
var _a2;
|
|
538
590
|
return __spreadProps(__spreadValues({}, item), {
|
|
539
591
|
image: (_a2 = item.image) != null ? _a2 : typeof resolveImage === "function" ? resolveImage(item) : void 0
|
|
540
592
|
});
|
|
541
593
|
}), primaryColor, onAddToCart }));
|
|
542
594
|
case "table":
|
|
543
|
-
return /* @__PURE__ */ import_react5.default.createElement("div", { className: "my-4 p-4 bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 shadow-sm overflow-auto
|
|
595
|
+
return /* @__PURE__ */ import_react5.default.createElement("div", { className: `${isCompact ? "my-3 p-3 max-h-[320px]" : "my-4 p-4 max-h-[400px]"} bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 shadow-sm overflow-auto` }, config.title && /* @__PURE__ */ import_react5.default.createElement("h4", { className: `${isCompact ? "text-[11px]" : "text-xs"} font-semibold text-slate-500 mb-2` }, config.title), hasDescription && /* @__PURE__ */ import_react5.default.createElement("p", { className: `mb-3 ${isCompact ? "text-xs" : "text-sm"} text-slate-500 dark:text-white/60` }, config.description), /* @__PURE__ */ import_react5.default.createElement(DataTable, { config: {
|
|
544
596
|
type: "table",
|
|
545
597
|
title: config.title,
|
|
546
598
|
description: config.description,
|
|
@@ -548,7 +600,7 @@ function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAd
|
|
|
548
600
|
dataKeys: config.dataKeys,
|
|
549
601
|
xAxisKey: config.xAxisKey,
|
|
550
602
|
data: config.data
|
|
551
|
-
} }));
|
|
603
|
+
}, viewportSize }));
|
|
552
604
|
default:
|
|
553
605
|
return null;
|
|
554
606
|
}
|
|
@@ -559,9 +611,12 @@ function MessageBubble({
|
|
|
559
611
|
isStreaming = false,
|
|
560
612
|
primaryColor = "#6366f1",
|
|
561
613
|
accentColor = "#8b5cf6",
|
|
562
|
-
onAddToCart
|
|
614
|
+
onAddToCart,
|
|
615
|
+
viewportSize = "large"
|
|
563
616
|
}) {
|
|
564
617
|
const isUser = message.role === "user";
|
|
618
|
+
const isCompact = viewportSize === "compact";
|
|
619
|
+
const isMedium = viewportSize === "medium";
|
|
565
620
|
const [showSources, setShowSources] = import_react5.default.useState(false);
|
|
566
621
|
const hasStructuredProductBlock = import_react5.default.useMemo(
|
|
567
622
|
() => /```(?:ui|json|chart)\s*[\s\S]*?"(?:view|type)"\s*:\s*"(?:carousel|products)"/i.test(message.content),
|
|
@@ -738,7 +793,8 @@ ${match.trim()}
|
|
|
738
793
|
primaryColor,
|
|
739
794
|
accentColor,
|
|
740
795
|
isStreaming,
|
|
741
|
-
onAddToCart
|
|
796
|
+
onAddToCart,
|
|
797
|
+
viewportSize
|
|
742
798
|
}
|
|
743
799
|
);
|
|
744
800
|
}
|
|
@@ -752,7 +808,8 @@ ${match.trim()}
|
|
|
752
808
|
primaryColor,
|
|
753
809
|
accentColor,
|
|
754
810
|
isStreaming,
|
|
755
|
-
onAddToCart
|
|
811
|
+
onAddToCart,
|
|
812
|
+
viewportSize
|
|
756
813
|
}
|
|
757
814
|
);
|
|
758
815
|
}
|
|
@@ -763,22 +820,22 @@ ${match.trim()}
|
|
|
763
820
|
return /* @__PURE__ */ import_react5.default.createElement("code", __spreadValues({ className }, props), typeof children === "string" || typeof children === "number" ? children : JSON.stringify(children));
|
|
764
821
|
}
|
|
765
822
|
}),
|
|
766
|
-
[primaryColor, accentColor, isStreaming, onAddToCart]
|
|
823
|
+
[primaryColor, accentColor, isStreaming, onAddToCart, viewportSize]
|
|
767
824
|
);
|
|
768
|
-
return /* @__PURE__ */ import_react5.default.createElement("div", { className: `flex gap-3 ${isUser ? "flex-row-reverse" : "flex-row"} items-start` }, /* @__PURE__ */ import_react5.default.createElement(
|
|
825
|
+
return /* @__PURE__ */ import_react5.default.createElement("div", { className: `flex ${isCompact ? "gap-2" : "gap-3"} ${isUser ? "flex-row-reverse" : "flex-row"} items-start` }, /* @__PURE__ */ import_react5.default.createElement(
|
|
769
826
|
"div",
|
|
770
827
|
{
|
|
771
|
-
className: `flex-shrink-0 w-8 h-8 rounded-full flex items-center justify-center shadow-lg ${isUser ? "text-white" : "bg-slate-100 dark:bg-white/10 text-slate-700 dark:text-white/80"}`,
|
|
828
|
+
className: `flex-shrink-0 ${isCompact ? "w-7 h-7" : "w-8 h-8"} rounded-full flex items-center justify-center shadow-lg ${isUser ? "text-white" : "bg-slate-100 dark:bg-white/10 text-slate-700 dark:text-white/80"}`,
|
|
772
829
|
style: isUser ? { background: `linear-gradient(135deg, ${primaryColor}, ${accentColor})` } : {}
|
|
773
830
|
},
|
|
774
|
-
isUser ? /* @__PURE__ */ import_react5.default.createElement(import_lucide_react4.User, { className: "w-4 h-4 text-white
|
|
775
|
-
), /* @__PURE__ */ import_react5.default.createElement("div", { className: `flex flex-col gap-1 max-w-[90%] ${isUser ? "items-end" : "items-start"}` }, /* @__PURE__ */ import_react5.default.createElement(
|
|
831
|
+
isUser ? /* @__PURE__ */ import_react5.default.createElement(import_lucide_react4.User, { className: `${isCompact ? "w-3.5 h-3.5" : "w-4 h-4"} text-white` }) : /* @__PURE__ */ import_react5.default.createElement(import_lucide_react4.Bot, { className: `${isCompact ? "w-3.5 h-3.5" : "w-4 h-4"}` })
|
|
832
|
+
), /* @__PURE__ */ import_react5.default.createElement("div", { className: `flex flex-col gap-1 ${isCompact ? "max-w-[94%]" : isMedium ? "max-w-[92%]" : "max-w-[90%]"} ${isUser ? "items-end" : "items-start"}` }, /* @__PURE__ */ import_react5.default.createElement(
|
|
776
833
|
"div",
|
|
777
834
|
{
|
|
778
|
-
className: `relative px-4 py-3 rounded-2xl
|
|
835
|
+
className: `relative ${isCompact ? "px-3 py-2.5 text-[13px]" : "px-4 py-3 text-sm"} rounded-2xl leading-relaxed shadow-sm dark:shadow-lg ${isUser ? "text-white rounded-tr-sm" : "bg-white dark:bg-white/5 border border-slate-200 dark:border-white/10 text-slate-800 dark:text-white/90 rounded-tl-sm"}`,
|
|
779
836
|
style: isUser ? { background: `linear-gradient(135deg, ${primaryColor}, ${accentColor})` } : {}
|
|
780
837
|
},
|
|
781
|
-
isStreaming && !message.content ? /* @__PURE__ */ import_react5.default.createElement("span", { className: "flex items-center gap-1 py-1" }, /* @__PURE__ */ import_react5.default.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:0ms]" }), /* @__PURE__ */ import_react5.default.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:150ms]" }), /* @__PURE__ */ import_react5.default.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:300ms]" })) : /* @__PURE__ */ import_react5.default.createElement("div", { className: `prose prose-sm max-w-none ${isUser ? "prose-invert" : "dark:prose-invert"}` }, /* @__PURE__ */ import_react5.default.createElement(import_react_markdown.default, { remarkPlugins: [import_remark_gfm.default], components: markdownComponents }, processedMarkdown), isStreaming && message.content && /* @__PURE__ */ import_react5.default.createElement("span", { className: "inline-block w-1.5 h-3.5 ml-1 bg-emerald-500 animate-pulse align-middle" }))
|
|
838
|
+
isStreaming && !message.content ? /* @__PURE__ */ import_react5.default.createElement("span", { className: "flex items-center gap-1 py-1" }, /* @__PURE__ */ import_react5.default.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:0ms]" }), /* @__PURE__ */ import_react5.default.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:150ms]" }), /* @__PURE__ */ import_react5.default.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:300ms]" })) : /* @__PURE__ */ import_react5.default.createElement("div", { className: `prose ${isCompact ? "prose-xs" : "prose-sm"} max-w-none break-words ${isUser ? "prose-invert" : "dark:prose-invert"}` }, /* @__PURE__ */ import_react5.default.createElement(import_react_markdown.default, { remarkPlugins: [import_remark_gfm.default], components: markdownComponents }, processedMarkdown), isStreaming && message.content && /* @__PURE__ */ import_react5.default.createElement("span", { className: "inline-block w-1.5 h-3.5 ml-1 bg-emerald-500 animate-pulse align-middle" }))
|
|
782
839
|
), !isUser && !hasStructuredProductBlock && allProducts.length > 0 && /* @__PURE__ */ import_react5.default.createElement("div", { className: "w-full mt-1" }, /* @__PURE__ */ import_react5.default.createElement(
|
|
783
840
|
ProductCarousel,
|
|
784
841
|
{
|
|
@@ -1075,6 +1132,8 @@ function ChatWindow({
|
|
|
1075
1132
|
const { ui, projectId } = useConfig();
|
|
1076
1133
|
const [input, setInput] = (0, import_react8.useState)("");
|
|
1077
1134
|
const [mounted, setMounted] = (0, import_react8.useState)(false);
|
|
1135
|
+
const [viewportSize, setViewportSize] = (0, import_react8.useState)("large");
|
|
1136
|
+
const windowRef = (0, import_react8.useRef)(null);
|
|
1078
1137
|
const bottomRef = (0, import_react8.useRef)(null);
|
|
1079
1138
|
const inputRef = (0, import_react8.useRef)(null);
|
|
1080
1139
|
const { messages, send, clear, isLoading, error } = useRagChat(projectId, {
|
|
@@ -1133,6 +1192,22 @@ function ChatWindow({
|
|
|
1133
1192
|
(0, import_react8.useEffect)(() => {
|
|
1134
1193
|
setMounted(true);
|
|
1135
1194
|
}, []);
|
|
1195
|
+
(0, import_react8.useEffect)(() => {
|
|
1196
|
+
const element = windowRef.current;
|
|
1197
|
+
if (!element || typeof ResizeObserver === "undefined") return;
|
|
1198
|
+
const resolveViewportSize = (width) => {
|
|
1199
|
+
if (width < 420) return "compact";
|
|
1200
|
+
if (width < 640) return "medium";
|
|
1201
|
+
return "large";
|
|
1202
|
+
};
|
|
1203
|
+
const observer = new ResizeObserver(([entry]) => {
|
|
1204
|
+
if (!entry) return;
|
|
1205
|
+
setViewportSize(resolveViewportSize(entry.contentRect.width));
|
|
1206
|
+
});
|
|
1207
|
+
setViewportSize(resolveViewportSize(element.getBoundingClientRect().width));
|
|
1208
|
+
observer.observe(element);
|
|
1209
|
+
return () => observer.disconnect();
|
|
1210
|
+
}, []);
|
|
1136
1211
|
(0, import_react8.useEffect)(() => {
|
|
1137
1212
|
var _a2;
|
|
1138
1213
|
if (messages.length > 0 || isLoading) {
|
|
@@ -1196,6 +1271,7 @@ function ChatWindow({
|
|
|
1196
1271
|
return /* @__PURE__ */ import_react8.default.createElement(
|
|
1197
1272
|
"div",
|
|
1198
1273
|
{
|
|
1274
|
+
ref: windowRef,
|
|
1199
1275
|
className: `relative flex flex-col border border-slate-200 dark:border-white/10 shadow-2xl transition-all duration-300 ${currentRadius} ${isGlass ? "bg-white/90 dark:bg-[#0f0f1a]/90 backdrop-blur-xl" : "bg-white dark:bg-[#0f0f1a]"} ${className}`,
|
|
1200
1276
|
style: __spreadValues({ "--primary": ui.primaryColor, "--accent": ui.accentColor }, style)
|
|
1201
1277
|
},
|
|
@@ -1292,7 +1368,8 @@ function ChatWindow({
|
|
|
1292
1368
|
isStreaming: isLoading && index === messages.length - 1 && msg.role === "assistant",
|
|
1293
1369
|
primaryColor: ui.primaryColor,
|
|
1294
1370
|
accentColor: ui.accentColor,
|
|
1295
|
-
onAddToCart
|
|
1371
|
+
onAddToCart,
|
|
1372
|
+
viewportSize
|
|
1296
1373
|
}
|
|
1297
1374
|
)), isLoading && ((_a = messages[messages.length - 1]) == null ? void 0 : _a.role) !== "assistant" && /* @__PURE__ */ import_react8.default.createElement(
|
|
1298
1375
|
MessageBubble,
|
|
@@ -1301,7 +1378,8 @@ function ChatWindow({
|
|
|
1301
1378
|
isStreaming: true,
|
|
1302
1379
|
primaryColor: ui.primaryColor,
|
|
1303
1380
|
accentColor: ui.accentColor,
|
|
1304
|
-
onAddToCart
|
|
1381
|
+
onAddToCart,
|
|
1382
|
+
viewportSize
|
|
1305
1383
|
}
|
|
1306
1384
|
), error && /* @__PURE__ */ import_react8.default.createElement("div", { className: "flex items-center gap-2 text-rose-500 dark:text-rose-400 text-sm bg-rose-50 dark:bg-rose-400/10 border border-rose-200 dark:border-rose-400/20 rounded-xl px-4 py-3" }, /* @__PURE__ */ import_react8.default.createElement(import_lucide_react5.TriangleAlert, { className: "w-4 h-4 flex-shrink-0" }), /* @__PURE__ */ import_react8.default.createElement("span", null, error)), /* @__PURE__ */ import_react8.default.createElement("div", { ref: bottomRef })),
|
|
1307
1385
|
/* @__PURE__ */ import_react8.default.createElement("div", { className: `px-4 pb-4 pt-2 border-t border-slate-200 dark:border-white/10 ${isGlass ? "bg-transparent" : "bg-white dark:bg-[#0f0f1a]"}` }, (suggestions.length > 0 || isSuggesting) && !isLoading && /* @__PURE__ */ import_react8.default.createElement("div", { className: "mb-3 flex flex-wrap gap-2 animate-in fade-in slide-in-from-bottom-2 duration-300" }, suggestions.map((suggestion) => /* @__PURE__ */ import_react8.default.createElement(
|