mirror of
https://github.com/stablyai/orca.git
synced 2026-10-02 16:02:15 +00:00
revert(sidebar): restore emerald dot for workspace 'done' status (#1294)
Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
@@ -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')
|
||||
|
||||
@@ -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 (
|
||||
<span
|
||||
className={cn('inline-flex shrink-0 items-center justify-center', box, className)}
|
||||
|
||||
@@ -23,16 +23,9 @@ describe('StatusIndicator', () => {
|
||||
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(/<span class="[^"]*rounded-full[^"]*bg-emerald-500/)
|
||||
expect(classNames).toContain('bg-emerald-500')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React from 'react'
|
||||
import { CircleCheck } from 'lucide-react'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { getWorktreeStatusLabel, type WorktreeStatus } from '@/lib/worktree-status'
|
||||
|
||||
@@ -19,12 +18,12 @@ const StatusIndicator = React.memo(function StatusIndicator({
|
||||
title,
|
||||
...rest
|
||||
}: StatusIndicatorProps) {
|
||||
// Why: surface the status label as a native tooltip so hovering the
|
||||
// indicator reveals the state — matters especially for 'active' vs
|
||||
// 'done' (dot vs check both in emerald). Callers pass aria-hidden="true"
|
||||
// alongside an sr-only label, so the `title` attribute is ignored by AT
|
||||
// and only serves sighted users on hover. Callers can override by
|
||||
// passing their own `title`.
|
||||
// Why: surface the status label as a native tooltip so hovering the dot
|
||||
// reveals the state — matters especially for 'active' vs 'done', which
|
||||
// share the same emerald dot. Callers pass aria-hidden="true" alongside
|
||||
// an sr-only label, so the `title` attribute is ignored by AT and only
|
||||
// serves sighted users on hover. Callers can override by passing their
|
||||
// own `title`.
|
||||
const resolvedTitle = title ?? getWorktreeStatusLabel(status)
|
||||
|
||||
if (status === 'working') {
|
||||
@@ -39,23 +38,6 @@ const StatusIndicator = React.memo(function StatusIndicator({
|
||||
)
|
||||
}
|
||||
|
||||
if (status === 'done') {
|
||||
// Why: agent-reported completion gets a check icon instead of a dot so
|
||||
// it is visually distinct from 'active' (terminal open, quiet), which
|
||||
// also renders emerald. Before this, users with the experimental
|
||||
// agent-tracking toggle couldn't tell a newly-opened quiet terminal
|
||||
// apart from a completed agent — both were emerald dots.
|
||||
return (
|
||||
<span
|
||||
className={cn('inline-flex h-3 w-3 shrink-0 items-center justify-center', className)}
|
||||
title={resolvedTitle}
|
||||
{...rest}
|
||||
>
|
||||
<CircleCheck className="size-3 text-emerald-500" aria-hidden="true" />
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<span
|
||||
className={cn('inline-flex h-3 w-3 shrink-0 items-center justify-center', className)}
|
||||
@@ -67,8 +49,12 @@ const StatusIndicator = React.memo(function StatusIndicator({
|
||||
'block size-2 rounded-full',
|
||||
status === 'permission'
|
||||
? 'bg-red-500'
|
||||
: status === 'active'
|
||||
? 'bg-emerald-500'
|
||||
: status === 'done' || status === 'active'
|
||||
? // Green dot for both hook-reported 'done' and the heuristic
|
||||
// 'active' (terminal open, quiet). Working uses a yellow
|
||||
// spinner so working vs done differ by motion; 'inactive'
|
||||
// stays grey.
|
||||
'bg-emerald-500'
|
||||
: 'bg-neutral-500/40'
|
||||
)}
|
||||
/>
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user