diff --git a/apps/web/src/components/usage/UsagePage.tsx b/apps/web/src/components/usage/UsagePage.tsx
index 8bc8a93a43d69..aa2fee8f1de22 100644
--- a/apps/web/src/components/usage/UsagePage.tsx
+++ b/apps/web/src/components/usage/UsagePage.tsx
@@ -87,6 +87,7 @@ import {
modelShare,
sortModelsByTokens,
speedCostSegments,
+ timeBreakdownMetric,
tokenTypeSegments,
} from "./usageBreakdown";
import {
@@ -638,6 +639,7 @@ function UsageOverview({
const breakdownPeriods: readonly (DailyTotals | HourlyTotals)[] = (
shownHourly ? merged.hourly : merged.daily
).toReversed();
+ const timeMetric = timeBreakdownMetric(metric);
const breakdownModels =
breakdown === "model" && metric === "tokens"
? sortModelsByTokens(merged.models)
@@ -961,7 +963,9 @@ function UsageOverview({
))}
Total |
- Tokens |
+
+ {timeMetric.secondaryLabel}
+ |
@@ -993,7 +997,7 @@ function UsageOverview({
figureClass(loadingProviders.has(provider)),
)}
>
- {formatUsd(period.byProvider.get(provider)?.costUsd ?? 0)}
+ {timeMetric.formatValue(period.byProvider.get(provider))}
))}
- {formatUsd(period.costUsd)}
+ {timeMetric.formatValue(period)}
|
- {formatTokens(period.totalTokens)}
+ {timeMetric.formatSecondaryValue(period)}
|
))
diff --git a/apps/web/src/components/usage/usageBreakdown.test.ts b/apps/web/src/components/usage/usageBreakdown.test.ts
index 14fe70017fcbc..15a34c5adb582 100644
--- a/apps/web/src/components/usage/usageBreakdown.test.ts
+++ b/apps/web/src/components/usage/usageBreakdown.test.ts
@@ -1,7 +1,34 @@
-import type { ModelTotals } from "@t3tools/shared/usageMerge";
+import type { DailyTotals, ModelTotals } from "@t3tools/shared/usageMerge";
import { describe, expect, it } from "vite-plus/test";
-import { cacheHitRate, costPerMillionTokens } from "./usageBreakdown";
+import { cacheHitRate, costPerMillionTokens, timeBreakdownMetric } from "./usageBreakdown";
+
+describe("time breakdown metric", () => {
+ const period: DailyTotals = {
+ day: "2026-10-22",
+ costUsd: 3.5,
+ totalTokens: 2_500_000,
+ byProvider: new Map([["codex", { costUsd: 3.5, totalTokens: 2_500_000 }]]),
+ };
+
+ it.each([
+ {
+ metric: "cost",
+ value: "$3.50",
+ missing: "$0.00",
+ secondaryLabel: "Tokens",
+ secondary: "2.50M",
+ },
+ { metric: "tokens", value: "2.50M", missing: "0", secondaryLabel: "Cost", secondary: "$3.50" },
+ ] as const)("shows $metric figures with the other metric trailing", (row) => {
+ const figures = timeBreakdownMetric(row.metric);
+ expect(figures.formatValue(period.byProvider.get("codex"))).toBe(row.value);
+ expect(figures.formatValue(period.byProvider.get("claude"))).toBe(row.missing);
+ expect(figures.formatValue(period)).toBe(row.value);
+ expect(figures.secondaryLabel).toBe(row.secondaryLabel);
+ expect(figures.formatSecondaryValue(period)).toBe(row.secondary);
+ });
+});
const model = (
name: string,
diff --git a/apps/web/src/components/usage/usageBreakdown.ts b/apps/web/src/components/usage/usageBreakdown.ts
index 7fecab508c223..0d5de08d508bb 100644
--- a/apps/web/src/components/usage/usageBreakdown.ts
+++ b/apps/web/src/components/usage/usageBreakdown.ts
@@ -1,13 +1,34 @@
import type { UsageTokenTotals } from "@t3tools/contracts";
+import { formatTokens, formatUsd } from "@t3tools/shared/usageFormat";
import {
isModelCostUnknown,
type CategoryCost,
+ type DailyTotals,
type ModelTotals,
type SpeedCost,
} from "@t3tools/shared/usageMerge";
import type { ShareSegment } from "./UsageShareBar";
+export function timeBreakdownMetric(metric: "cost" | "tokens") {
+ const formatCost = (totals: Pick | undefined) =>
+ formatUsd(totals?.costUsd ?? 0);
+ const formatTokenCount = (totals: Pick | undefined) =>
+ formatTokens(totals?.totalTokens ?? 0);
+
+ return metric === "tokens"
+ ? {
+ formatValue: formatTokenCount,
+ secondaryLabel: "Cost",
+ formatSecondaryValue: formatCost,
+ }
+ : {
+ formatValue: formatCost,
+ secondaryLabel: "Tokens",
+ formatSecondaryValue: formatTokenCount,
+ };
+}
+
export function sortModelsByTokens(models: readonly ModelTotals[]) {
return models.toSorted(
(left, right) => right.totalTokens - left.totalTokens || right.costUsd - left.costUsd,