From 7568fb67278b5420171fcb515ecb8a183e408fa7 Mon Sep 17 00:00:00 2001 From: unsupportedpastels Date: Tue, 8 Sep 2026 18:03:51 +0000 Subject: [PATCH] fix(desktop): let subagent header collapse roster and details --- .../status-stack/subagent-section.test.tsx | 41 +++++++++++++++-- .../status-stack/subagent-section.tsx | 45 ++++++++++--------- 2 files changed, 61 insertions(+), 25 deletions(-) diff --git a/apps/desktop/src/app/chat/composer/status-stack/subagent-section.test.tsx b/apps/desktop/src/app/chat/composer/status-stack/subagent-section.test.tsx index acdd0aea10..f67d564985 100644 --- a/apps/desktop/src/app/chat/composer/status-stack/subagent-section.test.tsx +++ b/apps/desktop/src/app/chat/composer/status-stack/subagent-section.test.tsx @@ -6,11 +6,14 @@ import { $subagentsBySession, upsertSubagent } from '@/store/subagents' import { ComposerStatusStack } from './index' +vi.mock('@/lib/use-enter-animation', () => ({ useEnterAnimation: () => undefined })) + vi.stubGlobal( 'ResizeObserver', class { disconnect() {} observe() {} + unobserve() {} } ) @@ -19,7 +22,7 @@ afterEach(() => { $subagentsBySession.set({}) }) -it('automatically previews bounded live work only from the composer session, including queued children', () => { +it('shows live work only from the composer session and keeps it hidden after collapse and progress', () => { for (let i = 0; i < 5; i++) { upsertSubagent('owner', { subagent_id: `child-${i}`, goal: `Task ${i}`, status: i ? 'queued' : 'running' }) } @@ -35,11 +38,18 @@ it('automatically previews bounded live work only from the composer session, inc expect(screen.getByText('Task 0')).toBeTruthy() expect(screen.getByText('Reading actual source')).toBeTruthy() - expect(screen.queryByText('Task 4')).toBeNull() expect(screen.queryByText('Private foreign task')).toBeNull() - expect(screen.getByRole('button', { name: /5 Subagents/ })).toBeTruthy() - fireEvent.click(screen.getByRole('button', { name: /5 Subagents/ })) + const header = screen.getByRole('button', { name: /5 Subagents/ }) + fireEvent.click(header) + expect(screen.queryByText('Task 0')).toBeNull() + expect(screen.queryByText('Task 4')).toBeNull() + expect(header.getAttribute('aria-expanded')).toBe('false') + act(() => upsertSubagent('owner', { subagent_id: 'child-0', text: 'More progress' }, false, 'subagent.progress')) + expect(screen.queryByText('Task 0')).toBeNull() + fireEvent.click(header) + expect(screen.getByText('Task 0')).toBeTruthy() expect(screen.getByText('Task 4')).toBeTruthy() + expect(screen.getByText('More progress')).toBeTruthy() view.rerender( @@ -48,6 +58,29 @@ it('automatically previews bounded live work only from the composer session, inc expect(screen.queryByText('Task 0')).toBeNull() }) +it('collapses a single worker and its selected detail using the caret, preserving the steering draft', () => { + upsertSubagent('owner', { subagent_id: 'child', goal: 'Single task' }) + + const view = render( + + + + ) + + fireEvent.click(screen.getByRole('button', { name: /Single task/ })) + expect(view.container.querySelector('[data-slot="composer-subagent-detail"]')).toBeTruthy() + const draft = screen.getByRole('textbox') + fireEvent.change(draft, { target: { value: 'Keep this draft' } }) + const header = screen.getByRole('button', { name: /1 Subagent/ }) + fireEvent.click(header.firstElementChild!) + expect(screen.queryByText('Single task')).toBeNull() + expect(view.container.querySelector('[data-slot="composer-subagent-detail"]')).toBeNull() + expect(header.getAttribute('aria-expanded')).toBe('false') + fireEvent.click(header) + expect(view.container.querySelector('[data-slot="composer-subagent-detail"]')).toBeTruthy() + expect((screen.getByRole('textbox') as HTMLInputElement).value).toBe('Keep this draft') +}) + it('retires the live frame only after every child settles, without depending on the parent busy state', () => { upsertSubagent('owner', { subagent_id: 'child', goal: 'Live task' }) render( diff --git a/apps/desktop/src/app/chat/composer/status-stack/subagent-section.tsx b/apps/desktop/src/app/chat/composer/status-stack/subagent-section.tsx index e9c3410ab7..7b08ce55de 100644 --- a/apps/desktop/src/app/chat/composer/status-stack/subagent-section.tsx +++ b/apps/desktop/src/app/chat/composer/status-stack/subagent-section.tsx @@ -64,32 +64,35 @@ export function SubagentSection({ sessionId }: SubagentSectionProps) { return (
item.status === 'running') ? t.agents.running : t.agents.queued} + className="text-(--ui-purple)" + spinner="braille" + /> + } + defaultCollapsed={false} icon={} label={t.statusStack.subagents(live.length)} - preview={ - <> - {live.slice(0, 3).map(row)} - {live.length > 3 && ( -

{t.agents.moreAgents(live.length - 3)}

- )} - - } >
{live.map(row)}
+ {detail && ( +
+ setDrafts(previous => ({ ...previous, [detail.id]: text }))} + subagentId={detail.id} + text={drafts[detail.id] ?? ''} + /> + + +
+ )}
- {detail && ( -
- setDrafts(previous => ({ ...previous, [detail.id]: text }))} - subagentId={detail.id} - text={drafts[detail.id] ?? ''} - /> - - -
- )}
) }