@@ -300,7 +300,7 @@ export function ContagionGraph({ cards, mcapMap, logos }: ContagionGraphProps) {
300300 Dependency Map
301301 </ CardTitle >
302302 < p className = "text-xs text-muted-foreground" >
303- Top { nodes . length } stablecoins by market cap. Arrow thickness shows collateral dependency weight. Hover edges for details. Click nodes for detail page .
303+ Top { nodes . length } stablecoins by market cap. Edge thickness shows dependency weight; color and dash encode type. Hover edges for details .
304304 </ p >
305305 </ CardHeader >
306306 < CardContent >
@@ -330,32 +330,15 @@ export function ContagionGraph({ cards, mcapMap, logos }: ContagionGraphProps) {
330330 </ clipPath >
331331 ) ;
332332 } ) }
333- { /* Arrowhead markers — markerUnits="userSpaceOnUse" so size is fixed regardless of stroke width */ }
334- { ( [ "collateral" , "mechanism" , "wrapper" ] as const ) . map ( ( type ) => (
335- < marker key = { type } id = { `arrow-${ type } ` } viewBox = "0 0 10 6" refX = "10" refY = "3"
336- markerWidth = "7" markerHeight = "5" markerUnits = "userSpaceOnUse" orient = "auto" >
337- < path d = "M0,0 L10,3 L0,6 Z" fill = { TYPE_COLORS [ type ] } />
338- </ marker >
339- ) ) }
340333 </ defs >
341334
342- { /* Edges — arrow direction: upstream (target in data) → dependent (source in data) */ }
335+ { /* Edges */ }
343336 { links . map ( ( link , i ) => {
344337 const srcId = typeof link . source === "string" ? link . source : ( link . source as GraphNode ) . id ;
345338 const tgtId = typeof link . target === "string" ? link . target : ( link . target as GraphNode ) . id ;
346- // srcId = dependent, tgtId = upstream (from data model)
347- // Arrow direction: upstream → dependent, so line goes tgt → src
348- const fromPos = positions . get ( tgtId ) ; // upstream
349- const toPos = positions . get ( srcId ) ; // dependent
350- const toNode = nodeMap . get ( srcId ) ;
351- if ( ! fromPos || ! toPos || ! toNode ) return null ;
352-
353- // Offset endpoint by target node radius so arrowhead touches boundary
354- const dx = toPos . x - fromPos . x ;
355- const dy = toPos . y - fromPos . y ;
356- const dist = Math . sqrt ( dx * dx + dy * dy ) ;
357- const endX = dist > 0 ? toPos . x - ( dx / dist ) * ( toNode . r + 4 ) : toPos . x ;
358- const endY = dist > 0 ? toPos . y - ( dy / dist ) * ( toNode . r + 4 ) : toPos . y ;
339+ const posA = positions . get ( srcId ) ;
340+ const posB = positions . get ( tgtId ) ;
341+ if ( ! posA || ! posB ) return null ;
359342
360343 const isEdgeDirectHovered = hoveredEdge === i ;
361344 const isConnected = connectedEdges . has ( i ) ;
@@ -374,20 +357,19 @@ export function ContagionGraph({ cards, mcapMap, logos }: ContagionGraphProps) {
374357 < g key = { `${ srcId } -${ tgtId } -${ i } ` } >
375358 { /* Invisible wide hit area */ }
376359 < line
377- x1 = { fromPos . x } y1 = { fromPos . y } x2 = { endX } y2 = { endY }
360+ x1 = { posA . x } y1 = { posA . y } x2 = { posB . x } y2 = { posB . y }
378361 stroke = "transparent" strokeWidth = { 14 }
379362 style = { { cursor : "pointer" } }
380363 onMouseEnter = { ( ) => setHoveredEdge ( i ) }
381364 onMouseLeave = { ( ) => setHoveredEdge ( null ) }
382365 />
383366 { /* Visible edge */ }
384367 < line
385- x1 = { fromPos . x } y1 = { fromPos . y } x2 = { endX } y2 = { endY }
368+ x1 = { posA . x } y1 = { posA . y } x2 = { posB . x } y2 = { posB . y }
386369 stroke = { typeColor }
387370 strokeWidth = { isEdgeDirectHovered ? sw + 1 : sw }
388371 opacity = { edgeOpacity }
389372 strokeDasharray = { dashArray }
390- markerEnd = { `url(#arrow-${ link . type } )` }
391373 pointerEvents = "none"
392374 />
393375 </ g >
0 commit comments