fix(settings): reserve skill badges for attention (#11413)

* fix(settings): reserve skill badges for attention

* test(settings): cover hidden checking badge
This commit is contained in:
Brennan Benson 2026-07-29 15:56:30 -07:00 committed by GitHub
parent b21f978c6d
commit 5c8013abaa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 50 additions and 38 deletions

View File

@ -1,7 +1,7 @@
// @vitest-environment happy-dom
import { renderToStaticMarkup } from 'react-dom/server'
import { Bot, Mic, Network, Puzzle } from 'lucide-react'
import { Bot, GitBranch, Mic, Network, Puzzle } from 'lucide-react'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { getDefaultSettings } from '../../../../shared/constants'
import { SettingsSidebar } from './SettingsSidebar'
@ -71,6 +71,24 @@ function renderSidebar(
icon: Bot,
installStatus: 'up-to-date'
},
{
id: 'voice-loading',
title: 'Voice Loading',
icon: Mic,
installStatus: 'checking'
},
{
id: 'linear',
title: 'Linear',
icon: GitBranch,
installStatus: 'update-available'
},
{
id: 'ephemeral-vms',
title: 'Ephemeral VMs',
icon: Bot,
installStatus: 'needs-attention'
},
{
id: 'plugins',
title: 'Plugins',
@ -126,12 +144,15 @@ describe('SettingsSidebar', () => {
expect(markup).toContain('--worktree-sidebar-foreground:#f0f4f8')
})
it('renders install state labels separately from static badges', () => {
it('reserves install state labels for actionable skill states', () => {
const markup = renderSidebar()
expect(markup).toContain('Not installed')
expect(markup).toContain('Installed')
expect(markup).toContain('Up to date')
expect(markup).not.toContain('Not installed')
expect(markup).not.toContain('Installed')
expect(markup).not.toContain('Up to date')
expect(markup).not.toContain('Checking...')
expect(markup).toContain('Update available')
expect(markup).toContain('Review skill')
expect(markup).toContain('Optional')
})

View File

@ -60,6 +60,17 @@ type SettingsSidebarProps = {
) => void
}
type VisibleInstallStatus = Extract<
SettingsNavInstallStatus,
'update-available' | 'needs-attention'
>
function isVisibleInstallStatus(
status: SettingsNavInstallStatus | undefined
): status is VisibleInstallStatus {
return status === 'update-available' || status === 'needs-attention'
}
type SettingsSetupGuideRowProps = {
progress: SettingsSetupGuideProgress
setupActive: boolean
@ -146,17 +157,8 @@ export function SettingsSidebar({
? 'bg-worktree-sidebar-accent font-medium text-worktree-sidebar-accent-foreground ring-1 ring-worktree-sidebar-ring/25'
: 'text-worktree-sidebar-foreground/60 hover:bg-worktree-sidebar-accent/60 hover:text-worktree-sidebar-foreground'
)
const installStatusLabel = (status: SettingsNavInstallStatus): string => {
const installStatusLabel = (status: VisibleInstallStatus): string => {
switch (status) {
case 'install':
return translate(
'auto.components.settings.AgentSkillSetupPanel.5289300939',
'Not installed'
)
case 'installed':
return translate('auto.components.settings.AgentSkillSetupPanel.9fcebceb2a', 'Installed')
case 'up-to-date':
return translate('auto.components.skills.SkillFreshnessStatusPill.upToDate', 'Up to date')
case 'update-available':
return translate(
'auto.components.skills.SkillFreshnessStatusPill.updateAvailable',
@ -165,23 +167,12 @@ export function SettingsSidebar({
case 'needs-attention':
return translate(
'auto.components.skills.SkillFreshnessStatusPill.needsAttention',
'Needs attention'
'Review skill'
)
case 'checking':
return translate('auto.components.settings.AgentSkillSetupPanel.68a468752e', 'Checking...')
}
}
const installStatusClassName = (status: SettingsNavInstallStatus): string =>
cn(
'ml-auto shrink-0 rounded-full border px-1.5 py-0.5 text-[10px] font-medium leading-none',
status === 'installed' || status === 'up-to-date'
? 'border-status-success-border bg-status-success-background text-status-success'
: status === 'update-available' || status === 'needs-attention'
? 'border-amber-500/40 bg-amber-500/10 text-amber-700 dark:text-amber-300'
: status === 'install'
? 'border-foreground/15 bg-foreground/10 text-foreground'
: 'border-border/50 bg-muted/30 text-muted-foreground'
)
const installStatusClassName =
'ml-auto shrink-0 rounded-full border border-amber-500/40 bg-amber-500/10 px-1.5 py-0.5 text-[10px] font-medium leading-none text-amber-700 dark:text-amber-300'
return (
<aside
@ -270,8 +261,8 @@ export function SettingsSidebar({
>
<Icon className="size-4 shrink-0" />
<span className="truncate">{section.title}</span>
{section.installStatus ? (
<span className={installStatusClassName(section.installStatus)}>
{isVisibleInstallStatus(section.installStatus) ? (
<span className={installStatusClassName}>
{installStatusLabel(section.installStatus)}
</span>
) : section.badge ? (

View File

@ -117,7 +117,7 @@ describe('SkillFreshnessStatusPill', () => {
const rendered = await renderPill('orca-cli')
// Why: a green pill over a copy the update cannot reach hides real drift.
expect(pillText(rendered)).toBe('Needs attention')
expect(pillText(rendered)).toBe('Review skill')
expect(detailsButton(rendered)?.textContent).toBe('Details')
})

View File

@ -27,7 +27,7 @@ function statusPill(status: SkillFreshnessDisplayStatus): React.JSX.Element {
<IntegrationStatusPill tone="attention">
{translate(
'auto.components.skills.SkillFreshnessStatusPill.needsAttention',
'Needs attention'
'Review skill'
)}
</IntegrationStatusPill>
)

View File

@ -3893,7 +3893,7 @@
"upToDate": "Up to date",
"installed": "Installed",
"details": "Details",
"needsAttention": "Needs attention"
"needsAttention": "Review skill"
}
},
"sidebar": {

View File

@ -3863,7 +3863,7 @@
"upToDate": "Actualizado",
"installed": "Instalado",
"details": "Details",
"needsAttention": "Needs attention"
"needsAttention": "Review skill"
},
"SkillUpdateResultRows": {
"stillOutdated": "Still out of date after the update ran."

View File

@ -3863,7 +3863,7 @@
"upToDate": "最新です",
"installed": "インストール済み",
"details": "Details",
"needsAttention": "Needs attention"
"needsAttention": "Review skill"
},
"SkillUpdateResultRows": {
"stillOutdated": "Still out of date after the update ran."

View File

@ -3863,7 +3863,7 @@
"upToDate": "최신 상태",
"installed": "설치됨",
"details": "Details",
"needsAttention": "Needs attention"
"needsAttention": "Review skill"
},
"SkillUpdateResultRows": {
"stillOutdated": "Still out of date after the update ran."

View File

@ -3863,7 +3863,7 @@
"upToDate": "已是最新",
"installed": "已安装",
"details": "Details",
"needsAttention": "Needs attention"
"needsAttention": "Review skill"
},
"SkillUpdateResultRows": {
"stillOutdated": "Still out of date after the update ran."