'use client'; import { useEffect, useState } from 'react'; import { InteractiveLineChart, Legend, ScatterChart, type ScatterPoint, type Series } from '@/components/charts'; import { fmtDate, fmtInt, fmtScore, fmtUsdPerM } from '@/lib/format'; /* Client wrappers that own their formatter functions: server pages cannot pass functions to client components, so the formatting choices (money, score, GB, GB/s, dates) live here and pages pass data only. */ /** Quality vs cheapest output price (log x), Pareto set highlighted and joined by a dashed line. */ export function EfficiencyScatter({ points, frontier, highlight, yLabel, height = 380 }: { points: ScatterPoint[]; frontier: { x: number; y: number }[]; highlight: string[]; yLabel: string; height?: number }) { // Rendered after mount only: the log-scale pixel positions differ in the last float digits between Node and the browser, // which React reports as a hydration mismatch. The Pareto list below the chart is server-rendered for crawlers. const [mounted, setMounted] = useState(false); useEffect(() => setMounted(true), []); if (!mounted) return
; return fmtUsdPerM(v)} yFormat={(v) => fmtScore(v)} frontier={frontier} highlight={highlight} height={height} labelTop={0} />; } /** Step chart of a hardware figure by release date (memory in GB or bandwidth in GB/s), one series per manufacturer. */ export function HardwareStepChart({ series, unit, yLabel, height = 280 }: { series: Series[]; unit: 'GB' | 'GB/s'; yLabel: string; height?: number }) { return ( <> `${fmtInt(v)} ${unit}`} yLabel={yLabel} xFormat={(x) => fmtDate(new Date(x).toISOString().slice(0, 10))} /> ); }