'use client' import { useMemo, useState, useEffect } from 'react' import { BarChart, Bar, LineChart, Line, AreaChart, Area, PieChart, Pie, Cell, RadarChart, Radar, PolarGrid, PolarAngleAxis, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer, FunnelChart, Funnel, LabelList, } from 'recharts' const CHART_COLORS = ['#6366f1', '#22d3ee', '#f59e0b', '#ef4444', '#10b981', '#a78bfa', '#fb923c', '#14b8a6'] interface ChartData { label: string value: number } interface NoteChartProps { type: 'bar' | 'horizontal-bar' | 'line' | 'area' | 'pie' | 'radar' | 'funnel' | 'gauge' title?: string data: ChartData[] colors?: string[] showLegend?: boolean height?: number } // Hook to detect dark mode without re-render loops function useDarkMode() { const [isDark, setIsDark] = useState(() => typeof document !== 'undefined' && document.documentElement.classList.contains('dark')) useEffect(() => { const checkDarkMode = () => { setIsDark(document.documentElement.classList.contains('dark')) } // Initial check checkDarkMode() // Listen for class changes on html element const observer = new MutationObserver(checkDarkMode) observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class'], }) return () => observer.disconnect() }, []) return isDark } export function NoteChart({ type, title, data, colors, showLegend, height = 250 }: NoteChartProps) { const chartColors = colors ?? CHART_COLORS const dark = useDarkMode() const ttStyle = { background: dark ? '#1C1C1C' : '#fff', border: dark ? '1px solid rgba(255,255,255,0.08)' : '1px solid rgba(0,0,0,0.08)', borderRadius: 8, fontSize: 12, color: dark ? '#fff' : '#000', } const tickStyle = { fill: dark ? 'rgba(255,255,255,0.55)' : 'rgba(0,0,0,0.55)', fontSize: 11 } const gridStroke = dark ? 'rgba(255,255,255,0.07)' : 'rgba(0,0,0,0.07)' const legendStyle = { fontSize: 12, color: dark ? 'rgba(255,255,255,0.6)' : 'rgba(0,0,0,0.6)' } const xKey = 'label' const yKeys = ['value'] const renderChart = () => { switch (type) { case 'bar': return ( {showLegend !== false && } ) case 'horizontal-bar': return ( {showLegend !== false && } ) case 'line': return ( {showLegend !== false && } ) case 'area': return ( {showLegend !== false && } ) case 'pie': return ( {data.map((_, i) => )} {showLegend !== false && } ) case 'radar': return ( ) case 'funnel': return ( ({ ...d, fill: chartColors[i % chartColors.length] }))} isAnimationActive> ) case 'gauge': { const value = data[0]?.value ?? 0 const max = Math.max(...data.map(d => d.value), 100) const pct = Math.min(Math.max(value / max, 0), 1) const color = pct > 0.7 ? '#10b981' : pct > 0.4 ? '#f59e0b' : '#ef4444' return (
{value} {title || 'Value'}
) } default: return null } } return (
{title &&

{title}

} {renderChart()}
) } // Parse chart from JSON or simple format export function parseChartFromCode(code: string): NoteChartProps | null { try { // Try JSON first const parsed = JSON.parse(code) if (parsed.type && parsed.data && Array.isArray(parsed.data)) { return { type: parsed.type, title: parsed.title, data: parsed.data, colors: parsed.colors, showLegend: parsed.showLegend, height: parsed.height, } } } catch {} // Try simple format: type, title, then label: value lines const lines = code.trim().split('\n').map(l => l.trim()).filter(Boolean) if (lines.length === 0) return null const firstLine = lines[0].toLowerCase() let type: NoteChartProps['type'] = 'bar' let title = '' if (['bar', 'horizontal-bar', 'line', 'area', 'pie', 'radar', 'funnel', 'gauge'].includes(firstLine)) { type = firstLine as NoteChartProps['type'] } else if (lines[0].includes(':')) { // No type specified, default to bar } else { title = lines[0] } const data: ChartData[] = [] for (const line of lines.slice(title ? 1 : type !== lines[0]?.toLowerCase() ? 0 : 1)) { const parts = line.split(/[:|]/) if (parts.length >= 2) { const label = parts[0].trim() const value = parseFloat(parts[1].trim()) if (!isNaN(value)) { data.push({ label, value }) } } } if (data.length === 0) return null return { type, title: title || undefined, data } } export function NoteChartFromCode({ code }: { code: string }) { const props = useMemo(() => parseChartFromCode(code), [code]) if (!props) return null return }