import type { Theme } from "@earendil-works/pi-coding-agent"; import type { QuotaPolarity } from "../config.ts"; import type { QuotaWindowView } from "../quota/policy.ts"; import { quotaProviderBrand } from "../quota/types.ts"; import { displayedPercent, formatResetDuration, formatResetShort, formatSnapshotAge, nearestExpiry, quotaTone, renderQuotaBar } from "./format.ts"; export const STATUS_KEY = "pi-meter"; export interface ChromeInput { local?: string; quota?: QuotaWindowView; quotaHint?: { label: string; value: string }; polarity: QuotaPolarity; now?: Date; } export function quotaWindowKind(window: { id: string; label: string }): string { const hay = `${window.id} ${window.label}`.toLowerCase(); if (hay.includes("5h") || hay.includes("session") || hay.includes("five")) return "5h"; if (hay.includes("week") || hay.includes("weekly")) return "week"; if (hay.includes("month")) return "month"; if (hay.includes("day") || hay.includes("daily")) return "day"; const first = window.label.trim().split(/\s+/)[0]; return first ? first.toLowerCase() : "quota"; } function quotaCaption(quota: QuotaWindowView, polarity: QuotaPolarity, now: Date): { label: string; value: string; tone: ReturnType } { const kind = quotaWindowKind(quota.window); const verb = polarity === "remaining" ? "left" : "used"; const percent = Math.round(displayedPercent(quota.window.usedPercent, polarity)); const reset = formatResetShort(quota.window.resetsAt, now); const age = quota.stale ? formatSnapshotAge(quota.fetchedAt, now) : undefined; return { label: `${quotaProviderBrand(quota.provider)} ${kind} ${verb}`, value: `${renderQuotaBar(quota.window.usedPercent, polarity)} ${percent}%${reset ? ` (${reset})` : ""}${age ? ` ${age}` : ""}`, tone: quotaTone(quota.window.usedPercent), }; } function resetCreditsCaption(quota: QuotaWindowView, now: Date): string | undefined { if (quota.provider !== "codex") return undefined; const count = quota.resets?.availableCount ?? 0; if (count <= 0) return undefined; const noun = count === 1 ? "reset" : "resets"; const next = formatResetDuration(nearestExpiry(quota.resets?.items, now), now); return next ? `${count} ${noun} ${next}` : `${count} ${noun}`; } export function renderStatusText(input: ChromeInput, theme: Theme): string | undefined { const now = input.now ?? new Date(); const parts: string[] = []; if (input.local) parts.push(theme.fg("muted", input.local)); if (input.quota) { const quota = quotaCaption(input.quota, input.polarity, now); parts.push(`${theme.fg("muted", quota.label)} ${theme.fg(quota.tone, quota.value)}`); const resets = resetCreditsCaption(input.quota, now); if (resets) parts.push(theme.fg("muted", resets)); } else if (input.quotaHint) { parts.push(theme.fg("muted", input.quotaHint.label), theme.fg("muted", input.quotaHint.value)); } if (parts.length === 0) return undefined; // Pi/Glance join foreign statuses with a space. A leading mid-dot keeps // "granted today" from reading as one phrase. return `${theme.fg("dim", "·")} ${parts.join(theme.fg("dim", " · "))}`; } export function renderChromeLine(input: ChromeInput, _width: number, theme: Theme): string[] { const text = renderStatusText(input, theme); return text ? [text] : []; }