diff --git a/CHANGELOG.md b/CHANGELOG.md index df829c2..4ec93a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,8 +7,12 @@ Write each change in both `### English` and `### 中文` under `## Unreleased`. ### English +- Refresh a single account from its card: the button re-probes that one account's health, credits, and model catalog and shows a skeleton while it runs, instead of reloading the whole page + ### 中文 +- 账号卡片支持单个刷新:按钮只重新探测该账号的运行状态、额度和模型目录,期间显示骨架屏,不再整页刷新 + ## 0.2.31 - 2026-09-01 ### English diff --git a/frontend/src/api/overview.ts b/frontend/src/api/overview.ts index e0f155e..c6c8218 100644 --- a/frontend/src/api/overview.ts +++ b/frontend/src/api/overview.ts @@ -85,6 +85,15 @@ export function updateProviderReasoning(provider: 'trae' | 'workbuddy', modelKey }) } +export function refreshAccount(accountId: string, options?: { quota?: boolean }) { + if (!accountId) throw new Error('account id required') + const query = options?.quota ? '?quota=1' : '' + return api(`/api/accounts/${encodeURIComponent(accountId)}/refresh${query}`, { + method: 'POST', + body: '{}', + }) +} + export function rewarmWorker(accountId?: string) { if (!accountId) throw new Error('account id required') return api(`/api/accounts/${encodeURIComponent(accountId)}/rewarm`, { method: 'POST', body: '{}' }) diff --git a/frontend/src/components/account/AccountCard.tsx b/frontend/src/components/account/AccountCard.tsx index 27fc5c1..2849abe 100644 --- a/frontend/src/components/account/AccountCard.tsx +++ b/frontend/src/components/account/AccountCard.tsx @@ -14,6 +14,7 @@ import { import gsap from 'gsap' import { ProviderMark } from '@/components/ProviderMark' import { CompactSwitch } from '@/components/ui/CompactSwitch' +import { AccountCardSkeleton } from '@/components/ui/PageSkeletons' import { QuotaMeter } from '@/components/account/QuotaMeter' import { RuntimeMeter } from '@/components/account/RuntimeMeter' import { @@ -23,7 +24,7 @@ import { } from '@/lib/account' import { accountProviderLabel } from '@/lib/provider' -export type AccountBusyKind = 'create' | 'import' | 'device' | 'pat' | 'callback' | 'rewarm' | 'toggle' | 'delete' | 'export' | 'settings' | 'checkin' +export type AccountBusyKind = 'create' | 'import' | 'device' | 'pat' | 'callback' | 'rewarm' | 'refresh' | 'toggle' | 'delete' | 'export' | 'settings' | 'checkin' type Translate = (key: string, vars?: Record) => string @@ -43,6 +44,7 @@ type Props = { onSubmitCallback?: () => void onExport: () => void onRewarm: () => void + onRefresh?: () => void onDelete: () => void onToggle: (selected: boolean) => void onToggleDropSystem: (selected: boolean) => void @@ -77,6 +79,7 @@ export function AccountCard({ onSubmitCallback, onExport, onRewarm, + onRefresh, onDelete, onToggle, onToggleDropSystem, @@ -153,6 +156,16 @@ export function AccountCard({ return () => context.revert() }, [authPanelOpen]) + // Refreshing replaces the whole card with a skeleton so stale quota and + // status are never left on screen while the account is re-probed. + if (busyKind === 'refresh') { + return ( +
+ +
+ ) + } + return ( {t('accountModels')} + {onRefresh ? ( + + + + + {t('refreshAccount')} + + ) : null}