Usage meter A quota bar that appears at three-quarters rather than at the wall, with the reset time, the per-action cost at the point of choice, and a colleague's share on a team plan. Reasoning and tool calls count, and are invisible. npx shadcn@latest add button Tokens this needs: --card, --card-foreground, --muted, --muted-foreground, --border, --chart-1, --scale-seq-2, --scale-seq-3 The status, chart, scale, state and direction names are an extension, not a rename. shadcn has --destructive and five --chart-* and nothing else in this territory. ──────────────────────────────────────────────────────────────────────── // UsageMeter.tsx ──────────────────────────────────────────────────────────────────────── import { Button } from "@/components/ui/button"; /** Who consumed what. On a team plan the colleague's share is the difference * between a product problem and a people problem. */ export type Share = { who: string; used: number }; /** A thing the viewer can choose, with what it costs. The cost is required: * marking it here, while they can still decide, is worth more than any * accuracy in the bar. */ export type Action = { id: string; label: string; cost: number }; /** Shown from three-quarters consumed. Always-on is ignored, and makes an * unbounded product feel metered. */ const APPEARS_AT = 0.75; const SHARE_COLOR = ["bg-scale-seq-3", "bg-scale-seq-2", "bg-scale-seq-1"]; const resetLabel = (at: Date, now: Date) => { const days = (at.getTime() - now.getTime()) / 86_400_000; return days < 7 ? at.toLocaleString("en-GB", { weekday: "short", hour: "2-digit", minute: "2-digit", hour12: false, timeZone: "UTC" }) : at.toLocaleDateString("en-GB", { day: "numeric", month: "short", timeZone: "UTC" }); }; export function UsageMeter({ limit, unit = "credit", shares, resetsAt, now = new Date(), actions, onChoose, fallback, stoppedCost }: { limit: number; /** Something the viewer can turn into work. "credit", not "token". */ unit?: string; /** The viewer first. */ shares: Share[]; /** A limit with no reset is a wall. */ resetsAt: Date; /** The clock to count against. Pass one to render on a server. */ now?: Date; actions: Action[]; onChoose: (id: string) => void; /** What still works once the budget is gone. Exhaustion is a designed state. */ fallback: string; /** What the last stopped response cost, if one was stopped. It counted. */ stoppedCost?: number; }) { const used = shares.reduce((n, s) => n + s.used, 0); if (used / limit < APPEARS_AT) return null; const left = Math.max(0, limit - used); const plural = (n: number) => `${n} ${unit}${n === 1 ? "" : "s"}`; return (
at the limit, {fallback} still answers
a stopped response still consumed what it wrote: {plural(stoppedCost)}
)}