@retrivora-ai/rag-engine 1.5.5 → 1.5.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-Q4MDH6C4.mjs → chunk-ID2Q4CF7.mjs} +2 -0
- package/dist/handlers/index.js +2 -0
- package/dist/handlers/index.mjs +1 -1
- package/dist/index.d.mts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +109 -40
- package/dist/index.mjs +109 -40
- package/dist/server.js +2 -0
- package/dist/server.mjs +1 -1
- package/package.json +1 -1
- package/src/components/ChatWindow.tsx +26 -0
- package/src/components/DynamicChart.tsx +32 -16
- package/src/components/MessageBubble.tsx +95 -27
- package/src/components/ProductCarousel.tsx +2 -2
- package/src/core/Pipeline.ts +2 -0
- package/src/types/props.ts +3 -0
package/dist/index.mjs
CHANGED
|
@@ -123,7 +123,7 @@ function ProductCarousel({ products, primaryColor = "#6366f1", onAddToCart }) {
|
|
|
123
123
|
};
|
|
124
124
|
if (!products || products.length === 0) return null;
|
|
125
125
|
if (products.length === 1) {
|
|
126
|
-
return /* @__PURE__ */ React3.createElement("div", { className: "my-4 w-[
|
|
126
|
+
return /* @__PURE__ */ React3.createElement("div", { className: "my-4 w-[min(88%,18rem)] min-w-0 max-w-full animate-fade-in-up" }, /* @__PURE__ */ React3.createElement(ProductCard, { product: products[0], primaryColor, onAddToCart }));
|
|
127
127
|
}
|
|
128
128
|
return /* @__PURE__ */ React3.createElement("div", { className: "relative w-full my-4 group/carousel animate-fade-in-up" }, canScroll && /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.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__ */ React3.createElement(
|
|
129
129
|
"button",
|
|
@@ -158,7 +158,7 @@ function ProductCarousel({ products, primaryColor = "#6366f1", onAddToCart }) {
|
|
|
158
158
|
"div",
|
|
159
159
|
{
|
|
160
160
|
key: product.id,
|
|
161
|
-
className: "snap-start flex-shrink-0 w-[
|
|
161
|
+
className: "snap-start flex-shrink-0 w-[min(88%,18rem)] min-w-[11rem] max-w-full"
|
|
162
162
|
},
|
|
163
163
|
/* @__PURE__ */ React3.createElement(ProductCard, { product, primaryColor, onAddToCart })
|
|
164
164
|
))
|
|
@@ -183,9 +183,20 @@ import {
|
|
|
183
183
|
ResponsiveContainer
|
|
184
184
|
} from "recharts";
|
|
185
185
|
var DEFAULT_COLORS = ["#6366f1", "#10b981", "#f59e0b", "#ef4444", "#8b5cf6", "#ec4899"];
|
|
186
|
-
function DynamicChart({
|
|
186
|
+
function DynamicChart({
|
|
187
|
+
config,
|
|
188
|
+
primaryColor = "#6366f1",
|
|
189
|
+
accentColor = "#8b5cf6",
|
|
190
|
+
viewportSize = "large"
|
|
191
|
+
}) {
|
|
187
192
|
const { type, data } = config;
|
|
188
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" };
|
|
189
200
|
const sanitizedData = React4.useMemo(() => {
|
|
190
201
|
if (!data) return [];
|
|
191
202
|
return data.map((item) => {
|
|
@@ -285,7 +296,7 @@ function DynamicChart({ config, primaryColor = "#6366f1", accentColor = "#8b5cf6
|
|
|
285
296
|
return row;
|
|
286
297
|
};
|
|
287
298
|
if (type === "pie") {
|
|
288
|
-
return /* @__PURE__ */ React4.createElement("div", { className:
|
|
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(
|
|
289
300
|
Pie,
|
|
290
301
|
{
|
|
291
302
|
data: sanitizedData,
|
|
@@ -293,31 +304,31 @@ function DynamicChart({ config, primaryColor = "#6366f1", accentColor = "#8b5cf6
|
|
|
293
304
|
nameKey: finalXKey,
|
|
294
305
|
cx: "50%",
|
|
295
306
|
cy: "50%",
|
|
296
|
-
outerRadius:
|
|
297
|
-
label:
|
|
307
|
+
outerRadius: pieOuterRadius,
|
|
308
|
+
label: pieLabel
|
|
298
309
|
},
|
|
299
310
|
sanitizedData.map((entry, index) => /* @__PURE__ */ React4.createElement(Cell, { key: `cell-${index}`, fill: stockAwareColor(entry, index) }))
|
|
300
|
-
), /* @__PURE__ */ React4.createElement(Tooltip, { content: (props) => renderTooltip(getTooltipRow(props.payload), typeof props.label === "string" ? props.label : void 0) }), /* @__PURE__ */ React4.createElement(Legend,
|
|
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 } }))));
|
|
301
312
|
}
|
|
302
|
-
return /* @__PURE__ */ React4.createElement("div", { className:
|
|
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(
|
|
303
314
|
Bar,
|
|
304
315
|
{
|
|
305
316
|
key,
|
|
306
317
|
dataKey: key,
|
|
307
318
|
fill: colors[index % colors.length],
|
|
308
319
|
radius: [4, 4, 0, 0],
|
|
309
|
-
barSize: Math.max(20, 60 / data.length)
|
|
320
|
+
barSize: isCompact ? Math.max(14, 42 / data.length) : Math.max(20, 60 / data.length)
|
|
310
321
|
}
|
|
311
|
-
))) : /* @__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:
|
|
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(
|
|
312
323
|
Line,
|
|
313
324
|
{
|
|
314
325
|
key,
|
|
315
326
|
type: "monotone",
|
|
316
327
|
dataKey: key,
|
|
317
328
|
stroke: colors[index % colors.length],
|
|
318
|
-
strokeWidth: 3,
|
|
319
|
-
dot: { r: 4, strokeWidth: 2 },
|
|
320
|
-
activeDot: { r: 6 }
|
|
329
|
+
strokeWidth: isCompact ? 2 : 3,
|
|
330
|
+
dot: { r: isCompact ? 3 : 4, strokeWidth: 2 },
|
|
331
|
+
activeDot: { r: isCompact ? 5 : 6 }
|
|
321
332
|
}
|
|
322
333
|
)))));
|
|
323
334
|
}
|
|
@@ -376,6 +387,14 @@ function sanitizeJson(raw) {
|
|
|
376
387
|
}
|
|
377
388
|
return s;
|
|
378
389
|
}
|
|
390
|
+
function extractLikelyJsonBlock(raw) {
|
|
391
|
+
const trimmed = raw.trim();
|
|
392
|
+
const objectStart = trimmed.indexOf("{");
|
|
393
|
+
const arrayStart = trimmed.indexOf("[");
|
|
394
|
+
const starts = [objectStart, arrayStart].filter((index) => index >= 0);
|
|
395
|
+
if (starts.length === 0) return trimmed;
|
|
396
|
+
return trimmed.slice(Math.min(...starts));
|
|
397
|
+
}
|
|
379
398
|
function resolveImage(data) {
|
|
380
399
|
for (const key of ["image", "img", "thumbnail"]) {
|
|
381
400
|
const v = data[key];
|
|
@@ -392,7 +411,9 @@ function normaliseChild(children) {
|
|
|
392
411
|
}
|
|
393
412
|
return children;
|
|
394
413
|
}
|
|
395
|
-
function DataTable({ config }) {
|
|
414
|
+
function DataTable({ config, viewportSize = "large" }) {
|
|
415
|
+
const isCompact = viewportSize === "compact";
|
|
416
|
+
const isMedium = viewportSize === "medium";
|
|
396
417
|
const keys = React5.useMemo(() => {
|
|
397
418
|
if (Array.isArray(config.columns) && config.columns.length) {
|
|
398
419
|
return config.columns;
|
|
@@ -414,11 +435,11 @@ function DataTable({ config }) {
|
|
|
414
435
|
if (typeof val === "boolean") return val ? "\u2713" : "\u2717";
|
|
415
436
|
return String(val);
|
|
416
437
|
};
|
|
417
|
-
return /* @__PURE__ */ React5.createElement("div", { className: "my-4 rounded-xl border border-slate-200 dark:border-white/10 shadow-sm overflow-hidden" }, config.title && /* @__PURE__ */ React5.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__ */ React5.createElement("p", { className: "text-xs font-semibold text-slate-600 dark:text-white/70 uppercase tracking-wide" }, config.title)), /* @__PURE__ */ React5.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React5.createElement("table", { className:
|
|
438
|
+
return /* @__PURE__ */ React5.createElement("div", { className: "my-4 rounded-xl border border-slate-200 dark:border-white/10 shadow-sm overflow-hidden" }, config.title && /* @__PURE__ */ React5.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__ */ React5.createElement("p", { className: "text-xs font-semibold text-slate-600 dark:text-white/70 uppercase tracking-wide" }, config.title)), /* @__PURE__ */ React5.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React5.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__ */ React5.createElement("thead", { className: "bg-slate-50 dark:bg-white/5 border-b border-slate-200 dark:border-white/10" }, /* @__PURE__ */ React5.createElement("tr", null, keys.map((k) => /* @__PURE__ */ React5.createElement(
|
|
418
439
|
"th",
|
|
419
440
|
{
|
|
420
441
|
key: k,
|
|
421
|
-
className: "px-4 py-3 font-semibold text-slate-700 dark:text-white/90 whitespace-nowrap
|
|
442
|
+
className: `${isCompact ? "px-2.5 py-2" : "px-4 py-3"} font-semibold text-slate-700 dark:text-white/90 whitespace-nowrap`
|
|
422
443
|
},
|
|
423
444
|
k
|
|
424
445
|
)))), /* @__PURE__ */ React5.createElement("tbody", null, config.data.map((row, ri) => /* @__PURE__ */ React5.createElement(
|
|
@@ -431,34 +452,54 @@ function DataTable({ config }) {
|
|
|
431
452
|
"td",
|
|
432
453
|
{
|
|
433
454
|
key: k,
|
|
434
|
-
className: "px-4 py-3 text-slate-600 dark:text-white/70 whitespace-nowrap
|
|
455
|
+
className: `${isCompact ? "px-2.5 py-2" : "px-4 py-3"} text-slate-600 dark:text-white/70 whitespace-nowrap`
|
|
435
456
|
},
|
|
436
457
|
formatCell(row[k])
|
|
437
458
|
))
|
|
438
459
|
))))), /* @__PURE__ */ React5.createElement("div", { className: "px-4 py-2 bg-slate-50 dark:bg-white/5 border-t border-slate-100 dark:border-white/5" }, /* @__PURE__ */ React5.createElement("p", { className: "text-[11px] text-slate-400 dark:text-white/30" }, config.data.length, " row", config.data.length !== 1 ? "s" : "")));
|
|
439
460
|
}
|
|
440
|
-
function
|
|
461
|
+
function normalizeTabularRows(rows, columns) {
|
|
462
|
+
if (!Array.isArray(rows)) return [];
|
|
463
|
+
return rows.map((row) => {
|
|
464
|
+
if (Array.isArray(row)) {
|
|
465
|
+
const keys = Array.isArray(columns) && columns.length > 0 ? columns : row.map((_, index) => `column_${index + 1}`);
|
|
466
|
+
return keys.reduce((acc, key, index) => {
|
|
467
|
+
acc[key] = row[index];
|
|
468
|
+
return acc;
|
|
469
|
+
}, {});
|
|
470
|
+
}
|
|
471
|
+
if (row && typeof row === "object") {
|
|
472
|
+
return row;
|
|
473
|
+
}
|
|
474
|
+
return { value: row };
|
|
475
|
+
}).filter((row) => Object.keys(row).length > 0);
|
|
476
|
+
}
|
|
477
|
+
function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAddToCart, viewportSize = "large" }) {
|
|
441
478
|
var _a;
|
|
442
479
|
const result = React5.useMemo(() => {
|
|
443
480
|
if (isStreaming) return { loading: true };
|
|
444
481
|
try {
|
|
445
482
|
const clean = rawContent.replace(/^```[a-z]*\s*/i, "").replace(/```\s*$/, "").trim();
|
|
446
|
-
const parsed = JSON.parse(sanitizeJson(clean));
|
|
483
|
+
const parsed = JSON.parse(sanitizeJson(extractLikelyJsonBlock(clean)));
|
|
447
484
|
const config2 = __spreadValues({}, parsed);
|
|
448
485
|
if (!config2.view) {
|
|
449
486
|
if (config2.type === "products" || Array.isArray(config2.items)) {
|
|
450
487
|
config2.view = "carousel";
|
|
451
488
|
config2.data = config2.data || config2.items || [];
|
|
452
|
-
} else if (["pie", "bar", "line"].includes(config2.type) || ["pie", "bar", "line"].includes(config2.chartType)) {
|
|
489
|
+
} else if (typeof config2.type === "string" && ["pie", "bar", "line"].includes(config2.type) || typeof config2.chartType === "string" && ["pie", "bar", "line"].includes(config2.chartType)) {
|
|
490
|
+
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";
|
|
453
491
|
config2.view = "chart";
|
|
454
|
-
config2.chartType =
|
|
492
|
+
config2.chartType = resolvedChartType;
|
|
455
493
|
config2.data = config2.data || [];
|
|
456
494
|
} else {
|
|
457
495
|
config2.view = "table";
|
|
458
496
|
config2.data = Array.isArray(config2.data) ? config2.data : Array.isArray(config2) ? config2 : [];
|
|
459
497
|
}
|
|
460
498
|
}
|
|
461
|
-
|
|
499
|
+
const normalizedConfig = __spreadProps(__spreadValues({}, config2), {
|
|
500
|
+
data: normalizeTabularRows(config2.data, config2.columns)
|
|
501
|
+
});
|
|
502
|
+
return { config: normalizedConfig };
|
|
462
503
|
} catch (err) {
|
|
463
504
|
return { error: String(err) };
|
|
464
505
|
}
|
|
@@ -470,11 +511,12 @@ function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAd
|
|
|
470
511
|
return /* @__PURE__ */ React5.createElement("pre", { className: "p-4 my-2 bg-slate-900 text-slate-100 rounded-lg text-[10px] overflow-auto max-h-40" }, /* @__PURE__ */ React5.createElement("code", null, rawContent));
|
|
471
512
|
}
|
|
472
513
|
const { config } = result;
|
|
514
|
+
const isCompact = viewportSize === "compact";
|
|
473
515
|
const hasInsights = Array.isArray(config.insights) && config.insights.length > 0;
|
|
474
516
|
const hasDescription = typeof config.description === "string" && config.description.trim().length > 0;
|
|
475
517
|
switch (config.view) {
|
|
476
518
|
case "chart":
|
|
477
|
-
return /* @__PURE__ */ React5.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
|
|
519
|
+
return /* @__PURE__ */ React5.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__ */ React5.createElement("h4", { className: `${isCompact ? "text-[11px] mb-3" : "text-xs mb-4"} font-semibold text-slate-500 px-2` }, config.title), hasDescription && /* @__PURE__ */ React5.createElement("p", { className: `px-2 ${isCompact ? "text-xs" : "text-sm"} text-slate-500 dark:text-white/60` }, config.description), /* @__PURE__ */ React5.createElement(
|
|
478
520
|
DynamicChart,
|
|
479
521
|
{
|
|
480
522
|
config: {
|
|
@@ -485,25 +527,26 @@ function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAd
|
|
|
485
527
|
colors: config.colors
|
|
486
528
|
},
|
|
487
529
|
primaryColor,
|
|
488
|
-
accentColor
|
|
530
|
+
accentColor,
|
|
531
|
+
viewportSize
|
|
489
532
|
}
|
|
490
533
|
), hasInsights && /* @__PURE__ */ React5.createElement("div", { className: "mt-4 flex flex-wrap gap-2 px-2" }, (_a = config.insights) == null ? void 0 : _a.map((insight) => /* @__PURE__ */ React5.createElement(
|
|
491
534
|
"span",
|
|
492
535
|
{
|
|
493
536
|
key: insight,
|
|
494
|
-
className:
|
|
537
|
+
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`
|
|
495
538
|
},
|
|
496
539
|
insight
|
|
497
540
|
))));
|
|
498
541
|
case "carousel":
|
|
499
|
-
return /* @__PURE__ */ React5.createElement("div", { className: "my-4" }, config.title && /* @__PURE__ */ React5.createElement("h4", { className: "mb-2 text-xs font-semibold text-slate-500
|
|
542
|
+
return /* @__PURE__ */ React5.createElement("div", { className: "my-4" }, config.title && /* @__PURE__ */ React5.createElement("h4", { className: `${isCompact ? "mb-1.5 text-[11px]" : "mb-2 text-xs"} font-semibold text-slate-500` }, config.title), hasDescription && /* @__PURE__ */ React5.createElement("p", { className: `mb-3 ${isCompact ? "text-xs" : "text-sm"} text-slate-500 dark:text-white/60` }, config.description), /* @__PURE__ */ React5.createElement(ProductCarousel, { products: config.data.map((item) => {
|
|
500
543
|
var _a2;
|
|
501
544
|
return __spreadProps(__spreadValues({}, item), {
|
|
502
545
|
image: (_a2 = item.image) != null ? _a2 : typeof resolveImage === "function" ? resolveImage(item) : void 0
|
|
503
546
|
});
|
|
504
547
|
}), primaryColor, onAddToCart }));
|
|
505
548
|
case "table":
|
|
506
|
-
return /* @__PURE__ */ React5.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
|
|
549
|
+
return /* @__PURE__ */ React5.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__ */ React5.createElement("h4", { className: `${isCompact ? "text-[11px]" : "text-xs"} font-semibold text-slate-500 mb-2` }, config.title), hasDescription && /* @__PURE__ */ React5.createElement("p", { className: `mb-3 ${isCompact ? "text-xs" : "text-sm"} text-slate-500 dark:text-white/60` }, config.description), /* @__PURE__ */ React5.createElement(DataTable, { config: {
|
|
507
550
|
type: "table",
|
|
508
551
|
title: config.title,
|
|
509
552
|
description: config.description,
|
|
@@ -511,7 +554,7 @@ function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAd
|
|
|
511
554
|
dataKeys: config.dataKeys,
|
|
512
555
|
xAxisKey: config.xAxisKey,
|
|
513
556
|
data: config.data
|
|
514
|
-
} }));
|
|
557
|
+
}, viewportSize }));
|
|
515
558
|
default:
|
|
516
559
|
return null;
|
|
517
560
|
}
|
|
@@ -522,9 +565,12 @@ function MessageBubble({
|
|
|
522
565
|
isStreaming = false,
|
|
523
566
|
primaryColor = "#6366f1",
|
|
524
567
|
accentColor = "#8b5cf6",
|
|
525
|
-
onAddToCart
|
|
568
|
+
onAddToCart,
|
|
569
|
+
viewportSize = "large"
|
|
526
570
|
}) {
|
|
527
571
|
const isUser = message.role === "user";
|
|
572
|
+
const isCompact = viewportSize === "compact";
|
|
573
|
+
const isMedium = viewportSize === "medium";
|
|
528
574
|
const [showSources, setShowSources] = React5.useState(false);
|
|
529
575
|
const hasStructuredProductBlock = React5.useMemo(
|
|
530
576
|
() => /```(?:ui|json|chart)\s*[\s\S]*?"(?:view|type)"\s*:\s*"(?:carousel|products)"/i.test(message.content),
|
|
@@ -701,7 +747,8 @@ ${match.trim()}
|
|
|
701
747
|
primaryColor,
|
|
702
748
|
accentColor,
|
|
703
749
|
isStreaming,
|
|
704
|
-
onAddToCart
|
|
750
|
+
onAddToCart,
|
|
751
|
+
viewportSize
|
|
705
752
|
}
|
|
706
753
|
);
|
|
707
754
|
}
|
|
@@ -715,7 +762,8 @@ ${match.trim()}
|
|
|
715
762
|
primaryColor,
|
|
716
763
|
accentColor,
|
|
717
764
|
isStreaming,
|
|
718
|
-
onAddToCart
|
|
765
|
+
onAddToCart,
|
|
766
|
+
viewportSize
|
|
719
767
|
}
|
|
720
768
|
);
|
|
721
769
|
}
|
|
@@ -726,22 +774,22 @@ ${match.trim()}
|
|
|
726
774
|
return /* @__PURE__ */ React5.createElement("code", __spreadValues({ className }, props), typeof children === "string" || typeof children === "number" ? children : JSON.stringify(children));
|
|
727
775
|
}
|
|
728
776
|
}),
|
|
729
|
-
[primaryColor, accentColor, isStreaming, onAddToCart]
|
|
777
|
+
[primaryColor, accentColor, isStreaming, onAddToCart, viewportSize]
|
|
730
778
|
);
|
|
731
|
-
return /* @__PURE__ */ React5.createElement("div", { className: `flex gap-3 ${isUser ? "flex-row-reverse" : "flex-row"} items-start` }, /* @__PURE__ */ React5.createElement(
|
|
779
|
+
return /* @__PURE__ */ React5.createElement("div", { className: `flex ${isCompact ? "gap-2" : "gap-3"} ${isUser ? "flex-row-reverse" : "flex-row"} items-start` }, /* @__PURE__ */ React5.createElement(
|
|
732
780
|
"div",
|
|
733
781
|
{
|
|
734
|
-
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"}`,
|
|
782
|
+
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"}`,
|
|
735
783
|
style: isUser ? { background: `linear-gradient(135deg, ${primaryColor}, ${accentColor})` } : {}
|
|
736
784
|
},
|
|
737
|
-
isUser ? /* @__PURE__ */ React5.createElement(User, { className: "w-4 h-4 text-white
|
|
738
|
-
), /* @__PURE__ */ React5.createElement("div", { className: `flex flex-col gap-1 max-w-[90%] ${isUser ? "items-end" : "items-start"}` }, /* @__PURE__ */ React5.createElement(
|
|
785
|
+
isUser ? /* @__PURE__ */ React5.createElement(User, { className: `${isCompact ? "w-3.5 h-3.5" : "w-4 h-4"} text-white` }) : /* @__PURE__ */ React5.createElement(Bot, { className: `${isCompact ? "w-3.5 h-3.5" : "w-4 h-4"}` })
|
|
786
|
+
), /* @__PURE__ */ React5.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__ */ React5.createElement(
|
|
739
787
|
"div",
|
|
740
788
|
{
|
|
741
|
-
className: `relative px-4 py-3 rounded-2xl
|
|
789
|
+
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"}`,
|
|
742
790
|
style: isUser ? { background: `linear-gradient(135deg, ${primaryColor}, ${accentColor})` } : {}
|
|
743
791
|
},
|
|
744
|
-
isStreaming && !message.content ? /* @__PURE__ */ React5.createElement("span", { className: "flex items-center gap-1 py-1" }, /* @__PURE__ */ React5.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:0ms]" }), /* @__PURE__ */ React5.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:150ms]" }), /* @__PURE__ */ React5.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:300ms]" })) : /* @__PURE__ */ React5.createElement("div", { className: `prose prose-sm max-w-none ${isUser ? "prose-invert" : "dark:prose-invert"}` }, /* @__PURE__ */ React5.createElement(ReactMarkdown, { remarkPlugins: [remarkGfm], components: markdownComponents }, processedMarkdown), isStreaming && message.content && /* @__PURE__ */ React5.createElement("span", { className: "inline-block w-1.5 h-3.5 ml-1 bg-emerald-500 animate-pulse align-middle" }))
|
|
792
|
+
isStreaming && !message.content ? /* @__PURE__ */ React5.createElement("span", { className: "flex items-center gap-1 py-1" }, /* @__PURE__ */ React5.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:0ms]" }), /* @__PURE__ */ React5.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:150ms]" }), /* @__PURE__ */ React5.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:300ms]" })) : /* @__PURE__ */ React5.createElement("div", { className: `prose ${isCompact ? "prose-xs" : "prose-sm"} max-w-none break-words ${isUser ? "prose-invert" : "dark:prose-invert"}` }, /* @__PURE__ */ React5.createElement(ReactMarkdown, { remarkPlugins: [remarkGfm], components: markdownComponents }, processedMarkdown), isStreaming && message.content && /* @__PURE__ */ React5.createElement("span", { className: "inline-block w-1.5 h-3.5 ml-1 bg-emerald-500 animate-pulse align-middle" }))
|
|
745
793
|
), !isUser && !hasStructuredProductBlock && allProducts.length > 0 && /* @__PURE__ */ React5.createElement("div", { className: "w-full mt-1" }, /* @__PURE__ */ React5.createElement(
|
|
746
794
|
ProductCarousel,
|
|
747
795
|
{
|
|
@@ -1024,6 +1072,8 @@ function ChatWindow({
|
|
|
1024
1072
|
const { ui, projectId } = useConfig();
|
|
1025
1073
|
const [input, setInput] = useState4("");
|
|
1026
1074
|
const [mounted, setMounted] = useState4(false);
|
|
1075
|
+
const [viewportSize, setViewportSize] = useState4("large");
|
|
1076
|
+
const windowRef = useRef3(null);
|
|
1027
1077
|
const bottomRef = useRef3(null);
|
|
1028
1078
|
const inputRef = useRef3(null);
|
|
1029
1079
|
const { messages, send, clear, isLoading, error } = useRagChat(projectId, {
|
|
@@ -1082,6 +1132,22 @@ function ChatWindow({
|
|
|
1082
1132
|
useEffect4(() => {
|
|
1083
1133
|
setMounted(true);
|
|
1084
1134
|
}, []);
|
|
1135
|
+
useEffect4(() => {
|
|
1136
|
+
const element = windowRef.current;
|
|
1137
|
+
if (!element || typeof ResizeObserver === "undefined") return;
|
|
1138
|
+
const resolveViewportSize = (width) => {
|
|
1139
|
+
if (width < 420) return "compact";
|
|
1140
|
+
if (width < 640) return "medium";
|
|
1141
|
+
return "large";
|
|
1142
|
+
};
|
|
1143
|
+
const observer = new ResizeObserver(([entry]) => {
|
|
1144
|
+
if (!entry) return;
|
|
1145
|
+
setViewportSize(resolveViewportSize(entry.contentRect.width));
|
|
1146
|
+
});
|
|
1147
|
+
setViewportSize(resolveViewportSize(element.getBoundingClientRect().width));
|
|
1148
|
+
observer.observe(element);
|
|
1149
|
+
return () => observer.disconnect();
|
|
1150
|
+
}, []);
|
|
1085
1151
|
useEffect4(() => {
|
|
1086
1152
|
var _a2;
|
|
1087
1153
|
if (messages.length > 0 || isLoading) {
|
|
@@ -1145,6 +1211,7 @@ function ChatWindow({
|
|
|
1145
1211
|
return /* @__PURE__ */ React8.createElement(
|
|
1146
1212
|
"div",
|
|
1147
1213
|
{
|
|
1214
|
+
ref: windowRef,
|
|
1148
1215
|
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}`,
|
|
1149
1216
|
style: __spreadValues({ "--primary": ui.primaryColor, "--accent": ui.accentColor }, style)
|
|
1150
1217
|
},
|
|
@@ -1241,7 +1308,8 @@ function ChatWindow({
|
|
|
1241
1308
|
isStreaming: isLoading && index === messages.length - 1 && msg.role === "assistant",
|
|
1242
1309
|
primaryColor: ui.primaryColor,
|
|
1243
1310
|
accentColor: ui.accentColor,
|
|
1244
|
-
onAddToCart
|
|
1311
|
+
onAddToCart,
|
|
1312
|
+
viewportSize
|
|
1245
1313
|
}
|
|
1246
1314
|
)), isLoading && ((_a = messages[messages.length - 1]) == null ? void 0 : _a.role) !== "assistant" && /* @__PURE__ */ React8.createElement(
|
|
1247
1315
|
MessageBubble,
|
|
@@ -1250,7 +1318,8 @@ function ChatWindow({
|
|
|
1250
1318
|
isStreaming: true,
|
|
1251
1319
|
primaryColor: ui.primaryColor,
|
|
1252
1320
|
accentColor: ui.accentColor,
|
|
1253
|
-
onAddToCart
|
|
1321
|
+
onAddToCart,
|
|
1322
|
+
viewportSize
|
|
1254
1323
|
}
|
|
1255
1324
|
), error && /* @__PURE__ */ React8.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__ */ React8.createElement(TriangleAlert, { className: "w-4 h-4 flex-shrink-0" }), /* @__PURE__ */ React8.createElement("span", null, error)), /* @__PURE__ */ React8.createElement("div", { ref: bottomRef })),
|
|
1256
1325
|
/* @__PURE__ */ React8.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__ */ React8.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__ */ React8.createElement(
|
package/dist/server.js
CHANGED
|
@@ -3952,6 +3952,8 @@ When the user asks for a visual representation, comparison, distribution, breakd
|
|
|
3952
3952
|
- Never use ASCII art, markdown tables as the primary answer, or text-only fake charts when a UI block is requested.
|
|
3953
3953
|
- Put any short natural-language explanation before or after the \`\`\`ui\`\`\` block, but keep it concise.
|
|
3954
3954
|
- Return valid JSON inside the \`\`\`ui\`\`\` block.
|
|
3955
|
+
- If the user explicitly asks for a pie chart, return "view": "chart" with "chartType": "pie", not a table fallback.
|
|
3956
|
+
- Do not prefix the JSON with labels like "Table View", "Alternative", or explanatory text inside the code block.
|
|
3955
3957
|
|
|
3956
3958
|
5. EXAMPLE
|
|
3957
3959
|
User: "provide me the distribution of products across categories and highlight which ones are in stock in a pie chart"
|
package/dist/server.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@retrivora-ai/rag-engine",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.6",
|
|
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",
|
|
@@ -65,6 +65,8 @@ export function ChatWindow({
|
|
|
65
65
|
const { ui, projectId } = useConfig();
|
|
66
66
|
const [input, setInput] = useState('');
|
|
67
67
|
const [mounted, setMounted] = useState(false);
|
|
68
|
+
const [viewportSize, setViewportSize] = useState<'compact' | 'medium' | 'large'>('large');
|
|
69
|
+
const windowRef = useRef<HTMLDivElement>(null);
|
|
68
70
|
const bottomRef = useRef<HTMLDivElement>(null);
|
|
69
71
|
const inputRef = useRef<HTMLTextAreaElement>(null);
|
|
70
72
|
const { messages, send, clear, isLoading, error } = useRagChat(projectId, {
|
|
@@ -136,6 +138,27 @@ export function ChatWindow({
|
|
|
136
138
|
setMounted(true);
|
|
137
139
|
}, []);
|
|
138
140
|
|
|
141
|
+
useEffect(() => {
|
|
142
|
+
const element = windowRef.current;
|
|
143
|
+
if (!element || typeof ResizeObserver === 'undefined') return;
|
|
144
|
+
|
|
145
|
+
const resolveViewportSize = (width: number) => {
|
|
146
|
+
if (width < 420) return 'compact';
|
|
147
|
+
if (width < 640) return 'medium';
|
|
148
|
+
return 'large';
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const observer = new ResizeObserver(([entry]) => {
|
|
152
|
+
if (!entry) return;
|
|
153
|
+
setViewportSize(resolveViewportSize(entry.contentRect.width));
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
setViewportSize(resolveViewportSize(element.getBoundingClientRect().width));
|
|
157
|
+
observer.observe(element);
|
|
158
|
+
|
|
159
|
+
return () => observer.disconnect();
|
|
160
|
+
}, []);
|
|
161
|
+
|
|
139
162
|
// Scroll to bottom on new message (but not on initial mount with empty messages)
|
|
140
163
|
useEffect(() => {
|
|
141
164
|
if (messages.length > 0 || isLoading) {
|
|
@@ -206,6 +229,7 @@ export function ChatWindow({
|
|
|
206
229
|
|
|
207
230
|
return (
|
|
208
231
|
<div
|
|
232
|
+
ref={windowRef}
|
|
209
233
|
className={`relative flex flex-col border border-slate-200 dark:border-white/10 shadow-2xl transition-all duration-300 ${currentRadius} ${
|
|
210
234
|
isGlass
|
|
211
235
|
? 'bg-white/90 dark:bg-[#0f0f1a]/90 backdrop-blur-xl'
|
|
@@ -344,6 +368,7 @@ export function ChatWindow({
|
|
|
344
368
|
primaryColor={ui.primaryColor}
|
|
345
369
|
accentColor={ui.accentColor}
|
|
346
370
|
onAddToCart={onAddToCart}
|
|
371
|
+
viewportSize={viewportSize}
|
|
347
372
|
/>
|
|
348
373
|
))
|
|
349
374
|
)}
|
|
@@ -356,6 +381,7 @@ export function ChatWindow({
|
|
|
356
381
|
primaryColor={ui.primaryColor}
|
|
357
382
|
accentColor={ui.accentColor}
|
|
358
383
|
onAddToCart={onAddToCart}
|
|
384
|
+
viewportSize={viewportSize}
|
|
359
385
|
/>
|
|
360
386
|
)}
|
|
361
387
|
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
BarChart, Bar, LineChart, Line, PieChart, Pie, Cell,
|
|
6
6
|
XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer
|
|
7
7
|
} from 'recharts';
|
|
8
|
+
import { ChatViewportSize } from '../types/props';
|
|
8
9
|
|
|
9
10
|
export interface ChartConfig {
|
|
10
11
|
type: 'bar' | 'line' | 'pie';
|
|
@@ -18,13 +19,28 @@ interface DynamicChartProps {
|
|
|
18
19
|
config: ChartConfig;
|
|
19
20
|
primaryColor?: string;
|
|
20
21
|
accentColor?: string;
|
|
22
|
+
viewportSize?: ChatViewportSize;
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
const DEFAULT_COLORS = ['#6366f1', '#10b981', '#f59e0b', '#ef4444', '#8b5cf6', '#ec4899'];
|
|
24
26
|
|
|
25
|
-
export function DynamicChart({
|
|
27
|
+
export function DynamicChart({
|
|
28
|
+
config,
|
|
29
|
+
primaryColor = '#6366f1',
|
|
30
|
+
accentColor = '#8b5cf6',
|
|
31
|
+
viewportSize = 'large',
|
|
32
|
+
}: DynamicChartProps) {
|
|
26
33
|
const { type, data } = config;
|
|
27
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;
|
|
39
|
+
const pieLabel = ({ name, percent }: { name?: string; percent?: number }) =>
|
|
40
|
+
isCompact
|
|
41
|
+
? `${name}`
|
|
42
|
+
: `${name} ${((percent ?? 0) * 100).toFixed(0)}%`;
|
|
43
|
+
const axisTick = { fontSize: isCompact ? 10 : 12, fill: '#64748b' };
|
|
28
44
|
|
|
29
45
|
// CRITICAL: Sanitize the data array to remove any non-primitive values (objects/arrays)
|
|
30
46
|
// This prevents the "Objects are not valid as a React child" error in Tooltips/Legends
|
|
@@ -176,7 +192,7 @@ export function DynamicChart({ config, primaryColor = '#6366f1', accentColor = '
|
|
|
176
192
|
// Handle Pie Chart separately as it has a different structure
|
|
177
193
|
if (type === 'pie') {
|
|
178
194
|
return (
|
|
179
|
-
<div className=
|
|
195
|
+
<div className={`w-full ${chartHeightClass} mt-4 mb-2 select-none overflow-hidden`}>
|
|
180
196
|
<ResponsiveContainer width="99%" height="100%">
|
|
181
197
|
<PieChart>
|
|
182
198
|
<Pie
|
|
@@ -185,15 +201,15 @@ export function DynamicChart({ config, primaryColor = '#6366f1', accentColor = '
|
|
|
185
201
|
nameKey={finalXKey}
|
|
186
202
|
cx="50%"
|
|
187
203
|
cy="50%"
|
|
188
|
-
outerRadius={
|
|
189
|
-
label={
|
|
204
|
+
outerRadius={pieOuterRadius}
|
|
205
|
+
label={pieLabel}
|
|
190
206
|
>
|
|
191
207
|
{sanitizedData.map((entry, index) => (
|
|
192
208
|
<Cell key={`cell-${index}`} fill={stockAwareColor(entry, index)} />
|
|
193
209
|
))}
|
|
194
210
|
</Pie>
|
|
195
211
|
<Tooltip content={(props) => renderTooltip(getTooltipRow(props.payload), typeof props.label === 'string' ? props.label : undefined)} />
|
|
196
|
-
<Legend />
|
|
212
|
+
<Legend wrapperStyle={{ fontSize: isCompact ? 10 : 12 }} />
|
|
197
213
|
</PieChart>
|
|
198
214
|
</ResponsiveContainer>
|
|
199
215
|
</div>
|
|
@@ -202,41 +218,41 @@ export function DynamicChart({ config, primaryColor = '#6366f1', accentColor = '
|
|
|
202
218
|
|
|
203
219
|
// Bar and Line charts
|
|
204
220
|
return (
|
|
205
|
-
<div className=
|
|
221
|
+
<div className={`w-full ${chartHeightClass} mt-4 mb-2 select-none overflow-hidden`}>
|
|
206
222
|
<ResponsiveContainer width="99%" height="100%">
|
|
207
223
|
{type === 'bar' ? (
|
|
208
224
|
<BarChart data={sanitizedData} margin={{ top: 10, right: 10, left: -20, bottom: 0 }}>
|
|
209
225
|
<CartesianGrid strokeDasharray="3 3" vertical={false} stroke="#e2e8f0" />
|
|
210
|
-
<XAxis dataKey={finalXKey} tick={
|
|
211
|
-
<YAxis tick={
|
|
226
|
+
<XAxis dataKey={finalXKey} tick={axisTick} tickLine={false} axisLine={{ stroke: '#cbd5e1' }} />
|
|
227
|
+
<YAxis tick={axisTick} tickLine={false} axisLine={false} />
|
|
212
228
|
<Tooltip content={(props) => renderTooltip(getTooltipRow(props.payload), typeof props.label === 'string' ? props.label : undefined)} cursor={{ fill: '#f1f5f9' }} />
|
|
213
|
-
<Legend wrapperStyle={{ fontSize: 12 }} />
|
|
229
|
+
<Legend wrapperStyle={{ fontSize: isCompact ? 10 : 12 }} />
|
|
214
230
|
{finalDataKeys.map((key, index) => (
|
|
215
231
|
<Bar
|
|
216
232
|
key={key}
|
|
217
233
|
dataKey={key}
|
|
218
234
|
fill={colors[index % colors.length]}
|
|
219
235
|
radius={[4, 4, 0, 0]}
|
|
220
|
-
barSize={Math.max(20, 60 / data.length)}
|
|
236
|
+
barSize={isCompact ? Math.max(14, 42 / data.length) : Math.max(20, 60 / data.length)}
|
|
221
237
|
/>
|
|
222
238
|
))}
|
|
223
239
|
</BarChart>
|
|
224
240
|
) : (
|
|
225
241
|
<LineChart data={sanitizedData} margin={{ top: 10, right: 10, left: -20, bottom: 0 }}>
|
|
226
242
|
<CartesianGrid strokeDasharray="3 3" vertical={false} stroke="#e2e8f0" />
|
|
227
|
-
<XAxis dataKey={finalXKey} tick={
|
|
228
|
-
<YAxis tick={
|
|
243
|
+
<XAxis dataKey={finalXKey} tick={axisTick} tickLine={false} axisLine={{ stroke: '#cbd5e1' }} />
|
|
244
|
+
<YAxis tick={axisTick} tickLine={false} axisLine={false} />
|
|
229
245
|
<Tooltip content={(props) => renderTooltip(getTooltipRow(props.payload), typeof props.label === 'string' ? props.label : undefined)} />
|
|
230
|
-
<Legend wrapperStyle={{ fontSize: 12 }} />
|
|
246
|
+
<Legend wrapperStyle={{ fontSize: isCompact ? 10 : 12 }} />
|
|
231
247
|
{finalDataKeys.map((key, index) => (
|
|
232
248
|
<Line
|
|
233
249
|
key={key}
|
|
234
250
|
type="monotone"
|
|
235
251
|
dataKey={key}
|
|
236
252
|
stroke={colors[index % colors.length]}
|
|
237
|
-
strokeWidth={3}
|
|
238
|
-
dot={{ r: 4, strokeWidth: 2 }}
|
|
239
|
-
activeDot={{ r: 6 }}
|
|
253
|
+
strokeWidth={isCompact ? 2 : 3}
|
|
254
|
+
dot={{ r: isCompact ? 3 : 4, strokeWidth: 2 }}
|
|
255
|
+
activeDot={{ r: isCompact ? 5 : 6 }}
|
|
240
256
|
/>
|
|
241
257
|
))}
|
|
242
258
|
</LineChart>
|