diff --git a/packages/views/runtimes/components/charts/daily-token-chart.tsx b/packages/views/runtimes/components/charts/daily-token-chart.tsx index d37efc180..ea06f1a73 100644 --- a/packages/views/runtimes/components/charts/daily-token-chart.tsx +++ b/packages/views/runtimes/components/charts/daily-token-chart.tsx @@ -1,34 +1,118 @@ +import { useMemo } from "react"; import { AreaChart, Area, XAxis, YAxis, CartesianGrid, + Tooltip, } from "recharts"; import { ChartContainer, - ChartTooltip, - ChartTooltipContent, - ChartLegend, - ChartLegendContent, type ChartConfig, } from "@multica/ui/components/ui/chart"; import type { DailyTokenData } from "../../utils"; import { formatTokens } from "../../utils"; const tokenChartConfig = { - input: { label: "Input", color: "hsl(var(--chart-1))" }, - output: { label: "Output", color: "hsl(var(--chart-2))" }, - cacheRead: { label: "Cache Read", color: "hsl(var(--chart-3))" }, - cacheWrite: { label: "Cache Write", color: "hsl(var(--chart-4))" }, + total: { label: "Total", color: "hsl(var(--chart-1))" }, } satisfies ChartConfig; +type DailyTokenRow = DailyTokenData & { total: number }; + +function computeNiceTicks(data: DailyTokenRow[], tickCount = 5): number[] { + const maxTotal = data.reduce( + (max, d) => (d.total > max ? d.total : max), + 0, + ); + if (maxTotal === 0) return [0]; + + const rawStep = maxTotal / (tickCount - 1); + const magnitude = Math.pow(10, Math.floor(Math.log10(rawStep))); + const niceSteps = [1, 2, 2.5, 5, 10]; + const niceStep = + magnitude * (niceSteps.find((s) => s * magnitude >= rawStep) ?? 10); + + const ticks: number[] = []; + for (let i = 0; i < tickCount; i++) { + ticks.push(niceStep * i); + } + if ((ticks[ticks.length - 1] ?? 0) < maxTotal) { + ticks.push(niceStep * tickCount); + } + return ticks; +} + +function TokenTooltipContent({ + active, + payload, + label, +}: { + active?: boolean; + payload?: Array<{ payload: DailyTokenRow }>; + label?: string; +}) { + if (!active || !payload?.length) return null; + const row = payload[0]!.payload; + + const items = [ + { label: "Input", value: row.input }, + { label: "Output", value: row.output }, + { label: "Cache Read", value: row.cacheRead }, + { label: "Cache Write", value: row.cacheWrite }, + ]; + + return ( +