import type { UsageProviderKind } from "@t3tools/contracts"; import { CheckIcon, RefreshCwIcon, XIcon } from "lucide-react"; import { useMemo, useState } from "react"; import type { DailyTotals, HourlyTotals } from "@t3tools/shared/usageMerge"; import { isElectron } from "../../env"; import { cn } from "../../lib/utils"; import { useUsage, type EnvironmentUsageStatus } from "../../state/usage"; import { enumerateDays, enumerateHourStarts, formatCount, formatDateTimeShort, formatDayShort, formatHourShort, formatPercent, formatTokens, formatUsd, makeWindow, } from "@t3tools/shared/usageFormat"; import { Button } from "../ui/button"; import { ScrollArea } from "../ui/scroll-area"; import { Select, SelectItem, SelectPopup, SelectTrigger, SelectValue } from "../ui/select"; import { SidebarInset } from "../ui/sidebar"; import { Toggle, ToggleGroup } from "../ui/toggle-group"; import { WorkspaceBreadcrumb, WorkspaceBreadcrumbItem, WorkspaceBreadcrumbSeparator, } from "../WorkspaceBreadcrumb"; import { WorkspacePageContainer } from "../WorkspacePageContainer"; import { WorkspacePageHeader } from "../WorkspacePageHeader"; import { UsageProviderChart, type UsageChartMetric } from "./UsageProviderChart"; import { PROVIDER_ORDER, PROVIDER_PRESENTATION } from "./usageProviders"; const WINDOW_OPTIONS = [ { days: 1, label: "Past 24h" }, { days: 7, label: "7 days" }, { days: 30, label: "30 days" }, { days: 90, label: "90 days" }, ] as const; export function UsagePage() { const [windowSelection, setWindowSelection] = useState(() => ({ days: 30, window: makeWindow(30), })); const [metric, setMetric] = useState("cost"); const [breakdown, setBreakdown] = useState<"model" | "time">("model"); const { days: windowDays, window } = windowSelection; const isPast24Hours = windowDays === 1; const { merged, environments, isPending, isPartial, refresh } = useUsage(window); // Hold the content until every environment is terminal. Rendering merged // totals while devices are still answering makes every number on the page // jump as each one lands. const settling = isPending || isPartial; const days = useMemo( () => enumerateDays(window.sinceDay, window.untilDay), [window.sinceDay, window.untilDay], ); const hours = useMemo( () => window.sinceTime === undefined || window.untilTime === undefined ? [] : enumerateHourStarts(window.sinceTime, window.untilTime), [window.sinceTime, window.untilTime], ); // Newest first: the window can run 90 periods, so the interesting end // belongs at the top of the table. const breakdownPeriods = useMemo( () => (isPast24Hours ? merged.hourly : merged.daily).toReversed(), [isPast24Hours, merged.daily, merged.hourly], ); const selectWindow = (days: number) => { setWindowSelection({ days, window: makeWindow(days, undefined, days === 1 ? "hour" : "day"), }); }; const refreshWindow = () => { const nextWindow = makeWindow(windowDays, undefined, isPast24Hours ? "hour" : "day"); if ( nextWindow.sinceDay === window.sinceDay && nextWindow.untilDay === window.untilDay && nextWindow.sinceTime === window.sinceTime && nextWindow.untilTime === window.untilTime ) { refresh(); } else { setWindowSelection({ days: windowDays, window: nextWindow }); } }; const windowLabel = isPast24Hours && window.sinceTime !== undefined && window.untilTime !== undefined ? `${formatDateTimeShort(window.sinceTime, window.timeZone)} to ${formatDateTimeShort(window.untilTime, window.timeZone)}` : `${formatDayShort(window.sinceDay)} to ${formatDayShort(window.untilDay)}`; const topbarContent = (

Usage

{windowLabel}
{ const value = next[0]; if (value === "cost" || value === "tokens") setMetric(value); }} > {(["cost", "tokens"] as const).map((option) => ( {option === "cost" ? "Cost" : "Tokens"} ))} { const value = next[0]; if (value) selectWindow(Number(value)); }} > {WINDOW_OPTIONS.map((option) => ( {option.label} ))}
); return (
{topbarContent} {settling ? ( <> {environments.length > 1 ? : null} ) : ( <>
{metric === "cost" ? formatUsd(merged.costUsd) : formatTokens(merged.totalTokens)} {metric === "cost" ? `${formatCount(merged.sessions)} sessions · API estimate` : `${formatCount(merged.sessions)} sessions`}
{PROVIDER_ORDER.map((provider) => { const totals = merged.providers.find((entry) => entry.provider === provider); const share = metric === "cost" ? (totals?.costShare ?? 0) : (totals?.tokenShare ?? 0); const providerSessions = totals?.sessions ?? 0; const sessionLabel = `${formatCount(providerSessions)} ${ providerSessions === 1 ? "session" : "sessions" }`; return (
{PROVIDER_PRESENTATION[provider].label} {sessionLabel} {metric === "cost" ? formatUsd(totals?.costUsd ?? 0) : formatTokens(totals?.totalTokens ?? 0)}
{metric === "cost" ? `${formatPercent(share)} of cost · ${formatTokens(totals?.totalTokens ?? 0)} tokens` : `${formatPercent(share)} of tokens · ${formatUsd(totals?.costUsd ?? 0)}`}
); })}

{isPast24Hours ? "Hourly" : "Daily"}{" "} {metric === "tokens" ? "processed tokens" : "cost"}

Totals

Breakdown

{ const value = next[0]; if (value === "model" || value === "time") setBreakdown(value); }} > {( [ { value: "model", label: "Model" }, { value: "time", label: isPast24Hours ? "Hour" : "Day" }, ] as const ).map((option) => ( {option.label} ))}
{breakdown === "model" ? ( {merged.models.length === 0 ? ( ) : ( merged.models.map((model) => ( )) )}
Model Cost Share Tokens
No activity in this window.
{model.model} {formatUsd(model.costUsd)} {formatPercent(model.costShare)} {formatTokens(model.totalTokens)}
) : ( {PROVIDER_ORDER.map((provider) => ( ))} {breakdownPeriods.length === 0 ? ( ) : ( breakdownPeriods.map((period) => ( {PROVIDER_ORDER.map((provider) => ( ))} )) )}
{isPast24Hours ? "Hour" : "Day"} {PROVIDER_PRESENTATION[provider].label} Total Tokens
No activity in this window.
{"hourStart" in period ? formatHourShort(period.hourStart, window.timeZone) : formatDayShort(period.day)} {formatUsd(period.byProvider.get(provider)?.costUsd ?? 0)} {formatUsd(period.costUsd)} {formatTokens(period.totalTokens)}
)}
)}
); } /** Brand mark for the harness a row belongs to. */ function ProviderMark({ provider, className, }: { readonly provider: UsageProviderKind; readonly className: string; }) { const Mark = PROVIDER_PRESENTATION[provider].mark; return ; } function Metric({ label, value }: { readonly label: string; readonly value: string }) { return (
{label} {value}
); } /** * Says plainly when the totals are incomplete: an environment that failed, or * one whose transcripts another environment already reported. Environments * that are still answering never reach this notice; the page shows the * loading skeleton until every one is terminal. */ function UsageCoverageNotice({ environments, duplicateSources, staleEnvironments, }: { readonly environments: readonly EnvironmentUsageStatus[]; readonly duplicateSources: readonly string[]; readonly staleEnvironments: readonly string[]; }) { const failed = environments.filter((environment) => environment.error !== null); const stale = environments.filter((environment) => staleEnvironments.includes(environment.environmentId), ); if (failed.length === 0 && stale.length === 0 && duplicateSources.length === 0) { return null; } return (
{failed.map((environment) => ( {environment.label} could not report usage. ))} {stale.map((environment) => ( {environment.label} runs an older server version and is excluded from totals. ))} {duplicateSources.length > 0 ? ( Counted once across environments sharing a transcript directory:{" "} {duplicateSources.join(", ")} ) : null}
); } /** * Per-device progress while the page waits for every environment to answer. * Only rendered with two or more devices; a lone device has nothing to * enumerate. */ function UsageDeviceStrip({ environments, }: { readonly environments: readonly EnvironmentUsageStatus[]; }) { const scanning = environments.filter( (environment) => environment.summary === null && environment.error === null, ); return (
{environments.map((environment) => { if (environment.summary !== null) { return ( {environment.label} ); } if (environment.error !== null) { return ( {environment.label} ); } return ( {environment.label}… ); })} {scanning.length === 1 ? "1 device still scanning" : `${scanning.length} devices still scanning`}
); } /** * Static stand-in with the loaded page's shape. No shimmer; blocks fill in * exactly once when the last device answers. */ function UsageSkeleton() { return ( <>
{PROVIDER_ORDER.map((provider) => (
))}

Totals

{["Processed tokens", "Cached input", "Uncached input", "Output", "Cache savings"].map( (label) => (
{label}
), )}
); }