import { useEffect, useRef } from 'react'; import { useWebSocket } from '../../hooks/useWebSocket'; import './MatrixStreamOverlay.css'; export default function MatrixStreamOverlay({ active, onClose }: { active: boolean; onClose: () => void }) { const { recentShares } = useWebSocket(); const canvasRef = useRef(null); // Keep a ref to the latest shares so the draw loop always sees fresh data // WITHOUT being listed as a useEffect dependency — this stops the animation // from restarting every time a new share arrives (fixes L7). const sharesRef = useRef(recentShares); sharesRef.current = recentShares; useEffect(() => { if (!active || !canvasRef.current) return; const canvas = canvasRef.current; const ctx = canvas.getContext('2d'); if (!ctx) return; const resize = () => { canvas.width = window.innerWidth; canvas.height = window.innerHeight; }; resize(); window.addEventListener('resize', resize); const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$+-*/=%""\'#&_(),.;:?!\\|{}<>[]^~'; const fontSize = 16; let columns = canvas.width / fontSize; let drops: number[] = Array(Math.floor(columns)).fill(1); const draw = () => { ctx.fillStyle = 'rgba(0, 0, 0, 0.05)'; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.fillStyle = '#0F0'; ctx.font = `${fontSize}px monospace`; const shares = sharesRef.current; for (let i = 0; i < drops.length; i++) { let text = letters.charAt(Math.floor(Math.random() * letters.length)); if (Math.random() > 0.99 && shares.length > 0) { const share = shares[Math.floor(Math.random() * shares.length)]; text = JSON.stringify({ agent: share.agent_id?.substring(0, 6), hash: share.hash?.substring(0, 8), valid: share.accepted }); ctx.fillStyle = share.accepted ? '#00f5ff' : '#ff4444'; ctx.fillText(text, i * fontSize, drops[i] * fontSize); ctx.fillStyle = '#0F0'; } else { ctx.fillText(text, i * fontSize, drops[i] * fontSize); } if (drops[i] * fontSize > canvas.height && Math.random() > 0.975) { drops[i] = 0; } drops[i]++; } }; const interval = setInterval(draw, 33); return () => { clearInterval(interval); window.removeEventListener('resize', resize); }; }, [active]); // recentShares intentionally excluded — read via sharesRef if (!active) return null; return (

RAW_SOCKET_STREAM [ACTIVE]

Click anywhere to close terminal
); }