@@ -41,6 +41,15 @@ function hasWebGLSupport() {
4141 }
4242}
4343
44+ function graphEndpointId ( endpoint ) {
45+ if ( endpoint && typeof endpoint === "object" ) return endpoint . id ;
46+ return endpoint ;
47+ }
48+
49+ function formatFlowType ( type ) {
50+ return String ( type || "semantic" ) . replace ( / [ _ - ] + / g, " " ) ;
51+ }
52+
4453function BrainFallbackGraph ( {
4554 graphData,
4655 memoryCt,
@@ -288,21 +297,78 @@ function BrainVisualizerComponent({ api = null, cortexBase = "http://127.0.0.1:7
288297
289298 const memoryCt = useMemo ( ( ) => graphData . nodes . filter ( n => n . group === "memory" ) . length , [ graphData ] ) ;
290299 const decisionCt = useMemo ( ( ) => graphData . nodes . filter ( n => n . group === "decision" ) . length , [ graphData ] ) ;
300+ const selectedFlow = useMemo ( ( ) => {
301+ const selectedId = selectedNode ?. id ;
302+ const neighborIds = new Set ( ) ;
303+ const typeCounts = new Map ( ) ;
304+ const flowLinks = [ ] ;
305+
306+ if ( ! selectedId ) {
307+ return {
308+ neighborIds,
309+ flowLinks,
310+ connectionCount : 0 ,
311+ primaryType : "idle" ,
312+ } ;
313+ }
314+
315+ for ( const link of graphData . links ) {
316+ const sourceId = graphEndpointId ( link . source ) ;
317+ const targetId = graphEndpointId ( link . target ) ;
318+ if ( sourceId !== selectedId && targetId !== selectedId ) continue ;
319+
320+ const neighborId = sourceId === selectedId ? targetId : sourceId ;
321+ const type = link . type || "semantic" ;
322+ neighborIds . add ( neighborId ) ;
323+ typeCounts . set ( type , ( typeCounts . get ( type ) || 0 ) + 1 ) ;
324+ flowLinks . push ( {
325+ neighborId,
326+ type,
327+ direction : sourceId === selectedId ? "outbound" : "inbound" ,
328+ weight : link . weight || 1 ,
329+ } ) ;
330+ }
331+
332+ const [ primaryType = "isolated" ] = [ ...typeCounts . entries ( ) ]
333+ . sort ( ( a , b ) => b [ 1 ] - a [ 1 ] )
334+ . map ( ( [ type ] ) => type ) ;
335+
336+ return {
337+ neighborIds,
338+ flowLinks : flowLinks . slice ( 0 , 5 ) ,
339+ connectionCount : flowLinks . length ,
340+ primaryType,
341+ } ;
342+ } , [ graphData . links , selectedNode ?. id ] ) ;
343+
344+ const isSelectedFlowLink = useCallback ( ( link ) => {
345+ const selectedId = selectedNode ?. id ;
346+ if ( ! selectedId ) return false ;
347+ const sourceId = graphEndpointId ( link . source ) ;
348+ const targetId = graphEndpointId ( link . target ) ;
349+ return sourceId === selectedId || targetId === selectedId ;
350+ } , [ selectedNode ?. id ] ) ;
351+
291352 const nodeThreeObject = useCallback ( ( node ) => {
292353 const color = new THREE . Color ( getAgentColor ( node . agent ) ) ;
293354 const baseColor = color . clone ( ) . multiplyScalar ( 0.7 ) ;
294355 const coreColor = color . clone ( ) . multiplyScalar ( 1.22 ) ;
295- const radius = Math . max ( 1.6 , ( node . val || 3 ) * 0.45 ) ;
356+ const selected = selectedNode ?. id === node . id ;
357+ const neighbor = ! selected && selectedFlow . neighborIds . has ( node . id ) ;
358+ const dimmed = Boolean ( selectedNode ) && ! selected && ! neighbor ;
359+ const radius = Math . max ( 1.6 , ( node . val || 3 ) * 0.45 ) * ( selected ? 1.34 : neighbor ? 1.12 : 1 ) ;
296360
297361 const group = new THREE . Group ( ) ;
298362 const core = new THREE . Mesh (
299363 new THREE . SphereGeometry ( radius , 18 , 18 ) ,
300364 new THREE . MeshStandardMaterial ( {
301365 color : baseColor ,
302366 emissive : baseColor ,
303- emissiveIntensity : 0.22 ,
367+ emissiveIntensity : selected ? 0.58 : neighbor ? 0.36 : 0.22 ,
304368 metalness : 0.05 ,
305369 roughness : 0.48 ,
370+ transparent : dimmed ,
371+ opacity : dimmed ? 0.32 : 1 ,
306372 } )
307373 ) ;
308374 group . add ( core ) ;
@@ -312,7 +378,7 @@ function BrainVisualizerComponent({ api = null, cortexBase = "http://127.0.0.1:7
312378 new THREE . MeshBasicMaterial ( {
313379 color,
314380 transparent : true ,
315- opacity : 0.18 ,
381+ opacity : dimmed ? 0.05 : selected ? 0.36 : neighbor ? 0.24 : 0.18 ,
316382 blending : THREE . AdditiveBlending ,
317383 depthWrite : false ,
318384 } )
@@ -324,14 +390,33 @@ function BrainVisualizerComponent({ api = null, cortexBase = "http://127.0.0.1:7
324390 new THREE . MeshStandardMaterial ( {
325391 color : coreColor ,
326392 emissive : coreColor ,
327- emissiveIntensity : 0.45 ,
393+ emissiveIntensity : selected ? 0.8 : 0.45 ,
328394 metalness : 0.25 ,
329395 roughness : 0.2 ,
396+ transparent : dimmed ,
397+ opacity : dimmed ? 0.44 : 1 ,
330398 } )
331399 ) ;
332400 group . add ( nucleus ) ;
401+
402+ if ( selected || neighbor ) {
403+ const ring = new THREE . Mesh (
404+ new THREE . TorusGeometry ( radius * ( selected ? 2.35 : 2.05 ) , selected ? 0.035 : 0.024 , 8 , 72 ) ,
405+ new THREE . MeshBasicMaterial ( {
406+ color,
407+ transparent : true ,
408+ opacity : selected ? 0.52 : 0.26 ,
409+ blending : THREE . AdditiveBlending ,
410+ depthWrite : false ,
411+ side : THREE . DoubleSide ,
412+ } )
413+ ) ;
414+ ring . rotation . x = Math . PI / 2 ;
415+ group . add ( ring ) ;
416+ }
417+
333418 return group ;
334- } , [ ] ) ;
419+ } , [ selectedFlow . neighborIds , selectedNode ] ) ;
335420
336421 // Error / loading states
337422 if ( error ) {
@@ -408,6 +493,10 @@ function BrainVisualizerComponent({ api = null, cortexBase = "http://127.0.0.1:7
408493 // 3D Graph
409494 return (
410495 < div className = "brain-container" onMouseDown = { ( ) => autoRotate && setAutoRotate ( false ) } onWheel = { ( ) => autoRotate && setAutoRotate ( false ) } >
496+ < div className = "brain-orbital-ring brain-orbital-ring-a" aria-hidden = "true" />
497+ < div className = "brain-orbital-ring brain-orbital-ring-b" aria-hidden = "true" />
498+ < div className = "brain-scanline" aria-hidden = "true" />
499+
411500 < div className = "brain-hud brain-hud-primary" >
412501 < div className = "brain-hud-copy" >
413502 < span className = "brain-mode" > Neural topology</ span >
@@ -421,6 +510,11 @@ function BrainVisualizerComponent({ api = null, cortexBase = "http://127.0.0.1:7
421510 < span className = "brain-stat" > < span className = "brain-label" > LINKS</ span > { graphData . links . length } </ span >
422511 < span className = "brain-stat" > < span className = "brain-label" > MEM</ span > { memoryCt } </ span >
423512 < span className = "brain-stat" > < span className = "brain-label" > DEC</ span > { decisionCt } </ span >
513+ { selectedNode ? (
514+ < span className = "brain-stat brain-stat-flow" >
515+ < span className = "brain-label" > FLOW</ span > { selectedFlow . connectionCount }
516+ </ span >
517+ ) : null }
424518 < button className = { `brain-toggle ${ autoRotate ? "active" : "" } ` } onClick = { ( ) => setAutoRotate ( r => ! r ) } style = { { display : "inline-flex" , alignItems : "center" , gap : 6 } } >
425519 { autoRotate ? < AppIcon name = "refresh" size = { 14 } /> : < AppIcon name = "activity" size = { 14 } /> }
426520 < span > { autoRotate ? "AUTO" : "MANUAL" } </ span >
@@ -438,6 +532,25 @@ function BrainVisualizerComponent({ api = null, cortexBase = "http://127.0.0.1:7
438532 < div className = "brain-detail-agent" style = { { color : getAgentColor ( selectedNode . agent ) } } > { selectedNode . agent } </ div >
439533 { selectedNode . fullText && < div className = "brain-detail-text" > { selectedNode . fullText } </ div > }
440534 { selectedNode . context && < div className = "brain-detail-ctx" > < span className = "brain-detail-ctx-label" > CONTEXT</ span > { selectedNode . context } </ div > }
535+ < div className = "brain-flow-panel" >
536+ < div className = "brain-flow-head" >
537+ < span > Recall Flow</ span >
538+ < strong > { formatFlowType ( selectedFlow . primaryType ) } </ strong >
539+ </ div >
540+ { selectedFlow . flowLinks . length ? (
541+ < div className = "brain-flow-list" >
542+ { selectedFlow . flowLinks . map ( ( link ) => (
543+ < div key = { `${ link . direction } -${ link . neighborId } -${ link . type } ` } className = "brain-flow-row" >
544+ < span className = { `brain-flow-direction ${ link . direction } ` } > { link . direction } </ span >
545+ < span className = "brain-flow-node" > { link . neighborId } </ span >
546+ < span className = "brain-flow-type" > { formatFlowType ( link . type ) } </ span >
547+ </ div >
548+ ) ) }
549+ </ div >
550+ ) : (
551+ < p className = "brain-flow-empty" > No immediate graph paths for this node.</ p >
552+ ) }
553+ </ div >
441554 < div className = "brain-detail-meta" >
442555 < span > Score: { selectedNode . score ?. toFixed ( 2 ) } </ span >
443556 < span > ID: { selectedNode . id } </ span >
@@ -471,13 +584,21 @@ function BrainVisualizerComponent({ api = null, cortexBase = "http://127.0.0.1:7
471584 nodeThreeObject = { nodeThreeObject }
472585 nodeThreeObjectExtend = { true }
473586 nodeLabel = { node => `${ node . label } (${ node . agent } )` }
474- linkColor = { link => link . type === "conflict" ? "#ff1744" : "rgba(0, 212, 255, 0.06)" }
475- linkWidth = { link => link . type === "conflict" ? 1.5 : 0.3 }
476- linkOpacity = { 0.15 }
477- linkDirectionalParticles = { link => link . type === "conflict" ? 3 : 0 }
478- linkDirectionalParticleWidth = { 1.5 }
479- linkDirectionalParticleColor = { ( ) => "#ff1744" }
480- backgroundColor = "#060a12"
587+ linkColor = { link => (
588+ link . type === "conflict"
589+ ? "#ff1744"
590+ : isSelectedFlowLink ( link )
591+ ? "rgba(64, 224, 255, 0.72)"
592+ : selectedNode
593+ ? "rgba(0, 212, 255, 0.035)"
594+ : "rgba(0, 212, 255, 0.06)"
595+ ) }
596+ linkWidth = { link => link . type === "conflict" ? 1.5 : isSelectedFlowLink ( link ) ? 1.1 : selectedNode ? 0.18 : 0.3 }
597+ linkOpacity = { selectedNode ? 0.32 : 0.15 }
598+ linkDirectionalParticles = { link => link . type === "conflict" ? 3 : isSelectedFlowLink ( link ) ? 2 : 0 }
599+ linkDirectionalParticleWidth = { link => isSelectedFlowLink ( link ) ? 1.8 : 1.5 }
600+ linkDirectionalParticleColor = { link => isSelectedFlowLink ( link ) ? "#40e0ff" : "#ff1744" }
601+ backgroundColor = "#040812"
481602 width = { dimensions . width }
482603 height = { dimensions . height }
483604 d3AlphaDecay = { 0.06 }
0 commit comments