fix(ui): simplify quota tooltip - delimiter lines only, full model names

- Remove tier group headers, use delimiter lines between tiers
- Show full model names (Claude Opus 4.5 (Thinking), etc.)
- Show "Exhausted" in percentage column instead of appending to name
- Add all known primary models when exhausted
This commit is contained in:
kaitranntt
2026-01-25 17:09:59 -05:00
parent 41d17e7cae
commit b2b8a85af6
3 changed files with 21 additions and 22 deletions
@@ -9,7 +9,6 @@ import {
getMinClaudeQuota, getMinClaudeQuota,
getModelsWithTiers, getModelsWithTiers,
groupModelsByTier, groupModelsByTier,
getTierLabel,
type ModelTier, type ModelTier,
} from '@/lib/utils'; } from '@/lib/utils';
import { PRIVACY_BLUR_CLASS } from '@/contexts/privacy-context'; import { PRIVACY_BLUR_CLASS } from '@/contexts/privacy-context';
@@ -220,28 +219,28 @@ export function AccountCard({
</div> </div>
</TooltipTrigger> </TooltipTrigger>
<TooltipContent side="top" className="max-w-xs"> <TooltipContent side="top" className="max-w-xs">
<div className="text-xs space-y-2"> <div className="text-xs space-y-1">
<p className="font-medium">Model Quotas:</p> <p className="font-medium">Model Quotas:</p>
{(() => { {(() => {
const tiered = getModelsWithTiers(quota?.models || []); const tiered = getModelsWithTiers(quota?.models || []);
const groups = groupModelsByTier(tiered); const groups = groupModelsByTier(tiered);
const tierOrder: ModelTier[] = ['primary', 'gemini-3', 'gemini-2', 'other']; const tierOrder: ModelTier[] = ['primary', 'gemini-3', 'gemini-2', 'other'];
return tierOrder.map((tier) => { return tierOrder.map((tier, idx) => {
const models = groups.get(tier); const models = groups.get(tier);
if (!models || models.length === 0) return null; if (!models || models.length === 0) return null;
const isFirst = tierOrder
.slice(0, idx)
.every((t) => !groups.get(t)?.length);
return ( return (
<div key={tier} className="space-y-0.5"> <div key={tier}>
<div className="text-[10px] uppercase tracking-wider text-muted-foreground font-semibold border-b border-border/50 pb-0.5"> {!isFirst && <div className="border-t border-border/40 my-1" />}
{getTierLabel(tier)}
</div>
{models.map((m) => ( {models.map((m) => (
<div key={m.name} className="flex justify-between gap-4 pl-1"> <div key={m.name} className="flex justify-between gap-4">
<span className={cn('truncate', m.exhausted && 'text-red-500')}> <span className={cn('truncate', m.exhausted && 'text-red-500')}>
{m.displayName} {m.displayName}
{m.exhausted && ' (Exhausted)'}
</span> </span>
<span className={cn('font-mono', m.exhausted && 'text-red-500')}> <span className={cn('font-mono', m.exhausted && 'text-red-500')}>
{m.percentage}% {m.exhausted ? 'Exhausted' : `${m.percentage}%`}
</span> </span>
</div> </div>
))} ))}
@@ -36,7 +36,6 @@ import {
getMinClaudeQuota, getMinClaudeQuota,
getModelsWithTiers, getModelsWithTiers,
groupModelsByTier, groupModelsByTier,
getTierLabel,
type ModelTier, type ModelTier,
} from '@/lib/utils'; } from '@/lib/utils';
import { PRIVACY_BLUR_CLASS } from '@/contexts/privacy-context'; import { PRIVACY_BLUR_CLASS } from '@/contexts/privacy-context';
@@ -350,28 +349,28 @@ export function AccountItem({
</div> </div>
</TooltipTrigger> </TooltipTrigger>
<TooltipContent side="bottom" className="max-w-xs"> <TooltipContent side="bottom" className="max-w-xs">
<div className="text-xs space-y-2"> <div className="text-xs space-y-1">
<p className="font-medium">Model Quotas:</p> <p className="font-medium">Model Quotas:</p>
{(() => { {(() => {
const tiered = getModelsWithTiers(quota?.models || []); const tiered = getModelsWithTiers(quota?.models || []);
const groups = groupModelsByTier(tiered); const groups = groupModelsByTier(tiered);
const tierOrder: ModelTier[] = ['primary', 'gemini-3', 'gemini-2', 'other']; const tierOrder: ModelTier[] = ['primary', 'gemini-3', 'gemini-2', 'other'];
return tierOrder.map((tier) => { return tierOrder.map((tier, idx) => {
const models = groups.get(tier); const models = groups.get(tier);
if (!models || models.length === 0) return null; if (!models || models.length === 0) return null;
const isFirst = tierOrder
.slice(0, idx)
.every((t) => !groups.get(t)?.length);
return ( return (
<div key={tier} className="space-y-0.5"> <div key={tier}>
<div className="text-[10px] uppercase tracking-wider text-muted-foreground font-semibold border-b border-border/50 pb-0.5"> {!isFirst && <div className="border-t border-border/40 my-1" />}
{getTierLabel(tier)}
</div>
{models.map((m) => ( {models.map((m) => (
<div key={m.name} className="flex justify-between gap-4 pl-1"> <div key={m.name} className="flex justify-between gap-4">
<span className={cn('truncate', m.exhausted && 'text-red-500')}> <span className={cn('truncate', m.exhausted && 'text-red-500')}>
{m.displayName} {m.displayName}
{m.exhausted && ' (Exhausted)'}
</span> </span>
<span className={cn('font-mono', m.exhausted && 'text-red-500')}> <span className={cn('font-mono', m.exhausted && 'text-red-500')}>
{m.percentage}% {m.exhausted ? 'Exhausted' : `${m.percentage}%`}
</span> </span>
</div> </div>
))} ))}
+3 -2
View File
@@ -198,9 +198,10 @@ export function getClaudeResetTime<
// Known primary models to show when exhausted (removed from API response) // Known primary models to show when exhausted (removed from API response)
const KNOWN_PRIMARY_MODELS = [ const KNOWN_PRIMARY_MODELS = [
{ name: 'claude-opus-4-5', displayName: 'Claude Opus 4.5' }, { name: 'claude-opus-4-5-thinking', displayName: 'Claude Opus 4.5 (Thinking)' },
{ name: 'claude-sonnet-4-5', displayName: 'Claude Sonnet 4.5' }, { name: 'claude-sonnet-4-5', displayName: 'Claude Sonnet 4.5' },
{ name: 'gpt-oss-120b', displayName: 'GPT-OSS 120B' }, { name: 'claude-sonnet-4-5-thinking', displayName: 'Claude Sonnet 4.5 (Thinking)' },
{ name: 'gpt-oss-120b', displayName: 'GPT-OSS 120B (Medium)' },
]; ];
/** Model tier for visual grouping */ /** Model tier for visual grouping */