@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.
@@ -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"
@@ -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"
@@ -9,7 +9,7 @@ import {
9
9
  sseFrame,
10
10
  sseMetaFrame,
11
11
  sseTextFrame
12
- } from "../chunk-Q4MDH6C4.mjs";
12
+ } from "../chunk-ID2Q4CF7.mjs";
13
13
  import "../chunk-YLTMFW4M.mjs";
14
14
  import "../chunk-X4TOT24V.mjs";
15
15
  export {
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-[85%] sm:w-[48%] md:w-[31%] min-w-[200px] max-w-[300px] animate-fade-in-up" }, /* @__PURE__ */ import_react3.default.createElement(ProductCard, { product: products[0], primaryColor, onAddToCart }));
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-[85%] sm:w-[48%] md:w-[31%] min-w-[200px] max-w-[300px]"
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({ config, primaryColor = "#6366f1", accentColor = "#8b5cf6" }) {
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: "w-full h-72 min-h-[280px] 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(
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: 80,
334
- label: ({ name, percent }) => `${name} ${((percent != null ? percent : 0) * 100).toFixed(0)}%`
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, null))));
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: "w-full h-72 min-h-[280px] 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: { fontSize: 12, fill: "#64748b" }, tickLine: false, axisLine: { stroke: "#cbd5e1" } }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.YAxis, { tick: { fontSize: 12, fill: "#64748b" }, 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: 12 } }), finalDataKeys.map((key, index) => /* @__PURE__ */ import_react4.default.createElement(
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: { fontSize: 12, fill: "#64748b" }, tickLine: false, axisLine: { stroke: "#cbd5e1" } }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.YAxis, { tick: { fontSize: 12, fill: "#64748b" }, 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: 12 } }), finalDataKeys.map((key, index) => /* @__PURE__ */ import_react4.default.createElement(
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: "w-full text-left border-collapse 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(
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 UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAddToCart }) {
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 = config2.chartType || config2.type || "bar";
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
- return { config: config2 };
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" }, config.title && /* @__PURE__ */ import_react5.default.createElement("h4", { className: "text-xs font-semibold text-slate-500 mb-4 px-2" }, config.title), hasDescription && /* @__PURE__ */ import_react5.default.createElement("p", { className: "px-2 text-sm text-slate-500 dark:text-white/60" }, config.description), /* @__PURE__ */ import_react5.default.createElement(
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: "rounded-full border border-emerald-200 bg-emerald-50 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"
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" }, config.title), hasDescription && /* @__PURE__ */ import_react5.default.createElement("p", { className: "mb-3 text-sm text-slate-500 dark:text-white/60" }, config.description), /* @__PURE__ */ import_react5.default.createElement(ProductCarousel, { products: config.data.map((item) => {
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 max-h-[400px]" }, config.title && /* @__PURE__ */ import_react5.default.createElement("h4", { className: "text-xs font-semibold text-slate-500 mb-2" }, config.title), hasDescription && /* @__PURE__ */ import_react5.default.createElement("p", { className: "mb-3 text-sm text-slate-500 dark:text-white/60" }, config.description), /* @__PURE__ */ import_react5.default.createElement(DataTable, { config: {
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" }) : /* @__PURE__ */ import_react5.default.createElement(import_lucide_react4.Bot, { className: "w-4 h-4" })
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 text-sm 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"}`,
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(