1import type { UsageProviderKind } from "@t3tools/contracts";
2
3import { ProviderDriverKind } from "@t3tools/contracts";
4
5type UsageProviderPresentation = {
6 readonly label: string;
7 readonly color: string;
8 readonly driverKind: ProviderDriverKind;
9};
10
11/**
12 * Exhaustive presentation for providers supported by the usage contract.
13 * Declaration order is reused by every chart and table, so adding a provider
14 * only requires its contract support and one entry here.
15 */
16export const PROVIDER_PRESENTATION = {
17 codex: {
18 label: "Codex",
19 color: "var(--contrast-foreground)",
20 driverKind: ProviderDriverKind.make("codex"),
21 },
22 claude: {
23 label: "Claude Code",
24 color: "#d97757",
25 driverKind: ProviderDriverKind.make("claudeAgent"),
26 },
27 grok: {
28 label: "Grok Build",
29 // Contrast-aware neutral between the Codex series and muted chart chrome.
30 color: "color-mix(in oklab, var(--contrast-foreground) 72%, var(--background))",
31 driverKind: ProviderDriverKind.make("grok"),
32 },
33 cursor: { label: "Cursor", color: "#8b8b8b", driverKind: ProviderDriverKind.make("cursor") },
34 opencode: {
35 label: "OpenCode",
36 color: "#5b9bbd",
37 driverKind: ProviderDriverKind.make("opencode"),
38 },
39 antigravity: {
40 label: "Antigravity",
41 color: "#8c7bd1",
42 driverKind: ProviderDriverKind.make("antigravity"),
43 },
44 pi: {
45 label: "Pi",
46 color: "#4d9f8a",
47 driverKind: ProviderDriverKind.make("pi"),
48 },
49} satisfies Record<UsageProviderKind, UsageProviderPresentation>;
50
51/** Stable provider reading order across charts, summaries, tables, and hover rows. */
52export const PROVIDER_ORDER = Object.keys(PROVIDER_PRESENTATION) as UsageProviderKind[];
53
54/** Providers with real activity, independent of the metric currently displayed. */
55export function providersWithUsage(
56 totals: readonly {
57 readonly provider: UsageProviderKind;
58 readonly costUsd: number;
59 readonly totalTokens: number;
60 }[],
61): readonly UsageProviderKind[] {
62 const active = new Set(
63 totals
64 .filter((entry) => entry.totalTokens > 0 || entry.costUsd > 0)
65 .map((entry) => entry.provider),
66 );
67 return PROVIDER_ORDER.filter((provider) => active.has(provider));
68}