diff --git a/packages/cli/src/ui/components/SessionPreview.test.tsx b/packages/cli/src/ui/components/SessionPreview.test.tsx index b559c666b2..c228445fdf 100644 --- a/packages/cli/src/ui/components/SessionPreview.test.tsx +++ b/packages/cli/src/ui/components/SessionPreview.test.tsx @@ -35,7 +35,11 @@ function mockService(resolved: unknown) { } as never; } -function fakeResumedData() { +function fakeResumedData( + assistantParts: Array<{ text: string; thought?: boolean }> = [ + { text: 'Hi from assistant REPLY-MARKER' }, + ], +) { return { conversation: { sessionId: 's1', @@ -66,7 +70,7 @@ function fakeResumedData() { version: 'test', message: { role: 'model', - parts: [{ text: 'Hi from assistant REPLY-MARKER' }], + parts: assistantParts, }, }, ], @@ -112,6 +116,39 @@ describe('SessionPreview', () => { expect(frame).toContain('REPLY-MARKER'); }); + it('renders full resumed thinking content after load', async () => { + const thinkingText = Array.from( + { length: 40 }, + (_, index) => `RESUMED-THINKING-LINE-${index + 1}`, + ).join('\n'); + const svc = mockService( + fakeResumedData([ + { text: thinkingText, thought: true }, + { text: 'FINAL-ANSWER-MARKER' }, + ]), + ); + const { lastFrame } = render( + + + , + ); + await wait(100); + const frame = lastFrame() ?? ''; + expect(frame).toContain('Thinking…'); + expect(frame).toContain('RESUMED-THINKING-LINE-1'); + expect(frame).toContain('RESUMED-THINKING-LINE-40'); + expect(frame).toContain('FINAL-ANSWER-MARKER'); + expect(frame.indexOf('My session')).toBeLessThan( + frame.indexOf('RESUMED-THINKING-LINE-1'), + ); + }); + it('renders footer metadata (messageCount · time · branch)', async () => { const svc = mockService(fakeResumedData()); const { lastFrame } = render( diff --git a/packages/cli/src/ui/components/SessionPreview.tsx b/packages/cli/src/ui/components/SessionPreview.tsx index 08327ab291..47b8aea4fa 100644 --- a/packages/cli/src/ui/components/SessionPreview.tsx +++ b/packages/cli/src/ui/components/SessionPreview.tsx @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { Box, Text } from 'ink'; +import { Box, Static, Text } from 'ink'; import { useEffect, useMemo, useState } from 'react'; import type { ResumedSessionData, @@ -126,56 +126,86 @@ export function SessionPreview(props: SessionPreviewProps) { } const metaLine = metaParts.join(' · '); + const header = ( + + + {sessionTitle ?? t('Session Preview')} + + + ); + const topSeparator = ( + + {'─'.repeat(separatorWidth)} + + ); + const footerSeparator = ( + + {'─'.repeat(separatorWidth)} + + ); + const meta = metaLine ? ( + + {metaLine} + + ) : null; + const footer = ( + + + {t('Enter to resume · Esc to back')} + + + ); + + if (data && !error) { + return ( + + ( + + )), + footerSeparator, + ...(meta ? [meta] : []), + footer, + ]} + > + {(item) => item} + + + ); + } + return ( {/* Header */} - - - {sessionTitle ?? t('Session Preview')} - - - - {'─'.repeat(separatorWidth)} - + {header} + {topSeparator} - {/* Body: render all items, let the terminal's scrollback own overflow. */} + {/* Body */} {error ? ( {error} - ) : !data ? ( + ) : ( {t('Loading session preview...')} - ) : ( - - {items.map((item) => ( - - ))} - )} {/* Footer */} - - {'─'.repeat(separatorWidth)} - - {metaLine && ( - - {metaLine} - - )} - - - {t('Enter to resume · Esc to back')} - - + {footerSeparator} + {meta} + {footer} ); }