@@ -385,7 +385,7 @@ function SubAgentToolCall({ toolCall: toolCallProp }: { toolCall: CopilotToolCal
385385 />
386386 ) }
387387 { renderSubAgentTable ( ) }
388- < WorkflowEditSummary toolCall = { toolCall } />
388+ { /* WorkflowEditSummary is rendered outside SubAgentContent for edit subagent */ }
389389 { showButtons && < RunSkipButtons toolCall = { toolCall } /> }
390390 </ div >
391391 )
@@ -406,7 +406,7 @@ function getDisplayNameForSubAgent(toolCall: CopilotToolCall): string {
406406/**
407407 * Max height for subagent content before internal scrolling kicks in
408408 */
409- const SUBAGENT_MAX_HEIGHT = 125
409+ const SUBAGENT_MAX_HEIGHT = 200
410410
411411/**
412412 * Interval for auto-scroll during streaming (ms)
@@ -582,6 +582,16 @@ function SubAgentContent({
582582 } ) }
583583 </ div >
584584 ) }
585+
586+ { /* Render WorkflowEditSummary outside the collapsible container for edit_workflow tool calls */ }
587+ { blocks
588+ . filter ( ( block ) => block . type === 'subagent_tool_call' && block . toolCall ?. name === 'edit_workflow' )
589+ . map ( ( block , index ) => (
590+ < WorkflowEditSummary
591+ key = { `edit-summary-${ block . toolCall ?. id || index } ` }
592+ toolCall = { block . toolCall ! }
593+ />
594+ ) ) }
585595 </ div >
586596 )
587597}
@@ -607,16 +617,25 @@ function isSpecialToolCall(toolCall: CopilotToolCall): boolean {
607617 * Expands inline on click to show individual blocks with their icons.
608618 */
609619function WorkflowEditSummary ( { toolCall } : { toolCall : CopilotToolCall } ) {
610- const [ isExpanded , setIsExpanded ] = useState ( false )
611-
612- // Get workflow name from registry
613- const activeWorkflowId = useWorkflowRegistry ( ( s ) => s . activeWorkflowId )
614- const workflows = useWorkflowRegistry ( ( s ) => s . workflows )
615- const workflowName = activeWorkflowId ? workflows [ activeWorkflowId ] ?. name : undefined
616-
617620 // Get block data from current workflow state
618621 const blocks = useWorkflowStore ( ( s ) => s . blocks )
619622
623+ // Cache block info on first render (before diff is applied) so we can show
624+ // deleted blocks properly even after they're removed from the workflow
625+ const cachedBlockInfoRef = useRef < Record < string , { name : string ; type : string } > > ( { } )
626+
627+ // Update cache with current block info (only add, never remove)
628+ useEffect ( ( ) => {
629+ for ( const [ blockId , block ] of Object . entries ( blocks ) ) {
630+ if ( ! cachedBlockInfoRef . current [ blockId ] ) {
631+ cachedBlockInfoRef . current [ blockId ] = {
632+ name : block . name || '' ,
633+ type : block . type || '' ,
634+ }
635+ }
636+ }
637+ } , [ blocks ] )
638+
620639 // Show for edit_workflow regardless of state
621640 if ( toolCall . name !== 'edit_workflow' ) {
622641 return null
@@ -632,10 +651,19 @@ function WorkflowEditSummary({ toolCall }: { toolCall: CopilotToolCall }) {
632651 }
633652
634653 // Group operations by type with block info
654+ interface SubBlockPreview {
655+ id : string
656+ value : any
657+ }
658+
635659 interface BlockChange {
636660 blockId : string
637661 blockName : string
638662 blockType : string
663+ /** All subblocks for add operations */
664+ subBlocks ?: SubBlockPreview [ ]
665+ /** Only changed subblocks for edit operations */
666+ changedSubBlocks ?: SubBlockPreview [ ]
639667 }
640668
641669 const addedBlocks : BlockChange [ ] = [ ]
@@ -646,10 +674,11 @@ function WorkflowEditSummary({ toolCall }: { toolCall: CopilotToolCall }) {
646674 const blockId = op . block_id
647675 if ( ! blockId ) continue
648676
649- // Get block info from current workflow state or operation params
677+ // Get block info from current workflow state, cached state, or operation params
650678 const currentBlock = blocks [ blockId ]
651- let blockName = currentBlock ?. name || ''
652- let blockType = currentBlock ?. type || ''
679+ const cachedBlock = cachedBlockInfoRef . current [ blockId ]
680+ let blockName = currentBlock ?. name || cachedBlock ?. name || ''
681+ let blockType = currentBlock ?. type || cachedBlock ?. type || ''
653682
654683 // For add operations, get info from params (type is stored as params.type)
655684 if ( op . operation_type === 'add' && op . params ) {
@@ -662,11 +691,45 @@ function WorkflowEditSummary({ toolCall }: { toolCall: CopilotToolCall }) {
662691 blockType = op . params . type || ''
663692 }
664693
694+ // Skip edge-only edit operations (like how we don't highlight blocks on canvas for edge changes)
695+ // An edit is edge-only if params only contains 'connections' and nothing else meaningful
696+ if ( op . operation_type === 'edit' && op . params ) {
697+ const paramKeys = Object . keys ( op . params )
698+ const isEdgeOnlyEdit = paramKeys . length === 1 && paramKeys [ 0 ] === 'connections'
699+ if ( isEdgeOnlyEdit ) {
700+ continue
701+ }
702+ }
703+
704+ // For delete operations, check if block info was provided in operation
705+ if ( op . operation_type === 'delete' ) {
706+ // Some delete operations may include block_name and block_type
707+ blockName = blockName || op . block_name || ''
708+ blockType = blockType || op . block_type || ''
709+ }
710+
665711 // Fallback name to type or ID
666712 if ( ! blockName ) blockName = blockType || blockId
667713
668714 const change : BlockChange = { blockId, blockName, blockType }
669715
716+ // Extract subblock info from operation params
717+ if ( op . params ?. inputs && typeof op . params . inputs === 'object' ) {
718+ const subBlocks : SubBlockPreview [ ] = [ ]
719+ for ( const [ id , value ] of Object . entries ( op . params . inputs ) ) {
720+ // Skip empty values and connections
721+ if ( value === null || value === undefined || value === '' ) continue
722+ subBlocks . push ( { id, value } )
723+ }
724+ if ( subBlocks . length > 0 ) {
725+ if ( op . operation_type === 'add' ) {
726+ change . subBlocks = subBlocks
727+ } else if ( op . operation_type === 'edit' ) {
728+ change . changedSubBlocks = subBlocks
729+ }
730+ }
731+ }
732+
670733 switch ( op . operation_type ) {
671734 case 'add' :
672735 addedBlocks . push ( change )
@@ -691,83 +754,108 @@ function WorkflowEditSummary({ toolCall }: { toolCall: CopilotToolCall }) {
691754 return getBlock ( blockType )
692755 }
693756
694- // Render a single block row (toolbar style: colored square with white icon)
695- const renderBlockRow = (
757+ // Format subblock value for display
758+ const formatSubBlockValue = ( value : any ) : string => {
759+ if ( value === null || value === undefined ) return ''
760+ if ( typeof value === 'string' ) {
761+ // Truncate long strings
762+ return value . length > 60 ? `${ value . slice ( 0 , 60 ) } ...` : value
763+ }
764+ if ( typeof value === 'boolean' ) return value ? 'true' : 'false'
765+ if ( typeof value === 'number' ) return String ( value )
766+ if ( Array . isArray ( value ) ) {
767+ if ( value . length === 0 ) return '[]'
768+ return `[${ value . length } items]`
769+ }
770+ if ( typeof value === 'object' ) {
771+ const keys = Object . keys ( value )
772+ if ( keys . length === 0 ) return '{}'
773+ return `{${ keys . length } fields}`
774+ }
775+ return String ( value )
776+ }
777+
778+ // Format subblock ID to readable label
779+ const formatSubBlockLabel = ( id : string ) : string => {
780+ return id
781+ . replace ( / ( [ A - Z ] ) / g, ' $1' )
782+ . replace ( / [ _ - ] / g, ' ' )
783+ . trim ( )
784+ . split ( ' ' )
785+ . map ( ( word ) => word . charAt ( 0 ) . toUpperCase ( ) + word . slice ( 1 ) . toLowerCase ( ) )
786+ . join ( ' ' )
787+ }
788+
789+ // Render a single block item with action icon and details
790+ const renderBlockItem = (
696791 change : BlockChange ,
697792 type : 'add' | 'edit' | 'delete'
698793 ) => {
699794 const blockConfig = getBlockConfig ( change . blockType )
700795 const Icon = blockConfig ?. icon
701796 const bgColor = blockConfig ?. bgColor || '#6B7280'
702797
703- const symbols = {
798+ const actionIcons = {
704799 add : { symbol : '+' , color : 'text-[#22c55e]' } ,
705- edit : { symbol : '• ' , color : 'text-[#f97316]' } ,
800+ edit : { symbol : '~ ' , color : 'text-[#f97316]' } ,
706801 delete : { symbol : '-' , color : 'text-[#ef4444]' } ,
707802 }
708- const { symbol, color } = symbols [ type ]
803+ const { symbol, color } = actionIcons [ type ]
804+
805+ const subBlocksToShow = type === 'add' ? change . subBlocks : type === 'edit' ? change . changedSubBlocks : undefined
709806
710807 return (
711808 < div
712809 key = { `${ type } -${ change . blockId } ` }
713- className = 'flex items-center gap-2 px-2.5 py-1.5 '
810+ className = 'rounded-md border border-[var(--border-1)] bg-[var(--surface-1)] overflow-hidden '
714811 >
715- < span className = { `font-mono text-[11px] font-medium ${ color } w-3` } > { symbol } </ span >
716- { /* Toolbar-style icon: colored square with white icon */ }
717- < div
718- className = 'flex h-4 w-4 flex-shrink-0 items-center justify-center rounded-[3px]'
719- style = { { background : bgColor } }
720- >
721- { Icon && < Icon className = 'h-[10px] w-[10px] text-white' /> }
812+ { /* Block header */ }
813+ < div className = 'flex items-center justify-between px-2.5 py-2' >
814+ < div className = 'flex items-center gap-2' >
815+ { /* Toolbar-style icon: colored square with white icon */ }
816+ < div
817+ className = 'flex h-4 w-4 flex-shrink-0 items-center justify-center rounded-[3px]'
818+ style = { { background : bgColor } }
819+ >
820+ { Icon && < Icon className = 'h-[10px] w-[10px] text-white' /> }
821+ </ div >
822+ < span
823+ className = { `font-medium font-season text-[12px] ${ type === 'delete' ? 'text-[var(--text-tertiary)] line-through' : 'text-[var(--text-primary)]' } ` }
824+ >
825+ { change . blockName }
826+ </ span >
827+ </ div >
828+ { /* Action icon in top right */ }
829+ < span className = { `font-mono text-[14px] font-bold ${ color } ` } > { symbol } </ span >
722830 </ div >
723- < span
724- className = { `font-season text-[12px] ${ type === 'delete' ? 'text-[var(--text-tertiary)] line-through' : 'text-[var(--text-secondary)]' } ` }
725- >
726- { change . blockName }
727- </ span >
831+
832+ { /* Subblock details */ }
833+ { subBlocksToShow && subBlocksToShow . length > 0 && (
834+ < div className = 'border-t border-[var(--border-1)] bg-[var(--surface-2)] px-2.5 py-1.5' >
835+ { subBlocksToShow . map ( ( sb ) => (
836+ < div
837+ key = { sb . id }
838+ className = 'flex items-start gap-1.5 py-0.5 text-[11px]'
839+ >
840+ < span className = { `font-medium ${ type === 'edit' ? 'text-[#f97316]' : 'text-[var(--text-tertiary)]' } ` } >
841+ { formatSubBlockLabel ( sb . id ) } :
842+ </ span >
843+ < span className = 'text-[var(--text-muted)] line-clamp-1 break-all' >
844+ { formatSubBlockValue ( sb . value ) }
845+ </ span >
846+ </ div >
847+ ) ) }
848+ </ div >
849+ ) }
728850 </ div >
729851 )
730852 }
731853
732854 return (
733- < div className = 'mt-2 w-full overflow-hidden rounded-md border border-[var(--border-1)] bg-[var(--surface-1)]' >
734- { /* Header row - always visible */ }
735- < button
736- type = 'button'
737- onClick = { ( ) => setIsExpanded ( ! isExpanded ) }
738- className = 'flex w-full items-center justify-between px-2.5 py-2 transition-colors hover:bg-[var(--surface-2)]'
739- >
740- < div className = 'flex items-center gap-2' >
741- < span className = 'font-medium font-season text-[12px] text-[var(--text-primary)]' >
742- { workflowName || 'Workflow' }
743- </ span >
744- < span className = 'flex items-center gap-1.5' >
745- { addedBlocks . length > 0 && (
746- < span className = 'font-mono text-[11px] font-medium text-[#22c55e]' > +{ addedBlocks . length } </ span >
747- ) }
748- { editedBlocks . length > 0 && (
749- < span className = 'font-mono text-[11px] font-medium text-[#f97316]' > •{ editedBlocks . length } </ span >
750- ) }
751- { deletedBlocks . length > 0 && (
752- < span className = 'font-mono text-[11px] font-medium text-[#ef4444]' > -{ deletedBlocks . length } </ span >
753- ) }
754- </ span >
755- </ div >
756- { isExpanded ? (
757- < ChevronUp className = 'h-3.5 w-3.5 text-[var(--text-tertiary)]' />
758- ) : (
759- < ChevronDown className = 'h-3.5 w-3.5 text-[var(--text-tertiary)]' />
760- ) }
761- </ button >
762-
763- { /* Expanded block list */ }
764- { isExpanded && (
765- < div className = 'border-t border-[var(--border-1)]' >
766- { addedBlocks . map ( ( change ) => renderBlockRow ( change , 'add' ) ) }
767- { editedBlocks . map ( ( change ) => renderBlockRow ( change , 'edit' ) ) }
768- { deletedBlocks . map ( ( change ) => renderBlockRow ( change , 'delete' ) ) }
769- </ div >
770- ) }
855+ < div className = 'mt-2 flex flex-col gap-1.5' >
856+ { addedBlocks . map ( ( change ) => renderBlockItem ( change , 'add' ) ) }
857+ { editedBlocks . map ( ( change ) => renderBlockItem ( change , 'edit' ) ) }
858+ { deletedBlocks . map ( ( change ) => renderBlockItem ( change , 'delete' ) ) }
771859 </ div >
772860 )
773861}
0 commit comments