Skip to content

Commit 2267629

Browse files
committed
v0.6.0 - UI: upgrade Brain map flow view
Add a cinematic Brain overlay with holographic rings and scanline treatment. Highlight selected-node recall paths, dim unrelated nodes, and surface immediate flow links in the detail panel.
1 parent b472eb5 commit 2267629

3 files changed

Lines changed: 356 additions & 14 deletions

File tree

desktop/cortex-control-center/src/BrainVisualizer.jsx

Lines changed: 133 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
4453
function 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}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { readFileSync } from "node:fs";
2+
import { describe, expect, it } from "vitest";
3+
4+
const source = readFileSync(new URL("./BrainVisualizer.jsx", import.meta.url), "utf8");
5+
const css = readFileSync(new URL("./styles.css", import.meta.url), "utf8");
6+
7+
function readBlock(text, needle) {
8+
const start = text.indexOf(needle);
9+
expect(start, `missing block ${needle}`).toBeGreaterThanOrEqual(0);
10+
11+
const bodyStart = text.indexOf("{", start);
12+
expect(bodyStart, `missing body for ${needle}`).toBeGreaterThanOrEqual(0);
13+
14+
let depth = 1;
15+
for (let index = bodyStart + 1; index < text.length; index += 1) {
16+
if (text[index] === "{") {
17+
depth += 1;
18+
} else if (text[index] === "}") {
19+
depth -= 1;
20+
}
21+
22+
if (depth === 0) {
23+
return text.slice(bodyStart + 1, index);
24+
}
25+
}
26+
27+
throw new Error(`unterminated block ${needle}`);
28+
}
29+
30+
describe("Brain visualizer", () => {
31+
it("keeps selected-node recall flow visible in the graph and details panel", () => {
32+
expect(source).toContain("const selectedFlow = useMemo");
33+
expect(source).toContain("const isSelectedFlowLink = useCallback");
34+
expect(source).toContain("brain-stat brain-stat-flow");
35+
expect(source).toContain("brain-flow-panel");
36+
expect(source).toContain("linkDirectionalParticles={link => link.type === \"conflict\" ? 3 : isSelectedFlowLink(link) ? 2 : 0}");
37+
});
38+
39+
it("renders the cinematic Brain overlay without bypassing reduced motion", () => {
40+
expect(source).toContain("brain-orbital-ring brain-orbital-ring-a");
41+
expect(source).toContain("brain-orbital-ring brain-orbital-ring-b");
42+
expect(source).toContain("brain-scanline");
43+
44+
expect(readBlock(css, ".brain-orbital-ring {")).toContain("animation: brain-ring-drift 28s linear infinite");
45+
expect(css).toContain("animation: brain-scanline-drift 9s var(--motion-ease) infinite");
46+
expect(css).toContain(':root[data-cortex-effective-reduced-motion="reduce"] .brain-orbital-ring');
47+
expect(css).toContain(':root:not([data-cortex-reduced-motion="full"]) .brain-scanline');
48+
});
49+
});

0 commit comments

Comments
 (0)