import { useEffect, useRef, useState } from "react" import { BrainCircuit, Flame, Layers } from "lucide-react" import { endpoint } from "@/lib/api" import { useLocale } from "./i18n" interface ExpertMap { rows: number; cols: number; map: string; hits: string; seq: number } interface AtlasEntry { affinity: Record; entropy: number; top: string; label: string } const TIER_KEYS = ["tier.disk", "tier.ram", "tier.vram"] as const const TIER_RGB: [number, number, number][] = [[58, 71, 80], [90, 155, 216], [78, 214, 165]] function depthRoleKey(row: number, rows: number, isMtp: boolean): string { if (isMtp) return "brain.mtp" const f = row / Math.max(rows - 1, 1) if (f < 0.2) return "brain.early" if (f < 0.45) return "brain.lowerMiddle" if (f < 0.7) return "brain.upperMiddle" if (f < 0.9) return "brain.late" return "brain.final" } export function Brain({ baseUrl, apiKey, connected }: { baseUrl: string; apiKey: string; connected: boolean }) { const { t } = useLocale() const canvasRef = useRef(null) const wrapRef = useRef(null) const [wrapSize, setWrapSize] = useState({ w: 1200, h: 700 }) const [data, setData] = useState(null) const [atlas, setAtlas] = useState | null>(null) const [tip, setTip] = useState<{ x: number; y: number; row: number; col: number; tier: number; heat: number } | null>(null) const pulseRef = useRef(null) // per-expert pulse intensity 0..1 const lastSeq = useRef(0) const rafRef = useRef(0) // load the expert atlas if published (measured topic affinity, #175) useEffect(() => { fetch("/experts.json").then(r => r.ok ? r.json() : null).then(d => { if (d?.experts) setAtlas(d.experts) }).catch(() => {}) }, []) // track container size for responsive cell sizing useEffect(() => { const el = wrapRef.current if (!el) return const ro = new ResizeObserver(() => { setWrapSize({ w: el.clientWidth - 24, h: el.clientHeight - 24 }) }) ro.observe(el) return () => ro.disconnect() }, []) // poll /experts useEffect(() => { if (!connected) return let disposed = false const base = baseUrl.replace(/\/v1\/?$/, "") const poll = async () => { try { const res = await fetch(endpoint(base, "/experts"), { headers: apiKey ? { Authorization: `Bearer ${apiKey}` } : {} }) const next = (await res.json()) as ExpertMap if (disposed || !next.rows) return setData(next) if (next.seq !== lastSeq.current && next.hits) { lastSeq.current = next.seq const n = next.rows * next.cols if (!pulseRef.current || pulseRef.current.length !== n) pulseRef.current = new Float32Array(n) const p = pulseRef.current for (let i = 0; i < n; i++) { const byte = parseInt(next.hits.substr((i >> 3) * 2, 2), 16) || 0 if (byte & (1 << (i & 7))) p[i] = 1 } } } catch { /* engine busy or restarting — keep the last frame */ } } void poll() const t = window.setInterval(() => void poll(), 1500) return () => { disposed = true; window.clearInterval(t) } }, [baseUrl, apiKey, connected]) // render loop: grid + decaying pulses useEffect(() => { const canvas = canvasRef.current if (!canvas || !data) return const ctx = canvas.getContext("2d") if (!ctx) return const { rows, cols, map } = data const cell = Math.max(2, Math.floor(Math.min(wrapSize.w / cols, wrapSize.h / rows))) const gap = cell >= 4 ? 1 : 0 canvas.width = cols * (cell + gap) canvas.height = rows * (cell + gap) const draw = () => { ctx.clearRect(0, 0, canvas.width, canvas.height) const p = pulseRef.current for (let r = 0; r < rows; r++) { for (let c = 0; c < cols; c++) { const i = r * cols + c const byte = parseInt(map.substr(i * 2, 2), 16) || 0 const tier = byte >> 6 const heat = byte & 63 const [R, G, B] = TIER_RGB[tier] ?? TIER_RGB[0] // heat scales brightness: cold experts dim, hot experts full colour const lum = 0.35 + 0.65 * Math.min(heat / 24, 1) let rr = R * lum, gg = G * lum, bb = B * lum const pulse = p ? p[i] : 0 if (pulse > 0.01) { rr += (255 - rr) * pulse; gg += (255 - gg) * pulse; bb += (255 - bb) * pulse } ctx.fillStyle = `rgb(${rr | 0},${gg | 0},${bb | 0})` ctx.fillRect(c * (cell + gap), r * (cell + gap), cell, cell) } } let alive = false if (p) for (let i = 0; i < p.length; i++) { if (p[i] > 0.01) { p[i] *= 0.94; alive = true } else p[i] = 0 } if (alive) rafRef.current = requestAnimationFrame(draw) } draw() const keepalive = window.setInterval(() => { if (!rafRef.current) draw(); rafRef.current = 0 }, 400) return () => { cancelAnimationFrame(rafRef.current); window.clearInterval(keepalive) } }, [data, wrapSize]) const onMove = (e: React.MouseEvent) => { if (!data) return const rect = e.currentTarget.getBoundingClientRect() const scaleX = e.currentTarget.width / rect.width const scaleY = e.currentTarget.height / rect.height const cell = Math.max(2, Math.floor(Math.min(wrapSize.w / data.cols, wrapSize.h / data.rows))) const gap = cell >= 4 ? 1 : 0 const col = Math.floor(((e.clientX - rect.left) * scaleX) / (cell + gap)) const row = Math.floor(((e.clientY - rect.top) * scaleY) / (cell + gap)) if (row < 0 || row >= data.rows || col < 0 || col >= data.cols) { setTip(null); return } const byte = parseInt(data.map.substr((row * data.cols + col) * 2, 2), 16) || 0 setTip({ x: e.clientX, y: e.clientY, row, col, tier: byte >> 6, heat: byte & 63 }) } const totals = data ? (() => { const t = [0, 0, 0] for (let i = 0; i < data.rows * data.cols; i++) t[(parseInt(data.map.substr(i * 2, 2), 16) || 0) >> 6]++ return t })() : [0, 0, 0] return (
{t("brain.title")} — {data ? t("brain.layers", { rows: data.rows, cols: data.cols }) : t("brain.waiting")}
{t("tier.vram")} {totals[2].toLocaleString()} {t("tier.ram")} {totals[1].toLocaleString()} {t("tier.disk")} {totals[0].toLocaleString()} {t("brain.brightnessHint")} {t("brain.flashHint")}
setTip(null)} /> {!connected &&

{t("brain.connectHint")}

}
{tip && data && (() => { const isMtp = tip.row === data.rows - 1 const realLayer = isMtp ? 78 : tip.row + 3 const entry = atlas?.[`${realLayer}:${tip.col}`] return (
Layer {realLayer}{isMtp ? " (MTP)" : ""} · Expert {tip.col}
Tier: {t(TIER_KEYS[tip.tier])}
Heat: {tip.heat === 0 ? t("brain.neverRouted") : t("brain.selections", { heat: tip.heat })}
{entry ? <>
{entry.label.startsWith("specialist") ? t("brain.specialist", { top: entry.top }) : t("brain.generalist")} (entropy {entry.entropy})
{Object.entries(entry.affinity).sort((a, b) => b[1] - a[1]).slice(0, 3) .map(([c, p]) => `${c} ${Math.round(p * 100)}%`).join(" · ")}
:
{t(depthRoleKey(tip.row, data.rows, isMtp))}
}
) })()}
) }