@@ -469,12 +469,8 @@ function finishStreamingMessage(messageId: string): void {
469469 }
470470 chatState . currentlyStreamingNodeId = undefined
471471
472- // Collapse all subagent tree nodes when streaming finishes
473- if ( message . subagentUIState ) {
474- message . subagentUIState . expanded . clear ( )
475- message . subagentUIState . focusNodeId = null
476- message . subagentUIState . firstChildProgress . clear ( )
477- }
472+ // Keep expansion state when streaming finishes
473+ // (no auto-collapse behavior)
478474
479475 updateContentLines ( )
480476 renderChat ( )
@@ -530,12 +526,7 @@ export function renderAssistantMessage(
530526 message . subagentTree && message . subagentTree . postContent
531527 const shouldShowOnlyPostContent = isFullyCollapsed && hasPostContentToShow
532528
533- // Hide parent content when a child is processing in THIS message
534- const isProcessingChildren =
535- chatState . currentlyStreamingNodeId ?. includes ( '/' ) &&
536- chatState . currentStreamingMessageId === message . id
537-
538- if ( ! shouldShowOnlyPostContent && ! isProcessingChildren ) {
529+ if ( ! shouldShowOnlyPostContent ) {
539530 // Show preview or full content based on expansion state
540531 const shouldShowPreview = hasSubagents && ! isMainExpanded
541532
@@ -554,8 +545,8 @@ export function renderAssistantMessage(
554545 const previewLines = wrappedLines . slice ( 0 , PREVIEW_LINES )
555546
556547 previewLines . forEach ( ( line ) => {
557- const indentedLine = ' ' + line // 4 spaces for assistant content with subagents
558- appendWrappedLine ( lines , indentedLine , 4 , metrics , [ ] , 0 )
548+ const indentedLine = line // 0 spaces to match subagent indentation
549+ appendWrappedLine ( lines , indentedLine , 0 , metrics , [ ] , 0 )
559550 } )
560551
561552 // Add "..." if content was truncated
@@ -568,8 +559,8 @@ export function renderAssistantMessage(
568559 const contentLines = message . content . split ( '\n' )
569560
570561 contentLines . forEach ( ( line ) => {
571- const indentedLine = hasSubagents ? ' ' + line : line // 4 spaces if has subagents, 0 if simple message
572- const indentLevel = hasSubagents ? 4 : 0
562+ const indentedLine = line // 0 spaces to match subagent indentation
563+ const indentLevel = 0
573564 appendWrappedLine ( lines , indentedLine , indentLevel , metrics , [ ] , 0 )
574565 } )
575566 }
@@ -997,35 +988,13 @@ async function streamTextToNodeProperty(
997988 chatState . currentlyStreamingNodeId = node . id
998989 }
999990
1000- let hasCollapsed = false
1001-
1002991 const words = text . split ( ' ' )
1003992 for ( let i = 0 ; i < words . length ; i ++ ) {
1004993 const word = words [ i ]
1005994 const isLastWord = i === words . length - 1
1006995
1007- // If streaming postContent, automatically collapse this node ONLY on first word
1008- // This prevents the delay by deferring the collapse until streaming actually begins
1009- if ( property === 'postContent' && ! hasCollapsed ) {
1010- const currentStreamingMessage = chatState . messages . find (
1011- ( m ) => m . id === chatState . currentStreamingMessageId ,
1012- )
1013- if ( currentStreamingMessage && currentStreamingMessage . subagentUIState ) {
1014- const uiState = currentStreamingMessage . subagentUIState
1015-
1016- // Collapse this specific node
1017- uiState . expanded . delete ( node . id )
1018-
1019- // Also collapse any child nodes of this node
1020- const childPrefix = node . id + '/'
1021- uiState . expanded . forEach ( ( nodeId ) => {
1022- if ( nodeId . startsWith ( childPrefix ) ) {
1023- uiState . expanded . delete ( nodeId )
1024- }
1025- } )
1026- hasCollapsed = true
1027- }
1028- }
996+ // No auto-collapse behavior when streaming postContent
997+ // Keep expansion state as-is
1029998
1030999 const chunk = ( i === 0 ? '' : ' ' ) + word
10311000 if ( property === 'postContent' ) {
@@ -1539,16 +1508,11 @@ export function renderSubagentTree(
15391508 metrics ,
15401509 )
15411510
1542- // Content - 4 additional spaces beyond header indentation
1511+ // Content - indented to align with the header
15431512 // Only show content if expanded or has no children
1544- // Also hide content if one of this node's descendants is currently processing in THIS message
1545- const isProcessingChildren =
1546- chatState . currentlyStreamingNodeId ?. startsWith ( nodeId + '/' ) &&
1547- chatState . currentStreamingMessageId === messageId
1548-
1549- if ( node . content && ( isExpanded || ! hasChildren ) && ! isProcessingChildren ) {
1513+ if ( node . content && ( isExpanded || ! hasChildren ) ) {
15501514 const contentLines = node . content . split ( '\n' )
1551- const contentIndentSpaces = headerIndentSpaces + 4
1515+ const contentIndentSpaces = 4 * depth
15521516 const contentPrefix = ' ' . repeat ( contentIndentSpaces )
15531517 contentLines . forEach ( ( line ) => {
15541518 if ( line . trim ( ) ) {
@@ -1572,7 +1536,7 @@ export function renderSubagentTree(
15721536 } else if ( hasChildren && ! isExpanded && node . postContent ) {
15731537 // Show postContent for collapsed nodes with children
15741538 const postLines = node . postContent . split ( '\n' )
1575- const postIndentSpaces = 4 * depth + 4 // Same as header indentation + 4 extra spaces
1539+ const postIndentSpaces = 4 * depth // Same as header indentation
15761540 const postPrefix = ' ' . repeat ( postIndentSpaces )
15771541 postLines . forEach ( ( line ) => {
15781542 if ( line . trim ( ) ) {
@@ -1600,7 +1564,7 @@ export function renderSubagentTree(
16001564 const rootNodeId = tree . id
16011565 if ( tree . postContent && ! uiState . expanded . has ( rootNodeId ) ) {
16021566 const postLines = tree . postContent . split ( '\n' )
1603- const postPrefix = ' ' // 4 extra spaces for indentation
1567+ const postPrefix = '' // 0 spaces to match assistant indentation
16041568 postLines . forEach ( ( line ) => {
16051569 if ( line . trim ( ) ) {
16061570 appendWrappedLine (
@@ -1718,6 +1682,11 @@ function handleToggleAction(key: any): boolean {
17181682 const isExpanded = uiState . expanded . has ( actualNodeId )
17191683
17201684 if ( isExpanded ) {
1685+ // Check if any descendants are currently streaming - if so, don't allow collapse
1686+ if ( isAnyDescendantStreaming ( actualNodeId , focusedMessage . id ) ) {
1687+ return true // Consume the key press but don't collapse
1688+ }
1689+
17211690 // Collapse the node
17221691 uiState . expanded . delete ( actualNodeId )
17231692 // Remove all descendant nodes from expanded set
@@ -1774,6 +1743,11 @@ function handleArrowToggleAction(key: any): boolean {
17741743 if ( key . name === 'left' ) {
17751744 // Left arrow: close (collapse) if expanded, otherwise navigate to previous toggle
17761745 if ( isExpanded ) {
1746+ // Check if any descendants are currently streaming - if so, don't allow collapse
1747+ if ( isAnyDescendantStreaming ( actualNodeId , focusedMessage . id ) ) {
1748+ return true // Consume the key press but don't collapse
1749+ }
1750+
17771751 uiState . expanded . delete ( actualNodeId )
17781752 // Remove all descendant nodes from expanded set
17791753 const descendantPrefix = actualNodeId + '/'
@@ -1987,6 +1961,22 @@ function clearAllToggleFocus(): boolean {
19871961 return hadFocus
19881962}
19891963
1964+ function isAnyDescendantStreaming ( nodeId : string , messageId : string ) : boolean {
1965+ // Check if the current streaming node is this node or any of its descendants
1966+ if (
1967+ chatState . currentlyStreamingNodeId &&
1968+ chatState . currentStreamingMessageId === messageId
1969+ ) {
1970+ // If the streaming node starts with our nodeId followed by '/', it's a descendant
1971+ // Or if it's exactly our nodeId, we're streaming
1972+ return (
1973+ chatState . currentlyStreamingNodeId === nodeId ||
1974+ chatState . currentlyStreamingNodeId . startsWith ( nodeId + '/' )
1975+ )
1976+ }
1977+ return false
1978+ }
1979+
19901980function autoFocusLatestToggle ( ) : void {
19911981 const latestMessageId = findLatestAssistantMessageWithChildren ( )
19921982 if ( ! latestMessageId ) return
0 commit comments