feat(desktop): order the token usage page usage, activity, then models

The per-model table moves below the activity heatmap and insights under
its own "Usage by model" heading.
This commit is contained in:
程序员阿江(Relakkes)
2026-10-08 21:54:38 +08:00
parent 273e5d9786
commit 9d25e6120c
7 changed files with 51 additions and 28 deletions
+1
View File
@@ -1197,6 +1197,7 @@ Row 9, all 8 cells: continuing from straight down, turning left through lower-le
'settings.activity.messages': 'Messages',
'settings.activity.tools': 'Tools',
'settings.activity.activityInsights': 'Activity insights',
'settings.activity.modelUsage': 'Usage by model',
'settings.activity.activeRate': 'Active rate',
'settings.activity.mostUsedModel': 'Most used model',
'settings.activity.exploredSkills': 'Skills explored',
+1
View File
@@ -1198,6 +1198,7 @@ export const jp: Record<TranslationKey, string> = {
'settings.activity.messages': 'メッセージ',
'settings.activity.tools': 'ツール',
'settings.activity.activityInsights': 'アクティビティ分析',
'settings.activity.modelUsage': 'モデル別の使用量',
'settings.activity.activeRate': 'アクティブ率',
'settings.activity.mostUsedModel': '最も使ったモデル',
'settings.activity.exploredSkills': '使用したスキル',
+1
View File
@@ -1200,6 +1200,7 @@ export const kr: Record<TranslationKey, string> = {
'settings.activity.messages': '메시지',
'settings.activity.tools': '도구',
'settings.activity.activityInsights': '활동 인사이트',
'settings.activity.modelUsage': '모델별 사용량',
'settings.activity.activeRate': '활성 비율',
'settings.activity.mostUsedModel': '가장 많이 쓴 모델',
'settings.activity.exploredSkills': '사용한 스킬',
+1
View File
@@ -1197,6 +1197,7 @@ export const zh: Record<TranslationKey, string> = {
'settings.activity.messages': '訊息',
'settings.activity.tools': '工具',
'settings.activity.activityInsights': '活動洞察',
'settings.activity.modelUsage': '模型用量',
'settings.activity.activeRate': '活躍率',
'settings.activity.mostUsedModel': '最常用模型',
'settings.activity.exploredSkills': '已使用的技能',
+1
View File
@@ -1196,6 +1196,7 @@ export const zh: Record<TranslationKey, string> = {
'settings.activity.messages': '消息',
'settings.activity.tools': '工具',
'settings.activity.activityInsights': '活动洞察',
'settings.activity.modelUsage': '模型用量',
'settings.activity.activeRate': '活跃率',
'settings.activity.mostUsedModel': '最常用模型',
'settings.activity.exploredSkills': '已使用的技能',
@@ -412,6 +412,22 @@ describe('ActivitySettings', () => {
])
})
it('orders the page usage, then activity, then the per-model breakdown', async () => {
render(<ActivitySettings />)
await flushActivityLoad()
const usage = screen.getByRole('list', { name: 'Token usage by day' })
const activity = screen.getByRole('heading', { name: 'Token Activity' })
const insights = screen.getByRole('heading', { name: 'Activity insights' })
const models = screen.getByRole('heading', { name: 'Usage by model' })
const follows = (a: Node, b: Node) => Boolean(a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_FOLLOWING)
expect(follows(usage, activity)).toBe(true)
expect(follows(activity, insights)).toBe(true)
expect(follows(insights, models)).toBe(true)
expect(follows(models, screen.getByRole('table'))).toBe(true)
})
it('supports localized heatmap mode switches and persisted display name edits', async () => {
useSettingsStore.setState({ locale: 'zh' })
render(<ActivitySettings />)
+30 -28
View File
@@ -1001,34 +1001,6 @@ export function ActivitySettings() {
</Card>
)}
{!isLoading && !error && hasUsage && modelRows.length > 0 && (
<Card surface="lowest" padding="none" role="table" className="mt-3 overflow-hidden">
<div
role="row"
className="grid grid-cols-[minmax(0,1.6fr)_minmax(0,0.8fr)_minmax(0,0.8fr)_minmax(0,0.8fr)] gap-3 bg-[var(--color-surface-container)] px-4 py-2.5 text-xs font-semibold text-[var(--color-text-tertiary)]"
>
<span role="columnheader" className="min-w-0 truncate">{t('settings.agents.model')}</span>
<span role="columnheader" className="min-w-0 truncate text-right">{t('settings.activity.freshTokens')}</span>
<span role="columnheader" className="min-w-0 truncate text-right">{t('settings.activity.cachedTokens')}</span>
<span role="columnheader" className="min-w-0 truncate text-right">{t('settings.activity.estimatedCost')}</span>
</div>
{modelRows.map((row) => (
<div
key={row.model}
role="row"
className="grid grid-cols-[minmax(0,1.6fr)_minmax(0,0.8fr)_minmax(0,0.8fr)_minmax(0,0.8fr)] items-center gap-3 border-t border-[var(--color-border)] px-4 py-2.5 text-[13px] tabular-nums text-[var(--color-text-primary)]"
>
<span role="cell" className="min-w-0 truncate font-mono text-xs" title={row.model}>{row.model}</span>
<span role="cell" className="min-w-0 truncate text-right">{formatTokens(row.fresh)}</span>
<span role="cell" className="min-w-0 truncate text-right">{formatTokens(row.cached)}</span>
<span role="cell" className="min-w-0 truncate text-right">
{row.unpriced ? <span className="text-[var(--color-text-tertiary)]">—</span> : formatCostUSD(row.costUSD, locale)}
</span>
</div>
))}
</Card>
)}
<Modal
open={isEditingProfile}
onClose={cancelProfileEdit}
@@ -1265,6 +1237,36 @@ export function ActivitySettings() {
</Card>
</SettingsSection>
{modelRows.length > 0 && (
<SettingsSection title={t('settings.activity.modelUsage')}>
<Card surface="lowest" padding="none" role="table" className="overflow-hidden">
<div
role="row"
className="grid grid-cols-[minmax(0,1.6fr)_minmax(0,0.8fr)_minmax(0,0.8fr)_minmax(0,0.8fr)] gap-3 bg-[var(--color-surface-container)] px-4 py-2.5 text-xs font-semibold text-[var(--color-text-tertiary)]"
>
<span role="columnheader" className="min-w-0 truncate">{t('settings.agents.model')}</span>
<span role="columnheader" className="min-w-0 truncate text-right">{t('settings.activity.freshTokens')}</span>
<span role="columnheader" className="min-w-0 truncate text-right">{t('settings.activity.cachedTokens')}</span>
<span role="columnheader" className="min-w-0 truncate text-right">{t('settings.activity.estimatedCost')}</span>
</div>
{modelRows.map((row) => (
<div
key={row.model}
role="row"
className="grid grid-cols-[minmax(0,1.6fr)_minmax(0,0.8fr)_minmax(0,0.8fr)_minmax(0,0.8fr)] items-center gap-3 border-t border-[var(--color-border)] px-4 py-2.5 text-[13px] tabular-nums text-[var(--color-text-primary)]"
>
<span role="cell" className="min-w-0 truncate font-mono text-xs" title={row.model}>{row.model}</span>
<span role="cell" className="min-w-0 truncate text-right">{formatTokens(row.fresh)}</span>
<span role="cell" className="min-w-0 truncate text-right">{formatTokens(row.cached)}</span>
<span role="cell" className="min-w-0 truncate text-right">
{row.unpriced ? <span className="text-[var(--color-text-tertiary)]">—</span> : formatCostUSD(row.costUSD, locale)}
</span>
</div>
))}
</Card>
</SettingsSection>
)}
{topPluginItems.length > 0 && (
<SettingsSection title={t('settings.activity.mostUsedPluginsAndSkills')}>
<Card surface="lowest" padding="none" className="overflow-hidden">