diff --git a/src/renderer/src/components/skills/SkillUpdateRow.tsx b/src/renderer/src/components/skills/SkillUpdateRow.tsx index 84d7c76af67..6b5a77382e0 100644 --- a/src/renderer/src/components/skills/SkillUpdateRow.tsx +++ b/src/renderer/src/components/skills/SkillUpdateRow.tsx @@ -1,4 +1,4 @@ -import { CheckCircle2, ChevronDown, Circle, XCircle } from 'lucide-react' +import { AlertTriangle, CheckCircle2, ChevronDown, Circle, XCircle } from 'lucide-react' import type { SkillFreshnessGroupModel } from './skill-freshness-grouping' import { translate } from '@/i18n/i18n' import { Badge } from '@/components/ui/badge' @@ -10,14 +10,10 @@ import { skippedReason } from './skill-freshness-skipped-reason' export type SkillRowState = 'available' | 'blocked' | 'pending' | 'done' | 'failed' /** - * The single status slot, sitting between the name and the location count. - * - * Why one slot rather than a leading icon column: a leading icon has nothing to - * show in the resting state, and reserving the box for it just indents every - * name past an empty gap. Swapping badge for icon in place keeps the name - * flush-left and the row's right edge fixed in every state. + * Leading status glyph. Absent for `available` on purpose: an empty reserved box + * would just indent the name past a gap with nothing in it. */ -function StateSlot({ state }: { state: SkillRowState }): React.JSX.Element { +function StateIcon({ state }: { state: SkillRowState }): React.JSX.Element | null { switch (state) { case 'done': return @@ -26,31 +22,44 @@ function StateSlot({ state }: { state: SkillRowState }): React.JSX.Element { case 'pending': return case 'blocked': - return ( - - {translate('auto.components.skills.SkillFreshnessRow.statusCantUpdate', 'Skipped')} - - ) + return case 'available': - return ( - - {translate( - 'auto.components.skills.SkillFreshnessRow.statusUpdateAvailable', - 'Update available' - )} - - ) + return null } } +/** Sits immediately right of the name, so the label reads as part of it. */ +function StateBadge({ state }: { state: SkillRowState }): React.JSX.Element | null { + if (state === 'blocked') { + return ( + + {translate('auto.components.skills.SkillFreshnessRow.statusCantUpdate', 'Skipped')} + + ) + } + if (state === 'available') { + return ( + + {translate( + 'auto.components.skills.SkillFreshnessRow.statusUpdateAvailable', + 'Update available' + )} + + ) + } + // Why: once a run owns the row the leading glyph carries the state. A stale + // "Update available" beside a green check would contradict it. + return null +} + /** * One skill in the update dialog, used unchanged in every state. * * The header keeps identical geometry from "update available" through running to - * the result — only the status slot's contents change — so pressing Update + * the result — the row keeps its place and its right edge — so pressing Update * doesn't replace the dialog's layout with a different one. Locations live * behind a per-skill disclosure instead of being dumped inline, because a skill * with several plugin-cache copies otherwise buries the actions. @@ -76,10 +85,13 @@ export function SkillUpdateRow({ state === 'pending' ? 'opacity-60' : '' }`} > - - {group.name} + + + + {group.name} + + - {locationCount === 1 ? translate('auto.components.skills.SkillUpdateRow.oneLocation', '1 location')