import { ChatGptUsageButton } from "../settings/ChatGptUsageButton"; import { usesChatGptSharing } from "@t3tools/shared/usageLimits"; import { RefreshIcon } from "~/components/ui/refresh-icon"; import { useAtomValue } from "@effect/atom-react"; import { Atom } from "effect/reactivity"; import { USAGE_CONTRACT_VERSION, type EnvironmentId, type UsageProviderKind, type UsageSummaryInput, } from "@t3tools/contracts"; import { CircleAlertIcon, ChevronDownIcon, InfoIcon, SlidersHorizontalIcon } from "lucide-react"; import { useEffect, useEffectEvent, useId, useRef, useState } from "react"; import { settlePromise } from "@t3tools/client-runtime/state/runtime"; import * as AsyncResult from "effect/reactivity/AsyncResult"; import * as Cause from "effect/Cause"; import { refreshUsageLimits } from "@t3tools/client-runtime/state/usage"; import { updatingProvidersLabel, usageEnvironmentProgress, usageLoadingState, } from "@t3tools/client-runtime/state/usage-progress"; import { isCompatibleUsageContractVersion, isModelCostUnknown, type DailyTotals, type HourlyTotals, type MergedUsage, } from "@t3tools/shared/usageMerge"; import { cn } from "../../lib/utils"; import { usePrimarySettings } from "../../hooks/useSettings"; import { resolveHour12 } from "../../timestampFormat"; import { appAtomRegistry } from "~/rpc/atomRegistry"; import { environmentPresentations } from "../../state/presentation"; import { primaryServerKeybindingsAtom, serverEnvironment } from "../../state/server"; import { isCommandPaletteOpen } from "../../commandPaletteBus"; import { isModelPickerOpen } from "../../modelPickerVisibility"; import { shortcutLabelForCommand } from "../../keybindings"; import { useUsage, type EnvironmentUsageStatus } from "../../state/usage"; import { useAtomCommand } from "../../state/use-atom-command"; import { enumerateDays, enumerateHourStarts, formatCount, formatDateTimeShort, formatDayShort, formatHourShort, formatPercent, formatTokens, formatUsageContractMismatch, formatUsd, makeWindow, usageSourceMessages, } from "@t3tools/shared/usageFormat"; import { Button, InlineButton } from "../ui/button"; import { ProviderInstanceIcon } from "../chat/ProviderInstanceIcon"; import { Menu, MenuCheckboxItem, MenuItem, MenuPopup, MenuSeparator, MenuTrigger, } from "../ui/menu"; import { ScrollArea } from "../ui/scroll-area"; import { Select, SelectItem, SelectPopup, SelectTrigger, SelectValue } from "../ui/select"; import { AppRouteModal, AppRouteModalClose } from "../AppRouteModal"; import { Skeleton } from "../ui/skeleton"; import { Toggle, ToggleGroup } from "../ui/toggle-group"; import { Tooltip, TooltipPopup, TooltipTrigger } from "../ui/tooltip"; import { Popover, PopoverPopup, PopoverTrigger } from "../ui/popover"; import { WorkspaceBreadcrumb, WorkspaceBreadcrumbItem, WorkspaceBreadcrumbSeparator, WorkspaceBreadcrumbText, } from "../WorkspaceBreadcrumb"; import { WorkspacePageContainer } from "../WorkspacePageContainer"; import { UsageLimitsSection } from "./UsageLimits"; import { UsagePriceOverrides } from "./UsagePriceOverrides"; import { UsageProviderChart } from "./UsageProviderChart"; import { SpeedPremium, UsageModelDialog } from "./UsageModelDialog"; import { UsageShareBar } from "./UsageShareBar"; import { costTypeSegments, modelShare, sortModelsByTokens, speedCostSegments, tokenTypeSegments, usageEmptyState, } from "./usageBreakdown"; import { METRIC_OPTIONS, WINDOW_OPTIONS, resolveUsageShortcut, type UsageMetric, } from "./usageShortcuts"; import { PROVIDER_ORDER, PROVIDER_PRESENTATION, providersWithUsage, rankProvidersByMetric, } from "./usageProviders"; import { readUsagePagePreferences, saveUsagePagePreferences, type UsagePagePreferences, } from "./usagePagePreferences"; function isUsageMetric(value: string | null | undefined): value is UsageMetric { return METRIC_OPTIONS.some((option) => option.value === value); } function isUsageWindowDays(value: number): value is UsagePagePreferences["windowDays"] { return WINDOW_OPTIONS.some((option) => option.days === value); } const providerLabel = (provider: UsageProviderKind) => PROVIDER_PRESENTATION[provider].label; function usageLoading( showingKept: boolean, selectedEnvironments: Parameters[0], refreshingUsage: boolean, hiddenProviders: ReadonlySet, ) { if (showingKept) { return { partial: true, everyProvider: true, providers: new Set() }; } const state = usageLoadingState(selectedEnvironments, refreshingUsage); const providers = new Set([...state.providers].filter((p) => !hiddenProviders.has(p))); return { partial: state.everyProvider || providers.size > 0, everyProvider: state.everyProvider, providers, }; } /** Keep dialog selection outside UsagePage so opening/closing rerenders only its host. */ const selectedUsageModelKeyAtom = Atom.make(null).pipe( Atom.keepAlive, Atom.withLabel("usage:selected-model-key"), ); function selectUsageModel(key: string | null): void { appAtomRegistry.set(selectedUsageModelKeyAtom, key); } function UsageModelDialogHost({ models, onSetPrice, ...dialogProps }: Omit[0], "model" | "onSetPrice" | "onClose"> & { readonly models: MergedUsage["models"]; readonly onSetPrice: (model: string) => void; }) { const selectedKey = useAtomValue(selectedUsageModelKeyAtom); // oxlint-disable-next-line t3code/no-unreasoned-state-sync -- clears the shared selected-model atom on unmount useEffect(() => () => selectUsageModel(null), []); const model = selectedKey === null ? undefined : models.find((candidate) => `${candidate.provider}:${candidate.model}` === selectedKey); if (model === undefined) return null; return ( { selectUsageModel(null); onSetPrice(model.model); }} onClose={() => selectUsageModel(null)} /> ); } export function UsagePage() { const [preferences, setPreferences] = useState(readUsagePagePreferences); const keybindings = useAtomValue(primaryServerKeybindingsAtom); const hour12 = usePrimarySettings((settings) => resolveHour12(settings.timestampFormat)); const shortcutTitle = ( option: (typeof METRIC_OPTIONS)[number] | (typeof WINDOW_OPTIONS)[number], ) => { const shortcut = shortcutLabelForCommand(keybindings, option.command, { context: { usagePageOpen: true }, }); return shortcut ? `${option.label} (${shortcut})` : option.label; }; const [windowSelection, setWindowSelection] = useState(() => ({ days: preferences.windowDays, window: makeWindow( preferences.windowDays, undefined, preferences.windowDays === 1 ? "hour" : "day", ), })); const metric = preferences.metric; const showingLimits = metric === "limits"; const [isRefreshing, setIsRefreshing] = useState(false); const [limitsNow, setLimitsNow] = useState(() => Date.now()); const refreshingRef = useRef(false); const [priceDialog, setPriceDialog] = useState<{ readonly model?: string } | null>(null); const [selectedEnvironmentIds, setSelectedEnvironmentIds] = useState | null>(null); const hiddenProviders = new Set(preferences.hiddenProviders); const { days: windowDays, window } = windowSelection; const isPast24Hours = windowDays === 1; const { merged: answeredUsage, environments, selectedEnvironments, shown, isPartial, refresh, } = useUsage(window, selectedEnvironmentIds, hiddenProviders); const merged = shown?.merged ?? answeredUsage; const shownWindow = shown?.window ?? window; const shownHourly = shownWindow.resolution === "hour"; const refreshingUsage = isRefreshing && !showingLimits; const showingKept = shown !== null && shown.window !== window; const presentations = useAtomValue(environmentPresentations.presentationsAtom); const presentationsWithProviders = useAtomValue( environmentPresentations.presentationsWithProvidersAtom, ); const refreshProviders = useAtomCommand(serverEnvironment.refreshProviders, { reportFailure: false, }); const canReadDiagnostics = selectedEnvironments.some( (environment) => environment.canReadDiagnostics, ); const updatePreferences = (patch: Partial) => { const nextPreferences = { ...preferences, ...patch }; setPreferences(nextPreferences); saveUsagePagePreferences(nextPreferences); }; const selectWindow = (days: number) => { if (!isUsageWindowDays(days)) return; updatePreferences({ windowDays: days }); setWindowSelection({ days, window: makeWindow(days, undefined, days === 1 ? "hour" : "day"), }); }; const selectMetric = (nextMetric: UsageMetric) => { if (nextMetric === "limits") setLimitsNow(Date.now()); updatePreferences({ metric: nextMetric }); }; const refreshLimits = (automatic = false) => // Settled so the timestamp still advances when a refresh throws. settlePromise(() => Promise.all( Array.from(presentations, ([environmentId, presentation]) => { if (selectedEnvironmentIds !== null && !selectedEnvironmentIds.has(environmentId)) return; if (presentation.connection.phase === "connected" && presentation.serverConfig !== null) { return refreshUsageLimits( environmentId, () => refreshProviders({ environmentId, input: {} }), automatic, ); } }), ), ).then((result) => { setLimitsNow(Date.now()); if (!AsyncResult.isSuccess(result)) throw Cause.squash(result.cause); }); const onUsageKeyDown = useEffectEvent((event: KeyboardEvent) => { if ( event.defaultPrevented || event.repeat || event.isComposing || isCommandPaletteOpen() || isModelPickerOpen() ) return; const command = resolveUsageShortcut(event, keybindings); const metricOption = METRIC_OPTIONS.find((option) => option.command === command); const periodOption = WINDOW_OPTIONS.find((option) => option.command === command); if (!metricOption && !periodOption) return; event.preventDefault(); event.stopPropagation(); if (metricOption) selectMetric(metricOption.value); if (periodOption && !showingLimits) selectWindow(periodOption.days); }); // oxlint-disable-next-line t3code/no-unreasoned-state-sync -- window capture keydown listener for usage metric and period shortcuts useEffect(() => { globalThis.window.addEventListener("keydown", onUsageKeyDown, true); return () => globalThis.window.removeEventListener("keydown", onUsageKeyDown, true); }, []); const refreshWindow = () => { if (refreshingRef.current) return; if (showingLimits) { refreshingRef.current = true; setIsRefreshing(true); void refreshLimits().finally(() => { refreshingRef.current = false; setIsRefreshing(false); }); return; } 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 ) { setWindowSelection({ days: windowDays, window: nextWindow }); } refreshingRef.current = true; setIsRefreshing(true); void refresh(nextWindow).finally(() => { refreshingRef.current = false; setIsRefreshing(false); }); }; const connectedLimitsEnvironments = [...presentations] .filter( ([environmentId, presentation]) => presentation.connection.phase === "connected" && presentation.serverConfig !== null && (selectedEnvironmentIds === null || selectedEnvironmentIds.has(environmentId)), ) .map(([environmentId]) => environmentId) .sort() .join(","); const autoRefreshLimits = useEffectEvent(() => { void refreshLimits(true); }); // oxlint-disable-next-line t3code/no-unreasoned-state-sync -- refreshes provider limits from the server when the connected environment set changes useEffect(() => { if (showingLimits && connectedLimitsEnvironments) autoRefreshLimits(); }, [showingLimits, connectedLimitsEnvironments]); const days = enumerateDays(shownWindow.sinceDay, shownWindow.untilDay); const hours = shownWindow.sinceTime === undefined || shownWindow.untilTime === undefined ? [] : enumerateHourStarts(shownWindow.sinceTime, shownWindow.untilTime); const showChatGptSharing = !hiddenProviders.has("codex") && [...presentationsWithProviders].some( ([id, presentation]) => (selectedEnvironmentIds === null || selectedEnvironmentIds.has(id)) && (presentation.serverConfig?.providers ?? []).some(usesChatGptSharing), ); const windowLabel = shownHourly && shownWindow.sinceTime !== undefined && shownWindow.untilTime !== undefined ? `${formatDateTimeShort(shownWindow.sinceTime, shownWindow.timeZone, hour12)} to ${formatDateTimeShort(shownWindow.untilTime, shownWindow.timeZone, hour12)}` : `${formatDayShort(shownWindow.sinceDay)} to ${formatDayShort(shownWindow.untilDay)}`; const topbarContent = (

Usage

setPriceDialog({})} /> updatePreferences({ hiddenProviders: next })} />
{!showingLimits ? ( {windowLabel} ) : null}
{/* Limits are always current, so they have no period. The period sits before the metric tabs so dropping it never moves them. */} {showingLimits ? null : ( { const value = next[0]; if (value) selectWindow(Number(value)); }} > {WINDOW_OPTIONS.map((option) => ( {option.label} ))} )} { const value = next[0]; if (isUsageMetric(value)) selectMetric(value); }} > {METRIC_OPTIONS.map((option) => ( {option.label} ))}
{showingLimits ? null : ( )}
); return (
{topbarContent}
{selectedEnvironments.length === 0 ? (

{environments.length === 0 ? `Connect an environment to see ${showingLimits ? "limits" : "usage"}.` : `Select an environment to see ${showingLimits ? "limits" : "usage"}.`}

) : showingLimits ? ( ) : shown === null ? ( ) : !canReadDiagnostics ? (
{selectedEnvironments.map((environment) => (

{selectedEnvironments.length > 1 ? `${environment.label}: ` : null} {environment.error}

))}
) : ( updatePreferences({ breakdown })} onShowAllProviders={() => updatePreferences({ hiddenProviders: [] })} onRefresh={refreshWindow} onOpenModelPrices={() => setPriceDialog({})} /> )}
{showingLimits ? null : ( setPriceDialog({ model })} /> )} {priceDialog ? ( { if (!open) setPriceDialog(null); }} /> ) : null}
); } function UsageOverview({ merged, shownWindow, hour12, days, hours, showingKept, metric, selectedEnvironments, hiddenProviders, refreshingUsage, showChatGptSharing, onSelectModel, breakdown, onBreakdownChange, onShowAllProviders, onRefresh, onOpenModelPrices, }: { readonly merged: MergedUsage; readonly shownWindow: UsageSummaryInput; readonly hour12: boolean; readonly days: readonly string[]; readonly hours: readonly string[]; readonly showingKept: boolean; readonly metric: "tokens" | "cost"; readonly selectedEnvironments: readonly EnvironmentUsageStatus[]; readonly hiddenProviders: ReadonlySet; readonly refreshingUsage: boolean; readonly showChatGptSharing: boolean; readonly onSelectModel: (key: string) => void; readonly breakdown: NonNullable; readonly onBreakdownChange: (breakdown: NonNullable) => void; readonly onShowAllProviders: () => void; readonly onRefresh: () => void; readonly onOpenModelPrices: () => void; }) { const [showExactValues, setShowExactValues] = useState(false); const shownHourly = shownWindow.resolution === "hour"; const loading = usageLoading(showingKept, selectedEnvironments, refreshingUsage, hiddenProviders); // Copy the primitive; JSX reads of loading.partial would merge all derivations into one compiler cache // scope. const loadingPartial = loading.partial === true; const loadingProviders = new Set( PROVIDER_ORDER.filter((provider) => loading.everyProvider || loading.providers.has(provider)), ); const providersWithData = new Set(providersWithUsage(merged.providers)); // Only loading.providers excludes hidden providers without data. Filter a fresh array so the compiler can // cache independently. const activeProviders = [...PROVIDER_ORDER].filter( (provider) => providersWithData.has(provider) || loading.providers.has(provider), ); const chartLoadingProviders = new Set( activeProviders.filter((provider) => loadingProviders.has(provider)), ); const sourceMessages = usageSourceMessages( selectedEnvironments.flatMap( (environment) => environment.summary?.sources.filter( (source) => !hiddenProviders.has(source.fingerprint.provider), ) ?? [], ), ); const breakdownPeriods: readonly (DailyTotals | HourlyTotals)[] = ( shownHourly ? merged.hourly : merged.daily ).toReversed(); const breakdownModels = breakdown === "model" && metric === "tokens" ? sortModelsByTokens(merged.models) : merged.models; const rankedProviders = rankProvidersByMetric(activeProviders, merged.providers, metric); const allProvidersHidden = PROVIDER_ORDER.every((provider) => hiddenProviders.has(provider)); const emptyState = usageEmptyState({ environments: selectedEnvironments, hasUsage: merged.models.length > 0, loading: loadingPartial, allProvidersHidden, }); if (emptyState !== null) { return (

{emptyState === "hidden" ? "All providers are hidden" : emptyState === "pending" ? "Waiting for usage" : emptyState === "unavailable" ? "Usage is unavailable" : "No usage reported in this period"}

{emptyState === "hidden" ? "Show providers to see their cost and tokens." : emptyState === "pending" ? "Selected environments are still connecting or reporting. Totals are not complete yet." : emptyState === "unavailable" ? "Check the selected environments, then refresh to try again." : "Try another period or provider. Only activity recorded in the selected environments is included."}

{sourceMessages.map((message) => (

{message}

))} {showChatGptSharing ? (
ChatGPT shared usage
) : null} {emptyState === "hidden" ? ( ) : emptyState === "unavailable" ? ( ) : emptyState === "empty" && hiddenProviders.size > 0 ? ( ) : null}
); } return (
{sourceMessages.map((message) => (

{message}

))} {merged.costQuality.unpricedShare > 0 ? (

Cost excludes {formatPercent(merged.costQuality.unpricedShare)} of records with no price. Tokens include them.{" "} Set model prices

) : null}
{formatCount(merged.sessions)} sessions {metric === "cost" && " · API estimate"}
{showChatGptSharing ? (
ChatGPT shared usage
) : null} {rankedProviders.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" }`; const providerLoading = loadingProviders.has(provider); const awaitingData = providerLoading && !providersWithData.has(provider); return (
{PROVIDER_PRESENTATION[provider].label} {sessionLabel} {awaitingData ? "-" : 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)}`}
); })}

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

Breakdown

{ const value = next[0]; if (value === "model" || value === "time") { onBreakdownChange(value); } }} > {( [ { value: "model", label: "Model" }, { value: "time", label: shownHourly ? "Hour" : "Day" }, ] as const ).map((option) => ( {option.label} ))}
{breakdown !== "time" ? ( <>

Highest {metric === "cost" ? "estimated cost" : "token use"} first. Bars and percentages show share of displayed {metric}.

{showExactValues ? (

Exact cost and tokens are shown. If needed, scroll the table horizontally for more columns.

) : null}
{breakdownModels.map((row, index) => { const model = row; const key = `${model.provider}:${model.model}`; const share = modelShare(model, metric); return ( ); })}
Usage by {breakdown}, sorted by {metric === "cost" ? "estimated cost" : "tokens"}, highest first
# Model Cost{metric === "cost" ? ↓ : null} % of {metric} Tokens{metric === "tokens" ? ↓ : null}
{index + 1} {share !== null && share > 0 ? (
) : null}
{metric === "cost" && share !== null ? ( Share of cost: {formatPercent(share)} ) : null} {share === null ? "n/a" : formatPercent(share)} {metric === "tokens" && share !== null ? ( Share of tokens: {formatPercent(share)} ) : null}

Costs are API estimates, not your subscription bill. Rounded rows may differ from the total.

) : (
{activeProviders.map((provider) => ( ))} {breakdownPeriods.length === 0 ? ( ) : ( breakdownPeriods.map((period) => ( {activeProviders.map((provider) => ( ))} )) )}
Usage by {shownHourly ? "hour" : "day"}, newest first
{shownHourly ? "Hour" : "Day"} {PROVIDER_PRESENTATION[provider].label} Total Tokens
No activity in this window.
{"hourStart" in period ? formatHourShort(period.hourStart, shownWindow.timeZone, hour12) : formatDayShort(period.day)} {formatUsd(period.byProvider.get(provider)?.costUsd ?? 0)} {formatUsd(period.costUsd)} {formatTokens(period.totalTokens)}
)}

Totals

{merged.totalTokens > 0 ? (
{metric === "tokens" ? ( ) : ( <> {merged.speedCost.fast + merged.speedCost.ultrafast > 0 ? ( } /> ) : null} )}
) : null}
); } const PRECISE_USD = new Intl.NumberFormat("en-US", { style: "currency", currency: "USD", minimumFractionDigits: 2, maximumFractionDigits: 6, }); const preciseUsd = (value: number) => PRECISE_USD.format(value); function UsageFigure({ value, precise, focusable = false, exact = false, }: { readonly value: string; readonly precise: string; readonly focusable?: boolean; readonly exact?: boolean; }) { const tooltipId = useId(); if (exact) return {precise}; return ( } > {value} {precise} {precise} ); } function UsageInfo({ label, children }: { readonly label: string; readonly children: string }) { return ( } aria-label={label}> {children} ); } function UsageCost({ exact, model, }: { readonly exact: boolean; readonly model: MergedUsage["models"][number]; }) { const unknown = isModelCostUnknown(model); return ( <> {unknown ? ( Unpriced ) : ( )} {!unknown && model.unpricedRecords > 0 ? ( Partial {`Excludes ${formatCount(model.unpricedRecords)} of ${formatCount(model.records)} records with no price. Their tokens are included.`} ) : null} ); } function ProviderMark({ provider, className, }: { readonly provider: UsageProviderKind; readonly className: string; }) { const presentation = PROVIDER_PRESENTATION[provider]; return ( ); } function figureClass(loading: boolean) { return cn("transition-opacity", loading && "opacity-40 delay-150"); } function Metric({ label, value, loading, }: { readonly label: string; readonly value: string; readonly loading: boolean; }) { return (
{label} {value}
); } function UsageCoverageNotice({ environments, duplicateSources, contractMismatches, }: { readonly environments: readonly EnvironmentUsageStatus[]; readonly duplicateSources: readonly string[]; readonly contractMismatches: MergedUsage["contractMismatches"]; }) { const pending = environments.filter( (environment) => environment.summary === null && environment.error === null, ); const failed = environments.filter((environment) => environment.error !== null); const mismatchByEnvironment = new Map( contractMismatches.map((mismatch) => [mismatch.environmentId, mismatch]), ); const incompatible = environments.flatMap((environment) => { const mismatch = mismatchByEnvironment.get(environment.environmentId); return mismatch === undefined ? [] : [{ environment, mismatch }]; }); if ( pending.length === 0 && failed.length === 0 && incompatible.length === 0 && duplicateSources.length === 0 ) { return null; } return (
{pending.length > 0 ? ( Waiting for {pending.map((environment) => environment.label).join(", ")}. Totals are incomplete. ) : null} {failed.map((environment) => ( {environment.label}: {environment.error} ))} {incompatible.map(({ environment, mismatch }) => ( {formatUsageContractMismatch(environment.label, mismatch)} ))} {duplicateSources.length > 0 ? ( Counted once across environments sharing a transcript directory:{" "} {duplicateSources.join(", ")} ) : null}
); } function UsageEnvironmentFilter({ environments, selectedEnvironments, selectedEnvironmentIds, onSelectionChange, showUsageStatus, refreshing, isPartial, duplicateSources, contractMismatches, onOpenModelPrices, }: { readonly environments: readonly EnvironmentUsageStatus[]; readonly selectedEnvironments: readonly EnvironmentUsageStatus[]; readonly selectedEnvironmentIds: ReadonlySet | null; readonly onSelectionChange: (ids: ReadonlySet | null) => void; readonly showUsageStatus: boolean; readonly refreshing: boolean; readonly isPartial: boolean; readonly duplicateSources: readonly string[]; readonly contractMismatches: MergedUsage["contractMismatches"]; readonly onOpenModelPrices: () => void; }) { const allSelected = selectedEnvironmentIds === null; const label = allSelected ? "All environments" : selectedEnvironments.length === 1 ? selectedEnvironments[0]!.label : `${selectedEnvironments.length} environments`; const hasIssue = selectedEnvironments.some((environment) => environment.error !== null) || contractMismatches.length > 0; return ( } className="group/usage-environment min-w-0 max-w-full"> {label} {showUsageStatus && hasIssue ? ( ) : ( )} onSelectionChange(checked ? null : new Set())} > All environments {environments.map((environment) => { const checked = selectedEnvironmentIds === null || selectedEnvironmentIds.has(environment.environmentId); const progress = usageEnvironmentProgress(environment, refreshing); const status = environment.error !== null ? "Unavailable" : environment.summary !== null && !isCompatibleUsageContractVersion( environment.summary.contractVersion, USAGE_CONTRACT_VERSION, ) ? "Update required" : !environment.isConnected ? "Connecting…" : progress.phase === "loading" ? "Scanning…" : progress.phase === "stale" ? "Refreshing…" : progress.phase === "partway" ? updatingProvidersLabel(progress.providers, providerLabel) : "Ready"; return ( { const next = new Set(selectedEnvironments.map((entry) => entry.environmentId)); if (nextChecked) next.add(environment.environmentId); else next.delete(environment.environmentId); onSelectionChange(next.size === environments.length ? null : next); }} > {environment.label} {showUsageStatus ? ( {status} ) : null} ); })} {environments.length === 0 ? (

No environments connected.

) : null} {showUsageStatus && isPartial ? (

Totals are partial while selected environments scan.

) : null} {showUsageStatus ? ( ) : null} Model prices
); } function UsageProviderFilter({ hiddenProviders, onChange, }: { readonly hiddenProviders: ReadonlySet; readonly onChange: (hiddenProviders: readonly UsageProviderKind[]) => void; }) { const visible = PROVIDER_ORDER.filter((provider) => !hiddenProviders.has(provider)); const label = visible.length === PROVIDER_ORDER.length ? "All providers" : visible.length === 0 ? "No providers" : visible.length === 1 ? PROVIDER_PRESENTATION[visible[0]!].label : `${visible.length} providers`; return ( } className="group/usage-provider min-w-0 max-w-full"> {label} onChange(checked ? [] : PROVIDER_ORDER)} > All providers {PROVIDER_ORDER.map((provider) => ( onChange( PROVIDER_ORDER.filter((entry) => entry === provider ? !checked : hiddenProviders.has(entry), ), ) } > {PROVIDER_PRESENTATION[provider].label} ))} ); } function UsageSkeleton() { return ( <>
{PROVIDER_ORDER.map((provider) => (
))}

Breakdown

Totals

); } function MetricSkeletons({ labels }: { readonly labels: readonly string[] }) { return (
{labels.map((label) => (
{label}
))}
); }