mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-09 04:30:20 +08:00
fix(frontend): align Antigravity quota summaries
This commit is contained in:
@@ -104,7 +104,7 @@ describe('pool key display panels', () => {
|
||||
meterClass: 'text-emerald-600',
|
||||
},
|
||||
{
|
||||
label: 'Claude额度',
|
||||
label: 'Claude & ChatGPT',
|
||||
remainingPercent: 100,
|
||||
resetText: '1h 后重置',
|
||||
meterText: '100%',
|
||||
@@ -119,7 +119,7 @@ describe('pool key display panels', () => {
|
||||
expect(root.querySelector('[data-testid="pool-quota-rows"]')?.className).toContain('space-y-2')
|
||||
expect(Array.from(root.querySelectorAll('[data-testid="pool-quota-period-label"]')).map(node => node.textContent)).toEqual([
|
||||
'Gemini额度',
|
||||
'Claude额度',
|
||||
'Claude & ChatGPT',
|
||||
])
|
||||
expect(Array.from(root.querySelectorAll('[data-testid="pool-quota-meter-text"]')).map(node => node.textContent)).toEqual(['90.6%–100%', '100%'])
|
||||
expect(root.querySelectorAll('[data-testid="pool-quota-progress-track"]')).toHaveLength(2)
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
@update:model-value="$emit('update:open', $event)"
|
||||
>
|
||||
<template
|
||||
v-if="providerId && items.length > 0"
|
||||
v-if="providerId && rawItems.length > 0"
|
||||
#header-actions
|
||||
>
|
||||
<DropdownMenu :modal="false">
|
||||
@@ -32,7 +32,7 @@
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem
|
||||
v-for="item in items"
|
||||
v-for="item in rawItems"
|
||||
:key="item.model"
|
||||
:title="item.model"
|
||||
@select="handleTestModel(item.model)"
|
||||
@@ -56,13 +56,13 @@
|
||||
<div class="min-w-0 flex-1 mr-2">
|
||||
<div
|
||||
class="text-muted-foreground truncate"
|
||||
:title="item.model"
|
||||
:title="item.label"
|
||||
>
|
||||
{{ item.label }}
|
||||
</div>
|
||||
</div>
|
||||
<span :class="getQuotaRemainingClass(item.usedPercent)">
|
||||
{{ item.remainingPercent.toFixed(1) }}%
|
||||
{{ item.detail || `${item.remainingPercent.toFixed(1)}%` }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="relative w-full h-1.5 bg-border rounded-full overflow-hidden">
|
||||
@@ -122,6 +122,7 @@ import {
|
||||
compareAntigravityQuotaItems,
|
||||
dedupeAntigravityQuotaItemsByLabel,
|
||||
resolveAntigravityQuotaLabel,
|
||||
summarizeAntigravityQuotaItems,
|
||||
} from '@/features/providers/utils/antigravityQuota'
|
||||
|
||||
const props = defineProps<{
|
||||
@@ -143,6 +144,7 @@ interface QuotaItem {
|
||||
usedPercent: number
|
||||
remainingPercent: number
|
||||
resetSeconds: number | null
|
||||
detail?: string
|
||||
}
|
||||
|
||||
const { error: showError, success: showSuccess } = useToast()
|
||||
@@ -236,7 +238,7 @@ function buildItemsFromQuotaSnapshot(quota: QuotaStatusSnapshot | null | undefin
|
||||
return dedupeAntigravityQuotaItemsByLabel(items)
|
||||
}
|
||||
|
||||
const items = computed<QuotaItem[]>(() => {
|
||||
const rawItems = computed<QuotaItem[]>(() => {
|
||||
const snapshotItems = buildItemsFromQuotaSnapshot(props.quotaSnapshot)
|
||||
if (snapshotItems.length > 0) return snapshotItems
|
||||
|
||||
@@ -288,6 +290,8 @@ const items = computed<QuotaItem[]>(() => {
|
||||
return dedupeAntigravityQuotaItemsByLabel(result)
|
||||
})
|
||||
|
||||
const items = computed<QuotaItem[]>(() => summarizeAntigravityQuotaItems(rawItems.value))
|
||||
|
||||
async function handleTestModel(modelName: string) {
|
||||
if (!props.providerId || testingModel.value) return
|
||||
|
||||
|
||||
@@ -376,12 +376,13 @@
|
||||
/>
|
||||
<div class="grid grid-cols-2 gap-3">
|
||||
<ProviderQuotaProgressRow
|
||||
v-for="item in getAntigravityQuotaPreviewForKey(key)"
|
||||
v-for="item in getAntigravityQuotaSummaryForKey(key)"
|
||||
:key="item.model"
|
||||
:label="item.label"
|
||||
:title="item.model"
|
||||
:title="item.label"
|
||||
:used-percent="item.usedPercent"
|
||||
:remaining-percent="item.remainingPercent"
|
||||
:meter-text="item.detail"
|
||||
:meter-class="getQuotaRemainingClass(item.usedPercent)"
|
||||
:bar-class="getQuotaRemainingBarColor(item.usedPercent)"
|
||||
>
|
||||
@@ -401,14 +402,6 @@
|
||||
</div>
|
||||
</template>
|
||||
</ProviderQuotaProgressRow>
|
||||
<button
|
||||
v-if="getAntigravityQuotaHiddenCountForKey(key) > 0"
|
||||
type="button"
|
||||
class="col-span-2 text-left text-[10px] text-muted-foreground hover:text-foreground transition-colors"
|
||||
@click="openAntigravityQuotaDialog(key)"
|
||||
>
|
||||
{{ legacyT('另有') }} {{ getAntigravityQuotaHiddenCountForKey(key) }} {{ legacyT('个模型,查看全部') }}
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
@@ -989,6 +982,7 @@ import {
|
||||
compareAntigravityQuotaItems,
|
||||
dedupeAntigravityQuotaItemsByLabel,
|
||||
resolveAntigravityQuotaLabel,
|
||||
summarizeAntigravityQuotaItems,
|
||||
} from '@/features/providers/utils/antigravityQuota'
|
||||
import {
|
||||
deleteEndpointKey,
|
||||
@@ -3374,6 +3368,7 @@ interface AntigravityQuotaItem {
|
||||
usedPercent: number
|
||||
remainingPercent: number
|
||||
resetSeconds: number | null
|
||||
detail?: string
|
||||
}
|
||||
|
||||
interface GeminiCliQuotaItem {
|
||||
@@ -3579,20 +3574,14 @@ function getAntigravityQuotaItemsFromSnapshot(key: EndpointAPIKey): AntigravityQ
|
||||
return dedupeAntigravityQuotaItemsByLabel(items)
|
||||
}
|
||||
|
||||
const ANTIGRAVITY_QUOTA_PREVIEW_LIMIT = 6
|
||||
|
||||
function getAntigravityQuotaItemsForKey(key: EndpointAPIKey): AntigravityQuotaItem[] {
|
||||
const snapshotItems = getAntigravityQuotaItemsFromSnapshot(key)
|
||||
if (snapshotItems.length > 0) return snapshotItems
|
||||
return getAntigravityQuotaItems(key.upstream_metadata)
|
||||
}
|
||||
|
||||
function getAntigravityQuotaPreviewForKey(key: EndpointAPIKey): AntigravityQuotaItem[] {
|
||||
return getAntigravityQuotaItemsForKey(key).slice(0, ANTIGRAVITY_QUOTA_PREVIEW_LIMIT)
|
||||
}
|
||||
|
||||
function getAntigravityQuotaHiddenCountForKey(key: EndpointAPIKey): number {
|
||||
return Math.max(getAntigravityQuotaItemsForKey(key).length - ANTIGRAVITY_QUOTA_PREVIEW_LIMIT, 0)
|
||||
function getAntigravityQuotaSummaryForKey(key: EndpointAPIKey): AntigravityQuotaItem[] {
|
||||
return summarizeAntigravityQuotaItems(getAntigravityQuotaItemsForKey(key))
|
||||
}
|
||||
|
||||
function getResetCountdownText(
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
:class="meterClass"
|
||||
data-testid="provider-quota-progress-meter"
|
||||
>
|
||||
{{ normalizedRemainingPercent.toFixed(1) }}%
|
||||
{{ meterText || `${normalizedRemainingPercent.toFixed(1)}%` }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="relative w-full h-1.5 bg-border rounded-full overflow-hidden">
|
||||
@@ -47,6 +47,7 @@ const props = withDefaults(defineProps<{
|
||||
barClass?: string
|
||||
footerClass?: string
|
||||
resetText?: string | null
|
||||
meterText?: string | null
|
||||
}>(), {
|
||||
usedPercent: null,
|
||||
remainingPercent: null,
|
||||
@@ -55,6 +56,7 @@ const props = withDefaults(defineProps<{
|
||||
barClass: '',
|
||||
footerClass: '',
|
||||
resetText: null,
|
||||
meterText: null,
|
||||
})
|
||||
|
||||
function normalizePercent(value: number | null | undefined): number | null {
|
||||
|
||||
+19
-13
@@ -102,7 +102,7 @@ function mount(metadata: UpstreamMetadata) {
|
||||
}
|
||||
|
||||
describe('AntigravityQuotaDialog', () => {
|
||||
it('renders opaque quota identifiers with concise visible labels', () => {
|
||||
it('hides quota buckets outside the shared pool summary families', () => {
|
||||
const rawIdentifier = 'RateLimitResetCredit_05cbb6eeeb9c81918e011d8300f9ebfb'
|
||||
const { root, unmount } = mount({
|
||||
antigravity: {
|
||||
@@ -116,14 +116,15 @@ describe('AntigravityQuotaDialog', () => {
|
||||
},
|
||||
})
|
||||
|
||||
expect(root.textContent).toContain('Key-1')
|
||||
expect(root.textContent).toContain('暂无配额数据')
|
||||
expect(root.textContent).not.toContain('Key-1')
|
||||
expect(root.textContent).not.toContain(rawIdentifier)
|
||||
expect(root.textContent).toContain('25.0%')
|
||||
expect(root.textContent).not.toContain('25.0%')
|
||||
|
||||
unmount()
|
||||
})
|
||||
|
||||
it('orders important Gemini and Claude quota rows before low-priority rows', () => {
|
||||
it('renders only the shared Gemini and Claude ChatGPT family summaries', () => {
|
||||
const { root, unmount } = mount({
|
||||
antigravity: {
|
||||
quota_by_model: {
|
||||
@@ -164,16 +165,20 @@ describe('AntigravityQuotaDialog', () => {
|
||||
|
||||
expect(text).not.toContain('gemini-pro-agent')
|
||||
expect(text).not.toContain('claude-opus-4-6-thinking')
|
||||
expect(text.indexOf('Claude Opus 4.6 (Thinking)')).toBeLessThan(text.indexOf('Gemini 3.1 Pro (High)'))
|
||||
expect(text.indexOf('Gemini 3.1 Pro (High)')).toBeLessThan(text.indexOf('Gemini 3.5 Flash (High)'))
|
||||
expect(text.indexOf('Gemini 3.5 Flash (High)')).toBeLessThan(text.indexOf('Gemini 3.5 Flash (Medium)'))
|
||||
expect(text.indexOf('Gemini 3.5 Flash (Medium)')).toBeLessThan(text.indexOf('Tab Flash Lite Preview'))
|
||||
expect(text.indexOf('Tab Flash Lite Preview')).toBeLessThan(text.indexOf('chat_20706'))
|
||||
expect(text).toContain('Gemini额度')
|
||||
expect(text).toContain('80%–95%')
|
||||
expect(text).toContain('Claude & ChatGPT')
|
||||
expect(text).toContain('100%')
|
||||
expect(text).not.toContain('Gemini 3.1 Pro (High)')
|
||||
expect(text).not.toContain('Gemini 3.5 Flash (High)')
|
||||
expect(text).not.toContain('Gemini 3.5 Flash (Medium)')
|
||||
expect(text).not.toContain('Tab Flash Lite Preview')
|
||||
expect(text).not.toContain('chat_20706')
|
||||
|
||||
unmount()
|
||||
})
|
||||
|
||||
it('renders one row for duplicate quota labels and keeps the preferred active bucket', () => {
|
||||
it('collapses duplicate model buckets into one family range', () => {
|
||||
const { root, unmount } = mount({
|
||||
antigravity: {
|
||||
quota_by_model: {
|
||||
@@ -197,10 +202,11 @@ describe('AntigravityQuotaDialog', () => {
|
||||
})
|
||||
const text = root.textContent || ''
|
||||
|
||||
expect(text.match(/Gemini 3\.1 Pro \(High\)/g)).toHaveLength(1)
|
||||
expect(text).toContain('40.0%')
|
||||
expect(text.match(/Gemini额度/g)).toHaveLength(1)
|
||||
expect(text).toContain('40%–90%')
|
||||
expect(text).not.toContain('95.0%')
|
||||
expect(text.indexOf('Gemini 3.1 Pro (High)')).toBeLessThan(text.indexOf('Gemini 3.5 Flash (High)'))
|
||||
expect(text).not.toContain('Gemini 3.1 Pro (High)')
|
||||
expect(text).not.toContain('Gemini 3.5 Flash (High)')
|
||||
|
||||
unmount()
|
||||
})
|
||||
|
||||
@@ -16,7 +16,7 @@ describe('summarizeAntigravityQuotaItems', () => {
|
||||
|
||||
expect(items.map(item => [item.label, item.remainingPercent, item.detail])).toEqual([
|
||||
['Gemini额度', 90.6, '90.6%–95%'],
|
||||
['Claude额度', 82, '82%–100%'],
|
||||
['Claude & ChatGPT', 82, '82%–100%'],
|
||||
])
|
||||
expect(items.map(item => item.resetSeconds)).toEqual([180, 120])
|
||||
expect(items[1]?.model).toBe('claude-sonnet-4-6')
|
||||
|
||||
@@ -9,7 +9,7 @@ export interface AntigravityQuotaSortableItem {
|
||||
const ANTIGRAVITY_QUOTA_GROUPS = [
|
||||
{ label: 'Gemini额度', matches: (model: string) => model.startsWith('gemini-') },
|
||||
{
|
||||
label: 'Claude额度',
|
||||
label: 'Claude & ChatGPT',
|
||||
matches: (model: string) => model.startsWith('claude-') || model.startsWith('gpt-'),
|
||||
},
|
||||
] as const
|
||||
|
||||
Reference in New Issue
Block a user