@retrivora-ai/rag-engine 1.5.5 → 1.5.6
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 +109 -40
- package/dist/index.mjs +109 -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 +95 -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,54 @@ 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 UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAddToCart, viewportSize = "large" }) {
|
|
478
515
|
var _a;
|
|
479
516
|
const result = import_react5.default.useMemo(() => {
|
|
480
517
|
if (isStreaming) return { loading: true };
|
|
481
518
|
try {
|
|
482
519
|
const clean = rawContent.replace(/^```[a-z]*\s*/i, "").replace(/```\s*$/, "").trim();
|
|
483
|
-
const parsed = JSON.parse(sanitizeJson(clean));
|
|
520
|
+
const parsed = JSON.parse(sanitizeJson(extractLikelyJsonBlock(clean)));
|
|
484
521
|
const config2 = __spreadValues({}, parsed);
|
|
485
522
|
if (!config2.view) {
|
|
486
523
|
if (config2.type === "products" || Array.isArray(config2.items)) {
|
|
487
524
|
config2.view = "carousel";
|
|
488
525
|
config2.data = config2.data || config2.items || [];
|
|
489
|
-
} else if (["pie", "bar", "line"].includes(config2.type) || ["pie", "bar", "line"].includes(config2.chartType)) {
|
|
526
|
+
} else if (typeof config2.type === "string" && ["pie", "bar", "line"].includes(config2.type) || typeof config2.chartType === "string" && ["pie", "bar", "line"].includes(config2.chartType)) {
|
|
527
|
+
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
528
|
config2.view = "chart";
|
|
491
|
-
config2.chartType =
|
|
529
|
+
config2.chartType = resolvedChartType;
|
|
492
530
|
config2.data = config2.data || [];
|
|
493
531
|
} else {
|
|
494
532
|
config2.view = "table";
|
|
495
533
|
config2.data = Array.isArray(config2.data) ? config2.data : Array.isArray(config2) ? config2 : [];
|
|
496
534
|
}
|
|
497
535
|
}
|
|
498
|
-
|
|
536
|
+
const normalizedConfig = __spreadProps(__spreadValues({}, config2), {
|
|
537
|
+
data: normalizeTabularRows(config2.data, config2.columns)
|
|
538
|
+
});
|
|
539
|
+
return { config: normalizedConfig };
|
|
499
540
|
} catch (err) {
|
|
500
541
|
return { error: String(err) };
|
|
501
542
|
}
|
|
@@ -507,11 +548,12 @@ function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAd
|
|
|
507
548
|
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
549
|
}
|
|
509
550
|
const { config } = result;
|
|
551
|
+
const isCompact = viewportSize === "compact";
|
|
510
552
|
const hasInsights = Array.isArray(config.insights) && config.insights.length > 0;
|
|
511
553
|
const hasDescription = typeof config.description === "string" && config.description.trim().length > 0;
|
|
512
554
|
switch (config.view) {
|
|
513
555
|
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
|
|
556
|
+
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
557
|
DynamicChart,
|
|
516
558
|
{
|
|
517
559
|
config: {
|
|
@@ -522,25 +564,26 @@ function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAd
|
|
|
522
564
|
colors: config.colors
|
|
523
565
|
},
|
|
524
566
|
primaryColor,
|
|
525
|
-
accentColor
|
|
567
|
+
accentColor,
|
|
568
|
+
viewportSize
|
|
526
569
|
}
|
|
527
570
|
), 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
571
|
"span",
|
|
529
572
|
{
|
|
530
573
|
key: insight,
|
|
531
|
-
className:
|
|
574
|
+
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
575
|
},
|
|
533
576
|
insight
|
|
534
577
|
))));
|
|
535
578
|
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
|
|
579
|
+
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
580
|
var _a2;
|
|
538
581
|
return __spreadProps(__spreadValues({}, item), {
|
|
539
582
|
image: (_a2 = item.image) != null ? _a2 : typeof resolveImage === "function" ? resolveImage(item) : void 0
|
|
540
583
|
});
|
|
541
584
|
}), primaryColor, onAddToCart }));
|
|
542
585
|
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
|
|
586
|
+
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
587
|
type: "table",
|
|
545
588
|
title: config.title,
|
|
546
589
|
description: config.description,
|
|
@@ -548,7 +591,7 @@ function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAd
|
|
|
548
591
|
dataKeys: config.dataKeys,
|
|
549
592
|
xAxisKey: config.xAxisKey,
|
|
550
593
|
data: config.data
|
|
551
|
-
} }));
|
|
594
|
+
}, viewportSize }));
|
|
552
595
|
default:
|
|
553
596
|
return null;
|
|
554
597
|
}
|
|
@@ -559,9 +602,12 @@ function MessageBubble({
|
|
|
559
602
|
isStreaming = false,
|
|
560
603
|
primaryColor = "#6366f1",
|
|
561
604
|
accentColor = "#8b5cf6",
|
|
562
|
-
onAddToCart
|
|
605
|
+
onAddToCart,
|
|
606
|
+
viewportSize = "large"
|
|
563
607
|
}) {
|
|
564
608
|
const isUser = message.role === "user";
|
|
609
|
+
const isCompact = viewportSize === "compact";
|
|
610
|
+
const isMedium = viewportSize === "medium";
|
|
565
611
|
const [showSources, setShowSources] = import_react5.default.useState(false);
|
|
566
612
|
const hasStructuredProductBlock = import_react5.default.useMemo(
|
|
567
613
|
() => /```(?:ui|json|chart)\s*[\s\S]*?"(?:view|type)"\s*:\s*"(?:carousel|products)"/i.test(message.content),
|
|
@@ -738,7 +784,8 @@ ${match.trim()}
|
|
|
738
784
|
primaryColor,
|
|
739
785
|
accentColor,
|
|
740
786
|
isStreaming,
|
|
741
|
-
onAddToCart
|
|
787
|
+
onAddToCart,
|
|
788
|
+
viewportSize
|
|
742
789
|
}
|
|
743
790
|
);
|
|
744
791
|
}
|
|
@@ -752,7 +799,8 @@ ${match.trim()}
|
|
|
752
799
|
primaryColor,
|
|
753
800
|
accentColor,
|
|
754
801
|
isStreaming,
|
|
755
|
-
onAddToCart
|
|
802
|
+
onAddToCart,
|
|
803
|
+
viewportSize
|
|
756
804
|
}
|
|
757
805
|
);
|
|
758
806
|
}
|
|
@@ -763,22 +811,22 @@ ${match.trim()}
|
|
|
763
811
|
return /* @__PURE__ */ import_react5.default.createElement("code", __spreadValues({ className }, props), typeof children === "string" || typeof children === "number" ? children : JSON.stringify(children));
|
|
764
812
|
}
|
|
765
813
|
}),
|
|
766
|
-
[primaryColor, accentColor, isStreaming, onAddToCart]
|
|
814
|
+
[primaryColor, accentColor, isStreaming, onAddToCart, viewportSize]
|
|
767
815
|
);
|
|
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(
|
|
816
|
+
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
817
|
"div",
|
|
770
818
|
{
|
|
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"}`,
|
|
819
|
+
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
820
|
style: isUser ? { background: `linear-gradient(135deg, ${primaryColor}, ${accentColor})` } : {}
|
|
773
821
|
},
|
|
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(
|
|
822
|
+
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"}` })
|
|
823
|
+
), /* @__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
824
|
"div",
|
|
777
825
|
{
|
|
778
|
-
className: `relative px-4 py-3 rounded-2xl
|
|
826
|
+
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
827
|
style: isUser ? { background: `linear-gradient(135deg, ${primaryColor}, ${accentColor})` } : {}
|
|
780
828
|
},
|
|
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" }))
|
|
829
|
+
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
830
|
), !isUser && !hasStructuredProductBlock && allProducts.length > 0 && /* @__PURE__ */ import_react5.default.createElement("div", { className: "w-full mt-1" }, /* @__PURE__ */ import_react5.default.createElement(
|
|
783
831
|
ProductCarousel,
|
|
784
832
|
{
|
|
@@ -1075,6 +1123,8 @@ function ChatWindow({
|
|
|
1075
1123
|
const { ui, projectId } = useConfig();
|
|
1076
1124
|
const [input, setInput] = (0, import_react8.useState)("");
|
|
1077
1125
|
const [mounted, setMounted] = (0, import_react8.useState)(false);
|
|
1126
|
+
const [viewportSize, setViewportSize] = (0, import_react8.useState)("large");
|
|
1127
|
+
const windowRef = (0, import_react8.useRef)(null);
|
|
1078
1128
|
const bottomRef = (0, import_react8.useRef)(null);
|
|
1079
1129
|
const inputRef = (0, import_react8.useRef)(null);
|
|
1080
1130
|
const { messages, send, clear, isLoading, error } = useRagChat(projectId, {
|
|
@@ -1133,6 +1183,22 @@ function ChatWindow({
|
|
|
1133
1183
|
(0, import_react8.useEffect)(() => {
|
|
1134
1184
|
setMounted(true);
|
|
1135
1185
|
}, []);
|
|
1186
|
+
(0, import_react8.useEffect)(() => {
|
|
1187
|
+
const element = windowRef.current;
|
|
1188
|
+
if (!element || typeof ResizeObserver === "undefined") return;
|
|
1189
|
+
const resolveViewportSize = (width) => {
|
|
1190
|
+
if (width < 420) return "compact";
|
|
1191
|
+
if (width < 640) return "medium";
|
|
1192
|
+
return "large";
|
|
1193
|
+
};
|
|
1194
|
+
const observer = new ResizeObserver(([entry]) => {
|
|
1195
|
+
if (!entry) return;
|
|
1196
|
+
setViewportSize(resolveViewportSize(entry.contentRect.width));
|
|
1197
|
+
});
|
|
1198
|
+
setViewportSize(resolveViewportSize(element.getBoundingClientRect().width));
|
|
1199
|
+
observer.observe(element);
|
|
1200
|
+
return () => observer.disconnect();
|
|
1201
|
+
}, []);
|
|
1136
1202
|
(0, import_react8.useEffect)(() => {
|
|
1137
1203
|
var _a2;
|
|
1138
1204
|
if (messages.length > 0 || isLoading) {
|
|
@@ -1196,6 +1262,7 @@ function ChatWindow({
|
|
|
1196
1262
|
return /* @__PURE__ */ import_react8.default.createElement(
|
|
1197
1263
|
"div",
|
|
1198
1264
|
{
|
|
1265
|
+
ref: windowRef,
|
|
1199
1266
|
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
1267
|
style: __spreadValues({ "--primary": ui.primaryColor, "--accent": ui.accentColor }, style)
|
|
1201
1268
|
},
|
|
@@ -1292,7 +1359,8 @@ function ChatWindow({
|
|
|
1292
1359
|
isStreaming: isLoading && index === messages.length - 1 && msg.role === "assistant",
|
|
1293
1360
|
primaryColor: ui.primaryColor,
|
|
1294
1361
|
accentColor: ui.accentColor,
|
|
1295
|
-
onAddToCart
|
|
1362
|
+
onAddToCart,
|
|
1363
|
+
viewportSize
|
|
1296
1364
|
}
|
|
1297
1365
|
)), isLoading && ((_a = messages[messages.length - 1]) == null ? void 0 : _a.role) !== "assistant" && /* @__PURE__ */ import_react8.default.createElement(
|
|
1298
1366
|
MessageBubble,
|
|
@@ -1301,7 +1369,8 @@ function ChatWindow({
|
|
|
1301
1369
|
isStreaming: true,
|
|
1302
1370
|
primaryColor: ui.primaryColor,
|
|
1303
1371
|
accentColor: ui.accentColor,
|
|
1304
|
-
onAddToCart
|
|
1372
|
+
onAddToCart,
|
|
1373
|
+
viewportSize
|
|
1305
1374
|
}
|
|
1306
1375
|
), 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
1376
|
/* @__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(
|