import { useMemo, useCallback } from "react"; import { AreaChart, Area, XAxis, YAxis, Tooltip, CartesianGrid, ResponsiveContainer, } from "recharts"; import type { TabDataPoint, MemoryDataPoint, ServerDataPoint, } from "../../stores/useAppStore"; interface Props { data: TabDataPoint[]; memoryData?: MemoryDataPoint[]; serverData?: ServerDataPoint[]; instances: { id: string; profileName: string }[]; selectedInstanceId: string | null; onSelectInstance: (id: string) => void; } const COLORS = [ "#6366f1", "#8b5cf6", "#06b6d4", "#10b981", "#f59e0b", "#ef4444", "#ec4899", "#14b8a6", ]; function formatTime(timestamp: number): string { return new Date(timestamp).toLocaleTimeString("en-GB", { hour: "2-digit", minute: "2-digit", }); } function formatMetricValue(value: number, maximumFractionDigits = 0): string { if (!Number.isFinite(value)) return "0"; return new Intl.NumberFormat("en-US", { maximumFractionDigits }).format( value, ); } /* ── Glassmorphism Tooltip ── */ function GlassTooltip({ active, payload, label, instances, }: { active?: boolean; payload?: readonly { dataKey: string; value: number; color: string }[]; label?: string | number; instances: { id: string; profileName: string }[]; }) { if (!active || !payload?.length) return null; return (
{formatTime(label as number)}
{payload.map((entry) => { const nameStr = String(entry.dataKey); const isMemory = nameStr.endsWith("_mem"); const isHeap = nameStr === "goHeapMB"; const instId = isMemory ? nameStr.replace("_mem", "") : nameStr; const inst = instances.find((i) => i.id === instId); const displayName = isHeap ? "Server Heap" : inst?.profileName || instId; const suffix = isMemory ? " (mem)" : isHeap ? "" : " (tabs)"; const formattedVal = isMemory || isHeap ? `${formatMetricValue(Number(entry.value), 1)}MB` : formatMetricValue(Number(entry.value)); return (
{displayName} {suffix} {formattedVal}
); })}
); } /* ── Animated Cursor ── */ function AnimatedCursor({ points, height, }: { points?: Array<{ x: number; y: number }>; width?: number; height?: number; }) { if (!points?.length || !height) return null; const x = points[0].x; return ( ); } /* ── Loading Dots ── */ function LoadingDots({ text }: { text: string }) { return (
{[0, 1, 2].map((i) => ( ))}
{text}
); } /* ── Main Component ── */ export default function TabsChart({ data, memoryData, serverData, instances, selectedInstanceId, onSelectInstance, }: Props) { const instanceColors = useMemo(() => { const colors: Record = {}; instances.forEach((inst, i) => { colors[inst.id] = COLORS[i % COLORS.length]; }); return colors; }, [instances]); const mergedData = useMemo(() => { const memByTime = new Map((memoryData || []).map((m) => [m.timestamp, m])); const serverByTime = new Map( (serverData || []).map((s) => [s.timestamp, s]), ); const baseData = data.length > 0 ? data : (serverData || []).map((s) => ({ timestamp: s.timestamp })); return baseData.map((d) => { const merged: Record = { timestamp: d.timestamp }; for (const [key, val] of Object.entries(d)) { if (key !== "timestamp") merged[key] = val as number; } const mem = memByTime.get(d.timestamp); if (mem) { for (const [key, val] of Object.entries(mem)) { if (key !== "timestamp") merged[`${key}_mem`] = val; } } const srv = serverByTime.get(d.timestamp); if (srv) merged.goHeapMB = srv.goHeapMB; return merged; }); }, [data, memoryData, serverData]); // Current values for header badges const currentValues = useMemo(() => { if (mergedData.length === 0) return null; const latest = mergedData[mergedData.length - 1]; const vals: Array<{ label: string; value: string; color: string }> = []; instances.forEach((inst) => { const v = latest[inst.id]; if (v !== undefined) { vals.push({ label: inst.profileName, value: `${formatMetricValue(Number(v))} tabs`, color: instanceColors[inst.id] || COLORS[0], }); } const memV = latest[`${inst.id}_mem`]; if (memV !== undefined) { vals.push({ label: `${inst.profileName} mem`, value: `${formatMetricValue(Number(memV), 1)}MB`, color: instanceColors[inst.id] || COLORS[0], }); } }); if (latest.goHeapMB !== undefined) { vals.push({ label: "Heap", value: `${formatMetricValue(Number(latest.goHeapMB), 1)}MB`, color: "#94a3b8", }); } return vals; }, [mergedData, instances, instanceColors]); // eslint-disable-next-line @typescript-eslint/no-explicit-any const tooltipContent = useCallback( (props: any) => , [instances], ); if (mergedData.length < 2) { return ( ); } const hasMemory = memoryData && memoryData.length > 0; const hasServer = serverData && serverData.length > 0; return (
Monitoring
Live telemetry {/* Pulse indicator */}
{/* Current value badges */} {currentValues?.map((cv) => ( {cv.value} ))} {/* Metric type badges */} Tabs {hasMemory && ( Memory )} {hasServer && ( Heap )}
{/* Gradients for each instance */} {instances.map((inst) => { const color = instanceColors[inst.id] || COLORS[0]; return ( ); })} {/* Gradient for server heap */} formatMetricValue(Number(value))} /> {(hasMemory || hasServer) && ( `${formatMetricValue(Number(value), 1)}MB` } /> )} } /> {/* Tab count areas (solid gradient fill) */} {instances.map((inst) => ( onSelectInstance(inst.id), style: { cursor: "pointer", filter: `drop-shadow(0 0 4px ${instanceColors[inst.id]}80)`, }, }} animationDuration={800} animationEasing="ease-in-out" /> ))} {/* Memory areas (dashed, lighter fill) */} {hasMemory && instances.map((inst) => ( ))} {/* Server heap area (dotted, gray) */} {hasServer && ( )}
); }