@retrivora-ai/rag-engine 1.5.7 → 1.5.8

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/index.js CHANGED
@@ -228,12 +228,36 @@ function DynamicChart({
228
228
  }) {
229
229
  const { type, data } = config;
230
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" };
231
+ const containerRef = import_react4.default.useRef(null);
232
+ const [containerWidth, setContainerWidth] = import_react4.default.useState(0);
233
+ import_react4.default.useEffect(() => {
234
+ const element = containerRef.current;
235
+ if (!element || typeof ResizeObserver === "undefined") return;
236
+ const updateWidth = (width) => {
237
+ setContainerWidth(Math.round(width));
238
+ };
239
+ const observer = new ResizeObserver(([entry]) => {
240
+ if (!entry) return;
241
+ updateWidth(entry.contentRect.width);
242
+ });
243
+ updateWidth(element.getBoundingClientRect().width);
244
+ observer.observe(element);
245
+ return () => observer.disconnect();
246
+ }, []);
247
+ const isTiny = containerWidth > 0 && containerWidth < 300;
248
+ const isCompact = viewportSize === "compact" || containerWidth > 0 && containerWidth < 420;
249
+ const isMedium = !isCompact && (viewportSize === "medium" || containerWidth > 0 && containerWidth < 640);
250
+ const chartHeightClass = isTiny ? "h-48 min-h-[180px]" : isCompact ? "h-56 min-h-[220px]" : isMedium ? "h-64 min-h-[250px]" : "h-72 min-h-[280px]";
251
+ const pieOuterRadius = isTiny ? 44 : isCompact ? 56 : isMedium ? 68 : 80;
252
+ const pieLabel = ({ name, percent }) => isTiny ? "" : isCompact ? `${name}` : `${name} ${((percent != null ? percent : 0) * 100).toFixed(0)}%`;
253
+ const axisTick = { fontSize: isTiny ? 9 : isCompact ? 10 : 12, fill: "#64748b" };
254
+ const showLegend = !isTiny;
255
+ const truncateAxisLabel = (value) => {
256
+ const text = String(value);
257
+ const maxLength = isTiny ? 6 : isCompact ? 10 : 16;
258
+ return text.length > maxLength ? `${text.slice(0, maxLength)}\u2026` : text;
259
+ };
260
+ const chartMargin = isTiny ? { top: 8, right: 4, left: -28, bottom: 0 } : isCompact ? { top: 10, right: 6, left: -24, bottom: 0 } : { top: 10, right: 10, left: -20, bottom: 0 };
237
261
  const sanitizedData = import_react4.default.useMemo(() => {
238
262
  if (!data) return [];
239
263
  return data.map((item) => {
@@ -333,7 +357,7 @@ function DynamicChart({
333
357
  return row;
334
358
  };
335
359
  if (type === "pie") {
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(
360
+ return /* @__PURE__ */ import_react4.default.createElement("div", { ref: containerRef, className: `w-full min-w-0 ${chartHeightClass} mt-4 mb-2 select-none overflow-hidden` }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.PieChart, null, /* @__PURE__ */ import_react4.default.createElement(
337
361
  import_recharts.Pie,
338
362
  {
339
363
  data: sanitizedData,
@@ -342,30 +366,30 @@ function DynamicChart({
342
366
  cx: "50%",
343
367
  cy: "50%",
344
368
  outerRadius: pieOuterRadius,
345
- label: pieLabel
369
+ label: isTiny ? false : pieLabel
346
370
  },
347
371
  sanitizedData.map((entry, index) => /* @__PURE__ */ import_react4.default.createElement(import_recharts.Cell, { key: `cell-${index}`, fill: stockAwareColor(entry, index) }))
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 } }))));
372
+ ), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Tooltip, { content: (props) => renderTooltip(getTooltipRow(props.payload), typeof props.label === "string" ? props.label : void 0) }), showLegend && /* @__PURE__ */ import_react4.default.createElement(import_recharts.Legend, { wrapperStyle: { fontSize: isCompact ? 10 : 12 } }))));
349
373
  }
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(
374
+ return /* @__PURE__ */ import_react4.default.createElement("div", { ref: containerRef, className: `w-full min-w-0 ${chartHeightClass} mt-4 mb-2 select-none overflow-hidden` }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, type === "bar" ? /* @__PURE__ */ import_react4.default.createElement(import_recharts.BarChart, { data: sanitizedData, margin: chartMargin }, /* @__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, tickFormatter: truncateAxisLabel, tickLine: false, axisLine: { stroke: "#cbd5e1" }, interval: 0, minTickGap: isTiny ? 2 : isCompact ? 6 : 12 }), /* @__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" } }), showLegend && /* @__PURE__ */ import_react4.default.createElement(import_recharts.Legend, { wrapperStyle: { fontSize: isCompact ? 10 : 12 } }), finalDataKeys.map((key, index) => /* @__PURE__ */ import_react4.default.createElement(
351
375
  import_recharts.Bar,
352
376
  {
353
377
  key,
354
378
  dataKey: key,
355
379
  fill: colors[index % colors.length],
356
380
  radius: [4, 4, 0, 0],
357
- barSize: isCompact ? Math.max(14, 42 / data.length) : Math.max(20, 60 / data.length)
381
+ barSize: isTiny ? Math.max(10, 28 / data.length) : isCompact ? Math.max(14, 42 / data.length) : Math.max(20, 60 / data.length)
358
382
  }
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(
383
+ ))) : /* @__PURE__ */ import_react4.default.createElement(import_recharts.LineChart, { data: sanitizedData, margin: chartMargin }, /* @__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, tickFormatter: truncateAxisLabel, tickLine: false, axisLine: { stroke: "#cbd5e1" }, interval: 0, minTickGap: isTiny ? 2 : isCompact ? 6 : 12 }), /* @__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) }), showLegend && /* @__PURE__ */ import_react4.default.createElement(import_recharts.Legend, { wrapperStyle: { fontSize: isCompact ? 10 : 12 } }), finalDataKeys.map((key, index) => /* @__PURE__ */ import_react4.default.createElement(
360
384
  import_recharts.Line,
361
385
  {
362
386
  key,
363
387
  type: "monotone",
364
388
  dataKey: key,
365
389
  stroke: colors[index % colors.length],
366
- strokeWidth: isCompact ? 2 : 3,
367
- dot: { r: isCompact ? 3 : 4, strokeWidth: 2 },
368
- activeDot: { r: isCompact ? 5 : 6 }
390
+ strokeWidth: isTiny ? 1.75 : isCompact ? 2 : 3,
391
+ dot: { r: isTiny ? 2.5 : isCompact ? 3 : 4, strokeWidth: 2 },
392
+ activeDot: { r: isTiny ? 4 : isCompact ? 5 : 6 }
369
393
  }
370
394
  )))));
371
395
  }
@@ -432,6 +456,9 @@ function extractLikelyJsonBlock(raw) {
432
456
  if (starts.length === 0) return trimmed;
433
457
  return trimmed.slice(Math.min(...starts));
434
458
  }
459
+ function looksLikeStructuredPayload(raw) {
460
+ return /"(view|chartType|type|data|items|rows|products|results)"\s*:/.test(raw);
461
+ }
435
462
  function resolveImage(data) {
436
463
  for (const key of ["image", "img", "thumbnail"]) {
437
464
  const v = data[key];
@@ -690,9 +717,10 @@ function MessageBubble({
690
717
  let raw = cleanContent || message.content;
691
718
  raw = raw.replace(/([^\n])\n\|/g, "$1\n\n|").replace(/(\|[^\n]*\|)\n\n+(?=\|)/g, "$1\n");
692
719
  if (!isStreaming) {
693
- raw = raw.replace(/(?:^|\n)\s*\{\s*[\s\S]*?"type":\s*"(?:pie|bar|line|products)"[\s\S]*?\}\s*(?:\n|$)/g, (match) => {
720
+ raw = raw.replace(/(?:^|\n)\s*\{[\s\S]*\}\s*(?:\n|$)/g, (match) => {
694
721
  if (match.includes("```")) return match;
695
- const type = match.includes('"products"') ? "json" : "chart";
722
+ if (!looksLikeStructuredPayload(match)) return match;
723
+ const type = match.includes('"view"') || match.includes('"chartType"') || match.includes('"type": "pie"') || match.includes('"type":"pie"') || match.includes('"type": "bar"') || match.includes('"type":"bar"') || match.includes('"type": "line"') || match.includes('"type":"line"') ? "ui" : "json";
696
724
  return `
697
725
 
698
726
  \`\`\`${type}
@@ -800,7 +828,7 @@ ${match.trim()}
800
828
  }
801
829
  if (!inline && lang === "json") {
802
830
  const content = String(children != null ? children : "").trim();
803
- if (content.includes('"type": "products"') || content.includes('"type":"products"')) {
831
+ if (looksLikeStructuredPayload(content)) {
804
832
  return /* @__PURE__ */ import_react5.default.createElement(
805
833
  UIDispatcher,
806
834
  {
package/dist/index.mjs CHANGED
@@ -191,12 +191,36 @@ function DynamicChart({
191
191
  }) {
192
192
  const { type, data } = config;
193
193
  const colors = config.colors || [primaryColor, accentColor, ...DEFAULT_COLORS];
194
- const isCompact = viewportSize === "compact";
195
- const isMedium = viewportSize === "medium";
196
- const chartHeightClass = isCompact ? "h-56 min-h-[220px]" : isMedium ? "h-64 min-h-[250px]" : "h-72 min-h-[280px]";
197
- const pieOuterRadius = isCompact ? 56 : isMedium ? 68 : 80;
198
- const pieLabel = ({ name, percent }) => isCompact ? `${name}` : `${name} ${((percent != null ? percent : 0) * 100).toFixed(0)}%`;
199
- const axisTick = { fontSize: isCompact ? 10 : 12, fill: "#64748b" };
194
+ const containerRef = React4.useRef(null);
195
+ const [containerWidth, setContainerWidth] = React4.useState(0);
196
+ React4.useEffect(() => {
197
+ const element = containerRef.current;
198
+ if (!element || typeof ResizeObserver === "undefined") return;
199
+ const updateWidth = (width) => {
200
+ setContainerWidth(Math.round(width));
201
+ };
202
+ const observer = new ResizeObserver(([entry]) => {
203
+ if (!entry) return;
204
+ updateWidth(entry.contentRect.width);
205
+ });
206
+ updateWidth(element.getBoundingClientRect().width);
207
+ observer.observe(element);
208
+ return () => observer.disconnect();
209
+ }, []);
210
+ const isTiny = containerWidth > 0 && containerWidth < 300;
211
+ const isCompact = viewportSize === "compact" || containerWidth > 0 && containerWidth < 420;
212
+ const isMedium = !isCompact && (viewportSize === "medium" || containerWidth > 0 && containerWidth < 640);
213
+ const chartHeightClass = isTiny ? "h-48 min-h-[180px]" : isCompact ? "h-56 min-h-[220px]" : isMedium ? "h-64 min-h-[250px]" : "h-72 min-h-[280px]";
214
+ const pieOuterRadius = isTiny ? 44 : isCompact ? 56 : isMedium ? 68 : 80;
215
+ const pieLabel = ({ name, percent }) => isTiny ? "" : isCompact ? `${name}` : `${name} ${((percent != null ? percent : 0) * 100).toFixed(0)}%`;
216
+ const axisTick = { fontSize: isTiny ? 9 : isCompact ? 10 : 12, fill: "#64748b" };
217
+ const showLegend = !isTiny;
218
+ const truncateAxisLabel = (value) => {
219
+ const text = String(value);
220
+ const maxLength = isTiny ? 6 : isCompact ? 10 : 16;
221
+ return text.length > maxLength ? `${text.slice(0, maxLength)}\u2026` : text;
222
+ };
223
+ const chartMargin = isTiny ? { top: 8, right: 4, left: -28, bottom: 0 } : isCompact ? { top: 10, right: 6, left: -24, bottom: 0 } : { top: 10, right: 10, left: -20, bottom: 0 };
200
224
  const sanitizedData = React4.useMemo(() => {
201
225
  if (!data) return [];
202
226
  return data.map((item) => {
@@ -296,7 +320,7 @@ function DynamicChart({
296
320
  return row;
297
321
  };
298
322
  if (type === "pie") {
299
- return /* @__PURE__ */ React4.createElement("div", { className: `w-full ${chartHeightClass} mt-4 mb-2 select-none overflow-hidden` }, /* @__PURE__ */ React4.createElement(ResponsiveContainer, { width: "99%", height: "100%" }, /* @__PURE__ */ React4.createElement(PieChart, null, /* @__PURE__ */ React4.createElement(
323
+ return /* @__PURE__ */ React4.createElement("div", { ref: containerRef, className: `w-full min-w-0 ${chartHeightClass} mt-4 mb-2 select-none overflow-hidden` }, /* @__PURE__ */ React4.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React4.createElement(PieChart, null, /* @__PURE__ */ React4.createElement(
300
324
  Pie,
301
325
  {
302
326
  data: sanitizedData,
@@ -305,30 +329,30 @@ function DynamicChart({
305
329
  cx: "50%",
306
330
  cy: "50%",
307
331
  outerRadius: pieOuterRadius,
308
- label: pieLabel
332
+ label: isTiny ? false : pieLabel
309
333
  },
310
334
  sanitizedData.map((entry, index) => /* @__PURE__ */ React4.createElement(Cell, { key: `cell-${index}`, fill: stockAwareColor(entry, index) }))
311
- ), /* @__PURE__ */ React4.createElement(Tooltip, { content: (props) => renderTooltip(getTooltipRow(props.payload), typeof props.label === "string" ? props.label : void 0) }), /* @__PURE__ */ React4.createElement(Legend, { wrapperStyle: { fontSize: isCompact ? 10 : 12 } }))));
335
+ ), /* @__PURE__ */ React4.createElement(Tooltip, { content: (props) => renderTooltip(getTooltipRow(props.payload), typeof props.label === "string" ? props.label : void 0) }), showLegend && /* @__PURE__ */ React4.createElement(Legend, { wrapperStyle: { fontSize: isCompact ? 10 : 12 } }))));
312
336
  }
313
- return /* @__PURE__ */ React4.createElement("div", { className: `w-full ${chartHeightClass} mt-4 mb-2 select-none overflow-hidden` }, /* @__PURE__ */ React4.createElement(ResponsiveContainer, { width: "99%", height: "100%" }, type === "bar" ? /* @__PURE__ */ React4.createElement(BarChart, { data: sanitizedData, margin: { top: 10, right: 10, left: -20, bottom: 0 } }, /* @__PURE__ */ React4.createElement(CartesianGrid, { strokeDasharray: "3 3", vertical: false, stroke: "#e2e8f0" }), /* @__PURE__ */ React4.createElement(XAxis, { dataKey: finalXKey, tick: axisTick, tickLine: false, axisLine: { stroke: "#cbd5e1" } }), /* @__PURE__ */ React4.createElement(YAxis, { tick: axisTick, tickLine: false, axisLine: false }), /* @__PURE__ */ React4.createElement(Tooltip, { content: (props) => renderTooltip(getTooltipRow(props.payload), typeof props.label === "string" ? props.label : void 0), cursor: { fill: "#f1f5f9" } }), /* @__PURE__ */ React4.createElement(Legend, { wrapperStyle: { fontSize: isCompact ? 10 : 12 } }), finalDataKeys.map((key, index) => /* @__PURE__ */ React4.createElement(
337
+ return /* @__PURE__ */ React4.createElement("div", { ref: containerRef, className: `w-full min-w-0 ${chartHeightClass} mt-4 mb-2 select-none overflow-hidden` }, /* @__PURE__ */ React4.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, type === "bar" ? /* @__PURE__ */ React4.createElement(BarChart, { data: sanitizedData, margin: chartMargin }, /* @__PURE__ */ React4.createElement(CartesianGrid, { strokeDasharray: "3 3", vertical: false, stroke: "#e2e8f0" }), /* @__PURE__ */ React4.createElement(XAxis, { dataKey: finalXKey, tick: axisTick, tickFormatter: truncateAxisLabel, tickLine: false, axisLine: { stroke: "#cbd5e1" }, interval: 0, minTickGap: isTiny ? 2 : isCompact ? 6 : 12 }), /* @__PURE__ */ React4.createElement(YAxis, { tick: axisTick, tickLine: false, axisLine: false }), /* @__PURE__ */ React4.createElement(Tooltip, { content: (props) => renderTooltip(getTooltipRow(props.payload), typeof props.label === "string" ? props.label : void 0), cursor: { fill: "#f1f5f9" } }), showLegend && /* @__PURE__ */ React4.createElement(Legend, { wrapperStyle: { fontSize: isCompact ? 10 : 12 } }), finalDataKeys.map((key, index) => /* @__PURE__ */ React4.createElement(
314
338
  Bar,
315
339
  {
316
340
  key,
317
341
  dataKey: key,
318
342
  fill: colors[index % colors.length],
319
343
  radius: [4, 4, 0, 0],
320
- barSize: isCompact ? Math.max(14, 42 / data.length) : Math.max(20, 60 / data.length)
344
+ barSize: isTiny ? Math.max(10, 28 / data.length) : isCompact ? Math.max(14, 42 / data.length) : Math.max(20, 60 / data.length)
321
345
  }
322
- ))) : /* @__PURE__ */ React4.createElement(LineChart, { data: sanitizedData, margin: { top: 10, right: 10, left: -20, bottom: 0 } }, /* @__PURE__ */ React4.createElement(CartesianGrid, { strokeDasharray: "3 3", vertical: false, stroke: "#e2e8f0" }), /* @__PURE__ */ React4.createElement(XAxis, { dataKey: finalXKey, tick: axisTick, tickLine: false, axisLine: { stroke: "#cbd5e1" } }), /* @__PURE__ */ React4.createElement(YAxis, { tick: axisTick, tickLine: false, axisLine: false }), /* @__PURE__ */ React4.createElement(Tooltip, { content: (props) => renderTooltip(getTooltipRow(props.payload), typeof props.label === "string" ? props.label : void 0) }), /* @__PURE__ */ React4.createElement(Legend, { wrapperStyle: { fontSize: isCompact ? 10 : 12 } }), finalDataKeys.map((key, index) => /* @__PURE__ */ React4.createElement(
346
+ ))) : /* @__PURE__ */ React4.createElement(LineChart, { data: sanitizedData, margin: chartMargin }, /* @__PURE__ */ React4.createElement(CartesianGrid, { strokeDasharray: "3 3", vertical: false, stroke: "#e2e8f0" }), /* @__PURE__ */ React4.createElement(XAxis, { dataKey: finalXKey, tick: axisTick, tickFormatter: truncateAxisLabel, tickLine: false, axisLine: { stroke: "#cbd5e1" }, interval: 0, minTickGap: isTiny ? 2 : isCompact ? 6 : 12 }), /* @__PURE__ */ React4.createElement(YAxis, { tick: axisTick, tickLine: false, axisLine: false }), /* @__PURE__ */ React4.createElement(Tooltip, { content: (props) => renderTooltip(getTooltipRow(props.payload), typeof props.label === "string" ? props.label : void 0) }), showLegend && /* @__PURE__ */ React4.createElement(Legend, { wrapperStyle: { fontSize: isCompact ? 10 : 12 } }), finalDataKeys.map((key, index) => /* @__PURE__ */ React4.createElement(
323
347
  Line,
324
348
  {
325
349
  key,
326
350
  type: "monotone",
327
351
  dataKey: key,
328
352
  stroke: colors[index % colors.length],
329
- strokeWidth: isCompact ? 2 : 3,
330
- dot: { r: isCompact ? 3 : 4, strokeWidth: 2 },
331
- activeDot: { r: isCompact ? 5 : 6 }
353
+ strokeWidth: isTiny ? 1.75 : isCompact ? 2 : 3,
354
+ dot: { r: isTiny ? 2.5 : isCompact ? 3 : 4, strokeWidth: 2 },
355
+ activeDot: { r: isTiny ? 4 : isCompact ? 5 : 6 }
332
356
  }
333
357
  )))));
334
358
  }
@@ -395,6 +419,9 @@ function extractLikelyJsonBlock(raw) {
395
419
  if (starts.length === 0) return trimmed;
396
420
  return trimmed.slice(Math.min(...starts));
397
421
  }
422
+ function looksLikeStructuredPayload(raw) {
423
+ return /"(view|chartType|type|data|items|rows|products|results)"\s*:/.test(raw);
424
+ }
398
425
  function resolveImage(data) {
399
426
  for (const key of ["image", "img", "thumbnail"]) {
400
427
  const v = data[key];
@@ -653,9 +680,10 @@ function MessageBubble({
653
680
  let raw = cleanContent || message.content;
654
681
  raw = raw.replace(/([^\n])\n\|/g, "$1\n\n|").replace(/(\|[^\n]*\|)\n\n+(?=\|)/g, "$1\n");
655
682
  if (!isStreaming) {
656
- raw = raw.replace(/(?:^|\n)\s*\{\s*[\s\S]*?"type":\s*"(?:pie|bar|line|products)"[\s\S]*?\}\s*(?:\n|$)/g, (match) => {
683
+ raw = raw.replace(/(?:^|\n)\s*\{[\s\S]*\}\s*(?:\n|$)/g, (match) => {
657
684
  if (match.includes("```")) return match;
658
- const type = match.includes('"products"') ? "json" : "chart";
685
+ if (!looksLikeStructuredPayload(match)) return match;
686
+ const type = match.includes('"view"') || match.includes('"chartType"') || match.includes('"type": "pie"') || match.includes('"type":"pie"') || match.includes('"type": "bar"') || match.includes('"type":"bar"') || match.includes('"type": "line"') || match.includes('"type":"line"') ? "ui" : "json";
659
687
  return `
660
688
 
661
689
  \`\`\`${type}
@@ -763,7 +791,7 @@ ${match.trim()}
763
791
  }
764
792
  if (!inline && lang === "json") {
765
793
  const content = String(children != null ? children : "").trim();
766
- if (content.includes('"type": "products"') || content.includes('"type":"products"')) {
794
+ if (looksLikeStructuredPayload(content)) {
767
795
  return /* @__PURE__ */ React5.createElement(
768
796
  UIDispatcher,
769
797
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@retrivora-ai/rag-engine",
3
- "version": "1.5.7",
3
+ "version": "1.5.8",
4
4
  "description": "Retrivora AI is a plug-and-play AI engine for RAG chat experiences — generic vector DB + LLM provider, embeddable or standalone.",
5
5
  "author": "Abhinav Alkuchi",
6
6
  "license": "MIT",
@@ -32,15 +32,57 @@ export function DynamicChart({
32
32
  }: DynamicChartProps) {
33
33
  const { type, data } = config;
34
34
  const colors = config.colors || [primaryColor, accentColor, ...DEFAULT_COLORS];
35
- const isCompact = viewportSize === 'compact';
36
- const isMedium = viewportSize === 'medium';
37
- const chartHeightClass = isCompact ? 'h-56 min-h-[220px]' : isMedium ? 'h-64 min-h-[250px]' : 'h-72 min-h-[280px]';
38
- const pieOuterRadius = isCompact ? 56 : isMedium ? 68 : 80;
35
+ const containerRef = React.useRef<HTMLDivElement>(null);
36
+ const [containerWidth, setContainerWidth] = React.useState(0);
37
+
38
+ React.useEffect(() => {
39
+ const element = containerRef.current;
40
+ if (!element || typeof ResizeObserver === 'undefined') return;
41
+
42
+ const updateWidth = (width: number) => {
43
+ setContainerWidth(Math.round(width));
44
+ };
45
+
46
+ const observer = new ResizeObserver(([entry]) => {
47
+ if (!entry) return;
48
+ updateWidth(entry.contentRect.width);
49
+ });
50
+
51
+ updateWidth(element.getBoundingClientRect().width);
52
+ observer.observe(element);
53
+
54
+ return () => observer.disconnect();
55
+ }, []);
56
+
57
+ const isTiny = containerWidth > 0 && containerWidth < 300;
58
+ const isCompact = viewportSize === 'compact' || (containerWidth > 0 && containerWidth < 420);
59
+ const isMedium = !isCompact && (viewportSize === 'medium' || (containerWidth > 0 && containerWidth < 640));
60
+ const chartHeightClass = isTiny
61
+ ? 'h-48 min-h-[180px]'
62
+ : isCompact
63
+ ? 'h-56 min-h-[220px]'
64
+ : isMedium
65
+ ? 'h-64 min-h-[250px]'
66
+ : 'h-72 min-h-[280px]';
67
+ const pieOuterRadius = isTiny ? 44 : isCompact ? 56 : isMedium ? 68 : 80;
39
68
  const pieLabel = ({ name, percent }: { name?: string; percent?: number }) =>
40
- isCompact
69
+ isTiny
70
+ ? ''
71
+ : isCompact
41
72
  ? `${name}`
42
73
  : `${name} ${((percent ?? 0) * 100).toFixed(0)}%`;
43
- const axisTick = { fontSize: isCompact ? 10 : 12, fill: '#64748b' };
74
+ const axisTick = { fontSize: isTiny ? 9 : isCompact ? 10 : 12, fill: '#64748b' };
75
+ const showLegend = !isTiny;
76
+ const truncateAxisLabel = (value: string | number) => {
77
+ const text = String(value);
78
+ const maxLength = isTiny ? 6 : isCompact ? 10 : 16;
79
+ return text.length > maxLength ? `${text.slice(0, maxLength)}…` : text;
80
+ };
81
+ const chartMargin = isTiny
82
+ ? { top: 8, right: 4, left: -28, bottom: 0 }
83
+ : isCompact
84
+ ? { top: 10, right: 6, left: -24, bottom: 0 }
85
+ : { top: 10, right: 10, left: -20, bottom: 0 };
44
86
 
45
87
  // CRITICAL: Sanitize the data array to remove any non-primitive values (objects/arrays)
46
88
  // This prevents the "Objects are not valid as a React child" error in Tooltips/Legends
@@ -192,8 +234,8 @@ export function DynamicChart({
192
234
  // Handle Pie Chart separately as it has a different structure
193
235
  if (type === 'pie') {
194
236
  return (
195
- <div className={`w-full ${chartHeightClass} mt-4 mb-2 select-none overflow-hidden`}>
196
- <ResponsiveContainer width="99%" height="100%">
237
+ <div ref={containerRef} className={`w-full min-w-0 ${chartHeightClass} mt-4 mb-2 select-none overflow-hidden`}>
238
+ <ResponsiveContainer width="100%" height="100%">
197
239
  <PieChart>
198
240
  <Pie
199
241
  data={sanitizedData}
@@ -202,14 +244,14 @@ export function DynamicChart({
202
244
  cx="50%"
203
245
  cy="50%"
204
246
  outerRadius={pieOuterRadius}
205
- label={pieLabel}
247
+ label={isTiny ? false : pieLabel}
206
248
  >
207
249
  {sanitizedData.map((entry, index) => (
208
250
  <Cell key={`cell-${index}`} fill={stockAwareColor(entry, index)} />
209
251
  ))}
210
252
  </Pie>
211
253
  <Tooltip content={(props) => renderTooltip(getTooltipRow(props.payload), typeof props.label === 'string' ? props.label : undefined)} />
212
- <Legend wrapperStyle={{ fontSize: isCompact ? 10 : 12 }} />
254
+ {showLegend && <Legend wrapperStyle={{ fontSize: isCompact ? 10 : 12 }} />}
213
255
  </PieChart>
214
256
  </ResponsiveContainer>
215
257
  </div>
@@ -218,41 +260,41 @@ export function DynamicChart({
218
260
 
219
261
  // Bar and Line charts
220
262
  return (
221
- <div className={`w-full ${chartHeightClass} mt-4 mb-2 select-none overflow-hidden`}>
222
- <ResponsiveContainer width="99%" height="100%">
263
+ <div ref={containerRef} className={`w-full min-w-0 ${chartHeightClass} mt-4 mb-2 select-none overflow-hidden`}>
264
+ <ResponsiveContainer width="100%" height="100%">
223
265
  {type === 'bar' ? (
224
- <BarChart data={sanitizedData} margin={{ top: 10, right: 10, left: -20, bottom: 0 }}>
266
+ <BarChart data={sanitizedData} margin={chartMargin}>
225
267
  <CartesianGrid strokeDasharray="3 3" vertical={false} stroke="#e2e8f0" />
226
- <XAxis dataKey={finalXKey} tick={axisTick} tickLine={false} axisLine={{ stroke: '#cbd5e1' }} />
268
+ <XAxis dataKey={finalXKey} tick={axisTick} tickFormatter={truncateAxisLabel} tickLine={false} axisLine={{ stroke: '#cbd5e1' }} interval={0} minTickGap={isTiny ? 2 : isCompact ? 6 : 12} />
227
269
  <YAxis tick={axisTick} tickLine={false} axisLine={false} />
228
270
  <Tooltip content={(props) => renderTooltip(getTooltipRow(props.payload), typeof props.label === 'string' ? props.label : undefined)} cursor={{ fill: '#f1f5f9' }} />
229
- <Legend wrapperStyle={{ fontSize: isCompact ? 10 : 12 }} />
271
+ {showLegend && <Legend wrapperStyle={{ fontSize: isCompact ? 10 : 12 }} />}
230
272
  {finalDataKeys.map((key, index) => (
231
273
  <Bar
232
274
  key={key}
233
275
  dataKey={key}
234
276
  fill={colors[index % colors.length]}
235
277
  radius={[4, 4, 0, 0]}
236
- barSize={isCompact ? Math.max(14, 42 / data.length) : Math.max(20, 60 / data.length)}
278
+ barSize={isTiny ? Math.max(10, 28 / data.length) : isCompact ? Math.max(14, 42 / data.length) : Math.max(20, 60 / data.length)}
237
279
  />
238
280
  ))}
239
281
  </BarChart>
240
282
  ) : (
241
- <LineChart data={sanitizedData} margin={{ top: 10, right: 10, left: -20, bottom: 0 }}>
283
+ <LineChart data={sanitizedData} margin={chartMargin}>
242
284
  <CartesianGrid strokeDasharray="3 3" vertical={false} stroke="#e2e8f0" />
243
- <XAxis dataKey={finalXKey} tick={axisTick} tickLine={false} axisLine={{ stroke: '#cbd5e1' }} />
285
+ <XAxis dataKey={finalXKey} tick={axisTick} tickFormatter={truncateAxisLabel} tickLine={false} axisLine={{ stroke: '#cbd5e1' }} interval={0} minTickGap={isTiny ? 2 : isCompact ? 6 : 12} />
244
286
  <YAxis tick={axisTick} tickLine={false} axisLine={false} />
245
287
  <Tooltip content={(props) => renderTooltip(getTooltipRow(props.payload), typeof props.label === 'string' ? props.label : undefined)} />
246
- <Legend wrapperStyle={{ fontSize: isCompact ? 10 : 12 }} />
288
+ {showLegend && <Legend wrapperStyle={{ fontSize: isCompact ? 10 : 12 }} />}
247
289
  {finalDataKeys.map((key, index) => (
248
290
  <Line
249
291
  key={key}
250
292
  type="monotone"
251
293
  dataKey={key}
252
294
  stroke={colors[index % colors.length]}
253
- strokeWidth={isCompact ? 2 : 3}
254
- dot={{ r: isCompact ? 3 : 4, strokeWidth: 2 }}
255
- activeDot={{ r: isCompact ? 5 : 6 }}
295
+ strokeWidth={isTiny ? 1.75 : isCompact ? 2 : 3}
296
+ dot={{ r: isTiny ? 2.5 : isCompact ? 3 : 4, strokeWidth: 2 }}
297
+ activeDot={{ r: isTiny ? 4 : isCompact ? 5 : 6 }}
256
298
  />
257
299
  ))}
258
300
  </LineChart>
@@ -102,6 +102,10 @@ function extractLikelyJsonBlock(raw: string): string {
102
102
  return trimmed.slice(Math.min(...starts));
103
103
  }
104
104
 
105
+ function looksLikeStructuredPayload(raw: string): boolean {
106
+ return /"(view|chartType|type|data|items|rows|products|results)"\s*:/.test(raw);
107
+ }
108
+
105
109
  // ─── Tiny helpers ─────────────────────────────────────────────────────────────
106
110
 
107
111
  function resolveImage(data: Record<string, unknown>): string | undefined {
@@ -538,10 +542,13 @@ export function MessageBubble({
538
542
  // This handles models (like Llama 3.2) that often omit the ```chart markers.
539
543
  if (!isStreaming) {
540
544
  // Look for objects that look like chart or product data
541
- raw = raw.replace(/(?:^|\n)\s*\{\s*[\s\S]*?"type":\s*"(?:pie|bar|line|products)"[\s\S]*?\}\s*(?:\n|$)/g, (match) => {
545
+ raw = raw.replace(/(?:^|\n)\s*\{[\s\S]*\}\s*(?:\n|$)/g, (match) => {
542
546
  // Only wrap if not already in a code block
543
547
  if (match.includes('```')) return match;
544
- const type = match.includes('"products"') ? 'json' : 'chart';
548
+ if (!looksLikeStructuredPayload(match)) return match;
549
+ const type = match.includes('"view"') || match.includes('"chartType"') || match.includes('"type": "pie"') || match.includes('"type":"pie"') || match.includes('"type": "bar"') || match.includes('"type":"bar"') || match.includes('"type": "line"') || match.includes('"type":"line"')
550
+ ? 'ui'
551
+ : 'json';
545
552
  return `\n\n\`\`\`${type}\n${match.trim()}\n\`\`\`\n\n`;
546
553
  });
547
554
  }
@@ -638,7 +645,7 @@ export function MessageBubble({
638
645
 
639
646
  if (!inline && lang === 'json') {
640
647
  const content = String(children ?? '').trim();
641
- if (content.includes('"type": "products"') || content.includes('"type":"products"')) {
648
+ if (looksLikeStructuredPayload(content)) {
642
649
  return (
643
650
  <UIDispatcher
644
651
  rawContent={content}