feat(agent-map): glow working entities

This commit is contained in:
Brennan Benson
2026-08-05 20:10:23 -07:00
parent e0f7ac5931
commit 3fdf36abed
4 changed files with 121 additions and 1 deletions
@@ -161,6 +161,55 @@ describe('AgentMap', () => {
expect(doneNode).toHaveAccessibleName(/unread/)
})
it('glows working agents and their worktree ring only', () => {
const done = card({
paneKey: 'done',
conversationName: 'Finished agent',
worktreeId: 'worktree-done',
worktreeName: 'Finished worktree',
bucket: 'done',
dotState: 'done',
finishedAt: NOW - 60_000
})
const { container } = renderMap([card(), done])
const workingNode = screen.getByRole('button', { name: /Agent alpha/ })
const doneNode = screen.getByRole('button', { name: /Finished agent/ })
const workingRing = screen.getByRole('button', {
name: /Open Agent map worktree details/
})
const doneRing = screen.getByRole('button', {
name: /Open Finished worktree worktree details/
})
expect(workingNode.querySelector('[data-agent-map-agent-working-glow]')).toBeInTheDocument()
expect(doneNode.querySelector('[data-agent-map-agent-working-glow]')).not.toBeInTheDocument()
expect(workingRing).toHaveClass('is-working')
expect(doneRing).not.toHaveClass('is-working')
expect(container.querySelectorAll('[data-agent-map-worktree-working-glow]')).toHaveLength(1)
})
it('keeps glow markup bounded for a large visible worktree', () => {
const cards = Array.from({ length: 120 }, (_, index) => {
const working = index % 2 === 0
return card({
paneKey: `pane-${index}`,
ptyId: `pty-${index}`,
tabId: `tab-${index}`,
leafId: `leaf-${index}`,
conversationName: `Agent ${index}`,
bucket: working ? 'working' : 'done',
dotState: working ? 'working' : 'done',
finishedAt: working ? null : NOW - 60_000
})
})
const { container } = renderMap(cards, { selectedPaneKey: 'pane-0' })
expect(container.querySelectorAll('[data-agent-map-agent-working-glow]')).toHaveLength(60)
expect(container.querySelectorAll('[data-agent-map-worktree-working-glow]')).toHaveLength(1)
expect(container.querySelectorAll('filter')).toHaveLength(0)
})
it('enlarges unread markers when the full fleet is zoomed out', () => {
const fleet = Array.from({ length: 72 }, (_, index) =>
card({
@@ -187,6 +187,7 @@ export const AgentMapWorktreeRingNode = memo(function AgentMapWorktreeRingNode({
}: AgentMapWorktreeRingNodeProps): React.JSX.Element {
const [detailsOpen, setDetailsOpen] = useState(false)
const selected = worktree.agents.some((agent) => agent.card.paneKey === selectedPaneKey)
const working = worktree.statusCounts.working > 0
const aggregate = !selected && shouldAggregateAgentMapWorktree(worktree, zoom, allowAggregation)
const agentsByPaneKey = new Map(worktree.agents.map((agent) => [agent.card.paneKey, agent]))
@@ -203,9 +204,19 @@ export const AgentMapWorktreeRingNode = memo(function AgentMapWorktreeRingNode({
}
}}
>
{working ? (
<circle
className="agent-map-worktree-working-glow"
data-agent-map-worktree-working-glow=""
cx={worktree.x}
cy={worktree.y}
r={worktree.radius}
aria-hidden="true"
/>
) : null}
<PopoverTrigger asChild>
<circle
className={`agent-map-worktree-ring${selected ? ' is-selected' : ''}${detailsOpen ? ' is-open' : ''}`}
className={`agent-map-worktree-ring${working ? ' is-working' : ''}${selected ? ' is-selected' : ''}${detailsOpen ? ' is-open' : ''}`}
data-agent-map-worktree=""
data-agent-count={worktree.agents.length}
cx={worktree.x}
@@ -297,6 +308,14 @@ export const AgentMapWorktreeRingNode = memo(function AgentMapWorktreeRingNode({
}}
onKeyDown={(event) => onAgentKeyDown(event, agent)}
>
{agent.status === 'working' ? (
<circle
className="agent-map-agent-working-glow"
data-agent-map-agent-working-glow=""
r={agent.radius + 1}
aria-hidden="true"
/>
) : null}
<circle className="agent-map-agent-hit" r={Math.max(10, agent.radius + 3)} />
<circle className="agent-map-agent-mark" r={agent.radius} />
<foreignObject
@@ -0,0 +1,32 @@
import { readFileSync } from 'node:fs'
import { resolve } from 'node:path'
import { describe, expect, it } from 'vitest'
function source(file: string): string {
return readFileSync(
resolve(process.cwd(), 'src/renderer/src/components/dashboard-popout', file),
'utf8'
)
}
describe('Agent Map glow performance boundary', () => {
it('uses one conditional SVG halo per working entity without filter effects', () => {
const component = source('AgentMapWorktreeRingNode.tsx')
expect(component.match(/data-agent-map-worktree-working-glow/g)).toHaveLength(1)
expect(component.match(/data-agent-map-agent-working-glow/g)).toHaveLength(1)
expect(component).not.toMatch(/<filter|filter=/)
})
it('keeps glow styling free of animated and filtered paint work', () => {
const css = source('agent-map.css')
const glowRules = css.match(/\.agent-map-(?:worktree|agent)-working-glow\s*\{[^}]+\}/gs)
expect(glowRules).toHaveLength(2)
for (const rule of glowRules ?? []) {
expect(rule).toContain('pointer-events: none')
expect(rule).toContain('vector-effect: non-scaling-stroke')
expect(rule).not.toMatch(/filter:|animation:|transition:/)
}
})
})
@@ -95,6 +95,14 @@
vector-effect: non-scaling-stroke;
}
.agent-map-worktree-working-glow {
fill: none;
pointer-events: none;
stroke: color-mix(in srgb, var(--color-yellow-500) 28%, transparent);
stroke-width: 9;
vector-effect: non-scaling-stroke;
}
.agent-map-worktree-ring:hover {
fill: color-mix(in srgb, var(--card) 72%, transparent);
stroke: var(--ring);
@@ -119,6 +127,10 @@
stroke-width: 2.5;
}
.agent-map-worktree-ring.is-working {
stroke: var(--color-yellow-500);
}
.agent-map-worktree-label {
font-size: 12px;
font-weight: 500;
@@ -187,6 +199,14 @@
vector-effect: non-scaling-stroke;
}
.agent-map-agent-working-glow {
fill: none;
pointer-events: none;
stroke: color-mix(in srgb, var(--color-yellow-500) 32%, transparent);
stroke-width: 7;
vector-effect: non-scaling-stroke;
}
.agent-map-agent-icon {
overflow: visible;
pointer-events: none;