feat: optimize model rate limit indicator layout with short aliases

- Change layout from fixed 3-column grid to vertical-first responsive
  columns (1 col for ≤4 items, 2 cols for ≤8, 3 cols for 9+)
- Add short aliases for all known model scope keys (e.g. COpus46, CSon46,
  G3PH, G3F) to reduce badge width
- Display countdown timer directly on each badge (supports h/m/s)
- Retain legacy scope aliases for backward compatibility
This commit is contained in:
erio
2026-02-24 22:11:50 +08:00
parent d8d4b0c0c7
commit a3ff317f1c

View File

@@ -77,13 +77,23 @@
</div> </div>
<!-- Model Rate Limit Indicators (Antigravity OAuth Smart Retry) --> <!-- Model Rate Limit Indicators (Antigravity OAuth Smart Retry) -->
<div v-if="activeModelRateLimits.length > 0" class="grid grid-cols-3 gap-1"> <div
<div v-for="item in activeModelRateLimits" :key="item.model" class="group relative"> v-if="activeModelRateLimits.length > 0"
:class="[
activeModelRateLimits.length <= 4
? 'flex flex-col gap-1'
: activeModelRateLimits.length <= 8
? 'columns-2 gap-x-2'
: 'columns-3 gap-x-2'
]"
>
<div v-for="item in activeModelRateLimits" :key="item.model" class="group relative mb-1 break-inside-avoid">
<span <span
class="inline-flex items-center gap-1 rounded bg-purple-100 px-1.5 py-0.5 text-xs font-medium text-purple-700 dark:bg-purple-900/30 dark:text-purple-400" class="inline-flex items-center gap-1 rounded bg-purple-100 px-1.5 py-0.5 text-xs font-medium text-purple-700 dark:bg-purple-900/30 dark:text-purple-400"
> >
<Icon name="exclamationTriangle" size="xs" :stroke-width="2" /> <Icon name="exclamationTriangle" size="xs" :stroke-width="2" />
{{ formatScopeName(item.model) }} {{ formatScopeName(item.model) }}
<span class="text-[10px] opacity-70">{{ formatModelResetTime(item.reset_at) }}</span>
</span> </span>
<!-- Tooltip --> <!-- Tooltip -->
<div <div
@@ -154,17 +164,50 @@ const activeModelRateLimits = computed(() => {
}) })
const formatScopeName = (scope: string): string => { const formatScopeName = (scope: string): string => {
const names: Record<string, string> = { const aliases: Record<string, string> = {
// Claude 系列
'claude-opus-4-6-thinking': 'COpus46',
'claude-sonnet-4-6': 'CSon46',
'claude-sonnet-4-5': 'CSon45',
'claude-sonnet-4-5-thinking': 'CSon45T',
// Gemini 2.5 系列
'gemini-2.5-flash': 'G25F',
'gemini-2.5-flash-lite': 'G25FL',
'gemini-2.5-flash-thinking': 'G25FT',
'gemini-2.5-pro': 'G25P',
// Gemini 3 系列
'gemini-3-flash': 'G3F',
'gemini-3.1-pro-high': 'G3PH',
'gemini-3.1-pro-low': 'G3PL',
'gemini-3-pro-image': 'G3PI',
// 其他
'gpt-oss-120b-medium': 'GPT120',
'tab_flash_lite_preview': 'TabFL',
// 旧版 scope 别名(兼容)
claude: 'Claude', claude: 'Claude',
claude_sonnet: 'Claude Sonnet', claude_sonnet: 'CSon',
claude_opus: 'Claude Opus', claude_opus: 'COpus',
claude_haiku: 'Claude Haiku', claude_haiku: 'CHaiku',
gemini_text: 'Gemini', gemini_text: 'Gemini',
gemini_image: 'Image', gemini_image: 'GImg',
gemini_flash: 'Gemini Flash', gemini_flash: 'GFlash',
gemini_pro: 'Gemini Pro' gemini_pro: 'GPro',
} }
return names[scope] || scope return aliases[scope] || scope
}
const formatModelResetTime = (resetAt: string): string => {
const date = new Date(resetAt)
const now = new Date()
const diffMs = date.getTime() - now.getTime()
if (diffMs <= 0) return ''
const totalSecs = Math.floor(diffMs / 1000)
const h = Math.floor(totalSecs / 3600)
const m = Math.floor((totalSecs % 3600) / 60)
const s = totalSecs % 60
if (h > 0) return `${h}h${m}m`
if (m > 0) return `${m}m${s}s`
return `${s}s`
} }
// Computed: is overloaded (529) // Computed: is overloaded (529)