@retrivora-ai/rag-engine 1.3.1 → 1.3.3

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.
@@ -1703,7 +1703,8 @@ When presenting structured data, statistics, or comparisons, decide if it is bes
1703
1703
  1. Use ONLY valid JSON (double quotes, no trailing commas).
1704
1704
  2. NO math expressions (e.g., use 100, NOT 50+50).
1705
1705
  3. dataKeys MUST exactly match the property names in the data objects.
1706
- 4. DO NOT output naked JSON; it MUST be wrapped in \`\`\`chart ... \`\`\`.
1706
+ 4. data objects MUST be FLAT (no nested objects or arrays).
1707
+ 5. DO NOT output naked JSON; it MUST be wrapped in \`\`\`chart ... \`\`\`.
1707
1708
  \`\`\`chart
1708
1709
  {
1709
1710
  "type": "bar" | "line" | "pie",
@@ -2265,7 +2266,8 @@ When presenting structured data, statistics, or comparisons, decide if it is bes
2265
2266
  1. Use ONLY valid JSON (double quotes, no trailing commas).
2266
2267
  2. NO math expressions (e.g., use 100, NOT 50+50).
2267
2268
  3. dataKeys MUST exactly match the property names in the data objects.
2268
- 4. DO NOT output naked JSON; it MUST be wrapped in \`\`\`chart ... \`\`\`.
2269
+ 4. data objects MUST be FLAT (no nested objects or arrays).
2270
+ 5. DO NOT output naked JSON; it MUST be wrapped in \`\`\`chart ... \`\`\`.
2269
2271
  \`\`\`chart
2270
2272
  {
2271
2273
  "type": "bar" | "line" | "pie",
@@ -3272,7 +3272,8 @@ When presenting structured data, statistics, or comparisons, decide if it is bes
3272
3272
  1. Use ONLY valid JSON (double quotes, no trailing commas).
3273
3273
  2. NO math expressions (e.g., use 100, NOT 50+50).
3274
3274
  3. dataKeys MUST exactly match the property names in the data objects.
3275
- 4. DO NOT output naked JSON; it MUST be wrapped in \`\`\`chart ... \`\`\`.
3275
+ 4. data objects MUST be FLAT (no nested objects or arrays).
3276
+ 5. DO NOT output naked JSON; it MUST be wrapped in \`\`\`chart ... \`\`\`.
3276
3277
  \`\`\`chart
3277
3278
  {
3278
3279
  "type": "bar" | "line" | "pie",
@@ -3828,7 +3829,8 @@ When presenting structured data, statistics, or comparisons, decide if it is bes
3828
3829
  1. Use ONLY valid JSON (double quotes, no trailing commas).
3829
3830
  2. NO math expressions (e.g., use 100, NOT 50+50).
3830
3831
  3. dataKeys MUST exactly match the property names in the data objects.
3831
- 4. DO NOT output naked JSON; it MUST be wrapped in \`\`\`chart ... \`\`\`.
3832
+ 4. data objects MUST be FLAT (no nested objects or arrays).
3833
+ 5. DO NOT output naked JSON; it MUST be wrapped in \`\`\`chart ... \`\`\`.
3832
3834
  \`\`\`chart
3833
3835
  {
3834
3836
  "type": "bar" | "line" | "pie",
@@ -9,7 +9,7 @@ import {
9
9
  sseFrame,
10
10
  sseMetaFrame,
11
11
  sseTextFrame
12
- } from "../chunk-AYOQ4LF4.mjs";
12
+ } from "../chunk-FQ7OYZLF.mjs";
13
13
  import "../chunk-YLTMFW4M.mjs";
14
14
  import "../chunk-X4TOT24V.mjs";
15
15
  export {
package/dist/index.js CHANGED
@@ -223,6 +223,30 @@ var DEFAULT_COLORS = ["#6366f1", "#10b981", "#f59e0b", "#ef4444", "#8b5cf6", "#e
223
223
  function DynamicChart({ config, primaryColor = "#6366f1", accentColor = "#8b5cf6" }) {
224
224
  const { type, data } = config;
225
225
  const colors = config.colors || [primaryColor, accentColor, ...DEFAULT_COLORS];
226
+ const sanitizedData = import_react4.default.useMemo(() => {
227
+ if (!data) return [];
228
+ return data.map((item) => {
229
+ const newItem = {};
230
+ Object.keys(item).forEach((k) => {
231
+ const val = item[k];
232
+ if (Array.isArray(val)) {
233
+ val.forEach((subItem) => {
234
+ if (typeof subItem === "object" && subItem !== null) {
235
+ Object.keys(subItem).forEach((sk) => {
236
+ const subVal = subItem[sk];
237
+ if (typeof subVal !== "object" || subVal === null) {
238
+ newItem[sk] = subVal;
239
+ }
240
+ });
241
+ }
242
+ });
243
+ } else if (typeof val !== "object" || val === null) {
244
+ newItem[k] = val;
245
+ }
246
+ });
247
+ return newItem;
248
+ });
249
+ }, [data]);
226
250
  if (!data || data.length === 0) {
227
251
  return /* @__PURE__ */ import_react4.default.createElement("div", { className: "p-4 text-sm text-slate-500" }, "No data available for chart.");
228
252
  }
@@ -238,18 +262,6 @@ function DynamicChart({ config, primaryColor = "#6366f1", accentColor = "#8b5cf6
238
262
  }).slice(0, 5);
239
263
  const finalXKey = String(resolvedXKey);
240
264
  const finalDataKeys = resolvedDataKeys.map(String).filter((k) => k !== "undefined");
241
- const sanitizedData = import_react4.default.useMemo(() => {
242
- return data.map((item) => {
243
- const newItem = {};
244
- Object.keys(item).forEach((k) => {
245
- const val = item[k];
246
- if (typeof val !== "object" || val === null) {
247
- newItem[k] = val;
248
- }
249
- });
250
- return newItem;
251
- });
252
- }, [data]);
253
265
  if (type === "pie") {
254
266
  const pieDataKey = finalDataKeys[0] || "value";
255
267
  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(
@@ -306,6 +318,90 @@ function DynamicChart({ config, primaryColor = "#6366f1", accentColor = "#8b5cf6
306
318
  }
307
319
 
308
320
  // src/components/MessageBubble.tsx
321
+ function sanitizeJson(raw) {
322
+ let s = raw.trim();
323
+ s = s.replace(
324
+ /:\s*(-?\d+(?:\.\d+)?)\s*([+\-*/])\s*(-?\d+(?:\.\d+)?)/g,
325
+ (_, a, op, b) => {
326
+ const n1 = parseFloat(a);
327
+ const n2 = parseFloat(b);
328
+ const result = op === "+" ? n1 + n2 : op === "-" ? n1 - n2 : op === "*" ? n1 * n2 : n2 !== 0 ? n1 / n2 : n1;
329
+ return `: ${result}`;
330
+ }
331
+ );
332
+ s = s.replace(/\/\/[^\n]*/g, "");
333
+ s = s.replace(/([{,]\s*)([A-Za-z_$][A-Za-z0-9_$]*)\s*:/g, '$1"$2":');
334
+ s = s.replace(/'([^'\\]*(?:\\.[^'\\]*)*)'/g, '"$1"');
335
+ s = s.replace(/,\s*([}\]])/g, "$1");
336
+ s = s.replace(/([{,])\s*""\s*(?=[,}])/g, "$1");
337
+ {
338
+ let inString = false;
339
+ let escape = false;
340
+ let braces = 0;
341
+ let brackets = 0;
342
+ for (const ch of s) {
343
+ if (escape) {
344
+ escape = false;
345
+ continue;
346
+ }
347
+ if (ch === "\\" && inString) {
348
+ escape = true;
349
+ continue;
350
+ }
351
+ if (ch === '"') {
352
+ inString = !inString;
353
+ continue;
354
+ }
355
+ if (inString) continue;
356
+ if (ch === "{") braces++;
357
+ else if (ch === "}") braces--;
358
+ else if (ch === "[") brackets++;
359
+ else if (ch === "]") brackets--;
360
+ }
361
+ if (inString) s += '"';
362
+ while (brackets > 0) {
363
+ s += "]";
364
+ brackets--;
365
+ }
366
+ while (braces > 0) {
367
+ s += "}";
368
+ braces--;
369
+ }
370
+ }
371
+ const lastClose = Math.max(s.lastIndexOf("}"), s.lastIndexOf("]"));
372
+ if (lastClose !== -1 && lastClose < s.length - 1) {
373
+ s = s.substring(0, lastClose + 1);
374
+ }
375
+ return s;
376
+ }
377
+ function resolveImage(data) {
378
+ for (const key of ["image", "img", "thumbnail"]) {
379
+ const v = data[key];
380
+ if (typeof v === "string" && v) return v;
381
+ }
382
+ if (Array.isArray(data.images) && typeof data.images[0] === "string") {
383
+ return data.images[0];
384
+ }
385
+ return void 0;
386
+ }
387
+ function ChartBlock({ rawContent, primaryColor, accentColor }) {
388
+ const result = import_react5.default.useMemo(() => {
389
+ if (!rawContent || rawContent === "undefined") return { error: "Empty chart config." };
390
+ try {
391
+ const sanitized = sanitizeJson(rawContent);
392
+ const config = JSON.parse(sanitized);
393
+ return { config };
394
+ } catch (err) {
395
+ const sanitized = sanitizeJson(rawContent);
396
+ console.error("[ChartBlock] Parsing failed.\nError:", err, "\nSanitized:\n", sanitized);
397
+ return { error: String(err) };
398
+ }
399
+ }, [rawContent]);
400
+ if ("error" in result) {
401
+ return /* @__PURE__ */ import_react5.default.createElement("div", { className: "p-4 bg-red-50 dark:bg-red-900/20 text-red-600 dark:text-red-400 rounded-lg text-sm border border-red-100 dark:border-red-900/30" }, /* @__PURE__ */ import_react5.default.createElement("p", { className: "font-medium mb-1" }, "Failed to render chart"), /* @__PURE__ */ import_react5.default.createElement("p", { className: "opacity-70 text-xs" }, "The generated configuration is invalid. Check the console for details."));
402
+ }
403
+ 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" }, /* @__PURE__ */ import_react5.default.createElement(DynamicChart, { config: result.config, primaryColor, accentColor }));
404
+ }
309
405
  function MessageBubble({
310
406
  message,
311
407
  sources,
@@ -316,28 +412,18 @@ function MessageBubble({
316
412
  }) {
317
413
  const isUser = message.role === "user";
318
414
  const [showSources, setShowSources] = import_react5.default.useState(false);
319
- const resolveImage = (data) => {
320
- if (!data) return void 0;
321
- const singleFields = ["image", "img", "thumbnail"];
322
- for (const field of singleFields) {
323
- const val = data[field];
324
- if (typeof val === "string" && val) return val;
325
- }
326
- if (Array.isArray(data.images) && data.images.length > 0) {
327
- if (typeof data.images[0] === "string") return data.images[0];
328
- }
329
- return void 0;
330
- };
331
415
  const productsFromSources = import_react5.default.useMemo(() => {
332
416
  if (isUser || !sources) return [];
333
417
  return sources.filter((s) => {
334
- const m = s.metadata || {};
418
+ var _a;
419
+ const m = (_a = s.metadata) != null ? _a : {};
335
420
  return m.price || m.image || m.img || m.thumbnail || m.images || m.brand || m.type === "product";
336
421
  }).map((s) => {
337
- const m = s.metadata || {};
422
+ var _a, _b, _c, _d;
423
+ const m = (_a = s.metadata) != null ? _a : {};
338
424
  return {
339
425
  id: s.id,
340
- name: m.name || m.title || s.content.split("\n")[0] || "Unknown Product",
426
+ name: (_d = (_c = (_b = m.name) != null ? _b : m.title) != null ? _c : s.content.split("\n")[0]) != null ? _d : "Unknown Product",
341
427
  brand: m.brand,
342
428
  price: m.price,
343
429
  image: resolveImage(m),
@@ -352,16 +438,19 @@ function MessageBubble({
352
438
  const products = [];
353
439
  let content = message.content;
354
440
  if (content.includes('"type": "products"') || content.includes('"type":"products"')) {
355
- const matches = Array.from(content.matchAll(jsonRegex));
356
- for (const m of matches) {
441
+ for (const match of content.matchAll(jsonRegex)) {
357
442
  try {
358
- const data = JSON.parse(m[1]);
443
+ const data = JSON.parse(match[1]);
359
444
  if (data.type === "products" && Array.isArray(data.items)) {
360
- const formattedItems = data.items.map((item) => __spreadProps(__spreadValues({}, item), {
361
- image: item.image || resolveImage(item)
362
- }));
363
- products.push(...formattedItems);
364
- content = content.replace(m[0], "");
445
+ products.push(
446
+ ...data.items.map((item) => {
447
+ var _a;
448
+ return __spreadProps(__spreadValues({}, item), {
449
+ image: (_a = item.image) != null ? _a : resolveImage(item)
450
+ });
451
+ })
452
+ );
453
+ content = content.replace(match[0], "");
365
454
  }
366
455
  } catch (e) {
367
456
  }
@@ -371,22 +460,82 @@ function MessageBubble({
371
460
  }, [message.content, isUser]);
372
461
  const allProducts = import_react5.default.useMemo(() => {
373
462
  if (productsFromContent.length > 0) return productsFromContent;
374
- const uniqueProducts = [];
375
- const seenIds = /* @__PURE__ */ new Set();
463
+ const seen = /* @__PURE__ */ new Set();
376
464
  const contentLower = message.content.toLowerCase();
377
- for (const p of productsFromSources) {
378
- const identifier = p.id || p.name;
379
- if (seenIds.has(identifier)) continue;
380
- const nameMatch = contentLower.includes(p.name.toLowerCase());
381
- const brandMatch = p.brand ? contentLower.includes(p.brand.toLowerCase()) : false;
382
- if (nameMatch || brandMatch) {
383
- seenIds.add(identifier);
384
- uniqueProducts.push(p);
465
+ return productsFromSources.filter((p) => {
466
+ var _a;
467
+ const id = String((_a = p.id) != null ? _a : p.name);
468
+ if (seen.has(id)) return false;
469
+ const mentioned = contentLower.includes(p.name.toLowerCase()) || (p.brand ? contentLower.includes(p.brand.toLowerCase()) : false);
470
+ if (mentioned) {
471
+ seen.add(id);
472
+ return true;
385
473
  }
386
- }
387
- return uniqueProducts;
474
+ return false;
475
+ });
388
476
  }, [productsFromSources, productsFromContent, message.content]);
389
- const hasProducts = allProducts.length > 0;
477
+ const markdownComponents = import_react5.default.useMemo(
478
+ () => ({
479
+ table: (_a) => {
480
+ var props = __objRest(_a, []);
481
+ return /* @__PURE__ */ import_react5.default.createElement("div", { className: "overflow-x-auto my-4 rounded-xl border border-slate-200 dark:border-white/10 shadow-sm" }, /* @__PURE__ */ import_react5.default.createElement("table", __spreadValues({ className: "w-full text-left border-collapse min-w-[400px]" }, props)));
482
+ },
483
+ thead: (_b) => {
484
+ var props = __objRest(_b, []);
485
+ return /* @__PURE__ */ import_react5.default.createElement(
486
+ "thead",
487
+ __spreadValues({
488
+ className: "bg-slate-50 dark:bg-white/5 border-b border-slate-200 dark:border-white/10"
489
+ }, props)
490
+ );
491
+ },
492
+ th: (_c) => {
493
+ var _d = _c, { children } = _d, props = __objRest(_d, ["children"]);
494
+ return /* @__PURE__ */ import_react5.default.createElement(
495
+ "th",
496
+ __spreadValues({
497
+ className: "p-3 font-semibold text-slate-700 dark:text-white/90 first:rounded-tl-xl last:rounded-tr-xl"
498
+ }, props),
499
+ normaliseChild(children)
500
+ );
501
+ },
502
+ td: (_e) => {
503
+ var _f = _e, { children } = _f, props = __objRest(_f, ["children"]);
504
+ return /* @__PURE__ */ import_react5.default.createElement(
505
+ "td",
506
+ __spreadValues({
507
+ className: "p-3 border-b border-slate-100 dark:border-white/5 last:border-0 text-slate-600 dark:text-white/70"
508
+ }, props),
509
+ normaliseChild(children)
510
+ );
511
+ },
512
+ code(_g) {
513
+ var _h = _g, {
514
+ inline,
515
+ className,
516
+ children
517
+ } = _h, props = __objRest(_h, [
518
+ "inline",
519
+ "className",
520
+ "children"
521
+ ]);
522
+ var _a;
523
+ const lang = (_a = /language-(\w+)/.exec(className != null ? className : "")) == null ? void 0 : _a[1];
524
+ if (!inline && lang === "chart") {
525
+ return /* @__PURE__ */ import_react5.default.createElement(
526
+ ChartBlock,
527
+ {
528
+ rawContent: String(children != null ? children : "").trim(),
529
+ primaryColor,
530
+ accentColor
531
+ }
532
+ );
533
+ }
534
+ return /* @__PURE__ */ import_react5.default.createElement("code", __spreadValues({ className }, props), typeof children === "string" || typeof children === "number" ? children : JSON.stringify(children));
535
+ }
536
+ }),
537
+ [primaryColor, accentColor]
538
+ );
390
539
  return /* @__PURE__ */ import_react5.default.createElement("div", { className: `flex gap-3 ${isUser ? "flex-row-reverse" : "flex-row"} items-start` }, /* @__PURE__ */ import_react5.default.createElement(
391
540
  "div",
392
541
  {
@@ -400,68 +549,8 @@ function MessageBubble({
400
549
  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"}`,
401
550
  style: isUser ? { background: `linear-gradient(135deg, ${primaryColor}, ${accentColor})` } : {}
402
551
  },
403
- 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(
404
- import_react_markdown.default,
405
- {
406
- remarkPlugins: [import_remark_gfm.default],
407
- components: {
408
- table: (_a) => {
409
- var props = __objRest(_a, []);
410
- return /* @__PURE__ */ import_react5.default.createElement("div", { className: "overflow-x-auto my-4 rounded-xl border border-slate-200 dark:border-white/10 shadow-sm" }, /* @__PURE__ */ import_react5.default.createElement("table", __spreadValues({ className: "w-full text-left border-collapse min-w-[400px]" }, props)));
411
- },
412
- thead: (_b) => {
413
- var props = __objRest(_b, []);
414
- return /* @__PURE__ */ import_react5.default.createElement("thead", __spreadValues({ className: "bg-slate-50 dark:bg-white/5 border-b border-slate-200 dark:border-white/10" }, props));
415
- },
416
- th: (_c) => {
417
- var _d = _c, { children } = _d, props = __objRest(_d, ["children"]);
418
- return /* @__PURE__ */ import_react5.default.createElement("th", __spreadValues({ className: "p-3 font-semibold text-slate-700 dark:text-white/90 first:rounded-tl-xl last:rounded-tr-xl" }, props), typeof children === "object" && children !== null && !Array.isArray(children) && !children.$$typeof ? JSON.stringify(children) : children);
419
- },
420
- td: (_e) => {
421
- var _f = _e, { children } = _f, props = __objRest(_f, ["children"]);
422
- return /* @__PURE__ */ import_react5.default.createElement("td", __spreadValues({ className: "p-3 border-b border-slate-100 dark:border-white/5 last:border-0 text-slate-600 dark:text-white/70" }, props), typeof children === "object" && children !== null && !Array.isArray(children) && !children.$$typeof ? JSON.stringify(children) : children);
423
- },
424
- code(_g) {
425
- var _h = _g, { inline, className, children } = _h, props = __objRest(_h, ["inline", "className", "children"]);
426
- const match = /language-(\w+)/.exec(className || "");
427
- const isChart = match && match[1] === "chart";
428
- if (!inline && isChart) {
429
- const rawContent = String(children || "").trim();
430
- if (!rawContent || rawContent === "undefined") {
431
- return null;
432
- }
433
- const sanitizeJson = (json) => {
434
- return json.replace(/:\s*(\d+(\.\d+)?)\s*([\+\-\*\/])\s*(\d+(\.\d+)?)/g, (_, n1, __, op, n2) => {
435
- var _a;
436
- const v1 = parseFloat(n1);
437
- const v2 = parseFloat(n2);
438
- const ops = { "+": v1 + v2, "-": v1 - v2, "*": v1 * v2, "/": v1 / v2 };
439
- return `: ${(_a = ops[op]) != null ? _a : v1}`;
440
- }).replace(/([{,])\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*:/g, '$1 "$2":').replace(/'([^'\\]*(?:\\.[^'\\]*)*)'/g, '"$1"').replace(/,\s*([\]}])/g, "$1").replace(/}\s*{/g, "},{").replace(/]\s*\[/g, "],[").replace(/\/\/.*$/gm, "").replace(/([{,])\s*""\s*([,}])/g, "$1$2");
441
- };
442
- try {
443
- const sanitized = sanitizeJson(rawContent);
444
- const config = JSON.parse(sanitized);
445
- 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" }, /* @__PURE__ */ import_react5.default.createElement(
446
- DynamicChart,
447
- {
448
- config,
449
- primaryColor,
450
- accentColor
451
- }
452
- ));
453
- } catch (err) {
454
- console.error("[MessageBubble] Chart parsing failed:", err, "\nSanitized content:", sanitizeJson(rawContent));
455
- return /* @__PURE__ */ import_react5.default.createElement("div", { className: "p-4 bg-red-50 dark:bg-red-900/20 text-red-600 dark:text-red-400 rounded-lg text-sm border border-red-100 dark:border-red-900/30" }, /* @__PURE__ */ import_react5.default.createElement("p", { className: "font-medium mb-1" }, "Failed to render chart"), /* @__PURE__ */ import_react5.default.createElement("p", { className: "opacity-70 text-xs" }, "The generated configuration is invalid. Check console for details."));
456
- }
457
- }
458
- return /* @__PURE__ */ import_react5.default.createElement("code", __spreadValues({ className }, props), typeof children === "string" || typeof children === "number" ? children : JSON.stringify(children));
459
- }
460
- }
461
- },
462
- cleanContent || message.content
463
- ), 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" }))
464
- ), !isUser && hasProducts && /* @__PURE__ */ import_react5.default.createElement("div", { className: "w-full mt-1" }, /* @__PURE__ */ import_react5.default.createElement(
552
+ 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 }, cleanContent || message.content), 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" }))
553
+ ), !isUser && allProducts.length > 0 && /* @__PURE__ */ import_react5.default.createElement("div", { className: "w-full mt-1" }, /* @__PURE__ */ import_react5.default.createElement(
465
554
  ProductCarousel,
466
555
  {
467
556
  products: allProducts,
@@ -481,6 +570,12 @@ function MessageBubble({
481
570
  " used"
482
571
  ), showSources && /* @__PURE__ */ import_react5.default.createElement("div", { className: "mt-2 flex flex-col gap-2" }, sources.map((src, i) => /* @__PURE__ */ import_react5.default.createElement(SourceCard, { key: src.id, source: src, index: i }))))));
483
572
  }
573
+ function normaliseChild(children) {
574
+ if (typeof children === "object" && children !== null && !Array.isArray(children) && !import_react5.default.isValidElement(children)) {
575
+ return JSON.stringify(children);
576
+ }
577
+ return children;
578
+ }
484
579
 
485
580
  // src/components/ConfigProvider.tsx
486
581
  var import_react6 = __toESM(require("react"));