diff --git a/ui-tui/src/__tests__/createGatewayEventHandler.test.ts b/ui-tui/src/__tests__/createGatewayEventHandler.test.ts index 0c0537a836..c17aa56530 100644 --- a/ui-tui/src/__tests__/createGatewayEventHandler.test.ts +++ b/ui-tui/src/__tests__/createGatewayEventHandler.test.ts @@ -93,7 +93,13 @@ describe('createGatewayEventHandler', () => { onEvent({ payload: { text: 'done' }, type: 'message.complete' } as any) expect(getTurnState().todos).toEqual([]) - expect(appended).toContainEqual({ kind: 'trail', role: 'system', text: '', todos }) + expect(appended).toContainEqual({ + kind: 'trail', + role: 'system', + text: '', + todoCollapsedByDefault: true, + todos + }) }) it('keeps the current todo list visible when the next message starts', () => { diff --git a/ui-tui/src/__tests__/turnStore.test.ts b/ui-tui/src/__tests__/turnStore.test.ts index b1b48565e3..04797fd162 100644 --- a/ui-tui/src/__tests__/turnStore.test.ts +++ b/ui-tui/src/__tests__/turnStore.test.ts @@ -26,6 +26,7 @@ describe('turnStore live progress helpers', () => { kind: 'trail', role: 'system', text: '', + todoCollapsedByDefault: true, todos: [ { content: 'prep', id: 'prep', status: 'completed' }, { content: 'serve', id: 'serve', status: 'completed' } diff --git a/ui-tui/src/app/turnStore.ts b/ui-tui/src/app/turnStore.ts index da4484ab80..e7f3366acc 100644 --- a/ui-tui/src/app/turnStore.ts +++ b/ui-tui/src/app/turnStore.ts @@ -49,12 +49,13 @@ export const archiveTodosAtTurnEnd = () => { return [] } + const done = isTodoDone(state.todos) const msg: Msg = { kind: 'trail', role: 'system', text: '', todos: state.todos, - ...(isTodoDone(state.todos) ? {} : { todoIncomplete: true }) + ...(done ? { todoCollapsedByDefault: true } : { todoIncomplete: true }) } patchTurnState({ todoCollapsed: false, todos: [] }) diff --git a/ui-tui/src/components/messageLine.tsx b/ui-tui/src/components/messageLine.tsx index 0be28410f3..a3d3f5844a 100644 --- a/ui-tui/src/components/messageLine.tsx +++ b/ui-tui/src/components/messageLine.tsx @@ -38,7 +38,14 @@ export const MessageLine = memo(function MessageLine({ const thinking = msg.thinking?.trim() ?? '' if (msg.kind === 'trail' && msg.todos?.length) { - return + return ( + + ) } if (msg.kind === 'trail' && (msg.tools?.length || tools.length || thinking)) { diff --git a/ui-tui/src/components/todoPanel.tsx b/ui-tui/src/components/todoPanel.tsx index 8b5b59b6a4..9480ee0af8 100644 --- a/ui-tui/src/components/todoPanel.tsx +++ b/ui-tui/src/components/todoPanel.tsx @@ -14,12 +14,14 @@ const rowColor = (t: Theme, status: TodoItem['status']) => { export const TodoPanel = memo(function TodoPanel({ collapsed, + defaultCollapsed = false, incomplete = false, onToggle, t, todos }: { collapsed?: boolean + defaultCollapsed?: boolean incomplete?: boolean onToggle?: () => void t: Theme @@ -28,7 +30,7 @@ export const TodoPanel = memo(function TodoPanel({ // Fallback local state for archived todos in transcript where there's no // external controller. Live TodoPanel passes collapsed+onToggle from the // turn store so clicks still work there. - const [localCollapsed, setLocalCollapsed] = useState(false) + const [localCollapsed, setLocalCollapsed] = useState(defaultCollapsed) const isControlled = typeof collapsed === 'boolean' const effectiveCollapsed = isControlled ? collapsed : localCollapsed diff --git a/ui-tui/src/types.ts b/ui-tui/src/types.ts index 62c4fd3e04..6aea78e3e4 100644 --- a/ui-tui/src/types.ts +++ b/ui-tui/src/types.ts @@ -118,6 +118,7 @@ export interface Msg { tools?: string[] todos?: TodoItem[] todoIncomplete?: boolean + todoCollapsedByDefault?: boolean } export type Role = 'assistant' | 'system' | 'tool' | 'user'