fix(activity): bell glyph for unread + larger row state icon

The unread mini-dot stacked on the AgentStateDot competed with the dot
itself for the eye and read like a status badge on the agent state.
Replace it with a small BellDot glyph at the icon's top-right corner so
the unread cue rhymes with the bell button on ThreadRow — one unread
vocabulary across both surfaces.

While here, bump the row state icon to a new 'lg' AgentStateDot size
(18px) so the green check sits center-of-mass in its 32px column instead
of floating small and high. Bump the WorktreeEventRow Plus glyph to
match.

Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
brennanb2025
2026-05-12 15:15:07 -07:00
co-authored by Orca
parent a2c0963c93
commit ba2ff6c40d
2 changed files with 21 additions and 18 deletions
@@ -44,7 +44,7 @@ export function agentStateLabel(state: AgentDotState): string {
type Props = {
state: AgentDotState
size?: 'sm' | 'md'
size?: 'sm' | 'md' | 'lg'
className?: string
}
@@ -53,9 +53,12 @@ export const AgentStateDot = React.memo(function AgentStateDot({
size = 'sm',
className
}: Props): React.JSX.Element {
const box = size === 'md' ? 'h-3 w-3' : 'h-2.5 w-2.5'
const inner = size === 'md' ? 'size-2' : 'size-1.5'
const icon = size === 'md' ? 'size-3' : 'size-2.5'
// Why ('lg' size): the Activity page renders this in its own 32px column
// alongside text rows, where 'md' (12px) reads small and floats. 'lg' (18px)
// sits center of mass in that cell.
const box = size === 'lg' ? 'h-[18px] w-[18px]' : size === 'md' ? 'h-3 w-3' : 'h-2.5 w-2.5'
const inner = size === 'lg' ? 'size-3' : size === 'md' ? 'size-2' : 'size-1.5'
const icon = size === 'lg' ? 'size-[18px]' : size === 'md' ? 'size-3' : 'size-2.5'
if (state === 'working') {
return (
@@ -352,22 +352,22 @@ function AgentEventRow({
event.unread && 'bg-accent/20'
)}
>
<div className="flex justify-center pt-1">
<div className="flex items-center justify-center pt-0.5">
<span className="relative inline-flex">
{/* Why: anchor the unread dot to the icon's own bounding box (not the
24×24 cell) so it tracks the icon's top-left corner regardless of
icon size. The previous absolute -left-1 top-1 inside a size-6 box
landed visibly off-center against the centered AgentStateDot. */}
{/* Why (bell as unread glyph): visually rhymes with the Bell button on
ThreadRow so both surfaces share one unread vocabulary. Sits at
the icon's top-right corner with a fill-background halo so the
bell reads cleanly against the row's tinted unread background. */}
{event.unread ? (
<span
className="absolute -top-1 -left-1 size-2 rounded-full bg-primary ring-2 ring-background"
<BellDot
className="absolute -top-1.5 -right-2 size-3.5 text-primary fill-background"
aria-hidden
/>
) : null}
<Tooltip>
<TooltipTrigger asChild>
<span className="inline-flex">
<AgentStateDot state={dotState} size="md" />
<AgentStateDot state={dotState} size="lg" />
</span>
</TooltipTrigger>
<TooltipContent side="top" sideOffset={4}>
@@ -454,17 +454,17 @@ function WorktreeEventRow({
event.unread && 'bg-accent/20'
)}
>
<div className="flex justify-center pt-0.5">
<div className="flex items-center justify-center pt-0.5">
<span className="relative inline-flex text-muted-foreground">
{/* Why: see AgentEventRow — anchor the unread dot to the icon, not the
cell, so it lines up at the icon's top-left corner. */}
{/* Why: see AgentEventRow — bell at the icon's top-right matches the
unread vocabulary used on ThreadRow. */}
{event.unread ? (
<span
className="absolute -top-1 -left-1 size-2 rounded-full bg-primary ring-2 ring-background"
<BellDot
className="absolute -top-1.5 -right-2 size-3.5 text-primary fill-background"
aria-hidden
/>
) : null}
<Plus className="size-4" />
<Plus className="size-[18px]" />
</span>
</div>
<div className="min-w-0">