1+ import {
2+ BLOCK_Z_BASE ,
3+ CONTAINER_CHILD_Z_BASE ,
4+ getEdgeZIndex ,
5+ getEdgeZIndexForTarget ,
6+ } from '@sim/workflow-renderer'
17import { type Edge , type Node , Position } from 'reactflow'
28
39/**
@@ -61,6 +67,24 @@ export interface HighlightOptions {
6167 selectedBlock ?: string
6268}
6369
70+ /** Semantic container depth used for z-order while docs positions stay flattened. */
71+ function getNestingDepth ( block : PreviewBlock , blocksById : Map < string , PreviewBlock > ) : number {
72+ let depth = 0
73+ let parentId = block . parentId
74+ const visited = new Set < string > ( )
75+
76+ while ( parentId && ! visited . has ( parentId ) ) {
77+ const parent = blocksById . get ( parentId )
78+ if ( ! parent ) break
79+
80+ visited . add ( parentId )
81+ depth += 1
82+ parentId = parent . parentId
83+ }
84+
85+ return depth
86+ }
87+
6488/**
6589 * Converts a {@link PreviewWorkflow} to React Flow nodes and edges.
6690 *
@@ -81,6 +105,7 @@ export function toReactFlowElements(
81105
82106 const nodes : Node [ ] = workflow . blocks . map ( ( block , index ) => {
83107 const isContainer = Boolean ( block . size )
108+ const nestingDepth = getNestingDepth ( block , blocksById )
84109 // Nested blocks are authored relative to their container; render them at
85110 // absolute coordinates (not React Flow parentNode children) so the edges
86111 // between a container and its nested blocks render reliably and on top.
@@ -92,7 +117,7 @@ export function toReactFlowElements(
92117 id : block . id ,
93118 type : isContainer ? 'previewContainer' : 'previewBlock' ,
94119 position,
95- zIndex : isContainer ? 0 : 1 ,
120+ zIndex : isContainer ? nestingDepth : block . parentId ? CONTAINER_CHILD_Z_BASE : BLOCK_Z_BASE ,
96121 ...( block . size ? { style : { width : block . size . width , height : block . size . height } } : { } ) ,
97122 data : {
98123 name : block . name ,
@@ -103,6 +128,7 @@ export function toReactFlowElements(
103128 tools : block . tools ,
104129 hideTargetHandle : block . hideTargetHandle ,
105130 size : block . size ,
131+ parentId : block . parentId ,
106132 index,
107133 animate,
108134 isHighlighted : highlightBlock === block . id || selectedBlock === block . id ,
@@ -127,6 +153,14 @@ export function toReactFlowElements(
127153 // so edges into and out of Loop/Parallel containers still connect.
128154 const sourceBlock = blocksById . get ( e . source )
129155 const targetBlock = blocksById . get ( e . target )
156+ const parentContainer = blocksById . get ( sourceBlock ?. parentId ?? targetBlock ?. parentId ?? '' )
157+ const baseZIndex = getEdgeZIndex (
158+ parentContainer ? getNestingDepth ( parentContainer , blocksById ) : undefined ,
159+ { isHighlighted : isEdgeHighlight }
160+ )
161+ const targetContainerZIndex = targetBlock ?. size
162+ ? getNestingDepth ( targetBlock , blocksById )
163+ : undefined
130164 const sourceHandle =
131165 e . sourceHandle ?? ( sourceBlock ?. size ? `${ sourceBlock . type } -end-source` : 'source' )
132166 const targetHandle = targetBlock ?. size ? undefined : 'target'
@@ -142,6 +176,7 @@ export function toReactFlowElements(
142176 } ,
143177 sourceHandle,
144178 targetHandle,
179+ zIndex : getEdgeZIndexForTarget ( baseZIndex , targetContainerZIndex ) ,
145180 data : {
146181 animate,
147182 delay : animate ? sourceIndex * BLOCK_STAGGER + BLOCK_STAGGER : 0 ,
0 commit comments