import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, } from "recharts"; import { type MetricUnit, type MetricScale, metricScaleInfo, formatScaled, } from "../lib/metricFormat"; export interface SeriesPoint { t: number; v: number | null; } export interface ChartSeries { label: string; points: SeriesPoint[]; } /** Merge multiple time-series into a single recharts-friendly array. */ function mergeSeries(series: ChartSeries[]): Record[] { const map = new Map>(); for (const s of series) { for (const p of s.points) { const existing = map.get(p.t) ?? { time: p.t }; existing[s.label] = p.v; map.set(p.t, existing); } } return [...map.values()].sort( (a, b) => (a.time as number) - (b.time as number), ); } function formatTime(ms: number): string { return new Date(ms).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", }); } const CHART_COLORS = [ "var(--color-chart-1)", "var(--color-chart-2)", "var(--color-chart-3)", "var(--color-chart-4)", "var(--color-chart-5)", ]; interface LineSeriesChartProps { series: ChartSeries[]; height?: number; /** Display unit for the Y axis + tooltip (drives decimal-prefix scaling). */ unit?: MetricUnit; /** "auto" picks a prefix from the data magnitude; k/m/g/t force one. */ scale?: MetricScale; } /** Shared recharts line-chart renderer used by PrometheusChart + qBit speed widgets. */ export function LineSeriesChart({ series, height = 300, unit = "none", scale = "auto", }: LineSeriesChartProps) { // Choose ONE (divisor, suffix) from the series magnitude so the axis and // tooltip stay consistent (e.g. all values shown in MB/s). const maxAbs = series.reduce((m, s) => { for (const p of s.points) { const v = p.v == null ? 0 : Math.abs(p.v); if (v > m) m = v; } return m; }, 0); const scaleInfo = metricScaleInfo(maxAbs, unit, scale); const fmt = (v: number | null | undefined) => formatScaled(v, scaleInfo, unit); return ( formatTime(Number(label))} formatter={(value) => fmt(Number(value))} contentStyle={{ backgroundColor: "var(--color-popover)", border: "1px solid var(--color-border)", borderRadius: "0.5rem", color: "var(--color-popover-foreground)", }} /> {series.map((s, i) => ( ))} ); }