11import { TextAttributes } from '@opentui/core'
2- import React , { memo , useCallback , useMemo , useRef , type ReactNode } from 'react'
2+ import React , {
3+ memo ,
4+ useCallback ,
5+ useMemo ,
6+ useRef ,
7+ type ReactNode ,
8+ } from 'react'
39
410import { AgentBlockGrid } from './agent-block-grid'
511import { AgentBranchItem } from './agent-branch-item'
@@ -13,9 +19,16 @@ import { useChatStore } from '../../state/chat-store'
1319import { isTextBlock } from '../../types/chat'
1420import { getAgentStatusInfo } from '../../utils/agent-helpers'
1521import { extractHtmlBlockMargins } from '../../utils/block-margins'
16- import { processBlocks , type BlockProcessorHandlers } from '../../utils/block-processor'
17- import { shouldRenderAsSimpleText } from '../../utils/constants'
18- import { isImplementorAgent , getImplementorIndex } from '../../utils/implementor-helpers'
22+ import {
23+ processBlocks ,
24+ type BlockProcessorHandlers ,
25+ } from '../../utils/block-processor'
26+ import { shouldRenderAsSimpleText , isMultiPromptEditor } from '../../utils/constants'
27+ import {
28+ isImplementorAgent ,
29+ getImplementorIndex ,
30+ getMultiPromptPreview ,
31+ } from '../../utils/implementor-helpers'
1932import { AGENT_CONTENT_HORIZONTAL_PADDING } from '../../utils/layout-helpers'
2033
2134import type {
@@ -26,6 +39,45 @@ import type {
2639} from '../../types/chat'
2740import type { MarkdownPalette } from '../../utils/markdown-renderer'
2841
42+ /**
43+ * Compute preview text for collapsed agent display.
44+ * Returns empty string when preview shouldn't be shown (expanded state).
45+ */
46+ function getCollapsedPreview (
47+ agentBlock : AgentContentBlock ,
48+ isStreaming : boolean ,
49+ isCollapsed : boolean ,
50+ ) : string {
51+ // No preview needed if expanded and not streaming
52+ if ( ! isStreaming && ! isCollapsed ) {
53+ return ''
54+ }
55+
56+ // For multi-prompt editors, try progress-focused preview first
57+ if ( isMultiPromptEditor ( agentBlock . agentType ) ) {
58+ const multiPromptPreview = getMultiPromptPreview (
59+ agentBlock . blocks ,
60+ agentBlock . status === 'complete' ,
61+ )
62+ if ( multiPromptPreview ) {
63+ return multiPromptPreview
64+ }
65+ }
66+
67+ // Default preview: use initialPrompt or first line of text content
68+ if ( agentBlock . initialPrompt ) {
69+ return sanitizePreview ( agentBlock . initialPrompt )
70+ }
71+
72+ const textContent =
73+ agentBlock . blocks
74+ ?. filter ( isTextBlock )
75+ . map ( ( b ) => b . content )
76+ . join ( '' ) || ''
77+ const firstLine = textContent . split ( '\n' ) . find ( ( line ) => line . trim ( ) ) || ''
78+ return `${ sanitizePreview ( firstLine ) } ...`
79+ }
80+
2981interface AgentBodyProps {
3082 agentBlock : Extract < ContentBlock , { type : 'agent' } >
3183 keyPrefix : string
@@ -51,7 +103,10 @@ interface AgentBodyPropsRef {
51103 onBuildMax : ( ) => void
52104 isLastMessage ?: boolean
53105 theme : ReturnType < typeof useTheme >
54- getAgentMarkdownOptions : ( indent : number ) => { codeBlockWidth : number ; palette : MarkdownPalette }
106+ getAgentMarkdownOptions : ( indent : number ) => {
107+ codeBlockWidth : number
108+ palette : MarkdownPalette
109+ }
55110}
56111
57112const AgentBody = memo (
@@ -75,7 +130,9 @@ const AgentBody = memo(
75130 return {
76131 codeBlockWidth : Math . max (
77132 10 ,
78- availableWidth - AGENT_CONTENT_HORIZONTAL_PADDING - indentationOffset ,
133+ availableWidth -
134+ AGENT_CONTENT_HORIZONTAL_PADDING -
135+ indentationOffset ,
79136 ) ,
80137 palette : {
81138 ...markdownPalette ,
@@ -110,7 +167,10 @@ const AgentBody = memo(
110167 const p = propsRef . current
111168 return (
112169 < ThinkingBlock
113- key = { reasoningBlocks [ 0 ] ?. thinkingId ?? `${ p . keyPrefix } -thinking-${ startIndex } ` }
170+ key = {
171+ reasoningBlocks [ 0 ] ?. thinkingId ??
172+ `${ p . keyPrefix } -thinking-${ startIndex } `
173+ }
114174 blocks = { reasoningBlocks }
115175 onToggleCollapsed = { p . onToggleCollapsed }
116176 availableWidth = { p . availableWidth }
@@ -179,7 +239,8 @@ const AgentBody = memo(
179239 if ( block . type === 'text' ) {
180240 const textBlock = block as TextContentBlock
181241 const nestedStatus = textBlock . status
182- const isNestedStreamingText = p . parentIsStreaming || nestedStatus === 'running'
242+ const isNestedStreamingText =
243+ p . parentIsStreaming || nestedStatus === 'running'
183244 const filteredNestedContent = isNestedStreamingText
184245 ? trimTrailingNewlines ( textBlock . content )
185246 : textBlock . content . trim ( )
@@ -211,7 +272,8 @@ const AgentBody = memo(
211272
212273 if ( block . type === 'html' ) {
213274 const htmlBlock = block as HtmlContentBlock
214- const { marginTop, marginBottom } = extractHtmlBlockMargins ( htmlBlock )
275+ const { marginTop, marginBottom } =
276+ extractHtmlBlockMargins ( htmlBlock )
215277
216278 return (
217279 < box
@@ -268,7 +330,9 @@ export const AgentBranchWrapper = memo(
268330 } : AgentBranchWrapperProps ) => {
269331 const theme = useTheme ( )
270332 // Derive streaming boolean for this specific agent to avoid re-renders when other agents change
271- const agentIsStreaming = useChatStore ( ( state ) => state . streamingAgents . has ( agentBlock . agentId ) )
333+ const agentIsStreaming = useChatStore ( ( state ) =>
334+ state . streamingAgents . has ( agentBlock . agentId ) ,
335+ )
272336
273337 if ( shouldRenderAsSimpleText ( agentBlock . agentType ) ) {
274338 const isStreaming = agentBlock . status === 'running' || agentIsStreaming
@@ -341,29 +405,15 @@ export const AgentBranchWrapper = memo(
341405 const isCollapsed = agentBlock . isCollapsed ?? false
342406 const isStreaming = agentBlock . status === 'running' || agentIsStreaming
343407
344- const allTextContent =
345- agentBlock . blocks
346- ?. filter ( isTextBlock )
347- . map ( ( nested ) => nested . content )
348- . join ( '' ) || ''
349-
350- const lines = allTextContent . split ( '\n' ) . filter ( ( line ) => line . trim ( ) )
351- const firstLine = lines [ 0 ] || ''
352-
353- const streamingPreview = isStreaming
354- ? agentBlock . initialPrompt
355- ? sanitizePreview ( agentBlock . initialPrompt )
356- : `${ sanitizePreview ( firstLine ) } ...`
357- : ''
358-
359- const finishedPreview =
360- ! isStreaming && isCollapsed && agentBlock . initialPrompt
361- ? sanitizePreview ( agentBlock . initialPrompt )
362- : ''
408+ // Compute collapsed preview text
409+ const preview = getCollapsedPreview ( agentBlock , isStreaming , isCollapsed )
363410
364- const isActive = isStreaming || agentBlock . status === 'running'
365- const { indicator : statusIndicator , label : statusLabel , color : statusColor } =
366- getAgentStatusInfo ( isActive ? 'running' : agentBlock . status , theme )
411+ const effectiveStatus = isStreaming ? 'running' : agentBlock . status
412+ const {
413+ indicator : statusIndicator ,
414+ label : statusLabel ,
415+ color : statusColor ,
416+ } = getAgentStatusInfo ( effectiveStatus , theme )
367417
368418 const onToggle = useCallback ( ( ) => {
369419 onToggleCollapsed ( agentBlock . agentId )
@@ -377,8 +427,7 @@ export const AgentBranchWrapper = memo(
377427 agentId = { agentBlock . agentId }
378428 isCollapsed = { isCollapsed }
379429 isStreaming = { isStreaming }
380- streamingPreview = { streamingPreview }
381- finishedPreview = { finishedPreview }
430+ preview = { preview }
382431 statusLabel = { statusLabel ?? undefined }
383432 statusColor = { statusColor }
384433 statusIndicator = { statusIndicator }
0 commit comments