csv-charts-ai 0.1.0

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/README.md ADDED
@@ -0,0 +1,94 @@
1
+ # csv-charts-ai
2
+
3
+ Reusable chart components for CSV/tabular data visualization, powered by [Recharts](https://recharts.org/).
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install csv-charts-ai
9
+ # or
10
+ pnpm add csv-charts-ai
11
+ ```
12
+
13
+ ### Peer Dependencies
14
+
15
+ ```bash
16
+ npm install react recharts lucide-react
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ ```tsx
22
+ import { ChartDisplay, type TabularData, type ChartConfig } from "csv-charts-ai";
23
+
24
+ const data: TabularData = {
25
+ headers: ["Category", "Sales"],
26
+ rows: [
27
+ ["Electronics", "1200"],
28
+ ["Clothing", "800"],
29
+ ["Food", "950"],
30
+ ],
31
+ columns: [
32
+ { name: "Category", type: "string", index: 0 },
33
+ { name: "Sales", type: "number", index: 1 },
34
+ ],
35
+ rowCount: 3,
36
+ };
37
+
38
+ const charts: ChartConfig[] = [
39
+ {
40
+ id: "chart-1",
41
+ type: "bar",
42
+ title: "Sales by Category",
43
+ description: "Comparison of sales across categories",
44
+ xAxis: "Category",
45
+ yAxis: "Sales",
46
+ aggregation: "sum",
47
+ },
48
+ ];
49
+
50
+ function App() {
51
+ return <ChartDisplay data={data} charts={charts} />;
52
+ }
53
+ ```
54
+
55
+ ## Components
56
+
57
+ ### `ChartDisplay`
58
+
59
+ Main component that renders multiple charts from configuration.
60
+
61
+ | Prop | Type | Description |
62
+ |------|------|-------------|
63
+ | `data` | `TabularData` | The tabular data to visualize |
64
+ | `charts` | `ChartConfig[]` | Array of chart configurations |
65
+ | `onRegenerate` | `(chart: ChartConfig) => Promise<void>` | Optional callback for chart regeneration |
66
+ | `cardWrapper` | `React.ComponentType` | Optional wrapper component for each chart card |
67
+
68
+ ### `SingleChart`
69
+
70
+ Renders an individual chart with toolbar controls (sort, zoom, trendline, export).
71
+
72
+ ### `ChartToolbar`
73
+
74
+ Toolbar component with sort, filter, zoom, trendline, and export controls.
75
+
76
+ ### `processChartData`
77
+
78
+ Utility function that processes `TabularData` into chart-ready data points with aggregation support.
79
+
80
+ ## Chart Types
81
+
82
+ - **bar** — Category comparisons
83
+ - **line** — Trends over time
84
+ - **area** — Cumulative trends
85
+ - **scatter** — Correlations
86
+ - **pie** — Proportional distributions
87
+
88
+ ## Aggregation Types
89
+
90
+ `sum` | `avg` | `count` | `min` | `max` | `none`
91
+
92
+ ## License
93
+
94
+ MIT
@@ -0,0 +1,107 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ type ChartType = "bar" | "line" | "pie" | "scatter" | "area";
4
+ type AggregationType = "sum" | "avg" | "count" | "min" | "max" | "none";
5
+ interface TabularData {
6
+ headers: string[];
7
+ rows: string[][];
8
+ columns: {
9
+ name: string;
10
+ type: "string" | "number" | "date" | "boolean";
11
+ index: number;
12
+ }[];
13
+ rowCount: number;
14
+ }
15
+ interface ChartConfig {
16
+ id: string;
17
+ type: ChartType;
18
+ title: string;
19
+ description: string;
20
+ xAxis: string;
21
+ yAxis: string;
22
+ groupBy?: string;
23
+ aggregation: AggregationType;
24
+ dataConfig?: {
25
+ xColumn: string;
26
+ yColumn: string;
27
+ groupColumn?: string;
28
+ };
29
+ }
30
+ type SortOrder = "none" | "asc" | "desc";
31
+ type ChartDataPoint = Record<string, string | number>;
32
+ interface ChartTheme {
33
+ colors: string[];
34
+ background: string;
35
+ cardBackground: string;
36
+ border: string;
37
+ text: string;
38
+ textMuted: string;
39
+ textDimmed: string;
40
+ gridStroke: string;
41
+ tooltipBackground: string;
42
+ tooltipBorder: string;
43
+ accentPrimary: string;
44
+ accentSecondary: string;
45
+ accentSuccess: string;
46
+ accentDanger: string;
47
+ }
48
+ declare const defaultDarkTheme: ChartTheme;
49
+ declare const defaultLightTheme: ChartTheme;
50
+
51
+ interface ChartDisplayProps {
52
+ data: TabularData;
53
+ charts: ChartConfig[];
54
+ onRegenerate?: (chart: ChartConfig) => Promise<void>;
55
+ /** Optional wrapper component for each chart card */
56
+ cardWrapper?: React.ComponentType<{
57
+ children: React.ReactNode;
58
+ title?: string;
59
+ className?: string;
60
+ }>;
61
+ /** Optional theme override */
62
+ theme?: ChartTheme;
63
+ }
64
+ declare function ChartDisplay({ data, charts, onRegenerate, cardWrapper: CardWrapper, theme, }: ChartDisplayProps): react_jsx_runtime.JSX.Element | null;
65
+
66
+ interface SingleChartProps {
67
+ data: TabularData;
68
+ chart: ChartConfig;
69
+ onRegenerate?: (chart: ChartConfig) => Promise<void>;
70
+ }
71
+ declare function SingleChart({ data, chart, onRegenerate }: SingleChartProps): react_jsx_runtime.JSX.Element;
72
+
73
+ interface ChartToolbarProps {
74
+ chartType: ChartType;
75
+ sortOrder: SortOrder;
76
+ limitResults: number;
77
+ showBrush: boolean;
78
+ showTrendline: boolean;
79
+ isRegenerating: boolean;
80
+ hasRegenerate: boolean;
81
+ onToggleSort: () => void;
82
+ onLimitChange: (limit: number) => void;
83
+ onToggleBrush: () => void;
84
+ onToggleTrendline: () => void;
85
+ onExportCSV: () => void;
86
+ onExportPNG: () => void;
87
+ onRegenerate: () => void;
88
+ }
89
+ declare function ChartToolbar({ chartType, sortOrder, limitResults, showBrush, showTrendline, isRegenerating, hasRegenerate, onToggleSort, onLimitChange, onToggleBrush, onToggleTrendline, onExportCSV, onExportPNG, onRegenerate, }: ChartToolbarProps): react_jsx_runtime.JSX.Element;
90
+
91
+ interface ProcessedChartResult {
92
+ data: ChartDataPoint[];
93
+ seriesKeys: string[];
94
+ yKey: string;
95
+ }
96
+ declare const processChartData: (data: TabularData, chart: ChartConfig, sortOrder?: SortOrder, limit?: number) => ChartDataPoint[];
97
+ declare const processChartDataMultiSeries: (data: TabularData, chart: ChartConfig, sortOrder?: SortOrder, limit?: number) => ProcessedChartResult;
98
+
99
+ declare function ChartThemeProvider({ theme, children, }: {
100
+ theme: ChartTheme;
101
+ children: React.ReactNode;
102
+ }): react_jsx_runtime.JSX.Element;
103
+ declare function useChartTheme(): ChartTheme;
104
+
105
+ declare const COLORS: string[];
106
+
107
+ export { type AggregationType, COLORS, type ChartConfig, type ChartDataPoint, ChartDisplay, type ChartDisplayProps, type ChartTheme, ChartThemeProvider, ChartToolbar, type ChartType, type ProcessedChartResult, SingleChart, type SingleChartProps, type SortOrder, type TabularData, defaultDarkTheme, defaultLightTheme, processChartData, processChartDataMultiSeries, useChartTheme };
package/dist/index.js ADDED
@@ -0,0 +1,949 @@
1
+ // src/types.ts
2
+ var defaultDarkTheme = {
3
+ colors: [
4
+ "#8b5cf6",
5
+ "#06b6d4",
6
+ "#f43f5e",
7
+ "#eab308",
8
+ "#10b981",
9
+ "#3b82f6",
10
+ "#d946ef",
11
+ "#f97316"
12
+ ],
13
+ background: "rgba(15, 23, 42, 0.5)",
14
+ cardBackground: "rgba(255, 255, 255, 0.05)",
15
+ border: "rgba(255, 255, 255, 0.1)",
16
+ text: "#ffffff",
17
+ textMuted: "#9ca3af",
18
+ textDimmed: "#6b7280",
19
+ gridStroke: "#374151",
20
+ tooltipBackground: "#1f2937",
21
+ tooltipBorder: "1px solid #374151",
22
+ accentPrimary: "#8b5cf6",
23
+ accentSecondary: "#06b6d4",
24
+ accentSuccess: "#10b981",
25
+ accentDanger: "#ef4444"
26
+ };
27
+ var defaultLightTheme = {
28
+ colors: [
29
+ "#7c3aed",
30
+ "#0891b2",
31
+ "#e11d48",
32
+ "#ca8a04",
33
+ "#059669",
34
+ "#2563eb",
35
+ "#c026d3",
36
+ "#ea580c"
37
+ ],
38
+ background: "#ffffff",
39
+ cardBackground: "#f8fafc",
40
+ border: "#e2e8f0",
41
+ text: "#0f172a",
42
+ textMuted: "#64748b",
43
+ textDimmed: "#94a3b8",
44
+ gridStroke: "#e2e8f0",
45
+ tooltipBackground: "#ffffff",
46
+ tooltipBorder: "1px solid #e2e8f0",
47
+ accentPrimary: "#7c3aed",
48
+ accentSecondary: "#0891b2",
49
+ accentSuccess: "#059669",
50
+ accentDanger: "#dc2626"
51
+ };
52
+
53
+ // src/ThemeContext.tsx
54
+ import { createContext, useContext } from "react";
55
+ import { jsx } from "react/jsx-runtime";
56
+ var ChartThemeContext = createContext(defaultDarkTheme);
57
+ function ChartThemeProvider({
58
+ theme,
59
+ children
60
+ }) {
61
+ return /* @__PURE__ */ jsx(ChartThemeContext.Provider, { value: theme, children });
62
+ }
63
+ function useChartTheme() {
64
+ return useContext(ChartThemeContext);
65
+ }
66
+
67
+ // src/SingleChart.tsx
68
+ import { useState, useMemo, useCallback, useRef } from "react";
69
+ import {
70
+ BarChart,
71
+ Bar,
72
+ LineChart,
73
+ Line,
74
+ PieChart,
75
+ Pie,
76
+ ScatterChart,
77
+ Scatter,
78
+ AreaChart,
79
+ Area,
80
+ XAxis,
81
+ YAxis,
82
+ CartesianGrid,
83
+ Tooltip,
84
+ Legend,
85
+ ResponsiveContainer,
86
+ Cell,
87
+ Brush,
88
+ ReferenceLine
89
+ } from "recharts";
90
+ import { RefreshCw as RefreshCw2 } from "lucide-react";
91
+
92
+ // src/processChartData.ts
93
+ var processChartData = (data, chart, sortOrder = "none", limit = 20) => {
94
+ const result = processChartDataMultiSeries(data, chart, sortOrder, limit);
95
+ return result.data;
96
+ };
97
+ var processChartDataMultiSeries = (data, chart, sortOrder = "none", limit = 20) => {
98
+ let result = [];
99
+ const xColDef = data.columns.find(
100
+ (c) => c.name.toLowerCase() === chart.xAxis.toLowerCase()
101
+ );
102
+ const yColDef = data.columns.find(
103
+ (c) => c.name.toLowerCase() === chart.yAxis.toLowerCase()
104
+ );
105
+ const groupColDef = chart.groupBy ? data.columns.find(
106
+ (c) => c.name.toLowerCase() === chart.groupBy.toLowerCase()
107
+ ) : void 0;
108
+ if (!xColDef) return { data: [], seriesKeys: [], yKey: chart.yAxis };
109
+ const xCol = xColDef.name;
110
+ const xIdx = xColDef.index;
111
+ const isCountMode = chart.aggregation === "count";
112
+ const yCol = yColDef?.name ?? "count";
113
+ const yIdx = yColDef?.index ?? -1;
114
+ const supportsGroupBy = chart.type !== "pie" && chart.type !== "scatter";
115
+ if (supportsGroupBy && groupColDef && chart.aggregation && chart.aggregation !== "none") {
116
+ const groupIdx = groupColDef.index;
117
+ const allGroups = /* @__PURE__ */ new Set();
118
+ const grouped = /* @__PURE__ */ new Map();
119
+ data.rows.forEach((row) => {
120
+ const xVal = String(row[xIdx] ?? "").trim();
121
+ const groupVal = String(row[groupIdx] ?? "").trim();
122
+ if (!xVal || !groupVal) return;
123
+ allGroups.add(groupVal);
124
+ if (!grouped.has(xVal)) grouped.set(xVal, /* @__PURE__ */ new Map());
125
+ const xGroup = grouped.get(xVal);
126
+ if (isCountMode) {
127
+ const current = xGroup.get(groupVal) ?? {
128
+ sum: 0,
129
+ count: 0,
130
+ min: 0,
131
+ max: 0
132
+ };
133
+ xGroup.set(groupVal, { ...current, count: current.count + 1 });
134
+ } else if (yIdx >= 0) {
135
+ const yVal = parseFloat(String(row[yIdx] ?? "0"));
136
+ if (!isNaN(yVal)) {
137
+ const current = xGroup.get(groupVal) ?? {
138
+ sum: 0,
139
+ count: 0,
140
+ min: Infinity,
141
+ max: -Infinity
142
+ };
143
+ xGroup.set(groupVal, {
144
+ sum: current.sum + yVal,
145
+ count: current.count + 1,
146
+ min: Math.min(current.min, yVal),
147
+ max: Math.max(current.max, yVal)
148
+ });
149
+ }
150
+ }
151
+ });
152
+ const seriesKeys = Array.from(allGroups).slice(0, 8);
153
+ grouped.forEach((groupMap, xKey) => {
154
+ const point = { [xCol]: xKey };
155
+ seriesKeys.forEach((groupKey) => {
156
+ const stats = groupMap.get(groupKey);
157
+ if (stats) {
158
+ let value;
159
+ switch (chart.aggregation) {
160
+ case "sum":
161
+ value = stats.sum;
162
+ break;
163
+ case "avg":
164
+ value = stats.count > 0 ? stats.sum / stats.count : 0;
165
+ break;
166
+ case "count":
167
+ value = stats.count;
168
+ break;
169
+ case "min":
170
+ value = stats.min === Infinity ? 0 : stats.min;
171
+ break;
172
+ case "max":
173
+ value = stats.max === -Infinity ? 0 : stats.max;
174
+ break;
175
+ default:
176
+ value = stats.sum;
177
+ }
178
+ point[groupKey] = Math.round(value * 100) / 100;
179
+ } else {
180
+ point[groupKey] = 0;
181
+ }
182
+ });
183
+ result.push(point);
184
+ });
185
+ if (sortOrder !== "none" && seriesKeys[0]) {
186
+ const firstKey = seriesKeys[0];
187
+ result.sort((a, b) => {
188
+ const aVal = a[firstKey] ?? 0;
189
+ const bVal = b[firstKey] ?? 0;
190
+ return sortOrder === "desc" ? bVal - aVal : aVal - bVal;
191
+ });
192
+ }
193
+ return {
194
+ data: result.slice(0, limit),
195
+ seriesKeys,
196
+ yKey: yCol
197
+ };
198
+ }
199
+ if (chart.aggregation && chart.aggregation !== "none") {
200
+ const groups = /* @__PURE__ */ new Map();
201
+ data.rows.forEach((row) => {
202
+ const xVal = String(row[xIdx] ?? "").trim();
203
+ if (!xVal) return;
204
+ if (isCountMode) {
205
+ const current = groups.get(xVal) ?? {
206
+ sum: 0,
207
+ count: 0,
208
+ min: 0,
209
+ max: 0
210
+ };
211
+ groups.set(xVal, {
212
+ ...current,
213
+ count: current.count + 1
214
+ });
215
+ } else if (yIdx >= 0) {
216
+ const yVal = parseFloat(String(row[yIdx] ?? "0"));
217
+ if (!isNaN(yVal)) {
218
+ const current = groups.get(xVal) ?? {
219
+ sum: 0,
220
+ count: 0,
221
+ min: Infinity,
222
+ max: -Infinity
223
+ };
224
+ groups.set(xVal, {
225
+ sum: current.sum + yVal,
226
+ count: current.count + 1,
227
+ min: Math.min(current.min, yVal),
228
+ max: Math.max(current.max, yVal)
229
+ });
230
+ }
231
+ }
232
+ });
233
+ groups.forEach((stats, key) => {
234
+ let value;
235
+ switch (chart.aggregation) {
236
+ case "sum":
237
+ value = stats.sum;
238
+ break;
239
+ case "avg":
240
+ value = stats.count > 0 ? stats.sum / stats.count : 0;
241
+ break;
242
+ case "count":
243
+ value = stats.count;
244
+ break;
245
+ case "min":
246
+ value = stats.min === Infinity ? 0 : stats.min;
247
+ break;
248
+ case "max":
249
+ value = stats.max === -Infinity ? 0 : stats.max;
250
+ break;
251
+ default:
252
+ value = stats.sum;
253
+ }
254
+ result.push({ [xCol]: key, [yCol]: Math.round(value * 100) / 100 });
255
+ });
256
+ } else if (yIdx >= 0) {
257
+ result = data.rows.slice(0, Math.min(limit * 2, data.rows.length)).map((row) => ({
258
+ [xCol]: row[xIdx] ?? "",
259
+ [yCol]: parseFloat(String(row[yIdx] ?? "0"))
260
+ })).filter((item) => !isNaN(item[yCol]));
261
+ }
262
+ if (sortOrder !== "none") {
263
+ result.sort((a, b) => {
264
+ const aVal = a[yCol];
265
+ const bVal = b[yCol];
266
+ return sortOrder === "desc" ? bVal - aVal : aVal - bVal;
267
+ });
268
+ }
269
+ return {
270
+ data: result.slice(0, limit),
271
+ seriesKeys: [],
272
+ yKey: yCol
273
+ };
274
+ };
275
+
276
+ // src/ChartToolbar.tsx
277
+ import {
278
+ RefreshCw,
279
+ Download,
280
+ SortAsc,
281
+ SortDesc,
282
+ RotateCcw,
283
+ TrendingUp,
284
+ Filter,
285
+ Image as Image2
286
+ } from "lucide-react";
287
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
288
+ function ChartToolbar({
289
+ chartType,
290
+ sortOrder,
291
+ limitResults,
292
+ showBrush,
293
+ showTrendline,
294
+ isRegenerating,
295
+ hasRegenerate,
296
+ onToggleSort,
297
+ onLimitChange,
298
+ onToggleBrush,
299
+ onToggleTrendline,
300
+ onExportCSV,
301
+ onExportPNG,
302
+ onRegenerate
303
+ }) {
304
+ const supportsBrush = chartType === "line" || chartType === "area" || chartType === "bar";
305
+ const supportsTrendline = chartType === "bar" || chartType === "line" || chartType === "area";
306
+ return /* @__PURE__ */ jsxs("div", { className: "mb-4 flex flex-wrap items-center gap-2 rounded-xl border border-white/10 bg-white/5 p-3", children: [
307
+ /* @__PURE__ */ jsxs(
308
+ "button",
309
+ {
310
+ onClick: onToggleSort,
311
+ className: `flex items-center gap-1.5 rounded-lg px-3 py-1.5 text-sm transition-colors ${sortOrder !== "none" ? "border border-violet-500/30 bg-violet-500/20 text-violet-400" : "bg-white/5 text-gray-400 hover:bg-white/10 hover:text-white"}`,
312
+ title: "Sort by value",
313
+ children: [
314
+ sortOrder === "asc" ? /* @__PURE__ */ jsx2(SortAsc, { className: "h-4 w-4" }) : sortOrder === "desc" ? /* @__PURE__ */ jsx2(SortDesc, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx2(SortDesc, { className: "h-4 w-4 opacity-50" }),
315
+ "Sort"
316
+ ]
317
+ }
318
+ ),
319
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
320
+ /* @__PURE__ */ jsx2(Filter, { className: "h-4 w-4 text-gray-400" }),
321
+ /* @__PURE__ */ jsxs(
322
+ "select",
323
+ {
324
+ value: limitResults,
325
+ onChange: (e) => onLimitChange(Number(e.target.value)),
326
+ className: "rounded-lg border border-white/10 bg-white/5 px-2 py-1.5 text-sm text-gray-300 focus:border-violet-500/50 focus:outline-none",
327
+ children: [
328
+ /* @__PURE__ */ jsx2("option", { value: 10, children: "Top 10" }),
329
+ /* @__PURE__ */ jsx2("option", { value: 20, children: "Top 20" }),
330
+ /* @__PURE__ */ jsx2("option", { value: 50, children: "Top 50" }),
331
+ /* @__PURE__ */ jsx2("option", { value: 100, children: "Top 100" }),
332
+ /* @__PURE__ */ jsx2("option", { value: 999999, children: "All" })
333
+ ]
334
+ }
335
+ )
336
+ ] }),
337
+ supportsBrush && /* @__PURE__ */ jsxs(
338
+ "button",
339
+ {
340
+ onClick: onToggleBrush,
341
+ className: `flex items-center gap-1.5 rounded-lg px-3 py-1.5 text-sm transition-colors ${showBrush ? "border border-cyan-500/30 bg-cyan-500/20 text-cyan-400" : "bg-white/5 text-gray-400 hover:bg-white/10 hover:text-white"}`,
342
+ title: "Enable zoom/brush",
343
+ children: [
344
+ /* @__PURE__ */ jsx2(RotateCcw, { className: "h-4 w-4" }),
345
+ "Zoom"
346
+ ]
347
+ }
348
+ ),
349
+ supportsTrendline && /* @__PURE__ */ jsxs(
350
+ "button",
351
+ {
352
+ onClick: onToggleTrendline,
353
+ className: `flex items-center gap-1.5 rounded-lg px-3 py-1.5 text-sm transition-colors ${showTrendline ? "border border-emerald-500/30 bg-emerald-500/20 text-emerald-400" : "bg-white/5 text-gray-400 hover:bg-white/10 hover:text-white"}`,
354
+ title: "Show average line",
355
+ children: [
356
+ /* @__PURE__ */ jsx2(TrendingUp, { className: "h-4 w-4" }),
357
+ "Average"
358
+ ]
359
+ }
360
+ ),
361
+ /* @__PURE__ */ jsx2("div", { className: "flex-1" }),
362
+ /* @__PURE__ */ jsxs(
363
+ "button",
364
+ {
365
+ onClick: onExportPNG,
366
+ className: "flex items-center gap-1.5 rounded-lg bg-white/5 px-3 py-1.5 text-sm text-gray-400 transition-colors hover:bg-white/10 hover:text-white",
367
+ title: "Export chart as PNG image",
368
+ children: [
369
+ /* @__PURE__ */ jsx2(Image2, { className: "h-4 w-4" }),
370
+ "PNG"
371
+ ]
372
+ }
373
+ ),
374
+ /* @__PURE__ */ jsxs(
375
+ "button",
376
+ {
377
+ onClick: onExportCSV,
378
+ className: "flex items-center gap-1.5 rounded-lg bg-white/5 px-3 py-1.5 text-sm text-gray-400 transition-colors hover:bg-white/10 hover:text-white",
379
+ title: "Export chart data as CSV",
380
+ children: [
381
+ /* @__PURE__ */ jsx2(Download, { className: "h-4 w-4" }),
382
+ "CSV"
383
+ ]
384
+ }
385
+ ),
386
+ hasRegenerate && /* @__PURE__ */ jsxs(
387
+ "button",
388
+ {
389
+ onClick: onRegenerate,
390
+ disabled: isRegenerating,
391
+ className: "flex items-center gap-1.5 rounded-lg bg-violet-500/20 px-3 py-1.5 text-sm text-violet-400 transition-colors hover:bg-violet-500/30 disabled:opacity-50",
392
+ children: [
393
+ /* @__PURE__ */ jsx2(
394
+ RefreshCw,
395
+ {
396
+ className: `h-4 w-4 ${isRegenerating ? "animate-spin" : ""}`
397
+ }
398
+ ),
399
+ isRegenerating ? "..." : "Regenerate"
400
+ ]
401
+ }
402
+ )
403
+ ] });
404
+ }
405
+
406
+ // src/SingleChart.tsx
407
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
408
+ function SingleChart({ data, chart, onRegenerate }) {
409
+ const theme = useChartTheme();
410
+ const chartContainerRef = useRef(null);
411
+ const [isRegenerating, setIsRegenerating] = useState(false);
412
+ const [sortOrder, setSortOrder] = useState("none");
413
+ const [showBrush, setShowBrush] = useState(false);
414
+ const [showTrendline, setShowTrendline] = useState(false);
415
+ const [limitResults, setLimitResults] = useState(20);
416
+ const processed = useMemo(
417
+ () => processChartDataMultiSeries(data, chart, sortOrder, limitResults),
418
+ [data, chart, sortOrder, limitResults]
419
+ );
420
+ const { data: processedData, seriesKeys } = processed;
421
+ const isMultiSeries = seriesKeys.length > 0;
422
+ const xColName = data.columns.find(
423
+ (col) => col.name === chart.xAxis || col.name.toLowerCase() === chart.xAxis.toLowerCase()
424
+ )?.name ?? chart.xAxis;
425
+ const yColName = data.columns.find(
426
+ (col) => col.name === chart.yAxis || col.name.toLowerCase() === chart.yAxis.toLowerCase()
427
+ )?.name ?? chart.yAxis;
428
+ const average = useMemo(() => {
429
+ if (processedData.length === 0 || isMultiSeries) return 0;
430
+ const sum = processedData.reduce((acc, item) => {
431
+ const val = item[yColName];
432
+ return acc + (typeof val === "number" ? val : 0);
433
+ }, 0);
434
+ return sum / processedData.length;
435
+ }, [processedData, yColName, isMultiSeries]);
436
+ const handleRegenerate = async () => {
437
+ if (!onRegenerate) return;
438
+ setIsRegenerating(true);
439
+ try {
440
+ await onRegenerate(chart);
441
+ } finally {
442
+ setIsRegenerating(false);
443
+ }
444
+ };
445
+ const handleExportCSV = useCallback(() => {
446
+ if (processedData.length === 0) return;
447
+ const headers = Object.keys(processedData[0] ?? {}).join(",");
448
+ const rows = processedData.map(
449
+ (row) => Object.values(row).map((v) => `"${String(v)}"`).join(",")
450
+ ).join("\n");
451
+ const csv = `${headers}
452
+ ${rows}`;
453
+ const blob = new Blob([csv], { type: "text/csv" });
454
+ const url = URL.createObjectURL(blob);
455
+ const a = document.createElement("a");
456
+ a.href = url;
457
+ a.download = `${chart.title.replace(/\s+/g, "_")}.csv`;
458
+ a.click();
459
+ URL.revokeObjectURL(url);
460
+ }, [processedData, chart.title]);
461
+ const handleExportPNG = useCallback(() => {
462
+ const container = chartContainerRef.current;
463
+ if (!container) return;
464
+ const svgElement = container.querySelector("svg");
465
+ if (!svgElement) return;
466
+ const svgClone = svgElement.cloneNode(true);
467
+ svgClone.setAttribute(
468
+ "xmlns",
469
+ "http://www.w3.org/2000/svg"
470
+ );
471
+ const rect = svgElement.getBoundingClientRect();
472
+ svgClone.setAttribute("width", String(rect.width));
473
+ svgClone.setAttribute("height", String(rect.height));
474
+ const svgString = new XMLSerializer().serializeToString(svgClone);
475
+ const svgBlob = new Blob([svgString], {
476
+ type: "image/svg+xml;charset=utf-8"
477
+ });
478
+ const url = URL.createObjectURL(svgBlob);
479
+ const img = new Image();
480
+ img.onload = () => {
481
+ const canvas = document.createElement("canvas");
482
+ const scale = 2;
483
+ canvas.width = rect.width * scale;
484
+ canvas.height = rect.height * scale;
485
+ const ctx = canvas.getContext("2d");
486
+ if (!ctx) return;
487
+ ctx.scale(scale, scale);
488
+ ctx.fillStyle = theme.tooltipBackground;
489
+ ctx.fillRect(0, 0, rect.width, rect.height);
490
+ ctx.drawImage(img, 0, 0, rect.width, rect.height);
491
+ canvas.toBlob((blob) => {
492
+ if (!blob) return;
493
+ const pngUrl = URL.createObjectURL(blob);
494
+ const a = document.createElement("a");
495
+ a.href = pngUrl;
496
+ a.download = `${chart.title.replace(/\s+/g, "_")}.png`;
497
+ a.click();
498
+ URL.revokeObjectURL(pngUrl);
499
+ }, "image/png");
500
+ URL.revokeObjectURL(url);
501
+ };
502
+ img.src = url;
503
+ }, [chart.title, theme.tooltipBackground]);
504
+ const toggleSort = () => {
505
+ setSortOrder((prev) => {
506
+ if (prev === "none") return "desc";
507
+ if (prev === "desc") return "asc";
508
+ return "none";
509
+ });
510
+ };
511
+ const renderChartContent = (type, chartData, xKey, yKey, enableBrush, enableTrendline, avgValue) => {
512
+ const commonProps = {
513
+ data: chartData,
514
+ margin: { top: 20, right: 30, left: 20, bottom: 50 }
515
+ };
516
+ const tooltipStyle = {
517
+ backgroundColor: theme.tooltipBackground,
518
+ border: theme.tooltipBorder,
519
+ borderRadius: "8px"
520
+ };
521
+ const brushComponent = enableBrush ? /* @__PURE__ */ jsx3(
522
+ Brush,
523
+ {
524
+ dataKey: xKey,
525
+ height: 30,
526
+ stroke: theme.accentPrimary,
527
+ fill: "#1e1b4b"
528
+ }
529
+ ) : null;
530
+ const trendlineComponent = enableTrendline && avgValue && !isMultiSeries ? /* @__PURE__ */ jsx3(
531
+ ReferenceLine,
532
+ {
533
+ y: avgValue,
534
+ stroke: theme.accentSuccess,
535
+ strokeDasharray: "5 5",
536
+ label: {
537
+ value: `Avg: ${avgValue.toFixed(2)}`,
538
+ fill: theme.accentSuccess,
539
+ fontSize: 12
540
+ }
541
+ }
542
+ ) : null;
543
+ const xAxisProps = {
544
+ dataKey: xKey,
545
+ stroke: theme.textMuted,
546
+ fontSize: 11,
547
+ angle: -45,
548
+ textAnchor: "end",
549
+ height: 80,
550
+ interval: 0,
551
+ tick: { fill: theme.textMuted }
552
+ };
553
+ const yAxisProps = {
554
+ stroke: theme.textMuted,
555
+ fontSize: 12,
556
+ tick: { fill: theme.textMuted }
557
+ };
558
+ if (isMultiSeries && type !== "pie" && type !== "scatter") {
559
+ switch (type) {
560
+ case "bar":
561
+ return /* @__PURE__ */ jsxs2(BarChart, { ...commonProps, children: [
562
+ /* @__PURE__ */ jsx3(CartesianGrid, { strokeDasharray: "3 3", stroke: theme.gridStroke }),
563
+ /* @__PURE__ */ jsx3(XAxis, { ...xAxisProps }),
564
+ /* @__PURE__ */ jsx3(YAxis, { ...yAxisProps }),
565
+ /* @__PURE__ */ jsx3(Tooltip, { contentStyle: tooltipStyle }),
566
+ /* @__PURE__ */ jsx3(Legend, {}),
567
+ seriesKeys.map((key, i) => /* @__PURE__ */ jsx3(
568
+ Bar,
569
+ {
570
+ dataKey: key,
571
+ fill: theme.colors[i % theme.colors.length],
572
+ stackId: "stack",
573
+ radius: i === seriesKeys.length - 1 ? [4, 4, 0, 0] : void 0
574
+ },
575
+ key
576
+ )),
577
+ brushComponent
578
+ ] });
579
+ case "line":
580
+ return /* @__PURE__ */ jsxs2(LineChart, { ...commonProps, children: [
581
+ /* @__PURE__ */ jsx3(CartesianGrid, { strokeDasharray: "3 3", stroke: theme.gridStroke }),
582
+ /* @__PURE__ */ jsx3(XAxis, { ...xAxisProps }),
583
+ /* @__PURE__ */ jsx3(YAxis, { ...yAxisProps }),
584
+ /* @__PURE__ */ jsx3(Tooltip, { contentStyle: tooltipStyle }),
585
+ /* @__PURE__ */ jsx3(Legend, {}),
586
+ seriesKeys.map((key, i) => /* @__PURE__ */ jsx3(
587
+ Line,
588
+ {
589
+ type: "monotone",
590
+ dataKey: key,
591
+ stroke: theme.colors[i % theme.colors.length],
592
+ strokeWidth: 2,
593
+ dot: { fill: theme.colors[i % theme.colors.length] }
594
+ },
595
+ key
596
+ )),
597
+ brushComponent
598
+ ] });
599
+ case "area":
600
+ return /* @__PURE__ */ jsxs2(AreaChart, { ...commonProps, children: [
601
+ /* @__PURE__ */ jsx3("defs", { children: seriesKeys.map((key, i) => /* @__PURE__ */ jsxs2(
602
+ "linearGradient",
603
+ {
604
+ id: `grad-${chart.id}-${i}`,
605
+ x1: "0",
606
+ y1: "0",
607
+ x2: "0",
608
+ y2: "1",
609
+ children: [
610
+ /* @__PURE__ */ jsx3(
611
+ "stop",
612
+ {
613
+ offset: "5%",
614
+ stopColor: theme.colors[i % theme.colors.length],
615
+ stopOpacity: 0.6
616
+ }
617
+ ),
618
+ /* @__PURE__ */ jsx3(
619
+ "stop",
620
+ {
621
+ offset: "95%",
622
+ stopColor: theme.colors[i % theme.colors.length],
623
+ stopOpacity: 0
624
+ }
625
+ )
626
+ ]
627
+ },
628
+ key
629
+ )) }),
630
+ /* @__PURE__ */ jsx3(CartesianGrid, { strokeDasharray: "3 3", stroke: theme.gridStroke }),
631
+ /* @__PURE__ */ jsx3(XAxis, { ...xAxisProps }),
632
+ /* @__PURE__ */ jsx3(YAxis, { ...yAxisProps }),
633
+ /* @__PURE__ */ jsx3(Tooltip, { contentStyle: tooltipStyle }),
634
+ /* @__PURE__ */ jsx3(Legend, {}),
635
+ seriesKeys.map((key, i) => /* @__PURE__ */ jsx3(
636
+ Area,
637
+ {
638
+ type: "monotone",
639
+ dataKey: key,
640
+ stroke: theme.colors[i % theme.colors.length],
641
+ fillOpacity: 1,
642
+ fill: `url(#grad-${chart.id}-${i})`,
643
+ stackId: "stack"
644
+ },
645
+ key
646
+ )),
647
+ brushComponent
648
+ ] });
649
+ default:
650
+ return null;
651
+ }
652
+ }
653
+ switch (type) {
654
+ case "bar":
655
+ return /* @__PURE__ */ jsxs2(BarChart, { ...commonProps, children: [
656
+ /* @__PURE__ */ jsx3(CartesianGrid, { strokeDasharray: "3 3", stroke: theme.gridStroke }),
657
+ /* @__PURE__ */ jsx3(XAxis, { ...xAxisProps }),
658
+ /* @__PURE__ */ jsx3(YAxis, { ...yAxisProps }),
659
+ /* @__PURE__ */ jsx3(Tooltip, { contentStyle: tooltipStyle }),
660
+ /* @__PURE__ */ jsx3(Legend, {}),
661
+ trendlineComponent,
662
+ /* @__PURE__ */ jsx3(Bar, { dataKey: yKey, fill: theme.accentPrimary, radius: [4, 4, 0, 0], children: chartData.map((_, index) => /* @__PURE__ */ jsx3(
663
+ Cell,
664
+ {
665
+ fill: theme.colors[index % theme.colors.length]
666
+ },
667
+ `cell-${index}`
668
+ )) }),
669
+ brushComponent
670
+ ] });
671
+ case "line":
672
+ return /* @__PURE__ */ jsxs2(LineChart, { ...commonProps, children: [
673
+ /* @__PURE__ */ jsx3(CartesianGrid, { strokeDasharray: "3 3", stroke: theme.gridStroke }),
674
+ /* @__PURE__ */ jsx3(XAxis, { ...xAxisProps }),
675
+ /* @__PURE__ */ jsx3(YAxis, { ...yAxisProps }),
676
+ /* @__PURE__ */ jsx3(Tooltip, { contentStyle: tooltipStyle }),
677
+ /* @__PURE__ */ jsx3(Legend, {}),
678
+ trendlineComponent,
679
+ /* @__PURE__ */ jsx3(
680
+ Line,
681
+ {
682
+ type: "monotone",
683
+ dataKey: yKey,
684
+ stroke: theme.accentPrimary,
685
+ strokeWidth: 3,
686
+ dot: { fill: theme.accentPrimary },
687
+ activeDot: { r: 8 }
688
+ }
689
+ ),
690
+ brushComponent
691
+ ] });
692
+ case "area": {
693
+ const gradientId = `colorY-${chart.id}`;
694
+ return /* @__PURE__ */ jsxs2(AreaChart, { ...commonProps, children: [
695
+ /* @__PURE__ */ jsx3("defs", { children: /* @__PURE__ */ jsxs2("linearGradient", { id: gradientId, x1: "0", y1: "0", x2: "0", y2: "1", children: [
696
+ /* @__PURE__ */ jsx3(
697
+ "stop",
698
+ {
699
+ offset: "5%",
700
+ stopColor: theme.accentPrimary,
701
+ stopOpacity: 0.8
702
+ }
703
+ ),
704
+ /* @__PURE__ */ jsx3(
705
+ "stop",
706
+ {
707
+ offset: "95%",
708
+ stopColor: theme.accentPrimary,
709
+ stopOpacity: 0
710
+ }
711
+ )
712
+ ] }) }),
713
+ /* @__PURE__ */ jsx3(CartesianGrid, { strokeDasharray: "3 3", stroke: theme.gridStroke }),
714
+ /* @__PURE__ */ jsx3(XAxis, { ...xAxisProps }),
715
+ /* @__PURE__ */ jsx3(YAxis, { ...yAxisProps }),
716
+ /* @__PURE__ */ jsx3(Tooltip, { contentStyle: tooltipStyle }),
717
+ /* @__PURE__ */ jsx3(Legend, {}),
718
+ trendlineComponent,
719
+ /* @__PURE__ */ jsx3(
720
+ Area,
721
+ {
722
+ type: "monotone",
723
+ dataKey: yKey,
724
+ stroke: theme.accentPrimary,
725
+ fillOpacity: 1,
726
+ fill: `url(#${gradientId})`
727
+ }
728
+ ),
729
+ brushComponent
730
+ ] });
731
+ }
732
+ case "scatter":
733
+ return /* @__PURE__ */ jsxs2(ScatterChart, { ...commonProps, children: [
734
+ /* @__PURE__ */ jsx3(CartesianGrid, { strokeDasharray: "3 3", stroke: theme.gridStroke }),
735
+ /* @__PURE__ */ jsx3(
736
+ XAxis,
737
+ {
738
+ type: "category",
739
+ ...xAxisProps
740
+ }
741
+ ),
742
+ /* @__PURE__ */ jsx3(
743
+ YAxis,
744
+ {
745
+ type: "number",
746
+ dataKey: yKey,
747
+ ...yAxisProps
748
+ }
749
+ ),
750
+ /* @__PURE__ */ jsx3(
751
+ Tooltip,
752
+ {
753
+ cursor: { strokeDasharray: "3 3" },
754
+ contentStyle: tooltipStyle
755
+ }
756
+ ),
757
+ /* @__PURE__ */ jsx3(Legend, {}),
758
+ /* @__PURE__ */ jsx3(Scatter, { name: yKey, data: chartData, fill: theme.accentPrimary })
759
+ ] });
760
+ case "pie":
761
+ return /* @__PURE__ */ jsxs2(PieChart, { children: [
762
+ /* @__PURE__ */ jsx3(
763
+ Pie,
764
+ {
765
+ data: chartData,
766
+ innerRadius: 60,
767
+ outerRadius: 100,
768
+ paddingAngle: 5,
769
+ dataKey: yKey,
770
+ nameKey: xKey,
771
+ label: ({ name, percent }) => `${name}: ${((percent ?? 0) * 100).toFixed(0)}%`,
772
+ labelLine: { stroke: theme.textMuted },
773
+ children: chartData.map((_, index) => /* @__PURE__ */ jsx3(
774
+ Cell,
775
+ {
776
+ fill: theme.colors[index % theme.colors.length]
777
+ },
778
+ `cell-${index}`
779
+ ))
780
+ }
781
+ ),
782
+ /* @__PURE__ */ jsx3(Tooltip, { contentStyle: tooltipStyle }),
783
+ /* @__PURE__ */ jsx3(Legend, {})
784
+ ] });
785
+ default:
786
+ return null;
787
+ }
788
+ };
789
+ if (processedData.length === 0) {
790
+ return /* @__PURE__ */ jsx3("div", { className: "flex h-[300px] flex-col items-center justify-center gap-4 text-gray-400", children: /* @__PURE__ */ jsxs2("div", { className: "text-center", children: [
791
+ /* @__PURE__ */ jsx3("p", { className: "mb-1 font-medium text-red-400", children: "Unable to generate this chart" }),
792
+ /* @__PURE__ */ jsxs2("p", { className: "mb-4 text-sm text-gray-500", children: [
793
+ "Columns: ",
794
+ chart.xAxis,
795
+ ", ",
796
+ chart.yAxis
797
+ ] }),
798
+ onRegenerate && /* @__PURE__ */ jsxs2(
799
+ "button",
800
+ {
801
+ onClick: handleRegenerate,
802
+ disabled: isRegenerating,
803
+ className: "flex items-center gap-2 rounded-lg bg-violet-600 px-4 py-2 text-sm text-white transition-colors hover:bg-violet-500 disabled:opacity-50",
804
+ children: [
805
+ /* @__PURE__ */ jsx3(
806
+ RefreshCw2,
807
+ {
808
+ className: `h-4 w-4 ${isRegenerating ? "animate-spin" : ""}`
809
+ }
810
+ ),
811
+ isRegenerating ? "Regenerating..." : "Regenerate with AI"
812
+ ]
813
+ }
814
+ )
815
+ ] }) });
816
+ }
817
+ return /* @__PURE__ */ jsxs2("div", { children: [
818
+ /* @__PURE__ */ jsx3(
819
+ ChartToolbar,
820
+ {
821
+ chartType: chart.type,
822
+ sortOrder,
823
+ limitResults,
824
+ showBrush,
825
+ showTrendline,
826
+ isRegenerating,
827
+ hasRegenerate: !!onRegenerate,
828
+ onToggleSort: toggleSort,
829
+ onLimitChange: setLimitResults,
830
+ onToggleBrush: () => setShowBrush(!showBrush),
831
+ onToggleTrendline: () => setShowTrendline(!showTrendline),
832
+ onExportCSV: handleExportCSV,
833
+ onExportPNG: handleExportPNG,
834
+ onRegenerate: handleRegenerate
835
+ }
836
+ ),
837
+ /* @__PURE__ */ jsx3("div", { className: "h-[450px] w-full", ref: chartContainerRef, children: /* @__PURE__ */ jsx3(
838
+ ResponsiveContainer,
839
+ {
840
+ width: "100%",
841
+ height: "100%",
842
+ children: renderChartContent(
843
+ chart.type,
844
+ processedData,
845
+ xColName,
846
+ isMultiSeries ? "" : yColName,
847
+ showBrush,
848
+ showTrendline,
849
+ average
850
+ )
851
+ },
852
+ `chart-${chart.id}-${showBrush ? "brush" : "no-brush"}`
853
+ ) }),
854
+ /* @__PURE__ */ jsxs2("div", { className: "mt-4 flex flex-wrap gap-2", children: [
855
+ /* @__PURE__ */ jsxs2("span", { className: "rounded bg-white/10 px-2 py-0.5 text-xs text-gray-400", children: [
856
+ "X: ",
857
+ chart.xAxis
858
+ ] }),
859
+ /* @__PURE__ */ jsxs2("span", { className: "rounded bg-white/10 px-2 py-0.5 text-xs text-gray-400", children: [
860
+ "Y: ",
861
+ chart.yAxis
862
+ ] }),
863
+ chart.groupBy && /* @__PURE__ */ jsxs2("span", { className: "rounded bg-cyan-500/20 px-2 py-0.5 text-xs text-cyan-300", children: [
864
+ "Group: ",
865
+ chart.groupBy
866
+ ] }),
867
+ chart.aggregation && chart.aggregation !== "none" && /* @__PURE__ */ jsx3("span", { className: "rounded bg-violet-500/20 px-2 py-0.5 text-xs text-violet-300", children: chart.aggregation }),
868
+ /* @__PURE__ */ jsxs2("span", { className: "rounded bg-white/10 px-2 py-0.5 text-xs text-gray-400", children: [
869
+ processedData.length,
870
+ " items"
871
+ ] }),
872
+ isMultiSeries && /* @__PURE__ */ jsxs2("span", { className: "rounded bg-emerald-500/20 px-2 py-0.5 text-xs text-emerald-300", children: [
873
+ seriesKeys.length,
874
+ " series"
875
+ ] })
876
+ ] })
877
+ ] });
878
+ }
879
+
880
+ // src/ChartDisplay.tsx
881
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
882
+ function DefaultCard({
883
+ children,
884
+ className = ""
885
+ }) {
886
+ return /* @__PURE__ */ jsx4("div", { className, children });
887
+ }
888
+ function ChartDisplay({
889
+ data,
890
+ charts,
891
+ onRegenerate,
892
+ cardWrapper: CardWrapper = DefaultCard,
893
+ theme = defaultDarkTheme
894
+ }) {
895
+ if (charts.length === 0) return null;
896
+ return /* @__PURE__ */ jsx4(ChartThemeProvider, { theme, children: /* @__PURE__ */ jsx4("div", { className: "animate-fade-in space-y-6", children: charts.map((chart) => /* @__PURE__ */ jsx4(
897
+ CardWrapper,
898
+ {
899
+ title: chart.title,
900
+ className: "overflow-hidden rounded-2xl border border-white/10 bg-slate-900/50",
901
+ children: /* @__PURE__ */ jsxs3("div", { className: "p-6", children: [
902
+ /* @__PURE__ */ jsx4("h3", { className: "mb-2 bg-linear-to-r from-violet-400 to-fuchsia-400 bg-clip-text text-xl font-bold text-transparent", children: chart.title }),
903
+ /* @__PURE__ */ jsx4("p", { className: "mb-4 text-gray-400", children: chart.description }),
904
+ /* @__PURE__ */ jsx4(
905
+ SingleChart,
906
+ {
907
+ data,
908
+ chart,
909
+ onRegenerate
910
+ }
911
+ )
912
+ ] })
913
+ },
914
+ chart.id
915
+ )) }) });
916
+ }
917
+
918
+ // src/constants.ts
919
+ var COLORS = [
920
+ "#8b5cf6",
921
+ // Violet 500
922
+ "#06b6d4",
923
+ // Cyan 500
924
+ "#f43f5e",
925
+ // Rose 500
926
+ "#eab308",
927
+ // Yellow 500
928
+ "#10b981",
929
+ // Emerald 500
930
+ "#3b82f6",
931
+ // Blue 500
932
+ "#d946ef",
933
+ // Fuchsia 500
934
+ "#f97316"
935
+ // Orange 500
936
+ ];
937
+ export {
938
+ COLORS,
939
+ ChartDisplay,
940
+ ChartThemeProvider,
941
+ ChartToolbar,
942
+ SingleChart,
943
+ defaultDarkTheme,
944
+ defaultLightTheme,
945
+ processChartData,
946
+ processChartDataMultiSeries,
947
+ useChartTheme
948
+ };
949
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/types.ts","../src/ThemeContext.tsx","../src/SingleChart.tsx","../src/processChartData.ts","../src/ChartToolbar.tsx","../src/ChartDisplay.tsx","../src/constants.ts"],"sourcesContent":["export type ChartType = \"bar\" | \"line\" | \"pie\" | \"scatter\" | \"area\";\nexport type AggregationType =\n | \"sum\"\n | \"avg\"\n | \"count\"\n | \"min\"\n | \"max\"\n | \"none\";\n\nexport interface TabularData {\n headers: string[];\n rows: string[][];\n columns: {\n name: string;\n type: \"string\" | \"number\" | \"date\" | \"boolean\";\n index: number;\n }[];\n rowCount: number;\n}\n\nexport interface ChartConfig {\n id: string;\n type: ChartType;\n title: string;\n description: string;\n xAxis: string;\n yAxis: string;\n groupBy?: string;\n aggregation: AggregationType;\n dataConfig?: {\n xColumn: string;\n yColumn: string;\n groupColumn?: string;\n };\n}\n\nexport type SortOrder = \"none\" | \"asc\" | \"desc\";\nexport type ChartDataPoint = Record<string, string | number>;\n\n// Theme types\nexport interface ChartTheme {\n colors: string[];\n background: string;\n cardBackground: string;\n border: string;\n text: string;\n textMuted: string;\n textDimmed: string;\n gridStroke: string;\n tooltipBackground: string;\n tooltipBorder: string;\n accentPrimary: string;\n accentSecondary: string;\n accentSuccess: string;\n accentDanger: string;\n}\n\nexport const defaultDarkTheme: ChartTheme = {\n colors: [\n \"#8b5cf6\",\n \"#06b6d4\",\n \"#f43f5e\",\n \"#eab308\",\n \"#10b981\",\n \"#3b82f6\",\n \"#d946ef\",\n \"#f97316\",\n ],\n background: \"rgba(15, 23, 42, 0.5)\",\n cardBackground: \"rgba(255, 255, 255, 0.05)\",\n border: \"rgba(255, 255, 255, 0.1)\",\n text: \"#ffffff\",\n textMuted: \"#9ca3af\",\n textDimmed: \"#6b7280\",\n gridStroke: \"#374151\",\n tooltipBackground: \"#1f2937\",\n tooltipBorder: \"1px solid #374151\",\n accentPrimary: \"#8b5cf6\",\n accentSecondary: \"#06b6d4\",\n accentSuccess: \"#10b981\",\n accentDanger: \"#ef4444\",\n};\n\nexport const defaultLightTheme: ChartTheme = {\n colors: [\n \"#7c3aed\",\n \"#0891b2\",\n \"#e11d48\",\n \"#ca8a04\",\n \"#059669\",\n \"#2563eb\",\n \"#c026d3\",\n \"#ea580c\",\n ],\n background: \"#ffffff\",\n cardBackground: \"#f8fafc\",\n border: \"#e2e8f0\",\n text: \"#0f172a\",\n textMuted: \"#64748b\",\n textDimmed: \"#94a3b8\",\n gridStroke: \"#e2e8f0\",\n tooltipBackground: \"#ffffff\",\n tooltipBorder: \"1px solid #e2e8f0\",\n accentPrimary: \"#7c3aed\",\n accentSecondary: \"#0891b2\",\n accentSuccess: \"#059669\",\n accentDanger: \"#dc2626\",\n};\n","import { createContext, useContext } from \"react\";\nimport { type ChartTheme, defaultDarkTheme } from \"./types\";\n\nconst ChartThemeContext = createContext<ChartTheme>(defaultDarkTheme);\n\nexport function ChartThemeProvider({\n theme,\n children,\n}: {\n theme: ChartTheme;\n children: React.ReactNode;\n}) {\n return (\n <ChartThemeContext.Provider value={theme}>\n {children}\n </ChartThemeContext.Provider>\n );\n}\n\nexport function useChartTheme(): ChartTheme {\n return useContext(ChartThemeContext);\n}\n","import { useState, useMemo, useCallback, useRef } from \"react\";\nimport {\n BarChart,\n Bar,\n LineChart,\n Line,\n PieChart,\n Pie,\n ScatterChart,\n Scatter,\n AreaChart,\n Area,\n XAxis,\n YAxis,\n CartesianGrid,\n Tooltip,\n Legend,\n ResponsiveContainer,\n Cell,\n Brush,\n ReferenceLine,\n} from \"recharts\";\nimport { RefreshCw } from \"lucide-react\";\nimport type {\n TabularData,\n ChartConfig,\n ChartType,\n SortOrder,\n ChartDataPoint,\n} from \"./types\";\nimport { useChartTheme } from \"./ThemeContext\";\nimport { processChartDataMultiSeries } from \"./processChartData\";\nimport { ChartToolbar } from \"./ChartToolbar\";\n\nexport interface SingleChartProps {\n data: TabularData;\n chart: ChartConfig;\n onRegenerate?: (chart: ChartConfig) => Promise<void>;\n}\n\nexport function SingleChart({ data, chart, onRegenerate }: SingleChartProps) {\n const theme = useChartTheme();\n const chartContainerRef = useRef<HTMLDivElement>(null);\n const [isRegenerating, setIsRegenerating] = useState(false);\n const [sortOrder, setSortOrder] = useState<SortOrder>(\"none\");\n const [showBrush, setShowBrush] = useState(false);\n const [showTrendline, setShowTrendline] = useState(false);\n const [limitResults, setLimitResults] = useState<number>(20);\n\n const processed = useMemo(\n () => processChartDataMultiSeries(data, chart, sortOrder, limitResults),\n [data, chart, sortOrder, limitResults],\n );\n\n const { data: processedData, seriesKeys } = processed;\n const isMultiSeries = seriesKeys.length > 0;\n\n // Find actual column names from data\n const xColName =\n data.columns.find(\n (col) =>\n col.name === chart.xAxis ||\n col.name.toLowerCase() === chart.xAxis.toLowerCase(),\n )?.name ?? chart.xAxis;\n\n const yColName =\n data.columns.find(\n (col) =>\n col.name === chart.yAxis ||\n col.name.toLowerCase() === chart.yAxis.toLowerCase(),\n )?.name ?? chart.yAxis;\n\n // Calculate average for trend line\n const average = useMemo(() => {\n if (processedData.length === 0 || isMultiSeries) return 0;\n const sum = processedData.reduce((acc, item) => {\n const val = item[yColName];\n return acc + (typeof val === \"number\" ? val : 0);\n }, 0);\n return sum / processedData.length;\n }, [processedData, yColName, isMultiSeries]);\n\n const handleRegenerate = async () => {\n if (!onRegenerate) return;\n setIsRegenerating(true);\n try {\n await onRegenerate(chart);\n } finally {\n setIsRegenerating(false);\n }\n };\n\n const handleExportCSV = useCallback(() => {\n if (processedData.length === 0) return;\n\n const headers = Object.keys(processedData[0] ?? {}).join(\",\");\n const rows = processedData\n .map((row) =>\n Object.values(row)\n .map((v) => `\"${String(v)}\"`)\n .join(\",\"),\n )\n .join(\"\\n\");\n\n const csv = `${headers}\\n${rows}`;\n const blob = new Blob([csv], { type: \"text/csv\" });\n const url = URL.createObjectURL(blob);\n const a = document.createElement(\"a\");\n a.href = url;\n a.download = `${chart.title.replace(/\\s+/g, \"_\")}.csv`;\n a.click();\n URL.revokeObjectURL(url);\n }, [processedData, chart.title]);\n\n const handleExportPNG = useCallback(() => {\n const container = chartContainerRef.current;\n if (!container) return;\n\n const svgElement = container.querySelector(\"svg\");\n if (!svgElement) return;\n\n const svgClone = svgElement.cloneNode(true) as SVGSVGElement;\n svgClone.setAttribute(\n \"xmlns\",\n \"http://www.w3.org/2000/svg\",\n );\n // Ensure dimensions\n const rect = svgElement.getBoundingClientRect();\n svgClone.setAttribute(\"width\", String(rect.width));\n svgClone.setAttribute(\"height\", String(rect.height));\n\n const svgString = new XMLSerializer().serializeToString(svgClone);\n const svgBlob = new Blob([svgString], {\n type: \"image/svg+xml;charset=utf-8\",\n });\n const url = URL.createObjectURL(svgBlob);\n\n const img = new Image();\n img.onload = () => {\n const canvas = document.createElement(\"canvas\");\n const scale = 2; // retina\n canvas.width = rect.width * scale;\n canvas.height = rect.height * scale;\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return;\n ctx.scale(scale, scale);\n ctx.fillStyle = theme.tooltipBackground;\n ctx.fillRect(0, 0, rect.width, rect.height);\n ctx.drawImage(img, 0, 0, rect.width, rect.height);\n\n canvas.toBlob((blob) => {\n if (!blob) return;\n const pngUrl = URL.createObjectURL(blob);\n const a = document.createElement(\"a\");\n a.href = pngUrl;\n a.download = `${chart.title.replace(/\\s+/g, \"_\")}.png`;\n a.click();\n URL.revokeObjectURL(pngUrl);\n }, \"image/png\");\n\n URL.revokeObjectURL(url);\n };\n img.src = url;\n }, [chart.title, theme.tooltipBackground]);\n\n const toggleSort = () => {\n setSortOrder((prev) => {\n if (prev === \"none\") return \"desc\";\n if (prev === \"desc\") return \"asc\";\n return \"none\";\n });\n };\n\n // Render chart content helper\n const renderChartContent = (\n type: ChartType,\n chartData: ChartDataPoint[],\n xKey: string,\n yKey: string,\n enableBrush?: boolean,\n enableTrendline?: boolean,\n avgValue?: number,\n ) => {\n const commonProps = {\n data: chartData,\n margin: { top: 20, right: 30, left: 20, bottom: 50 },\n };\n\n const tooltipStyle = {\n backgroundColor: theme.tooltipBackground,\n border: theme.tooltipBorder,\n borderRadius: \"8px\",\n };\n\n const brushComponent = enableBrush ? (\n <Brush\n dataKey={xKey}\n height={30}\n stroke={theme.accentPrimary}\n fill=\"#1e1b4b\"\n />\n ) : null;\n\n const trendlineComponent =\n enableTrendline && avgValue && !isMultiSeries ? (\n <ReferenceLine\n y={avgValue}\n stroke={theme.accentSuccess}\n strokeDasharray=\"5 5\"\n label={{\n value: `Avg: ${avgValue.toFixed(2)}`,\n fill: theme.accentSuccess,\n fontSize: 12,\n }}\n />\n ) : null;\n\n const xAxisProps = {\n dataKey: xKey,\n stroke: theme.textMuted,\n fontSize: 11,\n angle: -45,\n textAnchor: \"end\" as const,\n height: 80,\n interval: 0 as const,\n tick: { fill: theme.textMuted },\n };\n\n const yAxisProps = {\n stroke: theme.textMuted,\n fontSize: 12,\n tick: { fill: theme.textMuted },\n };\n\n // Multi-series rendering\n if (isMultiSeries && type !== \"pie\" && type !== \"scatter\") {\n switch (type) {\n case \"bar\":\n return (\n <BarChart {...commonProps}>\n <CartesianGrid strokeDasharray=\"3 3\" stroke={theme.gridStroke} />\n <XAxis {...xAxisProps} />\n <YAxis {...yAxisProps} />\n <Tooltip contentStyle={tooltipStyle} />\n <Legend />\n {seriesKeys.map((key, i) => (\n <Bar\n key={key}\n dataKey={key}\n fill={theme.colors[i % theme.colors.length]}\n stackId=\"stack\"\n radius={\n i === seriesKeys.length - 1 ? [4, 4, 0, 0] : undefined\n }\n />\n ))}\n {brushComponent}\n </BarChart>\n );\n case \"line\":\n return (\n <LineChart {...commonProps}>\n <CartesianGrid strokeDasharray=\"3 3\" stroke={theme.gridStroke} />\n <XAxis {...xAxisProps} />\n <YAxis {...yAxisProps} />\n <Tooltip contentStyle={tooltipStyle} />\n <Legend />\n {seriesKeys.map((key, i) => (\n <Line\n key={key}\n type=\"monotone\"\n dataKey={key}\n stroke={theme.colors[i % theme.colors.length]}\n strokeWidth={2}\n dot={{ fill: theme.colors[i % theme.colors.length] }}\n />\n ))}\n {brushComponent}\n </LineChart>\n );\n case \"area\":\n return (\n <AreaChart {...commonProps}>\n <defs>\n {seriesKeys.map((key, i) => (\n <linearGradient\n key={key}\n id={`grad-${chart.id}-${i}`}\n x1=\"0\"\n y1=\"0\"\n x2=\"0\"\n y2=\"1\"\n >\n <stop\n offset=\"5%\"\n stopColor={theme.colors[i % theme.colors.length]}\n stopOpacity={0.6}\n />\n <stop\n offset=\"95%\"\n stopColor={theme.colors[i % theme.colors.length]}\n stopOpacity={0}\n />\n </linearGradient>\n ))}\n </defs>\n <CartesianGrid strokeDasharray=\"3 3\" stroke={theme.gridStroke} />\n <XAxis {...xAxisProps} />\n <YAxis {...yAxisProps} />\n <Tooltip contentStyle={tooltipStyle} />\n <Legend />\n {seriesKeys.map((key, i) => (\n <Area\n key={key}\n type=\"monotone\"\n dataKey={key}\n stroke={theme.colors[i % theme.colors.length]}\n fillOpacity={1}\n fill={`url(#grad-${chart.id}-${i})`}\n stackId=\"stack\"\n />\n ))}\n {brushComponent}\n </AreaChart>\n );\n default:\n return null;\n }\n }\n\n // Single-series rendering\n switch (type) {\n case \"bar\":\n return (\n <BarChart {...commonProps}>\n <CartesianGrid strokeDasharray=\"3 3\" stroke={theme.gridStroke} />\n <XAxis {...xAxisProps} />\n <YAxis {...yAxisProps} />\n <Tooltip contentStyle={tooltipStyle} />\n <Legend />\n {trendlineComponent}\n <Bar dataKey={yKey} fill={theme.accentPrimary} radius={[4, 4, 0, 0]}>\n {chartData.map((_, index) => (\n <Cell\n key={`cell-${index}`}\n fill={theme.colors[index % theme.colors.length]}\n />\n ))}\n </Bar>\n {brushComponent}\n </BarChart>\n );\n\n case \"line\":\n return (\n <LineChart {...commonProps}>\n <CartesianGrid strokeDasharray=\"3 3\" stroke={theme.gridStroke} />\n <XAxis {...xAxisProps} />\n <YAxis {...yAxisProps} />\n <Tooltip contentStyle={tooltipStyle} />\n <Legend />\n {trendlineComponent}\n <Line\n type=\"monotone\"\n dataKey={yKey}\n stroke={theme.accentPrimary}\n strokeWidth={3}\n dot={{ fill: theme.accentPrimary }}\n activeDot={{ r: 8 }}\n />\n {brushComponent}\n </LineChart>\n );\n\n case \"area\": {\n const gradientId = `colorY-${chart.id}`;\n return (\n <AreaChart {...commonProps}>\n <defs>\n <linearGradient id={gradientId} x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n <stop\n offset=\"5%\"\n stopColor={theme.accentPrimary}\n stopOpacity={0.8}\n />\n <stop\n offset=\"95%\"\n stopColor={theme.accentPrimary}\n stopOpacity={0}\n />\n </linearGradient>\n </defs>\n <CartesianGrid strokeDasharray=\"3 3\" stroke={theme.gridStroke} />\n <XAxis {...xAxisProps} />\n <YAxis {...yAxisProps} />\n <Tooltip contentStyle={tooltipStyle} />\n <Legend />\n {trendlineComponent}\n <Area\n type=\"monotone\"\n dataKey={yKey}\n stroke={theme.accentPrimary}\n fillOpacity={1}\n fill={`url(#${gradientId})`}\n />\n {brushComponent}\n </AreaChart>\n );\n }\n\n case \"scatter\":\n return (\n <ScatterChart {...commonProps}>\n <CartesianGrid strokeDasharray=\"3 3\" stroke={theme.gridStroke} />\n <XAxis\n type=\"category\"\n {...xAxisProps}\n />\n <YAxis\n type=\"number\"\n dataKey={yKey}\n {...yAxisProps}\n />\n <Tooltip\n cursor={{ strokeDasharray: \"3 3\" }}\n contentStyle={tooltipStyle}\n />\n <Legend />\n <Scatter name={yKey} data={chartData} fill={theme.accentPrimary} />\n </ScatterChart>\n );\n\n case \"pie\":\n return (\n <PieChart>\n <Pie\n data={chartData}\n innerRadius={60}\n outerRadius={100}\n paddingAngle={5}\n dataKey={yKey}\n nameKey={xKey}\n label={({ name, percent }) =>\n `${name}: ${((percent ?? 0) * 100).toFixed(0)}%`\n }\n labelLine={{ stroke: theme.textMuted }}\n >\n {chartData.map((_, index) => (\n <Cell\n key={`cell-${index}`}\n fill={theme.colors[index % theme.colors.length]}\n />\n ))}\n </Pie>\n <Tooltip contentStyle={tooltipStyle} />\n <Legend />\n </PieChart>\n );\n\n default:\n return null;\n }\n };\n\n if (processedData.length === 0) {\n return (\n <div className=\"flex h-[300px] flex-col items-center justify-center gap-4 text-gray-400\">\n <div className=\"text-center\">\n <p className=\"mb-1 font-medium text-red-400\">\n Unable to generate this chart\n </p>\n <p className=\"mb-4 text-sm text-gray-500\">\n Columns: {chart.xAxis}, {chart.yAxis}\n </p>\n {onRegenerate && (\n <button\n onClick={handleRegenerate}\n disabled={isRegenerating}\n className=\"flex items-center gap-2 rounded-lg bg-violet-600 px-4 py-2 text-sm text-white transition-colors hover:bg-violet-500 disabled:opacity-50\"\n >\n <RefreshCw\n className={`h-4 w-4 ${isRegenerating ? \"animate-spin\" : \"\"}`}\n />\n {isRegenerating ? \"Regenerating...\" : \"Regenerate with AI\"}\n </button>\n )}\n </div>\n </div>\n );\n }\n\n return (\n <div>\n <ChartToolbar\n chartType={chart.type}\n sortOrder={sortOrder}\n limitResults={limitResults}\n showBrush={showBrush}\n showTrendline={showTrendline}\n isRegenerating={isRegenerating}\n hasRegenerate={!!onRegenerate}\n onToggleSort={toggleSort}\n onLimitChange={setLimitResults}\n onToggleBrush={() => setShowBrush(!showBrush)}\n onToggleTrendline={() => setShowTrendline(!showTrendline)}\n onExportCSV={handleExportCSV}\n onExportPNG={handleExportPNG}\n onRegenerate={handleRegenerate}\n />\n\n {/* Chart */}\n <div className=\"h-[450px] w-full\" ref={chartContainerRef}>\n <ResponsiveContainer\n key={`chart-${chart.id}-${showBrush ? \"brush\" : \"no-brush\"}`}\n width=\"100%\"\n height=\"100%\"\n >\n {renderChartContent(\n chart.type,\n processedData,\n xColName,\n isMultiSeries ? \"\" : yColName,\n showBrush,\n showTrendline,\n average,\n )}\n </ResponsiveContainer>\n </div>\n\n {/* Metadata Tags */}\n <div className=\"mt-4 flex flex-wrap gap-2\">\n <span className=\"rounded bg-white/10 px-2 py-0.5 text-xs text-gray-400\">\n X: {chart.xAxis}\n </span>\n <span className=\"rounded bg-white/10 px-2 py-0.5 text-xs text-gray-400\">\n Y: {chart.yAxis}\n </span>\n {chart.groupBy && (\n <span className=\"rounded bg-cyan-500/20 px-2 py-0.5 text-xs text-cyan-300\">\n Group: {chart.groupBy}\n </span>\n )}\n {chart.aggregation && chart.aggregation !== \"none\" && (\n <span className=\"rounded bg-violet-500/20 px-2 py-0.5 text-xs text-violet-300\">\n {chart.aggregation}\n </span>\n )}\n <span className=\"rounded bg-white/10 px-2 py-0.5 text-xs text-gray-400\">\n {processedData.length} items\n </span>\n {isMultiSeries && (\n <span className=\"rounded bg-emerald-500/20 px-2 py-0.5 text-xs text-emerald-300\">\n {seriesKeys.length} series\n </span>\n )}\n </div>\n </div>\n );\n}\n","import type {\n TabularData,\n ChartConfig,\n SortOrder,\n ChartDataPoint,\n} from \"./types\";\n\nexport interface ProcessedChartResult {\n data: ChartDataPoint[];\n seriesKeys: string[];\n yKey: string;\n}\n\nexport const processChartData = (\n data: TabularData,\n chart: ChartConfig,\n sortOrder: SortOrder = \"none\",\n limit = 20,\n): ChartDataPoint[] => {\n const result = processChartDataMultiSeries(data, chart, sortOrder, limit);\n return result.data;\n};\n\nexport const processChartDataMultiSeries = (\n data: TabularData,\n chart: ChartConfig,\n sortOrder: SortOrder = \"none\",\n limit = 20,\n): ProcessedChartResult => {\n let result: ChartDataPoint[] = [];\n\n // Find actual columns (case-insensitive match)\n const xColDef = data.columns.find(\n (c) => c.name.toLowerCase() === chart.xAxis.toLowerCase(),\n );\n const yColDef = data.columns.find(\n (c) => c.name.toLowerCase() === chart.yAxis.toLowerCase(),\n );\n const groupColDef = chart.groupBy\n ? data.columns.find(\n (c) => c.name.toLowerCase() === chart.groupBy!.toLowerCase(),\n )\n : undefined;\n\n if (!xColDef) return { data: [], seriesKeys: [], yKey: chart.yAxis };\n\n const xCol = xColDef.name;\n const xIdx = xColDef.index;\n\n // For count aggregation, we don't need a valid Y column - we just count occurrences\n const isCountMode = chart.aggregation === \"count\";\n const yCol = yColDef?.name ?? \"count\";\n const yIdx = yColDef?.index ?? -1;\n\n // GroupBy mode: create multi-series data (not supported for pie/scatter)\n const supportsGroupBy =\n chart.type !== \"pie\" && chart.type !== \"scatter\";\n if (\n supportsGroupBy &&\n groupColDef &&\n chart.aggregation &&\n chart.aggregation !== \"none\"\n ) {\n const groupIdx = groupColDef.index;\n const allGroups = new Set<string>();\n\n // Nested grouping: xVal -> groupVal -> stats\n const grouped = new Map<\n string,\n Map<string, { sum: number; count: number; min: number; max: number }>\n >();\n\n data.rows.forEach((row) => {\n const xVal = String(row[xIdx] ?? \"\").trim();\n const groupVal = String(row[groupIdx] ?? \"\").trim();\n if (!xVal || !groupVal) return;\n\n allGroups.add(groupVal);\n\n if (!grouped.has(xVal)) grouped.set(xVal, new Map());\n const xGroup = grouped.get(xVal)!;\n\n if (isCountMode) {\n const current = xGroup.get(groupVal) ?? {\n sum: 0,\n count: 0,\n min: 0,\n max: 0,\n };\n xGroup.set(groupVal, { ...current, count: current.count + 1 });\n } else if (yIdx >= 0) {\n const yVal = parseFloat(String(row[yIdx] ?? \"0\"));\n if (!isNaN(yVal)) {\n const current = xGroup.get(groupVal) ?? {\n sum: 0,\n count: 0,\n min: Infinity,\n max: -Infinity,\n };\n xGroup.set(groupVal, {\n sum: current.sum + yVal,\n count: current.count + 1,\n min: Math.min(current.min, yVal),\n max: Math.max(current.max, yVal),\n });\n }\n }\n });\n\n const seriesKeys = Array.from(allGroups).slice(0, 8); // max 8 series\n\n grouped.forEach((groupMap, xKey) => {\n const point: ChartDataPoint = { [xCol]: xKey };\n seriesKeys.forEach((groupKey) => {\n const stats = groupMap.get(groupKey);\n if (stats) {\n let value: number;\n switch (chart.aggregation) {\n case \"sum\":\n value = stats.sum;\n break;\n case \"avg\":\n value = stats.count > 0 ? stats.sum / stats.count : 0;\n break;\n case \"count\":\n value = stats.count;\n break;\n case \"min\":\n value = stats.min === Infinity ? 0 : stats.min;\n break;\n case \"max\":\n value = stats.max === -Infinity ? 0 : stats.max;\n break;\n default:\n value = stats.sum;\n }\n point[groupKey] = Math.round(value * 100) / 100;\n } else {\n point[groupKey] = 0;\n }\n });\n result.push(point);\n });\n\n // Sort by first series value or alphabetically\n if (sortOrder !== \"none\" && seriesKeys[0]) {\n const firstKey = seriesKeys[0];\n result.sort((a, b) => {\n const aVal = (a[firstKey] as number) ?? 0;\n const bVal = (b[firstKey] as number) ?? 0;\n return sortOrder === \"desc\" ? bVal - aVal : aVal - bVal;\n });\n }\n\n return {\n data: result.slice(0, limit),\n seriesKeys,\n yKey: yCol,\n };\n }\n\n // Standard single-series mode (existing logic)\n if (chart.aggregation && chart.aggregation !== \"none\") {\n const groups = new Map<\n string,\n { sum: number; count: number; min: number; max: number }\n >();\n\n data.rows.forEach((row) => {\n const xVal = String(row[xIdx] ?? \"\").trim();\n if (!xVal) return;\n\n if (isCountMode) {\n const current = groups.get(xVal) ?? {\n sum: 0,\n count: 0,\n min: 0,\n max: 0,\n };\n groups.set(xVal, {\n ...current,\n count: current.count + 1,\n });\n } else if (yIdx >= 0) {\n const yVal = parseFloat(String(row[yIdx] ?? \"0\"));\n if (!isNaN(yVal)) {\n const current = groups.get(xVal) ?? {\n sum: 0,\n count: 0,\n min: Infinity,\n max: -Infinity,\n };\n groups.set(xVal, {\n sum: current.sum + yVal,\n count: current.count + 1,\n min: Math.min(current.min, yVal),\n max: Math.max(current.max, yVal),\n });\n }\n }\n });\n\n groups.forEach((stats, key) => {\n let value: number;\n switch (chart.aggregation) {\n case \"sum\":\n value = stats.sum;\n break;\n case \"avg\":\n value = stats.count > 0 ? stats.sum / stats.count : 0;\n break;\n case \"count\":\n value = stats.count;\n break;\n case \"min\":\n value = stats.min === Infinity ? 0 : stats.min;\n break;\n case \"max\":\n value = stats.max === -Infinity ? 0 : stats.max;\n break;\n default:\n value = stats.sum;\n }\n result.push({ [xCol]: key, [yCol]: Math.round(value * 100) / 100 });\n });\n } else if (yIdx >= 0) {\n result = data.rows\n .slice(0, Math.min(limit * 2, data.rows.length))\n .map((row) => ({\n [xCol]: row[xIdx] ?? \"\",\n [yCol]: parseFloat(String(row[yIdx] ?? \"0\")),\n }))\n .filter((item) => !isNaN(item[yCol] as number));\n }\n\n // Apply sorting\n if (sortOrder !== \"none\") {\n result.sort((a, b) => {\n const aVal = a[yCol] as number;\n const bVal = b[yCol] as number;\n return sortOrder === \"desc\" ? bVal - aVal : aVal - bVal;\n });\n }\n\n return {\n data: result.slice(0, limit),\n seriesKeys: [],\n yKey: yCol,\n };\n};\n","import {\n RefreshCw,\n Download,\n SortAsc,\n SortDesc,\n RotateCcw,\n TrendingUp,\n Filter,\n Image,\n} from \"lucide-react\";\nimport type { ChartType, SortOrder } from \"./types\";\n\ninterface ChartToolbarProps {\n chartType: ChartType;\n sortOrder: SortOrder;\n limitResults: number;\n showBrush: boolean;\n showTrendline: boolean;\n isRegenerating: boolean;\n hasRegenerate: boolean;\n onToggleSort: () => void;\n onLimitChange: (limit: number) => void;\n onToggleBrush: () => void;\n onToggleTrendline: () => void;\n onExportCSV: () => void;\n onExportPNG: () => void;\n onRegenerate: () => void;\n}\n\nexport function ChartToolbar({\n chartType,\n sortOrder,\n limitResults,\n showBrush,\n showTrendline,\n isRegenerating,\n hasRegenerate,\n onToggleSort,\n onLimitChange,\n onToggleBrush,\n onToggleTrendline,\n onExportCSV,\n onExportPNG,\n onRegenerate,\n}: ChartToolbarProps) {\n const supportsBrush =\n chartType === \"line\" || chartType === \"area\" || chartType === \"bar\";\n const supportsTrendline =\n chartType === \"bar\" || chartType === \"line\" || chartType === \"area\";\n\n return (\n <div className=\"mb-4 flex flex-wrap items-center gap-2 rounded-xl border border-white/10 bg-white/5 p-3\">\n {/* Sort Control */}\n <button\n onClick={onToggleSort}\n className={`flex items-center gap-1.5 rounded-lg px-3 py-1.5 text-sm transition-colors ${\n sortOrder !== \"none\"\n ? \"border border-violet-500/30 bg-violet-500/20 text-violet-400\"\n : \"bg-white/5 text-gray-400 hover:bg-white/10 hover:text-white\"\n }`}\n title=\"Sort by value\"\n >\n {sortOrder === \"asc\" ? (\n <SortAsc className=\"h-4 w-4\" />\n ) : sortOrder === \"desc\" ? (\n <SortDesc className=\"h-4 w-4\" />\n ) : (\n <SortDesc className=\"h-4 w-4 opacity-50\" />\n )}\n Sort\n </button>\n\n {/* Limit Results */}\n <div className=\"flex items-center gap-2\">\n <Filter className=\"h-4 w-4 text-gray-400\" />\n <select\n value={limitResults}\n onChange={(e) => onLimitChange(Number(e.target.value))}\n className=\"rounded-lg border border-white/10 bg-white/5 px-2 py-1.5 text-sm text-gray-300 focus:border-violet-500/50 focus:outline-none\"\n >\n <option value={10}>Top 10</option>\n <option value={20}>Top 20</option>\n <option value={50}>Top 50</option>\n <option value={100}>Top 100</option>\n <option value={999999}>All</option>\n </select>\n </div>\n\n {/* Brush/Zoom Toggle */}\n {supportsBrush && (\n <button\n onClick={onToggleBrush}\n className={`flex items-center gap-1.5 rounded-lg px-3 py-1.5 text-sm transition-colors ${\n showBrush\n ? \"border border-cyan-500/30 bg-cyan-500/20 text-cyan-400\"\n : \"bg-white/5 text-gray-400 hover:bg-white/10 hover:text-white\"\n }`}\n title=\"Enable zoom/brush\"\n >\n <RotateCcw className=\"h-4 w-4\" />\n Zoom\n </button>\n )}\n\n {/* Trend Line Toggle */}\n {supportsTrendline && (\n <button\n onClick={onToggleTrendline}\n className={`flex items-center gap-1.5 rounded-lg px-3 py-1.5 text-sm transition-colors ${\n showTrendline\n ? \"border border-emerald-500/30 bg-emerald-500/20 text-emerald-400\"\n : \"bg-white/5 text-gray-400 hover:bg-white/10 hover:text-white\"\n }`}\n title=\"Show average line\"\n >\n <TrendingUp className=\"h-4 w-4\" />\n Average\n </button>\n )}\n\n <div className=\"flex-1\" />\n\n {/* Export PNG Button */}\n <button\n onClick={onExportPNG}\n className=\"flex items-center gap-1.5 rounded-lg bg-white/5 px-3 py-1.5 text-sm text-gray-400 transition-colors hover:bg-white/10 hover:text-white\"\n title=\"Export chart as PNG image\"\n >\n <Image className=\"h-4 w-4\" />\n PNG\n </button>\n\n {/* Export CSV Button */}\n <button\n onClick={onExportCSV}\n className=\"flex items-center gap-1.5 rounded-lg bg-white/5 px-3 py-1.5 text-sm text-gray-400 transition-colors hover:bg-white/10 hover:text-white\"\n title=\"Export chart data as CSV\"\n >\n <Download className=\"h-4 w-4\" />\n CSV\n </button>\n\n {/* Regenerate Button */}\n {hasRegenerate && (\n <button\n onClick={onRegenerate}\n disabled={isRegenerating}\n className=\"flex items-center gap-1.5 rounded-lg bg-violet-500/20 px-3 py-1.5 text-sm text-violet-400 transition-colors hover:bg-violet-500/30 disabled:opacity-50\"\n >\n <RefreshCw\n className={`h-4 w-4 ${isRegenerating ? \"animate-spin\" : \"\"}`}\n />\n {isRegenerating ? \"...\" : \"Regenerate\"}\n </button>\n )}\n </div>\n );\n}\n","import type { TabularData, ChartConfig, ChartTheme } from \"./types\";\nimport { defaultDarkTheme } from \"./types\";\nimport { ChartThemeProvider } from \"./ThemeContext\";\nimport { SingleChart } from \"./SingleChart\";\n\nexport interface ChartDisplayProps {\n data: TabularData;\n charts: ChartConfig[];\n onRegenerate?: (chart: ChartConfig) => Promise<void>;\n /** Optional wrapper component for each chart card */\n cardWrapper?: React.ComponentType<{\n children: React.ReactNode;\n title?: string;\n className?: string;\n }>;\n /** Optional theme override */\n theme?: ChartTheme;\n}\n\nfunction DefaultCard({\n children,\n className = \"\",\n}: {\n children: React.ReactNode;\n title?: string;\n className?: string;\n}) {\n return <div className={className}>{children}</div>;\n}\n\nexport function ChartDisplay({\n data,\n charts,\n onRegenerate,\n cardWrapper: CardWrapper = DefaultCard,\n theme = defaultDarkTheme,\n}: ChartDisplayProps) {\n if (charts.length === 0) return null;\n\n return (\n <ChartThemeProvider theme={theme}>\n <div className=\"animate-fade-in space-y-6\">\n {charts.map((chart) => (\n <CardWrapper\n key={chart.id}\n title={chart.title}\n className=\"overflow-hidden rounded-2xl border border-white/10 bg-slate-900/50\"\n >\n <div className=\"p-6\">\n <h3 className=\"mb-2 bg-linear-to-r from-violet-400 to-fuchsia-400 bg-clip-text text-xl font-bold text-transparent\">\n {chart.title}\n </h3>\n <p className=\"mb-4 text-gray-400\">{chart.description}</p>\n <SingleChart\n data={data}\n chart={chart}\n onRegenerate={onRegenerate}\n />\n </div>\n </CardWrapper>\n ))}\n </div>\n </ChartThemeProvider>\n );\n}\n","// Re-export from defaultDarkTheme for backwards compatibility\nexport { defaultDarkTheme as COLORS_THEME } from \"./types\";\n\nexport const COLORS = [\n \"#8b5cf6\", // Violet 500\n \"#06b6d4\", // Cyan 500\n \"#f43f5e\", // Rose 500\n \"#eab308\", // Yellow 500\n \"#10b981\", // Emerald 500\n \"#3b82f6\", // Blue 500\n \"#d946ef\", // Fuchsia 500\n \"#f97316\", // Orange 500\n];\n"],"mappings":"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","names":["RefreshCw","Image","jsx","jsx","jsxs","RefreshCw","jsx","jsxs"]}
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "csv-charts-ai",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "import": "./dist/index.js",
10
+ "types": "./dist/index.d.ts"
11
+ }
12
+ },
13
+ "files": [
14
+ "dist"
15
+ ],
16
+ "scripts": {
17
+ "build": "tsup",
18
+ "dev": "tsup --watch",
19
+ "release": "semantic-release"
20
+ },
21
+ "peerDependencies": {
22
+ "lucide-react": ">=0.400.0",
23
+ "react": "^18.0.0 || ^19.0.0",
24
+ "recharts": "^3.0.0"
25
+ },
26
+ "devDependencies": {
27
+ "@semantic-release/changelog": "^6.0.3",
28
+ "@semantic-release/commit-analyzer": "^13.0.1",
29
+ "@semantic-release/git": "^10.0.1",
30
+ "@semantic-release/github": "^12.0.6",
31
+ "@semantic-release/npm": "^13.1.5",
32
+ "@semantic-release/release-notes-generator": "^14.1.0",
33
+ "@types/react": "^19.2.7",
34
+ "lucide-react": "^0.556.0",
35
+ "react": "^19.2.1",
36
+ "recharts": "^3.5.1",
37
+ "semantic-release": "^25.0.3",
38
+ "tsup": "^8.5.0",
39
+ "typescript": "^5.9.3"
40
+ },
41
+ "publishConfig": {
42
+ "access": "public"
43
+ }
44
+ }