// Small reusable visual widgets (icons, sparklines, funnel rows, etc.) // Expose React hooks globally so all Babel scripts share them safely. window.useState = React.useState; window.useEffect = React.useEffect; window.useRef = React.useRef; window.useMemo = React.useMemo; // ============ Icons (Lucide-style inline SVGs) ============ function Icon({ name, size = 16, strokeWidth = 2, className = "" }) { const paths = { home: <>, inbox: <>, pipeline: <>, campaign: <>, persona: <>, analytics: <>, settings: <>, bell: <>, search: <>, plus: <>, send: <>, reply: <>, sparkles: <>, arrow_up: <>, arrow_down: <>, arrow_right: <>, pause: <>, play: <>, filter: <>, download: <>, refresh: <>, check: <>, x: <>, flag: <>, sliders: <>, bolt: <>, user_plus: <>, clock: <>, mail: <>, pen: <>, sparkle: <>, cpu: <>, log: <>, menu: <>, arrow_left: <>, }; return ( {paths[name] || null} ); } // ============ Sparkline ============ function Sparkline({ data, height = 40, color }) { if (!data || !data.length) return null; const w = 100, h = 100; const min = Math.min(...data), max = Math.max(...data); const range = max - min || 1; const points = data.map((v, i) => [ (i / (data.length - 1)) * w, h - ((v - min) / range) * h * 0.9 - 5, ]); const line = points.map((p, i) => `${i ? 'L' : 'M'}${p[0].toFixed(1)},${p[1].toFixed(1)}`).join(' '); const area = `${line} L${w},${h} L0,${h} Z`; return ( {/* Last-point dot */} ); } // ============ Funnel ============ function Funnel({ rows }) { const max = rows[0].value; return (
{rows.map((r) => (
{r.label}
{r.value.toLocaleString()}
{(r.pct * 100).toFixed(r.pct < 0.01 ? 2 : 1)}%
))}
); } // ============ Status badge ============ function Status({ kind, children }) { return {children || kind}; } // ============ Tiny ring meter ============ function Ring({ pct = 0.5, size = 36, stroke = 4, color = "var(--primary)", label }) { const r = (size - stroke) / 2; const c = 2 * Math.PI * r; const off = c * (1 - pct); return (
{label && (
{label}
)}
); } Object.assign(window, { Icon, Sparkline, Funnel, Status, Ring });