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'