fix: handle large usage daily chart histories (#3714)

This commit is contained in:
Neil
2026-05-30 07:15:17 -07:00
committed by GitHub
parent 0dada8c879
commit bddf05b949
3 changed files with 63 additions and 10 deletions
@@ -11,18 +11,26 @@ function formatTokens(value: number): string {
return value.toLocaleString()
}
function getDailyTotal(entry: ClaudeUsageDailyPoint): number {
return entry.inputTokens + entry.outputTokens + entry.cacheReadTokens + entry.cacheWriteTokens
}
function getMaxDailyTotal(daily: ClaudeUsageDailyPoint[]): number {
let max = 1
// Why: all-time usage histories can exceed V8's argument limit if spread
// into Math.max, even though the chart only renders the last 10 days.
for (const entry of daily) {
max = Math.max(max, getDailyTotal(entry))
}
return max
}
type ClaudeUsageDailyChartProps = {
daily: ClaudeUsageDailyPoint[]
}
export function ClaudeUsageDailyChart({ daily }: ClaudeUsageDailyChartProps): React.JSX.Element {
const maxDailyTotal = Math.max(
1,
...daily.map(
(entry) =>
entry.inputTokens + entry.outputTokens + entry.cacheReadTokens + entry.cacheWriteTokens
)
)
const maxDailyTotal = getMaxDailyTotal(daily)
return (
<section className="rounded-lg border border-border/60 bg-card/40 p-4">
@@ -34,8 +42,7 @@ export function ClaudeUsageDailyChart({ daily }: ClaudeUsageDailyChartProps): Re
</div>
<div className="grid h-56 grid-cols-10 items-end gap-3">
{daily.slice(-10).map((entry) => {
const total =
entry.inputTokens + entry.outputTokens + entry.cacheReadTokens + entry.cacheWriteTokens
const total = getDailyTotal(entry)
const segments = [
{
key: 'cache-write',
@@ -11,12 +11,22 @@ function formatTokens(value: number): string {
return value.toLocaleString()
}
function getMaxDailyTotal(daily: CodexUsageDailyPoint[]): number {
let max = 1
// Why: all-time usage histories can exceed V8's argument limit if spread
// into Math.max, even though the chart only renders the last 10 days.
for (const entry of daily) {
max = Math.max(max, entry.totalTokens)
}
return max
}
type CodexUsageDailyChartProps = {
daily: CodexUsageDailyPoint[]
}
export function CodexUsageDailyChart({ daily }: CodexUsageDailyChartProps): React.JSX.Element {
const maxDailyTotal = Math.max(1, ...daily.map((entry) => entry.totalTokens))
const maxDailyTotal = getMaxDailyTotal(daily)
return (
<section className="rounded-lg border border-border/60 bg-card/40 p-4">
@@ -0,0 +1,36 @@
import { describe, expect, it } from 'vitest'
import type { ClaudeUsageDailyPoint } from '../../../../shared/claude-usage-types'
import type { CodexUsageDailyPoint } from '../../../../shared/codex-usage-types'
import { ClaudeUsageDailyChart } from './ClaudeUsageDailyChart'
import { CodexUsageDailyChart } from './CodexUsageDailyChart'
function makeDay(index: number): string {
return new Date(Date.UTC(2026, 0, 1 + index)).toISOString().slice(0, 10)
}
describe('usage daily charts', () => {
it('renders Codex/OpenCode daily charts for very large histories', () => {
const daily: CodexUsageDailyPoint[] = Array.from({ length: 130_000 }, (_, index) => ({
day: makeDay(index),
inputTokens: index + 1,
cachedInputTokens: 0,
outputTokens: 0,
reasoningOutputTokens: 0,
totalTokens: index + 1
}))
expect(() => CodexUsageDailyChart({ daily })).not.toThrow()
})
it('renders Claude daily charts for very large histories', () => {
const daily: ClaudeUsageDailyPoint[] = Array.from({ length: 130_000 }, (_, index) => ({
day: makeDay(index),
inputTokens: index + 1,
outputTokens: 0,
cacheReadTokens: 0,
cacheWriteTokens: 0
}))
expect(() => ClaudeUsageDailyChart({ daily })).not.toThrow()
})
})