@invergent/agent-chat-react 1.5.3 → 1.5.4

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.
@@ -0,0 +1,153 @@
1
+ import { cn } from './chunk-QSC4UIVT.js';
2
+ import { useRef, useState, useMemo, useEffect } from 'react';
3
+ import { Chart } from 'chart.js/auto';
4
+ import { useTheme } from 'next-themes';
5
+ import { jsxs, jsx } from 'react/jsx-runtime';
6
+
7
+ var DEFAULT_CHART_HEIGHT = 320;
8
+ var MAX_INLINE_CHART_HEIGHT = 800;
9
+ var LIGHT_THEME = {
10
+ text: "#444",
11
+ title: "#111",
12
+ grid: "#e5e7eb",
13
+ border: "#d1d5db"
14
+ };
15
+ var DARK_THEME = {
16
+ text: "#cbd5e1",
17
+ title: "#f1f5f9",
18
+ grid: "#374151",
19
+ border: "#4b5563"
20
+ };
21
+ function ArtifactChart({
22
+ spec,
23
+ fill = false
24
+ }) {
25
+ const { resolvedTheme } = useTheme();
26
+ const isDark = resolvedTheme === "dark";
27
+ const canvasRef = useRef(null);
28
+ const chartRef = useRef(null);
29
+ const [error, setError] = useState(null);
30
+ const config = useMemo(() => {
31
+ const base = cloneChartConfig(spec.chart_js ?? {});
32
+ return {
33
+ ...base,
34
+ options: mergeThemeOptions(base.options, base.type, isDark)
35
+ };
36
+ }, [spec.chart_js, isDark]);
37
+ useEffect(() => {
38
+ const canvas = canvasRef.current;
39
+ if (!canvas) return;
40
+ chartRef.current?.destroy();
41
+ chartRef.current = null;
42
+ setError(null);
43
+ try {
44
+ chartRef.current = new Chart(canvas, config);
45
+ } catch (e) {
46
+ setError(e instanceof Error ? e.message : String(e));
47
+ }
48
+ return () => {
49
+ chartRef.current?.destroy();
50
+ chartRef.current = null;
51
+ };
52
+ }, [config]);
53
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
54
+ /* @__PURE__ */ jsx(
55
+ "div",
56
+ {
57
+ className: cn("relative w-full", fill && "h-full min-h-80"),
58
+ style: fill ? void 0 : {
59
+ height: DEFAULT_CHART_HEIGHT,
60
+ maxHeight: MAX_INLINE_CHART_HEIGHT
61
+ },
62
+ children: /* @__PURE__ */ jsx("canvas", { ref: canvasRef })
63
+ }
64
+ ),
65
+ error && /* @__PURE__ */ jsxs("p", { className: "text-xs text-destructive", children: [
66
+ "Chart error: ",
67
+ error
68
+ ] }),
69
+ spec.caption && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: spec.caption })
70
+ ] });
71
+ }
72
+ function cloneChartConfig(value) {
73
+ return JSON.parse(JSON.stringify(value));
74
+ }
75
+ function mergeThemeOptions(options, chartType, isDark) {
76
+ const theme = isDark ? DARK_THEME : LIGHT_THEME;
77
+ const base = isRecord(options) ? options : {};
78
+ const plugins = isRecord(base.plugins) ? base.plugins : {};
79
+ const legend = isRecord(plugins.legend) ? plugins.legend : {};
80
+ const legendLabels = isRecord(legend.labels) ? legend.labels : {};
81
+ const title = isRecord(plugins.title) ? plugins.title : {};
82
+ const scales = isRecord(base.scales) ? base.scales : {};
83
+ return {
84
+ responsive: true,
85
+ maintainAspectRatio: false,
86
+ color: theme.text,
87
+ ...base,
88
+ plugins: {
89
+ ...plugins,
90
+ legend: {
91
+ ...legend,
92
+ labels: {
93
+ color: theme.text,
94
+ ...legendLabels
95
+ }
96
+ },
97
+ title: {
98
+ color: theme.title,
99
+ ...title
100
+ }
101
+ },
102
+ scales: mergeScales(scales, chartType, theme)
103
+ };
104
+ }
105
+ function mergeScales(scales, chartType, theme) {
106
+ const names = /* @__PURE__ */ new Set([...defaultScaleNames(chartType), ...Object.keys(scales)]);
107
+ const merged = {};
108
+ for (const name of names) {
109
+ const scale = isRecord(scales[name]) ? scales[name] : {};
110
+ const ticks = isRecord(scale.ticks) ? scale.ticks : {};
111
+ const grid = isRecord(scale.grid) ? scale.grid : {};
112
+ const title = isRecord(scale.title) ? scale.title : {};
113
+ merged[name] = {
114
+ ...scale,
115
+ ticks: {
116
+ color: theme.text,
117
+ ...ticks
118
+ },
119
+ grid: {
120
+ color: theme.grid,
121
+ ...grid
122
+ },
123
+ border: {
124
+ color: theme.border,
125
+ ...isRecord(scale.border) ? scale.border : {}
126
+ },
127
+ title: {
128
+ color: theme.title,
129
+ ...title
130
+ }
131
+ };
132
+ }
133
+ return merged;
134
+ }
135
+ function defaultScaleNames(chartType) {
136
+ if (chartType === "bar" || chartType === "line" || chartType === "scatter") {
137
+ return ["x", "y"];
138
+ }
139
+ if (chartType === "bubble") {
140
+ return ["x", "y"];
141
+ }
142
+ if (chartType === "radar" || chartType === "polarArea") {
143
+ return ["r"];
144
+ }
145
+ return [];
146
+ }
147
+ function isRecord(value) {
148
+ return typeof value === "object" && value !== null && !Array.isArray(value);
149
+ }
150
+
151
+ export { ArtifactChart };
152
+ //# sourceMappingURL=artifact-chart-X53FKRDZ.js.map
153
+ //# sourceMappingURL=artifact-chart-X53FKRDZ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/chat/artifacts/artifact-chart.tsx"],"names":["ChartJS"],"mappings":"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","file":"artifact-chart-X53FKRDZ.js","sourcesContent":["// Copyright (c) 2026, Invergent SA, developed by Flavius Burca\n// SPDX-License-Identifier: AGPL-3.0-only\n//\n// Chart artifact renderer — embeds a Chart.js configuration object.\n// Respects the app's dark/light theme and resizes with the thread column.\n\nimport { useEffect, useMemo, useRef, useState } from \"react\";\nimport { Chart as ChartJS } from \"chart.js/auto\";\nimport type { ChartConfiguration, ChartOptions } from \"chart.js\";\nimport { useTheme } from \"next-themes\";\nimport { cn } from \"../../../lib/utils\";\nimport type { ChartArtifactSpec } from \"../../../types\";\n\nconst DEFAULT_CHART_HEIGHT = 320;\nconst MAX_INLINE_CHART_HEIGHT = 800;\n\nconst LIGHT_THEME = {\n text: \"#444\",\n title: \"#111\",\n grid: \"#e5e7eb\",\n border: \"#d1d5db\",\n};\n\nconst DARK_THEME = {\n text: \"#cbd5e1\",\n title: \"#f1f5f9\",\n grid: \"#374151\",\n border: \"#4b5563\",\n};\n\nexport function ArtifactChart({\n spec,\n fill = false,\n}: {\n spec: ChartArtifactSpec;\n fill?: boolean;\n}) {\n const { resolvedTheme } = useTheme();\n const isDark = resolvedTheme === \"dark\";\n const canvasRef = useRef<HTMLCanvasElement>(null);\n const chartRef = useRef<ChartJS | null>(null);\n const [error, setError] = useState<string | null>(null);\n\n const config = useMemo<ChartConfiguration>(() => {\n const base = cloneChartConfig(spec.chart_js ?? {});\n return {\n ...base,\n options: mergeThemeOptions(base.options, base.type, isDark),\n } as ChartConfiguration;\n }, [spec.chart_js, isDark]);\n\n useEffect(() => {\n const canvas = canvasRef.current;\n if (!canvas) return;\n\n chartRef.current?.destroy();\n chartRef.current = null;\n setError(null);\n\n try {\n chartRef.current = new ChartJS(canvas, config);\n } catch (e) {\n setError(e instanceof Error ? e.message : String(e));\n }\n\n return () => {\n chartRef.current?.destroy();\n chartRef.current = null;\n };\n }, [config]);\n\n return (\n <div className=\"flex flex-col gap-2\">\n <div\n className={cn(\"relative w-full\", fill && \"h-full min-h-80\")}\n style={\n fill\n ? undefined\n : {\n height: DEFAULT_CHART_HEIGHT,\n maxHeight: MAX_INLINE_CHART_HEIGHT,\n }\n }\n >\n <canvas ref={canvasRef} />\n </div>\n {error && (\n <p className=\"text-xs text-destructive\">Chart error: {error}</p>\n )}\n {spec.caption && (\n <p className=\"text-xs text-muted-foreground\">{spec.caption}</p>\n )}\n </div>\n );\n}\n\nfunction cloneChartConfig(\n value: Record<string, unknown>,\n): Record<string, unknown> {\n return JSON.parse(JSON.stringify(value)) as Record<string, unknown>;\n}\n\nfunction mergeThemeOptions(\n options: unknown,\n chartType: unknown,\n isDark: boolean,\n): ChartOptions {\n const theme = isDark ? DARK_THEME : LIGHT_THEME;\n const base = isRecord(options) ? options : {};\n const plugins = isRecord(base.plugins) ? base.plugins : {};\n const legend = isRecord(plugins.legend) ? plugins.legend : {};\n const legendLabels = isRecord(legend.labels) ? legend.labels : {};\n const title = isRecord(plugins.title) ? plugins.title : {};\n const scales = isRecord(base.scales) ? base.scales : {};\n\n return {\n responsive: true,\n maintainAspectRatio: false,\n color: theme.text,\n ...base,\n plugins: {\n ...plugins,\n legend: {\n ...legend,\n labels: {\n color: theme.text,\n ...legendLabels,\n },\n },\n title: {\n color: theme.title,\n ...title,\n },\n },\n scales: mergeScales(scales, chartType, theme),\n } as ChartOptions;\n}\n\nfunction mergeScales(\n scales: Record<string, unknown>,\n chartType: unknown,\n theme: typeof LIGHT_THEME,\n): Record<string, unknown> {\n const names = new Set([...defaultScaleNames(chartType), ...Object.keys(scales)]);\n const merged: Record<string, unknown> = {};\n\n for (const name of names) {\n const scale = isRecord(scales[name]) ? scales[name] : {};\n const ticks = isRecord(scale.ticks) ? scale.ticks : {};\n const grid = isRecord(scale.grid) ? scale.grid : {};\n const title = isRecord(scale.title) ? scale.title : {};\n merged[name] = {\n ...scale,\n ticks: {\n color: theme.text,\n ...ticks,\n },\n grid: {\n color: theme.grid,\n ...grid,\n },\n border: {\n color: theme.border,\n ...(isRecord(scale.border) ? scale.border : {}),\n },\n title: {\n color: theme.title,\n ...title,\n },\n };\n }\n\n return merged;\n}\n\nfunction defaultScaleNames(chartType: unknown): string[] {\n if (chartType === \"bar\" || chartType === \"line\" || chartType === \"scatter\") {\n return [\"x\", \"y\"];\n }\n if (chartType === \"bubble\") {\n return [\"x\", \"y\"];\n }\n if (chartType === \"radar\" || chartType === \"polarArea\") {\n return [\"r\"];\n }\n return [];\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n"]}
@@ -0,0 +1,11 @@
1
+ import { clsx } from 'clsx';
2
+ import { twMerge } from 'tailwind-merge';
3
+
4
+ // src/lib/utils.ts
5
+ function cn(...inputs) {
6
+ return twMerge(clsx(inputs));
7
+ }
8
+
9
+ export { cn };
10
+ //# sourceMappingURL=chunk-QSC4UIVT.js.map
11
+ //# sourceMappingURL=chunk-QSC4UIVT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/utils.ts"],"names":[],"mappings":";;;;AAGO,SAAS,MAAM,MAAA,EAAsB;AAC1C,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC7B","file":"chunk-QSC4UIVT.js","sourcesContent":["import { clsx, type ClassValue } from \"clsx\"\nimport { twMerge } from \"tailwind-merge\"\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n"]}