diff --git a/src/dashboard.tsx b/src/dashboard.tsx index 741a619..f84254d 100644 --- a/src/dashboard.tsx +++ b/src/dashboard.tsx @@ -11,7 +11,7 @@ import { scanAndDetect, type WasteFinding, type WasteAction, type OptimizeResult import { estimateContextBudget, discoverProjectCwd, type ContextBudget } from './context-budget.js' import { dateKey } from './day-aggregator.js' import { CompareView } from './compare.js' -import { getPlanUsageOrNullForProjects, type PlanUsage } from './plan-usage.js' +import { getPlanUsageOrNull, type PlanUsage } from './plan-usage.js' import { planDisplayName } from './plans.js' import { join } from 'path' @@ -157,14 +157,15 @@ function fit(s: string, n: number): string { return s.length > n ? s.slice(0, n) : s.padEnd(n) } -function formatUsd(value: number): string { - return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', maximumFractionDigits: 2 }).format(value) -} - function renderPlanBar(percentUsed: number, width: number): string { - const capped = Math.max(0, Math.min(100, percentUsed)) - const filled = Math.round((capped / 100) * width) - return `${'▓'.repeat(filled)}${'░'.repeat(Math.max(0, width - filled))}` + if (percentUsed <= 100) { + const capped = Math.max(0, percentUsed) + const filled = Math.round((capped / 100) * width) + return `${'▓'.repeat(filled)}${'░'.repeat(Math.max(0, width - filled))}` + } + const factor = percentUsed / 100 + const chevrons = Math.min(4, Math.max(1, Math.floor(Math.log10(factor)) + 1)) + return `${'▓'.repeat(width)}${'▶'.repeat(chevrons)}` } function Overview({ projects, label, width, planUsage }: { projects: ProjectSummary[]; label: string; width: number; planUsage?: PlanUsage }) { @@ -179,7 +180,7 @@ function Overview({ projects, label, width, planUsage }: { projects: ProjectSumm const allInputTokens = totalInput + totalCacheRead + totalCacheWrite const cacheHit = allInputTokens > 0 ? (totalCacheRead / allInputTokens) * 100 : 0 - const planLabel = planUsage ? `${planDisplayName(planUsage.plan.id)} plan: ${formatUsd(planUsage.spentApiEquivalentUsd)} equiv / ${formatUsd(planUsage.budgetUsd)} included` : '' + const planLabel = planUsage ? `${planDisplayName(planUsage.plan.id)}: ${formatCost(planUsage.spentApiEquivalentUsd)} API-equivalent vs ${formatCost(planUsage.budgetUsd)} plan` : '' const planPct = planUsage ? `${planUsage.percentUsed.toFixed(1)}%` : '' const planColor = planUsage ? planUsage.status === 'over' @@ -219,10 +220,10 @@ function Overview({ projects, label, width, planUsage }: { projects: ProjectSumm {planUsage.status === 'under' - ? `Well within plan. Projected month: ${formatUsd(planUsage.projectedMonthUsd)} (reset in ${planUsage.daysUntilReset} days).` + ? `Well within plan. Projected month: ${formatCost(planUsage.projectedMonthUsd)} (reset in ${planUsage.daysUntilReset} days).` : planUsage.status === 'near' - ? `Approaching plan limit. Projected month: ${formatUsd(planUsage.projectedMonthUsd)} (reset in ${planUsage.daysUntilReset} days).` - : `You're ${(planUsage.spentApiEquivalentUsd / Math.max(planUsage.budgetUsd, 1)).toFixed(1)}x over subscription value; running on API overage pricing.`} + ? `Approaching plan limit. Projected month: ${formatCost(planUsage.projectedMonthUsd)} (reset in ${planUsage.daysUntilReset} days).` + : `${(planUsage.spentApiEquivalentUsd / Math.max(planUsage.budgetUsd, 1)).toFixed(1)}x your subscription value. Projected month: ${formatCost(planUsage.projectedMonthUsd)} (reset in ${planUsage.daysUntilReset} days).`} )} @@ -703,7 +704,7 @@ function InteractiveDashboard({ initialProjects, initialPeriod, initialProvider, if (reloadGenerationRef.current !== generation) return setProjects(filteredProjects) - const usage = await getPlanUsageOrNullForProjects(filteredProjects) + const usage = await getPlanUsageOrNull() if (reloadGenerationRef.current !== generation) return setPlanUsage(usage ?? undefined) } catch (error) { @@ -802,7 +803,7 @@ export async function renderDashboard(period: Period = 'week', provider: string await loadPricing() const range = customRange ?? getDateRange(period) const filteredProjects = filterProjectsByName(await parseAllSessions(range, provider), projectFilter, excludeFilter) - const planUsage = await getPlanUsageOrNullForProjects(filteredProjects) + const planUsage = await getPlanUsageOrNull() const isTTY = process.stdin.isTTY && process.stdout.isTTY if (isTTY) { const { waitUntilExit } = render( diff --git a/src/plan-usage.ts b/src/plan-usage.ts index b027e59..8b9e16b 100644 --- a/src/plan-usage.ts +++ b/src/plan-usage.ts @@ -143,12 +143,6 @@ export async function getPlanUsageOrNull(today = new Date()): Promise { - const plan = await readPlan() - if (!isActivePlan(plan)) return null - return getPlanUsageFromProjects(plan, projects, today) -} - export function isActivePlan(plan: Plan | undefined): plan is Plan { return Boolean(plan) && plan.id !== 'none' && Number.isFinite(plan.monthlyUsd) && plan.monthlyUsd > 0 }