From 3fdf36abed1afeaa28b2a87ca7704f3a7f9deb3b Mon Sep 17 00:00:00 2001
From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
Date: Wed, 5 Aug 2026 20:10:23 -0700
Subject: [PATCH] feat(agent-map): glow working entities
---
.../dashboard-popout/AgentMap.test.tsx | 49 +++++++++++++++++++
.../AgentMapWorktreeRingNode.tsx | 21 +++++++-
.../agent-map-glow.performance.test.ts | 32 ++++++++++++
.../components/dashboard-popout/agent-map.css | 20 ++++++++
4 files changed, 121 insertions(+), 1 deletion(-)
create mode 100644 src/renderer/src/components/dashboard-popout/agent-map-glow.performance.test.ts
diff --git a/src/renderer/src/components/dashboard-popout/AgentMap.test.tsx b/src/renderer/src/components/dashboard-popout/AgentMap.test.tsx
index 4a2e1665d33..ecd2910b01c 100644
--- a/src/renderer/src/components/dashboard-popout/AgentMap.test.tsx
+++ b/src/renderer/src/components/dashboard-popout/AgentMap.test.tsx
@@ -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({
diff --git a/src/renderer/src/components/dashboard-popout/AgentMapWorktreeRingNode.tsx b/src/renderer/src/components/dashboard-popout/AgentMapWorktreeRingNode.tsx
index 81371130af7..50c05b5a975 100644
--- a/src/renderer/src/components/dashboard-popout/AgentMapWorktreeRingNode.tsx
+++ b/src/renderer/src/components/dashboard-popout/AgentMapWorktreeRingNode.tsx
@@ -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 ? (
+
+ ) : null}
onAgentKeyDown(event, agent)}
>
+ {agent.status === 'working' ? (
+
+ ) : null}
{
+ 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(/ {
+ 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:/)
+ }
+ })
+})
diff --git a/src/renderer/src/components/dashboard-popout/agent-map.css b/src/renderer/src/components/dashboard-popout/agent-map.css
index db37aef12bc..1491516b307 100644
--- a/src/renderer/src/components/dashboard-popout/agent-map.css
+++ b/src/renderer/src/components/dashboard-popout/agent-map.css
@@ -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;