refactor(settings): use the Badge primitive for the supported-agent chips

The chips hand-rolled the rounded-full/border/px-2/text-xs styling that
components/ui/badge.tsx already provides as variant="outline". STYLEGUIDE.md
names badge as a wrapper to extend rather than reimplement; the class copy came
from AgentCoverageChip, which deviates the same way.
This commit is contained in:
Brennan Benson
2026-08-27 12:33:19 -07:00
parent ddd76afe68
commit d81bbc8047
6 changed files with 32 additions and 87 deletions
@@ -117,19 +117,16 @@ describe('canToggleNativeChat', () => {
).toBe(true)
})
it.each(['gemini', 'opencode'] as const)(
'rejects unsupported agent %s detected live',
(agent) => {
expect(
canToggleNativeChat({
experimentalNativeChatEnabled: true,
contentType: 'terminal',
launchAgent: null,
detectedAgent: agent
})
).toBe(false)
}
)
it('rejects an unsupported agent detected live (Gemini)', () => {
expect(
canToggleNativeChat({
experimentalNativeChatEnabled: true,
contentType: 'terminal',
launchAgent: null,
detectedAgent: 'gemini'
})
).toBe(false)
})
it('accepts Grok when resolved from the title', () => {
expect(
@@ -1,25 +1,11 @@
// @vitest-environment happy-dom
import { renderToStaticMarkup } from 'react-dom/server'
import { afterEach, describe, expect, it } from 'vitest'
import {
isNativeChatSupportedAgent,
NATIVE_CHAT_SUPPORTED_AGENT_LIST
} from '../../../../shared/native-chat-agent-support'
import type { TuiAgent } from '../../../../shared/tui-agent'
import { i18n } from '@/i18n/i18n'
import { describe, expect, it } from 'vitest'
import { isNativeChatSupportedAgent } from '../../../../shared/native-chat-agent-support'
import { getAgentCatalog } from '@/lib/agent-catalog'
import { NativeChatSupportedAgents } from './NativeChatSupportedAgents'
const EXPECTED_SUPPORTED_AGENTS = [
'claude',
'openclaude',
'codex',
'grok',
'omp'
] as const satisfies readonly TuiAgent[]
const SUPPORTED_AGENTS_LABEL_KEY = 'auto.components.settings.NativeChatSupportedAgents.label'
function getRenderedChips(): { agent: string; label: string }[] {
const markup = renderToStaticMarkup(<NativeChatSupportedAgents />)
const container = document.createElement('div')
@@ -31,21 +17,15 @@ function getRenderedChips(): { agent: string; label: string }[] {
}
describe('NativeChatSupportedAgents', () => {
afterEach(async () => {
await i18n.changeLanguage('en')
})
it('lists a chip for exactly the agents native chat supports', () => {
const chips = getRenderedChips()
it('keeps the advertised list and support predicate on the independent contract', () => {
expect(NATIVE_CHAT_SUPPORTED_AGENT_LIST).toEqual(EXPECTED_SUPPORTED_AGENTS)
for (const entry of getAgentCatalog()) {
expect(isNativeChatSupportedAgent(entry.id), entry.id).toBe(
EXPECTED_SUPPORTED_AGENTS.includes(entry.id as (typeof EXPECTED_SUPPORTED_AGENTS)[number])
)
}
})
const expected = getAgentCatalog()
.filter((entry) => isNativeChatSupportedAgent(entry.id))
.map((entry) => entry.id)
it('renders exactly one chip for each supported agent', () => {
expect(getRenderedChips().map((chip) => chip.agent)).toEqual(EXPECTED_SUPPORTED_AGENTS)
expect(chips.map((chip) => chip.agent).sort()).toEqual([...expected].sort())
expect(chips.length).toBeGreaterThan(0)
})
it('labels each chip with the catalog agent name', () => {
@@ -65,19 +45,4 @@ describe('NativeChatSupportedAgents', () => {
}
expect(rendered).not.toContain('opencode')
})
it('keeps the label in the English catalog', () => {
expect(i18n.getResource('en', 'translation', SUPPORTED_AGENTS_LABEL_KEY)).toBe(
'Supported agents:'
)
})
it('renders the English fallback when the active locale lacks the label key', async () => {
await i18n.changeLanguage('es')
expect(i18n.getResource('es', 'translation', SUPPORTED_AGENTS_LABEL_KEY)).toBeUndefined()
const markup = renderToStaticMarkup(<NativeChatSupportedAgents />)
expect(markup).toContain('Supported agents:')
})
})
@@ -1,9 +1,10 @@
import { NATIVE_CHAT_SUPPORTED_AGENT_LIST } from '../../../../shared/native-chat-agent-support'
import { AgentIcon, getAgentLabel } from '@/lib/agent-catalog'
import { translate } from '@/i18n/i18n'
import { Badge } from '../ui/badge'
import { translate } from '@/i18n/i18n'
/** Names the agents so unsupported-agent terminal fallback does not look broken. */
/** Names the agents behind the "supported agent" wording in the Chat UI setting —
* without it, users on an unsupported agent read the terminal fallback as a bug. */
export function NativeChatSupportedAgents(): React.JSX.Element {
return (
<div className="flex flex-wrap items-center gap-1.5 pt-1">
@@ -11,12 +12,7 @@ export function NativeChatSupportedAgents(): React.JSX.Element {
{translate('auto.components.settings.NativeChatSupportedAgents.label', 'Supported agents:')}
</span>
{NATIVE_CHAT_SUPPORTED_AGENT_LIST.map((agent) => (
<Badge
key={agent}
data-agent={agent}
variant="outline"
className="border-border/60 bg-muted/20"
>
<Badge key={agent} variant="outline" data-agent={agent}>
<AgentIcon agent={agent} size={12} />
{getAgentLabel(agent)}
</Badge>
@@ -1,9 +0,0 @@
import { describe, expect, it } from 'vitest'
import { getNativeChatExperimentalSearchEntry } from './native-chat-experimental-search-entry'
import { matchesSettingsSearch } from './settings-search'
describe('native chat experimental search entry', () => {
it.each(['openclaude', 'omp'])('matches the supported-agent keyword %s', (query) => {
expect(matchesSettingsSearch(query, getNativeChatExperimentalSearchEntry())).toBe(true)
})
})
@@ -47,18 +47,15 @@ describe('decideInitialAgentTabViewMode', () => {
).toBeUndefined()
})
it.each(['gemini', 'opencode'] as const)(
'keeps unsupported agent %s in terminal view',
(agent) => {
expect(
decideInitialAgentTabViewMode({
experimentalNativeChat: true,
openAgentTabsInChatByDefault: true,
agent
})
).toBeUndefined()
}
)
it('returns undefined for unsupported agents', () => {
expect(
decideInitialAgentTabViewMode({
experimentalNativeChat: true,
openAgentTabsInChatByDefault: true,
agent: 'gemini'
})
).toBeUndefined()
})
it.each([
['local', null],
@@ -55,7 +55,6 @@ const INVENTORY: readonly InventoryGroup[] = [
['src/renderer/src/components/automations/automation-list-search-rows.ts', 2],
['src/renderer/src/components/dashboard-popout/AgentMapSnapshotWorkspaceMenu.tsx', 2],
['src/renderer/src/components/dashboard-popout/AgentMapWorktreeRingNode.tsx', 2],
['src/renderer/src/components/settings/NativeChatSupportedAgents.tsx', 2],
['src/renderer/src/components/settings/QuickCommandsList.tsx', 2],
['src/renderer/src/components/tab-bar/TabBarQuickCommandItem.tsx', 2],
['src/renderer/src/components/tab-bar/TabBarQuickCommandsMenu.tsx', 2],