import { i18n } from "@/i18n/i18next"; import type { UsageDisplayAs } from "./preferences"; import type { UsageBalanceUnit } from "./types"; const providerUsageLabelKeys = { session: "providerUsage.labels.session", five_hour: "providerUsage.labels.session", weekly: "providerUsage.labels.weekly", monthly: "providerUsage.labels.monthly", code_review: "providerUsage.labels.codeReview", credits: "providerUsage.labels.credits", monthly_credits: "providerUsage.labels.monthlyCredits", } as const; export function formatProviderUsageLabel(id: string, fallback: string): string { const key = providerUsageLabelKeys[id as keyof typeof providerUsageLabelKeys]; return key ? i18n.t(key) : fallback; } export function clampPct(value: number): number { return Math.max(0, Math.min(100, value)); } export function formatPct(value: number, locale?: string): string { return new Intl.NumberFormat(locale, { style: "percent", maximumFractionDigits: 0, }).format(clampPct(value) / 100); } /** A localized percentage that says whether it is used or remaining. */ export function formatDisplayPct(value: number, displayAs: UsageDisplayAs, locale?: string): string { const percentage = formatPct(value, locale); return displayAs === "used" ? i18n.t("providerUsage.values.used", { percentage }) : i18n.t("providerUsage.values.remaining", { amount: percentage }); } type RelativeDuration = { unit: "now" } | { unit: "minutes" | "hours" | "days"; count: number }; function formatCount(value: number): string { return new Intl.NumberFormat(i18n.resolvedLanguage).format(value); } function relativeDuration(iso: string, now: number): RelativeDuration | null { const diffMs = new Date(iso).getTime() - now; if (!Number.isFinite(diffMs)) return null; if (diffMs <= 0) return { unit: "now" }; const diffMinutes = Math.floor(diffMs / 60_000); const diffHours = Math.floor(diffMinutes / 60); const diffDays = Math.floor(diffHours / 24); if (diffDays > 0) return { unit: "days", count: diffDays }; if (diffHours > 0) return { unit: "hours", count: diffHours }; return { unit: "minutes", count: diffMinutes }; } function formatRelativeDuration(duration: RelativeDuration): string | null { switch (duration.unit) { case "now": return null; case "days": return i18n.t("providerUsage.duration.days", { count: duration.count, value: formatCount(duration.count), }); case "hours": return i18n.t("providerUsage.duration.hours", { count: duration.count, value: formatCount(duration.count), }); case "minutes": return i18n.t("providerUsage.duration.minutes", { count: duration.count, value: formatCount(duration.count), }); } } export function formatResetLabel(iso: string | null | undefined, now = Date.now()): string | null { if (!iso) return null; const rel = relativeDuration(iso, now); if (!rel) return null; if (rel.unit === "now") return i18n.t("providerUsage.timing.resettingNow"); const duration = formatRelativeDuration(rel); return duration ? i18n.t("providerUsage.timing.resetsIn", { duration }) : null; } export function formatRunsOutLabel( iso: string | null | undefined, now = Date.now(), ): string | null { if (!iso) return null; const rel = relativeDuration(iso, now); if (!rel) return null; if (rel.unit === "now") return i18n.t("providerUsage.timing.runsOutNow"); const duration = formatRelativeDuration(rel); return duration ? i18n.t("providerUsage.timing.runsOutIn", { duration }) : null; } export function formatAgo(iso: string | null | undefined, now = Date.now()): string | null { if (!iso) return null; const diffMs = now - new Date(iso).getTime(); if (!Number.isFinite(diffMs)) return null; if (diffMs < 60_000) return i18n.t("providerUsage.timing.justNow"); const diffMinutes = Math.floor(diffMs / 60_000); const diffHours = Math.floor(diffMinutes / 60); const diffDays = Math.floor(diffHours / 24); if (diffDays > 0) { return i18n.t("providerUsage.timing.daysAgo", { count: diffDays, value: formatCount(diffDays), }); } if (diffHours > 0) { return i18n.t("providerUsage.timing.hoursAgo", { count: diffHours, value: formatCount(diffHours), }); } return i18n.t("providerUsage.timing.minutesAgo", { count: diffMinutes, value: formatCount(diffMinutes), }); } /** A balance amount as the app's language writes it: "$1,234.50", "12,345". */ export function formatAmount(value: number, unit: UsageBalanceUnit, locale?: string): string { switch (unit) { case "usd": return new Intl.NumberFormat(locale, { style: "currency", currency: "USD" }).format(value); case "tokens": return new Intl.NumberFormat(locale, { notation: "compact", maximumFractionDigits: 1, }).format(value); default: return new Intl.NumberFormat(locale, { maximumFractionDigits: 2 }).format(value); } }