From 8011ff44263005f7cd78a14fab3c1cff7e2d0e7f Mon Sep 17 00:00:00 2001 From: Sarthak Agrawal Date: Sat, 15 Aug 2026 17:20:14 +0530 Subject: [PATCH] refactor: reduce cognitive complexity in Home (batch 2b) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Batch 2b of #117 — extract helpers and sub-components to bring Home.tsx below the CCN 20 cap. All 7 violations resolved. AccountUsageRow (CCN 156→≤20): extracted 6 lookup maps, 8 helpers, 10 sub-components TokenUsageChart (23→≤20): extracted chartLabelFor, trendBadgeClass, trendArrow, barGradient WeeklyAgentSplit (36→≤20): extracted useAgentUsageRows hook, buildAgentSegments, buildAllRangeAgentSegments UsageByModel (23→≤20): extracted buildModelRows, modelEmptyMessage CodexEvidenceSummary (22→≤20): extracted formatCodexCostLabel Home (27→≤20): extracted ProviderTelemetrySection, LegacySummarySection, handleRedetect, handleCheckLive, handleDeleteAccount loadDashboard (25→≤20): simplified with seedCachedUsages, fetchMissingUsages, surfaceTokenUsageError Part of #117 Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- apps/desktop/src/pages/Home.tsx | 1927 +++++++++++++++++-------------- 1 file changed, 1068 insertions(+), 859 deletions(-) diff --git a/apps/desktop/src/pages/Home.tsx b/apps/desktop/src/pages/Home.tsx index e6ba6568..107d9ae3 100644 --- a/apps/desktop/src/pages/Home.tsx +++ b/apps/desktop/src/pages/Home.tsx @@ -252,6 +252,499 @@ function LocalModelBreakdown({ ); } +// ─── Provider styling lookup maps ─────────────────────────────────────────── + +const PROVIDER_DOT_COLOR: Record = { + anthropic: 'bg-amber-400', + google: 'bg-blue-400', + cursor: 'bg-violet-400', + devin: 'bg-orange-400', + grok: 'bg-sky-400', +}; + +const PROVIDER_BADGE_CLASS: Record = { + anthropic: 'bg-amber-500/15 text-amber-400', + google: 'bg-blue-500/15 text-blue-400', + cursor: 'bg-violet-500/15 text-violet-300', + devin: 'bg-orange-500/15 text-orange-400', + grok: 'bg-sky-500/15 text-sky-300', +}; + +const PROVIDER_REFRESH_CLASS: Record = { + anthropic: 'text-amber-400/70 hover:text-amber-400', + google: 'text-blue-400/70 hover:text-blue-400', + cursor: 'text-violet-300/70 hover:text-violet-300', + devin: 'text-orange-400/70 hover:text-orange-400', + grok: 'text-sky-300/70 hover:text-sky-300', +}; + +const PROVIDER_REFRESH_TITLE: Record = { + openai: 'Check live usage from OpenAI', + google: 'Check live usage from Google', + cursor: 'Check live plan usage from Cursor', + devin: 'Refresh Devin quota from Codeium', + grok: 'Refresh Grok credit usage from CLI logs', +}; + +const PRIMARY_WINDOW_LABEL: Record = { + anthropic: '5-hour window', + cursor: 'Monthly plan', + devin: 'Weekly quota', + grok: 'Monthly credits', +}; + +const SECONDARY_WINDOW_LABEL: Record = { + anthropic: '7-day window', + devin: 'Daily quota', +}; + +const LIVE_SUPPORTED_PROVIDERS = ['anthropic', 'openai', 'google', 'cursor', 'devin', 'grok']; + +function usageBarColor(pct: number): 'amber' | 'red' { + return pct >= 90 ? 'red' : 'amber'; +} + +function formatResetLabel(resetsInSecs: number | null | undefined): string | undefined { + if (resetsInSecs == null || resetsInSecs <= 0) return undefined; + return `resets in ${formatDuration(resetsInSecs)}`; +} + +function formatLiveErrorHint(liveError: string | null): string | null { + if (!liveError) return null; + if (/401|expired|invalid|re-?authenticate/i.test(liveError)) { + return 'Live windows unavailable — stored Claude credential is expired. Re-authenticate Claude Code (run `claude`, then /login).'; + } + return `Live usage unavailable: ${liveError}`; +} + +function resolveAccountPlan( + provider: string, + liveUsage: LiveUsageResult | null, + usage: AccountUsage | null, + accountPlan: string | null +): string | null { + if (provider === 'devin' || provider === 'grok') { + return liveUsage?.quota_plan ?? usage?.plan ?? accountPlan; + } + return usage?.plan ?? accountPlan; +} + +function providerDotColor(provider: string, isRateLimited: boolean, hasLive: boolean): string { + if (isRateLimited) return 'bg-red-500 animate-pulse'; + if (hasLive) return 'bg-emerald-500'; + return PROVIDER_DOT_COLOR[provider] ?? 'bg-emerald-400'; +} + +function bucketResetLabel(resetTime: string | null | undefined): string | undefined { + if (!resetTime) return undefined; + const resetMs = new Date(resetTime).getTime() - Date.now(); + if (resetMs <= 0) return undefined; + return `resets in ${formatDuration(Math.round(resetMs / 1000))}`; +} + +function dedupeGeminiQuotaBuckets(buckets: B[]): B[] { + const proBucket = buckets.find((b) => b.model_id.includes('pro')); + const flashBucket = buckets.find( + (b) => b.model_id.includes('flash') && !b.model_id.includes('lite') + ); + return [ + proBucket ? { ...proBucket, model_id: 'Pro' } : null, + flashBucket ? { ...flashBucket, model_id: 'Flash' } : null, + ].filter(Boolean) as B[]; +} + +function PrimaryUsageBar({ + provider, + fiveH, +}: { + provider: string; + fiveH: NonNullable; +}) { + if (fiveH.utilization_pct == null) return null; + const showPaceProjection = provider !== 'grok' || fiveH.window_total_secs != null; + return ( + + ); +} + +function SecondaryUsageBar({ + provider, + sevenD, +}: { + provider: string; + sevenD: NonNullable; +}) { + if (sevenD.utilization_pct == null || provider === 'grok') return null; + return ( + + ); +} + +function GrokBillingDisplay({ + grokBilling, +}: { + grokBilling: NonNullable; +}) { + return ( +
+ {formatGrokBillingSummary(grokBilling)} + {grokBilling.billing_period_end + ? ` · resets ${new Date(grokBilling.billing_period_end).toLocaleDateString()}` + : ''} + {grokBilling.stale && grokBilling.stale_reason ? ` — ${grokBilling.stale_reason}` : ''} +
+ ); +} + +function WindowNoteDisplay({ + isRateLimited, + utilizationPct, + windowNote, +}: { + isRateLimited: boolean; + utilizationPct: number | null | undefined; + windowNote: string; +}) { + const isCritical = isRateLimited || (utilizationPct ?? 0) >= 100; + return ( +
+ {windowNote} +
+ ); +} + +function OpenAILiveUsage({ liveUsage }: { liveUsage: LiveUsageResult | null }) { + const resetCredits = liveUsage?.reset_credits ?? 0; + return ( +
+ {resetCredits > 0 && ( + + {resetCredits} manual reset credit{resetCredits === 1 ? '' : 's'} available + + )} + {(liveUsage?.additional_windows ?? []).map((w) => ( + + {resetCredits > 0 ? ' · ' : ''} + {w.name}: {w.primary_pct ?? 0}% / {w.secondary_pct ?? 0}% (own pool) + + ))} + {liveUsage?.checked_at && ( + + {' · as of '} + {new Date(liveUsage.checked_at).toLocaleTimeString([], { + hour: '2-digit', + minute: '2-digit', + })} + + )} +
+ ); +} + +function GeminiQuotaBars({ + buckets, +}: { + buckets: NonNullable['buckets']>; +}) { + const deduped = dedupeGeminiQuotaBuckets(buckets); + return ( +
+ {deduped.map((b) => { + const pct = b.used_pct ?? 0; + const atLimit = b.remaining_fraction === 0; + return ( + + ); + })} +
+ ); +} + +function GeminiModelBars({ models }: { models: NonNullable }) { + const maxTokens = Math.max(...models.map((m) => m.tokens.total)); + return ( +
+ {models.map((m) => { + const pct = maxTokens > 0 ? (m.tokens.total / maxTokens) * 100 : 0; + return ( +
+ + {m.model} + +
+
+
+ + {formatTokens(m.tokens.total)} + +
+ ); + })} +
+ ); +} + +function GeminiUsageDisplay({ liveUsage }: { liveUsage: LiveUsageResult | null }) { + const hasLive = liveUsage?.supported === true; + const geminiToday = liveUsage?.today; + const geminiModels = liveUsage?.models; + const quotaBuckets = liveUsage?.quota_api?.buckets; + + if (!hasLive && !quotaBuckets) return null; + + return ( +
+ {geminiToday && ( +
+ Today +
+ + {geminiToday.sessions} session{geminiToday.sessions !== 1 ? 's' : ''} + {' · '} + {geminiToday.messages} msg{geminiToday.messages !== 1 ? 's' : ''} + + + {formatTokens(geminiToday.tokens.total)} + +
+
+ )} + {geminiToday && ( +
+ {formatTokens(geminiToday.tokens.input)} in + · + {formatTokens(geminiToday.tokens.output)} out + {geminiToday.tokens.cached > 0 && ( + <> + · + + {formatTokens(geminiToday.tokens.cached)} cached + + + )} + {geminiToday.tokens.thoughts > 0 && ( + <> + · + + {formatTokens(geminiToday.tokens.thoughts)} thinking + + + )} +
+ )} + {quotaBuckets && quotaBuckets.length > 0 && } + {!quotaBuckets && geminiModels && geminiModels.length > 0 && ( + + )} +
+ ); +} + +function CursorUsageDisplay({ + liveUsage, + weekSessions, +}: { + liveUsage: LiveUsageResult | null; + weekSessions: number; +}) { + const cursorPlan = liveUsage?.cursor_plan; + const cursorTokens = liveUsage?.cursor_tokens; + + if (!cursorPlan && !cursorTokens) return null; + + return ( +
+ {cursorTokens && cursorTokens.total > 0 && ( +
+ Tokens this cycle +
+ + {formatTokens(cursorTokens.total)} + +
+
+ )} + {cursorTokens && cursorTokens.total > 0 && ( +
+ {formatTokens(cursorTokens.input)} in + · + {formatTokens(cursorTokens.output)} out + {cursorTokens.cache_read > 0 && ( + <> + · + + {formatTokens(cursorTokens.cache_read)} cached + + + )} +
+ )} + {cursorPlan && ( +
+ Plan spend +
+ {cursorPlan.total_spend_cents != null && cursorPlan.limit_cents != null && ( + + ${(cursorPlan.total_spend_cents / 100).toFixed(2)} / $ + {(cursorPlan.limit_cents / 100).toFixed(2)} + + )} + {cursorPlan.total_pct_used != null && ( + + {cursorPlan.total_pct_used.toFixed(1)}% + + )} +
+
+ )} + {cursorTokens && cursorTokens.by_model.length > 1 && ( +
+ {cursorTokens.by_model.map((m) => { + const t = m.input_tokens + m.output_tokens + m.cache_read_tokens; + return ( +
+ {m.model ?? 'unknown'} + {formatTokens(t)} +
+ ); + })} +
+ )} + {cursorPlan?.display_message && ( +
{cursorPlan.display_message}
+ )} +
+ {weekSessions} session{weekSessions === 1 ? '' : 's'} indexed this week +
+
+ ); +} + +function LocalIndexedStats({ + provider, + weekTokens, + weekSessions, + usage, + hasLive, + liveErrorHint, + profileBreakdown, +}: { + provider: string; + weekTokens: number; + weekSessions: number; + usage: AccountUsage | null; + hasLive: boolean; + liveErrorHint: string | null; + profileBreakdown: AccountUsage['profile_breakdown']; +}) { + return ( +
+
+ + {formatTokens(weekTokens)} tokens this week + + + {weekSessions} sessions + + {usage && usage.week_cost > 0 && ( + + {formatMoney(usage.week_cost)} + + )} + {!hasLive && !liveErrorHint && ( + + {localTelemetryQualifier(provider)} + + )} +
+ + {liveErrorHint && ( +
+ + {liveErrorHint} +
+ )} + {profileBreakdown.length > 1 && ( +
+ {profileBreakdown.map((profile) => { + const profileTokens = profile.week_input_tokens + profile.week_output_tokens; + return ( +
+ + {profile.profile} + + + {formatTokens(profileTokens)} · {profile.week_sessions} sessions + +
+ ); + })} +
+ )} +
+ ); +} + +function SharedUsageNote({ provider }: { provider: string }) { + return ( +
+ + local stats shared with other {provider === 'anthropic' ? 'Claude' : 'accounts'} + +
+ ); +} + function AccountUsageRow({ account, usage, @@ -271,86 +764,32 @@ function AccountUsageRow({ onDelete: () => void; isSharedUsage: boolean; }) { - // Turn a raw live-usage error into an actionable hint. - const liveErrorHint = liveError - ? /401|expired|invalid|re-?authenticate/i.test(liveError) - ? 'Live windows unavailable — stored Claude credential is expired. Re-authenticate Claude Code (run `claude`, then /login).' - : `Live usage unavailable: ${liveError}` - : null; + const liveErrorHint = formatLiveErrorHint(liveError); const weekSessions = usage?.week_sessions ?? 0; const weekTokens = (usage?.week_input_tokens ?? 0) + (usage?.week_output_tokens ?? 0); const profileBreakdown = usage?.profile_breakdown ?? []; - const plan = - account.provider === 'devin' || account.provider === 'grok' - ? (liveUsage?.quota_plan ?? usage?.plan ?? account.plan) - : (usage?.plan ?? account.plan); - - // Live rate limit data — supported for providers with quota APIs or local caches - const isLiveSupported = ['anthropic', 'openai', 'google', 'cursor', 'devin', 'grok'].includes( - account.provider - ); + const plan = resolveAccountPlan(account.provider, liveUsage, usage, account.plan); + + const isLiveSupported = LIVE_SUPPORTED_PROVIDERS.includes(account.provider); const hasLive = liveUsage?.supported === true; const fiveH = liveUsage?.five_h; const sevenD = liveUsage?.seven_d; const isRateLimited = liveUsage?.status === 'rate_limited'; - // Gemini-specific live data - const geminiToday = liveUsage?.today; - const geminiModels = liveUsage?.models; - const quotaBuckets = liveUsage?.quota_api?.buckets; - - // Cursor-specific live data — from - // aiserver.v1.DashboardService.GetCurrentPeriodUsage + GetAggregatedUsageEvents - const cursorPlan = liveUsage?.cursor_plan; - const cursorTokens = liveUsage?.cursor_tokens; const windowNote = liveWindowNote(account.provider, fiveH?.utilization_pct); - const showPaceProjection = account.provider !== 'grok' || fiveH?.window_total_secs != null; - - // Determine bar color based on utilization - function barColor(pct: number): 'amber' | 'red' { - if (pct >= 90) return 'red'; - return 'amber'; - } + const dotColor = providerDotColor(account.provider, isRateLimited, hasLive); + const showLocalStats = !isSharedUsage && account.provider !== 'cursor'; return (
- {/* Header: name, plan badge, delete, check button */}
- + {account.name} {plan && ( {planLabel(plan)} @@ -364,30 +803,12 @@ function AccountUsageRow({ onClick={onCheckLive} disabled={checkingLive} className={`h-auto px-1.5 py-0.5 text-[10px] ${ - account.provider === 'anthropic' - ? 'text-amber-400/70 hover:text-amber-400' - : account.provider === 'google' - ? 'text-blue-400/70 hover:text-blue-400' - : account.provider === 'cursor' - ? 'text-violet-300/70 hover:text-violet-300' - : account.provider === 'devin' - ? 'text-orange-400/70 hover:text-orange-400' - : account.provider === 'grok' - ? 'text-sky-300/70 hover:text-sky-300' - : 'text-emerald-400/70 hover:text-emerald-400' + PROVIDER_REFRESH_CLASS[account.provider] ?? + 'text-emerald-400/70 hover:text-emerald-400' }`} title={ - account.provider === 'openai' - ? 'Check live usage from OpenAI' - : account.provider === 'google' - ? 'Check live usage from Google' - : account.provider === 'cursor' - ? 'Check live plan usage from Cursor' - : account.provider === 'devin' - ? 'Refresh Devin quota from Codeium' - : account.provider === 'grok' - ? 'Refresh Grok credit usage from CLI logs' - : 'Check live usage (makes a small API call)' + PROVIDER_REFRESH_TITLE[account.provider] ?? + 'Check live usage (makes a small API call)' } > {checkingLive ? '...' : 'Refresh'} @@ -396,374 +817,35 @@ function AccountUsageRow({
- {/* ── Utilization bars ──────────────────────────────────── */} - {hasLive && fiveH?.utilization_pct != null && ( - 0 - ? `resets in ${formatDuration(fiveH.resets_in_secs)}` - : undefined - } - color={barColor(fiveH.utilization_pct)} - windowTotalSecs={resolveUsageWindowTotalSecs( - account.provider, - 'primary', - showPaceProjection ? fiveH.window_total_secs : undefined - )} - resetsInSecs={showPaceProjection ? (fiveH.resets_in_secs ?? undefined) : undefined} - /> - )} - {hasLive && sevenD?.utilization_pct != null && account.provider !== 'grok' && ( - 0 - ? `resets in ${formatDuration(sevenD.resets_in_secs)}` - : undefined - } - color={barColor(sevenD.utilization_pct)} - windowTotalSecs={resolveUsageWindowTotalSecs( - account.provider, - 'secondary', - sevenD.window_total_secs - )} - resetsInSecs={sevenD.resets_in_secs ?? undefined} - /> - )} + {hasLive && fiveH && } + {hasLive && sevenD && } {account.provider === 'grok' && liveUsage?.grok_billing && ( -
- {formatGrokBillingSummary(liveUsage.grok_billing)} - {liveUsage.grok_billing.billing_period_end - ? ` · resets ${new Date(liveUsage.grok_billing.billing_period_end).toLocaleDateString()}` - : ''} - {liveUsage.grok_billing.stale && liveUsage.grok_billing.stale_reason - ? ` — ${liveUsage.grok_billing.stale_reason}` - : ''} -
- )} - {account.provider === 'openai' && hasLive && ( -
- {(liveUsage?.reset_credits ?? 0) > 0 && ( - - {liveUsage?.reset_credits} manual reset credit - {liveUsage?.reset_credits === 1 ? '' : 's'} available - - )} - {(liveUsage?.additional_windows ?? []).map((w) => ( - - {(liveUsage?.reset_credits ?? 0) > 0 ? ' · ' : ''} - {w.name}: {w.primary_pct ?? 0}% / {w.secondary_pct ?? 0}% (own pool) - - ))} - {liveUsage?.checked_at && ( - - {' · as of '} - {new Date(liveUsage.checked_at).toLocaleTimeString([], { - hour: '2-digit', - minute: '2-digit', - })} - - )} -
+ )} + {account.provider === 'openai' && hasLive && } {hasLive && windowNote && ( -
= 100 - ? 'border-red-500/20 bg-red-500/10 text-red-200/80' - : 'border-cyan-500/15 bg-cyan-500/10 text-cyan-100/70' - }`} - > - {windowNote} -
+ )} - - {/* ── Gemini-specific usage display ────────────────────── */} - {account.provider === 'google' && (hasLive || quotaBuckets) && ( -
- {/* Today summary — single compact row */} - {geminiToday && ( -
- Today -
- - {geminiToday.sessions} session{geminiToday.sessions !== 1 ? 's' : ''} - {' · '} - {geminiToday.messages} msg{geminiToday.messages !== 1 ? 's' : ''} - - - {formatTokens(geminiToday.tokens.total)} - -
-
- )} - - {/* Token split — inline row */} - {geminiToday && ( -
- {formatTokens(geminiToday.tokens.input)} in - · - {formatTokens(geminiToday.tokens.output)} out - {geminiToday.tokens.cached > 0 && ( - <> - · - - {formatTokens(geminiToday.tokens.cached)} cached - - - )} - {geminiToday.tokens.thoughts > 0 && ( - <> - · - - {formatTokens(geminiToday.tokens.thoughts)} thinking - - - )} -
- )} - - {/* Per-model quota bars — real usage % from Google API */} - {quotaBuckets && - quotaBuckets.length > 0 && - (() => { - // Collapse to one Pro + one Flash — variants share the same quota - const proBucket = quotaBuckets.find((b) => b.model_id.includes('pro')); - const flashBucket = quotaBuckets.find( - (b) => b.model_id.includes('flash') && !b.model_id.includes('lite') - ); - const dedupedBuckets = [ - proBucket ? { ...proBucket, model_id: 'Pro' } : null, - flashBucket ? { ...flashBucket, model_id: 'Flash' } : null, - ].filter(Boolean) as typeof quotaBuckets; - return ( -
- {dedupedBuckets.map((b) => { - const pct = b.used_pct ?? 0; - const atLimit = b.remaining_fraction === 0; - const resetLabel = b.reset_time - ? (() => { - const resetMs = new Date(b.reset_time).getTime() - Date.now(); - if (resetMs <= 0) return undefined; - return `resets in ${formatDuration(Math.round(resetMs / 1000))}`; - })() - : undefined; - return ( - = 90 ? 'red' : 'amber'} - /> - ); - })} -
- ); - })()} - - {/* Fallback: show local model breakdown if no quota API data */} - {!quotaBuckets && - geminiModels && - geminiModels.length > 0 && - (() => { - const maxTokens = Math.max(...geminiModels.map((m) => m.tokens.total)); - return ( -
- {geminiModels.map((m) => { - const pct = maxTokens > 0 ? (m.tokens.total / maxTokens) * 100 : 0; - return ( -
- - {m.model} - -
-
-
- - {formatTokens(m.tokens.total)} - -
- ); - })} -
- ); - })()} -
+ {account.provider === 'google' && } + {account.provider === 'cursor' && ( + )} - - {/* ── Cursor-specific plan usage (live from api2.cursor.sh) ─── */} - {account.provider === 'cursor' && (cursorPlan || cursorTokens) && ( -
- {/* Tokens row — this is the "millions" figure cursor.com shows. */} - {cursorTokens && cursorTokens.total > 0 && ( -
- Tokens this cycle -
- - {formatTokens(cursorTokens.total)} - -
-
- )} - {/* Per-token-type split: cache-read dominates on Cursor's pricing. */} - {cursorTokens && cursorTokens.total > 0 && ( -
- {formatTokens(cursorTokens.input)} in - · - {formatTokens(cursorTokens.output)} out - {cursorTokens.cache_read > 0 && ( - <> - · - - {formatTokens(cursorTokens.cache_read)} cached - - - )} -
- )} - {/* Plan spend row — dollar-denominated usage cap. */} - {cursorPlan && ( -
- Plan spend -
- {cursorPlan.total_spend_cents != null && cursorPlan.limit_cents != null && ( - - ${(cursorPlan.total_spend_cents / 100).toFixed(2)} / $ - {(cursorPlan.limit_cents / 100).toFixed(2)} - - )} - {cursorPlan.total_pct_used != null && ( - - {cursorPlan.total_pct_used.toFixed(1)}% - - )} -
-
- )} - {/* Per-model breakdown — usually just composer-2.5-fast but - future-proofed for users running multiple models. */} - {cursorTokens && cursorTokens.by_model.length > 1 && ( -
- {cursorTokens.by_model.map((m) => { - const t = m.input_tokens + m.output_tokens + m.cache_read_tokens; - return ( -
- {m.model ?? 'unknown'} - {formatTokens(t)} -
- ); - })} -
- )} - {cursorPlan?.display_message && ( -
{cursorPlan.display_message}
- )} -
- {weekSessions} session{weekSessions === 1 ? '' : 's'} indexed this week -
-
- )} - - {/* ── Local indexed stats ───────────────────────────────── */} - {/* Cursor: skip the local "tokens this week" line — Cursor doesn't - expose per-message tokens locally, so the live plan-usage block - above is the source of truth. */} - {!isSharedUsage && account.provider !== 'cursor' ? ( -
-
- - {formatTokens(weekTokens)} tokens this week - - - {weekSessions} sessions - - {usage && usage.week_cost > 0 && ( - - {formatMoney(usage.week_cost)} - - )} - {!hasLive && !liveErrorHint && ( - - {localTelemetryQualifier(account.provider)} - - )} -
- - {liveErrorHint && ( -
- - {liveErrorHint} -
- )} - {profileBreakdown.length > 1 && ( -
- {profileBreakdown.map((profile) => { - const profileTokens = profile.week_input_tokens + profile.week_output_tokens; - return ( -
- - {profile.profile} - - - {formatTokens(profileTokens)} · {profile.week_sessions} sessions - -
- ); - })} -
- )} -
+ {showLocalStats ? ( + ) : ( -
- - local stats shared with other{' '} - {account.provider === 'anthropic' ? 'Claude' : 'accounts'} - -
+ )}
@@ -982,6 +1064,49 @@ function TelemetryVisibilityEditor({ // Bars show API-equivalent USD cost per bucket. Hover previews stay inside the // chart; clicking pins a bucket and drives the agent/model panels below. +const CHART_MONTHS_LABEL = [ + 'Jan', + 'Feb', + 'Mar', + 'Apr', + 'May', + 'Jun', + 'Jul', + 'Aug', + 'Sep', + 'Oct', + 'Nov', + 'Dec', +]; + +function chartLabelFor(d: { date?: string; week_start?: string }): string { + const iso = d.date ?? d.week_start ?? ''; + if (!iso) return ''; + const [, mm, dd] = iso.split('-'); + const mIdx = parseInt(mm, 10) - 1; + const day = parseInt(dd, 10); + return `${CHART_MONTHS_LABEL[mIdx] ?? mm} ${day}`; +} + +function trendBadgeClass(pct: number): string { + if (pct > 5) return 'bg-amber-500/10 text-amber-300 ring-1 ring-amber-500/30'; + if (pct < -5) return 'bg-emerald-500/10 text-emerald-300 ring-1 ring-emerald-500/30'; + return 'bg-slate-500/10 text-slate-300 ring-1 ring-slate-500/30'; +} + +function trendArrow(pct: number): string { + if (pct > 5) return '▲'; + if (pct < -5) return '▼'; + return '•'; +} + +function barGradient(ratio: number, isActive: boolean): string { + if (isActive) return 'url(#bar-grad-hover)'; + if (ratio >= 0.7) return 'url(#bar-grad-hot)'; + if (ratio >= 0.35) return 'url(#bar-grad-warm)'; + return 'url(#bar-grad-cool)'; +} + function TokenUsageChart({ daily, weekly, @@ -1097,30 +1222,6 @@ function TokenUsageChart({ const barW = n > 0 ? (W - padX * 2) / n : 0; const chartH = H - padTop - padBottom; - const MONTHS = [ - 'Jan', - 'Feb', - 'Mar', - 'Apr', - 'May', - 'Jun', - 'Jul', - 'Aug', - 'Sep', - 'Oct', - 'Nov', - 'Dec', - ]; - - const labelFor = (d: { date?: string; week_start?: string }): string => { - const iso = d.date ?? d.week_start ?? ''; - if (!iso) return ''; - const [, mm, dd] = iso.split('-'); - const mIdx = parseInt(mm, 10) - 1; - const day = parseInt(dd, 10); - return `${MONTHS[mIdx] ?? mm} ${day}`; - }; - // Daily: label only on Mondays + first/last bar to avoid clutter. // Weekly: label every other bar, plus the most recent. const shouldLabel = (i: number, iso: string): boolean => { @@ -1147,22 +1248,16 @@ function TokenUsageChart({
{hovered - ? `${labelFor(hovered)} · ${formatMoney(hovered.cost)} · ${formatTokens(hovered.generated)} gen` + ? `${chartLabelFor(hovered)} · ${formatMoney(hovered.cost)} · ${formatTokens(hovered.generated)} gen` : `${periodLabel} · ${formatMoney(total)} · ${formatTokens(totalGen)} generated`}
{trendPct != null && Number.isFinite(trendPct) && ( 5 - ? 'bg-amber-500/10 text-amber-300 ring-1 ring-amber-500/30' - : trendPct < -5 - ? 'bg-emerald-500/10 text-emerald-300 ring-1 ring-emerald-500/30' - : 'bg-slate-500/10 text-slate-300 ring-1 ring-slate-500/30' - }`} + className={`inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-[10px] font-medium tabular-nums ${trendBadgeClass(trendPct)}`} title={trendLabel} > - {trendPct > 5 ? '▲' : trendPct < -5 ? '▼' : '•'} + {trendArrow(trendPct)} {`${trendPct > 0 ? '+' : ''}${trendPct.toFixed(0)}% avg`} )} @@ -1235,13 +1330,7 @@ function TokenUsageChart({ const isActive = activeIdx === i; const isPinned = pinned === i; const isLatest = i === n - 1; - const grad = isActive - ? 'url(#bar-grad-hover)' - : ratio >= 0.7 - ? 'url(#bar-grad-hot)' - : ratio >= 0.35 - ? 'url(#bar-grad-warm)' - : 'url(#bar-grad-cool)'; + const grad = barGradient(ratio, isActive); return ( {/* Full-height hit target so mouse doesn't need to land on a short bar. */} @@ -1345,7 +1434,7 @@ function TokenUsageChart({ fontWeight={isHover || isLast ? 600 : 400} fill={isHover ? '#f2c766' : isLast ? '#cbd5e1' : '#64748b'} > - {labelFor(d)} + {chartLabelFor(d)} ); })} @@ -1433,21 +1522,7 @@ function StackedBar({ title, segments }: { title: string; segments: AgentSegment ); } -function WeeklyAgentSplit({ - hiddenAgents, - agentByDay, - range, - focusDate, - focusMode, - active, -}: { - hiddenAgents: Set; - agentByDay: AgentDayUsage[]; - range: ModelRangeKey; - focusDate?: string | null; - focusMode?: 'daily' | 'weekly'; - active: boolean; -}) { +function useAgentUsageRows(active: boolean) { const [rows, setRows] = useState(null); const [cursorLedger, setCursorLedger] = useState(null); @@ -1461,8 +1536,6 @@ function WeeklyAgentSplit({ getAgentUsageBreakdown(), listProviderUsageLedger(50).catch(() => [] as ProviderUsageLedgerRow[]), ]); - // Most-recent cursor billing-cycle row from the live API — the real - // Cursor usage. cc_sessions only has the chars÷4 CLI estimate. const cursor = ledger .filter((l) => l.provider === 'cursor') @@ -1476,12 +1549,8 @@ function WeeklyAgentSplit({ } }; void fetchRows(); - // Startup runs a fast *partial* quick-index, then a full index minutes - // later. Without refetching, this bar stays frozen on the partial numbers - // (e.g. Claude far below its real total). Refresh when the indexer emits - // its completion event, plus a periodic fallback. const interval = setInterval(() => { - if (isWindowHidden()) return; // battery: skip background refetches + if (isWindowHidden()) return; void fetchRows(); }, 60_000); void (async () => { @@ -1501,69 +1570,114 @@ function WeeklyAgentSplit({ }; }, [active]); - if (!rows) return null; + return { rows, cursorLedger }; +} - // API-equivalent USD cost per agent. Cursor's cc_sessions cost is only the - // chars÷4 CLI estimate; when the live-API ledger has a cursor row with a - // billed cost, use that as the source of truth instead. - const cursorLedgerCost = - cursorLedger && cursorLedger.cost_usd != null ? cursorLedger.cost_usd : null; - let segments: AgentSegment[]; - if (focusDate && focusMode) { - const acc = new Map(); - for (const r of agentByDay) { - if (hiddenAgents.has(r.agent_type)) continue; - if (!agentDayInFocus(r.date, focusDate, focusMode)) continue; - acc.set(r.agent_type, (acc.get(r.agent_type) ?? 0) + r.cost); - } - segments = [...acc.entries()].map(([agent, cost]) => ({ - agent, - tokens: cost, - estimated: AGENT_PALETTE[agent]?.estimated ?? false, +function buildAllRangeAgentSegments( + rows: AgentUsageRow[], + hiddenAgents: Set, + cursorLedgerCost: number | null +): AgentSegment[] { + const segments = rows + .filter((r) => !hiddenAgents.has(r.agent_type)) + .map((r) => ({ + agent: r.agent_type, + tokens: r.agent_type === 'cursor' && cursorLedgerCost != null ? cursorLedgerCost : r.cost, + estimated: AGENT_PALETTE[r.agent_type]?.estimated ?? false, })); - } else if (range === 'all') { - segments = rows - .filter((r) => !hiddenAgents.has(r.agent_type)) - .map((r) => ({ - agent: r.agent_type, - tokens: r.agent_type === 'cursor' && cursorLedgerCost != null ? cursorLedgerCost : r.cost, - estimated: AGENT_PALETTE[r.agent_type]?.estimated ?? false, - })); - if ( - cursorLedgerCost != null && - !hiddenAgents.has('cursor') && - !rows.some((r) => r.agent_type === 'cursor') - ) { - segments.push({ agent: 'cursor', tokens: cursorLedgerCost, estimated: false }); - } - } else { - // Rolling window summed client-side from the per-day drill-down (the same - // day attribution as the daily chart). Cursor keeps its local estimate - // here — the ledger figure is a whole billing cycle, not window-sliceable. - const days = MODEL_RANGES.find((r) => r.key === range)?.days ?? 30; - const start = new Date(); - start.setHours(0, 0, 0, 0); - start.setDate(start.getDate() - (days - 1)); - const since = `${start.getFullYear()}-${String(start.getMonth() + 1).padStart(2, '0')}-${String(start.getDate()).padStart(2, '0')}`; + if ( + cursorLedgerCost != null && + !hiddenAgents.has('cursor') && + !rows.some((r) => r.agent_type === 'cursor') + ) { + segments.push({ agent: 'cursor', tokens: cursorLedgerCost, estimated: false }); + } + return segments; +} + +function buildAgentSegments( + rows: AgentUsageRow[], + agentByDay: AgentDayUsage[], + hiddenAgents: Set, + range: ModelRangeKey, + focusDate: string | null | undefined, + focusMode: 'daily' | 'weekly' | undefined, + cursorLedgerCost: number | null +): AgentSegment[] { + if (focusDate && focusMode) { const acc = new Map(); for (const r of agentByDay) { - if (r.date < since || hiddenAgents.has(r.agent_type)) continue; + if (hiddenAgents.has(r.agent_type)) continue; + if (!agentDayInFocus(r.date, focusDate, focusMode)) continue; acc.set(r.agent_type, (acc.get(r.agent_type) ?? 0) + r.cost); } - segments = [...acc.entries()].map(([agent, cost]) => ({ + return [...acc.entries()].map(([agent, cost]) => ({ agent, tokens: cost, estimated: AGENT_PALETTE[agent]?.estimated ?? false, })); } + if (range === 'all') return buildAllRangeAgentSegments(rows, hiddenAgents, cursorLedgerCost); + const days = MODEL_RANGES.find((r) => r.key === range)?.days ?? 30; + const start = new Date(); + start.setHours(0, 0, 0, 0); + start.setDate(start.getDate() - (days - 1)); + const since = `${start.getFullYear()}-${String(start.getMonth() + 1).padStart(2, '0')}-${String(start.getDate()).padStart(2, '0')}`; + const acc = new Map(); + for (const r of agentByDay) { + if (r.date < since || hiddenAgents.has(r.agent_type)) continue; + acc.set(r.agent_type, (acc.get(r.agent_type) ?? 0) + r.cost); + } + return [...acc.entries()].map(([agent, cost]) => ({ + agent, + tokens: cost, + estimated: AGENT_PALETTE[agent]?.estimated ?? false, + })); +} + +function agentFocusLabel( + focusDate: string | null | undefined, + focusMode: 'daily' | 'weekly' | undefined +): string | null { + if (!focusDate || !focusMode) return null; + return focusMode === 'weekly' + ? `week of ${formatChartDateLabel(focusDate)}` + : formatChartDateLabel(focusDate); +} + +function WeeklyAgentSplit({ + hiddenAgents, + agentByDay, + range, + focusDate, + focusMode, + active, +}: { + hiddenAgents: Set; + agentByDay: AgentDayUsage[]; + range: ModelRangeKey; + focusDate?: string | null; + focusMode?: 'daily' | 'weekly'; + active: boolean; +}) { + const { rows, cursorLedger } = useAgentUsageRows(active); + + if (!rows) return null; + + const cursorLedgerCost = + cursorLedger && cursorLedger.cost_usd != null ? cursorLedger.cost_usd : null; + const segments = buildAgentSegments( + rows, + agentByDay, + hiddenAgents, + range, + focusDate, + focusMode, + cursorLedgerCost + ); const rangeLabel = MODEL_RANGES.find((r) => r.key === range)?.label.toLowerCase() ?? 'all time'; - const focusLabel = - focusDate && focusMode - ? focusMode === 'weekly' - ? `week of ${formatChartDateLabel(focusDate)}` - : formatChartDateLabel(focusDate) - : null; + const focusLabel = agentFocusLabel(focusDate, focusMode); const title = focusLabel ? `By agent · ${focusLabel} · spend` : `By agent · ${rangeLabel} · spend`; @@ -2139,24 +2253,7 @@ const EMPTY_MODEL_USAGE_RANGES: ModelUsageRanges = { const MODEL_BREAKDOWN_TOP_N = 8; -/** Usage by model ($) — range synced with the parent panel. */ -function UsageByModel({ - ranges, - range, - focusDate, - focusMode, - focusData, - focusLoading, -}: { - ranges: ModelUsageRanges; - range: ModelRangeKey; - focusDate?: string | null; - focusMode?: 'daily' | 'weekly'; - focusData?: ModelUsage[] | null; - focusLoading?: boolean; -}) { - const focused = Boolean(focusDate && !focusLoading && focusData); - const data = focusDate != null ? (focusLoading ? [] : (focusData ?? [])) : ranges[range]; +function buildModelRows(data: ModelUsage[]) { const top = data.slice(0, MODEL_BREAKDOWN_TOP_N); const rest = data.slice(MODEL_BREAKDOWN_TOP_N); const rows = top.map((m) => ({ @@ -2177,6 +2274,39 @@ function UsageByModel({ color: '#475569', }); } + return rows; +} + +function modelEmptyMessage( + focusDate: string | null | undefined, + focusLoading: boolean | undefined, + range: ModelRangeKey +): string { + if (focusDate) { + return focusLoading ? 'Loading model spend…' : 'No model spend on this day.'; + } + return range === 'all' ? 'No model usage yet.' : 'No model usage in this window.'; +} + +/** Usage by model ($) — range synced with the parent panel. */ +function UsageByModel({ + ranges, + range, + focusDate, + focusMode, + focusData, + focusLoading, +}: { + ranges: ModelUsageRanges; + range: ModelRangeKey; + focusDate?: string | null; + focusMode?: 'daily' | 'weekly'; + focusData?: ModelUsage[] | null; + focusLoading?: boolean; +}) { + const focused = Boolean(focusDate && !focusLoading && focusData); + const data = focusDate != null ? (focusLoading ? [] : (focusData ?? [])) : ranges[range]; + const rows = buildModelRows(data); const max = Math.max(0.0001, ...rows.map((d) => d.value)); const total = data.reduce((acc, m) => acc + m.cost, 0); return ( @@ -2202,15 +2332,7 @@ function UsageByModel({ @@ -2536,6 +2658,19 @@ export function AdapterSourceHealthPanel({ runs }: { runs: SessionAdapterRun[] } ); } +function formatCodexCostLabel(report: CodexUsageReconciliation): string { + const minCost = (report.verified_cost_min_microusd ?? 0) / 1_000_000; + const maxCost = (report.verified_cost_max_microusd ?? 0) / 1_000_000; + const hasCostRange = report.verified_cost_min_microusd !== report.verified_cost_max_microusd; + if (report.unpriced_events > 0) { + return `${formatMoney(minCost)}–${formatMoney(maxCost)} priced portion · ${report.unpriced_events} unpriced`; + } + if (hasCostRange) { + return `${formatMoney(minCost)}–${formatMoney(maxCost)}`; + } + return formatMoney(minCost); +} + function CodexEvidenceSummary({ report, loading, @@ -2596,15 +2731,7 @@ function CodexEvidenceSummary({ report.stale_sessions; const complete = unresolvedSessions === 0 && report.pending_bytes === 0; const totalTokens = report.verified_totals.input_tokens + report.verified_totals.output_tokens; - const minCost = (report.verified_cost_min_microusd ?? 0) / 1_000_000; - const maxCost = (report.verified_cost_max_microusd ?? 0) / 1_000_000; - const hasCostRange = report.verified_cost_min_microusd !== report.verified_cost_max_microusd; - const costLabel = - report.unpriced_events > 0 - ? `${formatMoney(minCost)}–${formatMoney(maxCost)} priced portion · ${report.unpriced_events} unpriced` - : hasCostRange - ? `${formatMoney(minCost)}–${formatMoney(maxCost)}` - : formatMoney(minCost); + const costLabel = formatCodexCostLabel(report); return (
@@ -2722,6 +2849,274 @@ function CodexEvidenceSummary({ const CACHE_TTL_MS = 3 * 60 * 1000; // 3 minutes +function seedCachedUsages( + cachedUsagesResult: PromiseSettledResult[] +): Record { + const usageMap: Record = {}; + for (const r of cachedUsagesResult) { + if (r.status === 'fulfilled') { + const [id, usage] = r.value; + usageMap[id] = usage; + } + } + return usageMap; +} + +async function fetchMissingUsages( + accts: ProviderAccount[], + cachedAccounts: ProviderAccount[], + usageMap: Record +): Promise> { + const cachedIds = new Set(cachedAccounts.map((a) => a.id)); + const missing = accts.filter((a) => !cachedIds.has(a.id)); + if (missing.length > 0) { + const extraResults = await Promise.allSettled(missing.map((a) => checkAccountUsage(a.id))); + extraResults.forEach((r, i) => { + if (r.status === 'fulfilled') { + usageMap[missing[i].id] = r.value; + } + }); + } + return usageMap; +} + +function surfaceTokenUsageError(reason: unknown): string { + console.error('[CodeVetter] Usage load failed:', reason); + const msg = reason instanceof Error ? reason.message : String(reason); + if (msg === 'TAURI_NOT_AVAILABLE') { + return 'Tauri APIs not available. Run inside the desktop app to see live data.'; + } + return "Couldn't load your dashboard. Your saved data is safe — try again."; +} + +function ProviderTelemetrySection({ + loading, + accounts, + visibleAccounts, + accountUsages, + liveUsages, + liveErrors, + checkingLiveFor, + editingTelemetry, + setEditingTelemetry, + hiddenTelemetryItems, + toggleTelemetryItem, + showAllTelemetryItems, + onRedetect, + onCheckLive, + onDeleteAccount, +}: { + loading: boolean; + accounts: ProviderAccount[]; + visibleAccounts: ProviderAccount[]; + accountUsages: Record; + liveUsages: Record; + liveErrors: Record; + checkingLiveFor: string | null; + editingTelemetry: boolean; + setEditingTelemetry: React.Dispatch>; + hiddenTelemetryItems: Set; + toggleTelemetryItem: (id: string) => void; + showAllTelemetryItems: () => void; + onRedetect: () => void; + onCheckLive: (account: ProviderAccount) => void; + onDeleteAccount: (account: ProviderAccount) => void; +}) { + return ( +
+
+ + Provider telemetry + + live quota windows + local token history + +
+ {accounts.length > 0 && ( + + )} + +
+
+ {loading ? ( + + + + + + + ) : ( + + {editingTelemetry && ( + + )} +
+ ); +} + +function LegacySummarySection({ + tokenUsage, + loading, + liveSessionPolicy, +}: { + tokenUsage: TokenUsageStats | null; + loading: boolean; + liveSessionPolicy: LiveSessionEvidencePolicy | null; +}) { + const stats = [ + { + label: 'Today', + cost: tokenUsage?.today_cost ?? 0, + gen: tokenUsage?.today_generated ?? 0, + color: 'text-zinc-100', + }, + { + label: 'This week', + cost: tokenUsage?.week_cost ?? 0, + gen: tokenUsage?.week_generated ?? 0, + color: 'text-zinc-100', + }, + { + label: 'This month', + cost: tokenUsage?.month_cost ?? 0, + gen: tokenUsage?.month_generated ?? 0, + color: 'text-amber-300', + }, + { + label: 'This year', + cost: tokenUsage?.year_cost ?? 0, + gen: tokenUsage?.year_generated ?? 0, + color: 'text-zinc-100', + }, + ]; + + return ( +
+
+
+
Legacy blended summary
+

+ All-agent period estimates +

+
+
+ {liveSessionPolicy && ( + + live archive · {liveSessionPolicy.incremental_interval_secs}s ·{' '} + {liveSessionPolicy.supported_incremental_adapters.join(' + ')} + + )} + {liveSessionPolicy && liveSessionPolicy.incomplete_codex_sources > 0 && ( + + usage catching up · {liveSessionPolicy.incomplete_codex_sources} sources + + )} +
+
+ +
+ {stats.map((stat) => ( +
+ {stat.label} + + + {loading && !tokenUsage ? '--' : formatMoney(stat.cost)} + + + {loading && !tokenUsage ? 'loading' : `${formatTokens(stat.gen)} generated`} + + +
+ ))} +
+
+ ); +} + export default function Home() { const { pathname } = useLocation(); const navigate = useNavigate(); @@ -2832,34 +3227,13 @@ export default function Home() { .finally(() => setCodexReconciliationLoading(false)); // Seed usage map with cached-ID results that came back alongside the rest. - const usageMap: Record = {}; - cachedUsagesResult.forEach((r) => { - if (r.status === 'fulfilled') { - const [id, usage] = r.value; - usageMap[id] = usage; - } - }); + const usageMap = seedCachedUsages(cachedUsagesResult); if (accountsResult.status === 'fulfilled') { const accts = accountsResult.value; - setAccounts(accts); - - // Fetch usage only for accounts that weren't covered by the cached - // parallel fetch (new accounts since last load, or first-ever load). - const cachedIds = new Set(cachedAccounts.map((a) => a.id)); - const missing = accts.filter((a) => !cachedIds.has(a.id)); - if (missing.length > 0) { - const extraResults = await Promise.allSettled( - missing.map((a) => checkAccountUsage(a.id)) - ); - extraResults.forEach((r, i) => { - if (r.status === 'fulfilled') { - usageMap[missing[i].id] = r.value; - } - }); - } - setAccountUsages(usageMap); + const updatedMap = await fetchMissingUsages(accts, cachedAccounts, usageMap); + setAccountUsages(updatedMap); } else if (Object.keys(usageMap).length > 0) { setAccountUsages(usageMap); } @@ -2867,16 +3241,7 @@ export default function Home() { // If critical reads failed, surface a friendly message — full detail // goes to the console, never the raw IPC error to the user. if (tokenUsageResult.status === 'rejected') { - console.error('[CodeVetter] Usage load failed:', tokenUsageResult.reason); - const msg = - tokenUsageResult.reason instanceof Error - ? tokenUsageResult.reason.message - : String(tokenUsageResult.reason); - if (msg === 'TAURI_NOT_AVAILABLE') { - setError('Tauri APIs not available. Run inside the desktop app to see live data.'); - } else { - setError("Couldn't load your dashboard. Your saved data is safe — try again."); - } + setError(surfaceTokenUsageError(tokenUsageResult.reason)); } } catch (err) { console.error('[CodeVetter] Dashboard load failed:', err); @@ -2993,9 +3358,7 @@ export default function Home() { // ─── Auto-refresh live usage every 60s ───────────────────────────────── const refreshLiveUsage = useCallback(async (accts: ProviderAccount[]) => { - const supported = accts.filter((a) => - ['anthropic', 'openai', 'google', 'cursor', 'devin', 'grok'].includes(a.provider) - ); + const supported = accts.filter((a) => LIVE_SUPPORTED_PROVIDERS.includes(a.provider)); if (supported.length === 0) return; const results = await Promise.allSettled( @@ -3050,7 +3413,6 @@ export default function Home() { try { const result = await triggerIndex(); setIndexResult(result); - // Refresh dashboard after indexing (no spinners — user sees "Indexing..." state) await refreshDashboard(); } catch (err) { console.error('Trigger index failed:', err); @@ -3059,6 +3421,57 @@ export default function Home() { } }, [refreshDashboard]); + const handleRedetect = useCallback(async () => { + try { + const [result] = await Promise.all([detectProviderAccounts(), triggerIndex()]); + setAccounts(result.accounts); + if (result.accounts.length > 0) { + const usageResults = await Promise.allSettled( + result.accounts.map((a) => checkAccountUsage(a.id)) + ); + const usageMap: Record = {}; + usageResults.forEach((r, i) => { + if (r.status === 'fulfilled') { + usageMap[result.accounts[i].id] = r.value; + } + }); + setAccountUsages(usageMap); + } + refreshDashboard(); + } catch (err) { + console.error('Detection failed:', err); + } + }, [refreshDashboard]); + + const handleCheckLive = useCallback(async (account: ProviderAccount) => { + setCheckingLiveFor(account.id); + try { + const result = await checkLiveUsage(account.provider, account.api_key ?? undefined); + setLiveUsages((prev) => ({ ...prev, [account.id]: result })); + setLiveErrors((prev) => { + const next = { ...prev }; + delete next[account.id]; + return next; + }); + } catch (err) { + setLiveErrors((prev) => ({ ...prev, [account.id]: String(err) })); + } finally { + setCheckingLiveFor(null); + } + }, []); + + const handleDeleteAccount = useCallback( + async (account: ProviderAccount) => { + try { + await deleteProviderAccount(account.id); + refreshDashboard(); + } catch (err) { + console.error('Failed to delete account:', err); + } + }, + [refreshDashboard] + ); + // ─── Render ──────────────────────────────────────────────────────────── return ( @@ -3073,83 +3486,11 @@ export default function Home() { onOpenRecovery={() => navigate('/settings?section=usage')} /> -
-
-
-
Legacy blended summary
-

- All-agent period estimates -

-
-
- {liveSessionPolicy && ( - - live archive · {liveSessionPolicy.incremental_interval_secs}s ·{' '} - {liveSessionPolicy.supported_incremental_adapters.join(' + ')} - - )} - {liveSessionPolicy && liveSessionPolicy.incomplete_codex_sources > 0 && ( - - usage catching up · {liveSessionPolicy.incomplete_codex_sources} sources - - )} -
-
- - {/* Historical blended estimates remain available for continuity, but - never substitute for the verified Codex evidence panel above. */} -
- {[ - { - label: 'Today', - cost: tokenUsage?.today_cost ?? 0, - gen: tokenUsage?.today_generated ?? 0, - color: 'text-zinc-100', - }, - { - label: 'This week', - cost: tokenUsage?.week_cost ?? 0, - gen: tokenUsage?.week_generated ?? 0, - color: 'text-zinc-100', - }, - { - label: 'This month', - cost: tokenUsage?.month_cost ?? 0, - gen: tokenUsage?.month_generated ?? 0, - color: 'text-amber-300', - }, - { - label: 'This year', - cost: tokenUsage?.year_cost ?? 0, - gen: tokenUsage?.year_generated ?? 0, - color: 'text-zinc-100', - }, - ].map((stat) => ( -
- {stat.label} - - - {loading && !tokenUsage ? '--' : formatMoney(stat.cost)} - - - {loading && !tokenUsage ? 'loading' : `${formatTokens(stat.gen)} generated`} - - -
- ))} -
-
+ {/* Index result banner */} {indexResult && ( @@ -3183,155 +3524,23 @@ export default function Home() { )} {/* Usage — remaining per account */} -
-
- - Provider telemetry - - live quota windows + local token history - -
- {accounts.length > 0 && ( - - )} - -
-
- {loading ? ( - - - - - - - ) : ( - - {editingTelemetry && ( - - )} -
+ void handleRedetect()} + onCheckLive={(account) => void handleCheckLive(account)} + onDeleteAccount={(account) => void handleDeleteAccount(account)} + /> {tokenUsage && (