diff --git a/src/renderer/src/components/AgentStateDot.test.ts b/src/renderer/src/components/AgentStateDot.test.ts index 717a0ca44a8..e5617c81427 100644 --- a/src/renderer/src/components/AgentStateDot.test.ts +++ b/src/renderer/src/components/AgentStateDot.test.ts @@ -12,8 +12,9 @@ describe('AgentStateDot', () => { const markup = renderMarkup('done') // Why: 'done' renders a CircleCheck icon rather than a dot so it is - // visually distinct from other emerald-adjacent states across surfaces - // (mirrors StatusIndicator). Assertion targets the lucide 'circle-check' + // visually distinct from other emerald-adjacent states across surfaces. + // Note: the sidebar's StatusIndicator intentionally diverges and uses an + // emerald dot for 'done'. Assertion targets the lucide 'circle-check' // class hook + emerald text color, identifying the check icon without // coupling to the exact SVG path markup lucide emits. expect(markup).toContain('lucide-circle-check') diff --git a/src/renderer/src/components/AgentStateDot.tsx b/src/renderer/src/components/AgentStateDot.tsx index 2559222137b..cd7d20ff534 100644 --- a/src/renderer/src/components/AgentStateDot.tsx +++ b/src/renderer/src/components/AgentStateDot.tsx @@ -3,11 +3,15 @@ import { CircleCheck } from 'lucide-react' import { cn } from '@/lib/utils' // Why: shared state-indicator primitive so the dashboard and the sidebar's -// agent hover render the same state vocabulary identically. Most states render -// as a dot; 'working' renders a spinner and 'done' renders a check icon. It -// sits next to the agent icon (Claude/Codex/etc.) — two distinct glyphs: one -// for *who* (agent icon) and one for *what state* (this indicator). Keeping -// them separate keeps each scannable instead of fused into one decorated icon. +// agent hover share a single state vocabulary. Most states render as a dot; +// 'working' renders a spinner. 'done' intentionally diverges from the +// sidebar's StatusIndicator: the dashboard uses a check icon so completion +// is visually distinct from 'idle' (grey dot) and the sidebar's 'active' +// (emerald dot), while the sidebar collapses 'done'/'active' to the same +// emerald dot and relies on a tooltip. It sits next to the agent icon +// (Claude/Codex/etc.) — two distinct glyphs: one for *who* (agent icon) and +// one for *what state* (this indicator). Keeping them separate keeps each +// scannable instead of fused into one decorated icon. export type AgentDotState = | 'working' @@ -70,11 +74,10 @@ export const AgentStateDot = React.memo(function AgentStateDot({ } if (state === 'done') { - // Why: match StatusIndicator — agent-reported completion renders as an - // emerald check icon instead of an emerald dot so 'done' is visually - // distinct from other emerald states (e.g., sidebar 'active'). Keeping - // the dashboard and sidebar on the same glyph for 'done' is the whole - // point of this shared primitive (see file header). + // Why: the dashboard lists many agents, so a check glyph scans well for + // agent-reported completion and keeps 'done' visually distinct from + // 'idle' and other dot states at a glance. The sidebar's StatusIndicator + // intentionally diverges (emerald dot + tooltip) — see file header. return ( { expect(classNames).toContain('bg-emerald-500') }) - it('renders done as an emerald check icon, not a dot', () => { - const markup = renderMarkup('done') + it('renders done as an emerald dot', () => { + const classNames = renderDotClassNames('done') - // Why: 'done' uses a CircleCheck icon rather than a rounded-full dot - // so it is visually distinct from 'active' (also emerald). The assertion - // targets the lucide 'circle-check' class hook + emerald text color, - // which together identify the check icon without coupling to the exact - // SVG path markup lucide emits. - expect(markup).toContain('lucide-circle-check') - expect(markup).toContain('text-emerald-500') - expect(markup).not.toMatch(/ - ) - } - return ( diff --git a/src/renderer/src/components/sidebar/WorktreeCard.tsx b/src/renderer/src/components/sidebar/WorktreeCard.tsx index b6bd4add5da..0d793e8f6b3 100644 --- a/src/renderer/src/components/sidebar/WorktreeCard.tsx +++ b/src/renderer/src/components/sidebar/WorktreeCard.tsx @@ -157,7 +157,7 @@ const WorktreeCard = React.memo(function WorktreeCard({ // that the spinner flickered; the blocked/waiting/done states don't have // that problem — they're terminal (done) or attention-needed (blocked/ // waiting) and persist until the user acts. Retained "done" snapshots are - // consulted too so the done indicator keeps showing after the agent process exits, + // consulted too so the done dot keeps glowing after the agent process exits, // matching the dashboard's retention behavior. // // Priority (highest first): permission (blocked/waiting) > heuristic @@ -167,7 +167,7 @@ const WorktreeCard = React.memo(function WorktreeCard({ // completion. // heuristic 'working' wins over done because a spinner means the user has // already re-prompted the agent after it reported done — the newer "work - // in progress" signal is more informative than a retained completion indicator. + // in progress" signal is more informative than a retained completion dot. // Only the 'working' heuristic earns this precedence; 'active'/'inactive' // mean "quiet terminal", which shouldn't drown out a recent done. // Why: collapse live hook entries to booleans inside the selector so the