优化 Antigravity 配额展示顺序

This commit is contained in:
MMEXA
2026-07-08 22:34:29 +08:00
parent accd250226
commit 9ec48ab6b9
6 changed files with 266 additions and 78 deletions
@@ -60,13 +60,6 @@
>
{{ item.label }}
</div>
<div
v-if="item.label !== item.model"
class="text-[9px] leading-tight text-muted-foreground/55 truncate font-mono"
:title="item.model"
>
{{ item.model }}
</div>
</div>
<span :class="getQuotaRemainingClass(item.usedPercent)">
{{ item.remainingPercent.toFixed(1) }}%
@@ -125,6 +118,10 @@ import { testModel } from '@/api/endpoints/providers'
import type { UpstreamMetadata, QuotaStatusSnapshot, QuotaWindowSnapshot } from '@/api/endpoints/types'
import { useToast } from '@/composables/useToast'
import { parseApiError } from '@/utils/errorParser'
import {
compareAntigravityQuotaItems,
resolveAntigravityQuotaLabel,
} from '@/features/providers/utils/antigravityQuota'
const props = defineProps<{
open: boolean
@@ -194,32 +191,6 @@ function secondsUntilUnixReset(resetAt: unknown): number | null {
return Math.max(Math.floor(numericResetAt - now), 0)
}
function sortQuotaItems(a: QuotaItem, b: QuotaItem): number {
return (a.remainingPercent - b.remainingPercent)
|| ((a.resetSeconds ?? Number.POSITIVE_INFINITY) - (b.resetSeconds ?? Number.POSITIVE_INFINITY))
|| a.label.localeCompare(b.label)
|| a.model.localeCompare(b.model)
}
function isOpaqueAntigravityQuotaIdentifier(value: string): boolean {
return value.trim().startsWith('RateLimitResetCredit_')
}
function resolveQuotaLabel(
model: string,
rawLabel: unknown,
opaqueDisplayIndex: { value: number },
): string {
const candidate = String(rawLabel || '').trim()
if (candidate && !isOpaqueAntigravityQuotaIdentifier(candidate)) return candidate
if (isOpaqueAntigravityQuotaIdentifier(model) || (candidate && isOpaqueAntigravityQuotaIdentifier(candidate))) {
const label = `Key-${opaqueDisplayIndex.value}`
opaqueDisplayIndex.value += 1
return label
}
return candidate || model
}
function buildItemsFromQuotaSnapshot(quota: QuotaStatusSnapshot | null | undefined): QuotaItem[] {
if (!quota) return []
@@ -252,7 +223,7 @@ function buildItemsFromQuotaSnapshot(quota: QuotaStatusSnapshot | null | undefin
return {
model,
label: resolveQuotaLabel(model, window.label || window.model, opaqueDisplayIndex),
label: resolveAntigravityQuotaLabel(model, window.label || window.model, opaqueDisplayIndex),
usedPercent,
remainingPercent,
resetSeconds: getQuotaWindowLiveResetSeconds(quota, window),
@@ -260,7 +231,7 @@ function buildItemsFromQuotaSnapshot(quota: QuotaStatusSnapshot | null | undefin
})
.filter((item): item is QuotaItem => item !== null)
items.sort(sortQuotaItems)
items.sort(compareAntigravityQuotaItems)
return items
}
@@ -305,14 +276,14 @@ const items = computed<QuotaItem[]>(() => {
result.push({
model,
label: resolveQuotaLabel(model, info['display_name'], opaqueDisplayIndex),
label: resolveAntigravityQuotaLabel(model, info['display_name'], opaqueDisplayIndex),
usedPercent,
remainingPercent,
resetSeconds,
})
}
result.sort(sortQuotaItems)
result.sort(compareAntigravityQuotaItems)
return result
})
@@ -372,13 +372,6 @@
>
<template #footer>
<div class="mt-0.5 space-y-0.5">
<div
v-if="item.label !== item.model"
class="text-[9px] text-muted-foreground/55 truncate font-mono"
:title="item.model"
>
{{ item.model }}
</div>
<div
v-if="item.resetSeconds !== null || item.usedPercent > 0"
class="text-[9px] text-muted-foreground/70"
@@ -967,6 +960,10 @@ import ProviderMonthlyQuotaCard from '@/features/providers/components/ProviderMo
import ProviderQuotaProgressRow from '@/features/providers/components/ProviderQuotaProgressRow.vue'
import ProviderQuotaSectionHeader from '@/features/providers/components/ProviderQuotaSectionHeader.vue'
import { useProxyNodesStore } from '@/stores/proxy-nodes'
import {
compareAntigravityQuotaItems,
resolveAntigravityQuotaLabel,
} from '@/features/providers/utils/antigravityQuota'
import {
deleteEndpointKey,
recoverKeyHealth,
@@ -3385,32 +3382,6 @@ function coerceAntigravityRemainingFraction(value: number | string | null | unde
return Math.min(Math.max(numericValue, 0), 1)
}
function antigravityQuotaItemSort(a: AntigravityQuotaItem, b: AntigravityQuotaItem): number {
return (a.remainingPercent - b.remainingPercent)
|| ((a.resetSeconds ?? Number.POSITIVE_INFINITY) - (b.resetSeconds ?? Number.POSITIVE_INFINITY))
|| a.label.localeCompare(b.label)
|| a.model.localeCompare(b.model)
}
function isOpaqueAntigravityQuotaIdentifier(value: string): boolean {
return value.trim().startsWith('RateLimitResetCredit_')
}
function resolveAntigravityQuotaLabel(
model: string,
rawLabel: unknown,
opaqueDisplayIndex: { value: number },
): string {
const candidate = String(rawLabel || '').trim()
if (candidate && !isOpaqueAntigravityQuotaIdentifier(candidate)) return candidate
if (isOpaqueAntigravityQuotaIdentifier(model) || (candidate && isOpaqueAntigravityQuotaIdentifier(candidate))) {
const label = `Key-${opaqueDisplayIndex.value}`
opaqueDisplayIndex.value += 1
return label
}
return candidate || model
}
function getAntigravityQuotaItems(metadata: UpstreamMetadata | null | undefined): AntigravityQuotaItem[] {
const quotaByModel = metadata?.antigravity?.quota_by_model
if (!quotaByModel || typeof quotaByModel !== 'object') return []
@@ -3449,7 +3420,7 @@ function getAntigravityQuotaItems(metadata: UpstreamMetadata | null | undefined)
})
}
items.sort(antigravityQuotaItemSort)
items.sort(compareAntigravityQuotaItems)
return items
}
@@ -3493,7 +3464,7 @@ function getAntigravityQuotaItemsFromSnapshot(key: EndpointAPIKey): AntigravityQ
})
.filter((item): item is AntigravityQuotaItem => item !== null)
items.sort(antigravityQuotaItemSort)
items.sort(compareAntigravityQuotaItems)
return items
}
@@ -102,12 +102,13 @@ function mount(metadata: UpstreamMetadata) {
}
describe('AntigravityQuotaDialog', () => {
it('renders model display names without losing raw model identifiers', () => {
it('renders opaque quota identifiers with concise visible labels', () => {
const rawIdentifier = 'RateLimitResetCredit_05cbb6eeeb9c81918e011d8300f9ebfb'
const { root, unmount } = mount({
antigravity: {
quota_by_model: {
'RateLimitResetCredit_05cbb6eeeb9c81918e011d8300f9ebfb': {
display_name: 'RateLimitResetCredit_05cbb6eeeb9c81918e011d8300f9ebfb',
[rawIdentifier]: {
display_name: rawIdentifier,
remaining_fraction: 0.25,
used_percent: 75,
},
@@ -116,9 +117,45 @@ describe('AntigravityQuotaDialog', () => {
})
expect(root.textContent).toContain('Key-1')
expect(root.textContent).toContain('RateLimitResetCredit_05cbb6eeeb9c81918e011d8300f9ebfb')
expect(root.textContent).not.toContain(rawIdentifier)
expect(root.textContent).toContain('25.0%')
unmount()
})
it('orders important Gemini and Claude quota rows before low-priority rows', () => {
const { root, unmount } = mount({
antigravity: {
quota_by_model: {
tab_flash_lite_preview: {
display_name: 'Tab Flash Lite Preview',
remaining_fraction: 0.01,
used_percent: 99,
},
'gemini-3.5-flash-low': {
display_name: 'Gemini 3.5 Flash Low',
remaining_fraction: 0.8,
used_percent: 20,
},
'claude-opus-4-6-thinking': {
display_name: 'Claude Opus 4.6 Thinking',
remaining_fraction: 1,
used_percent: 0,
},
chat_20706: {
display_name: 'chat_20706',
remaining_fraction: 0,
used_percent: 100,
},
},
},
})
const text = root.textContent || ''
expect(text.indexOf('Claude Opus 4.6 Thinking')).toBeLessThan(text.indexOf('Gemini 3.5 Flash Low'))
expect(text.indexOf('Gemini 3.5 Flash Low')).toBeLessThan(text.indexOf('Tab Flash Lite Preview'))
expect(text.indexOf('Tab Flash Lite Preview')).toBeLessThan(text.indexOf('chat_20706'))
unmount()
})
})