import React, { useEffect, useState, useRef } from 'react'; import { Radio } from 'lucide-react'; import type { SimStep } from '../types'; interface Props { steps: SimStep[]; autoPlay?: boolean; } const ACTION_MESSAGES = { 'airlift': [ "AIRLIFT-CMD > Priority clearance for {zone}. Severe casualty vector detected.", "AIRLIFT-CMD > Scrambling MedEvac to {zone}. Holding pattern established.", "AIRLIFT-CMD > Bird is en route to {zone}. ETA 3 Mike." ], 'deploy_team': [ "GROUND-OPS > Moving rescue convoy to {zone}. Expecting heavy debris.", "GROUND-OPS > Tactical team deployed. Destination: {zone}.", "GROUND-OPS > Ground units mobilized to {zone}. Checking structural integrity." ], 'send_supplies': [ "LOGISTICS > Route confirmed. Supplying critical medical aid to {zone}.", "LOGISTICS > Dispatching transport. {zone} supply gap critical.", "LOGISTICS > Cargo secured. Routing to {zone} immediately." ], 'wait': [ "AI-OVERSEER > High risk detected. Wait penalty enforced. Re-calculating...", "AI-OVERSEER > Operations paused. Scanning telemetry.", "SYSTEM > Wait protocol engaged. Awaiting optimal deployment window." ] }; function generateMessage(step: SimStep): string { if (!step.action) return "SYSTEM > Analyzing telemetry data..."; // Normalize action name let actName = step.action.action; if (typeof actName === 'string') { if (actName.includes('airlift')) actName = 'airlift'; else if (actName.includes('deploy')) actName = 'deploy_team'; else if (actName.includes('supplies')) actName = 'send_supplies'; else if (actName.includes('wait')) actName = 'wait'; } const target = step.action.to_zone || step.action.from_zone || (step.action as any).zone || 'Unknown'; const msgList = ACTION_MESSAGES[actName as keyof typeof ACTION_MESSAGES] || ["SYSTEM > Executing unspecified action on {zone}."]; // Pick deterministic but varied message based on step index (or just random, but deterministic is better so it doesn't flicker). const msgIndex = (step.reward ? Math.abs(Math.floor(step.reward * 100)) : 0) % msgList.length; let text = msgList[msgIndex]; return text.replace('{zone}', `Zone ${target}`); } export function CommsInterceptTerminal({ steps, autoPlay = false }: Props) { const [visibleCount, setVisibleCount] = useState(autoPlay ? 0 : steps.length); const scrollRef = useRef(null); useEffect(() => { if (autoPlay) { setVisibleCount(0); const interval = setInterval(() => { setVisibleCount(prev => { if (prev < steps.length) return prev + 1; clearInterval(interval); return prev; }); }, 800); // New message every 800ms return () => clearInterval(interval); } else { setVisibleCount(steps.length); } }, [steps, autoPlay]); useEffect(() => { if (scrollRef.current) { scrollRef.current.scrollTop = scrollRef.current.scrollHeight; } }, [visibleCount, steps]); const visibleSteps = steps.slice(0, visibleCount); return (
{/* Header */}

Live Comms Intercept

Encrypted SECURE
{/* Scanlines Overlay - Add pointer-events-none so it doesn't block scrolling */}
{/* Terminal Content */}
{visibleSteps.length === 0 && (
Awaiting signals...
)} {visibleSteps.map((step, index) => { const msg = generateMessage(step); const isSystem = msg.startsWith("SYSTEM") || msg.startsWith("AI-OVERSEER"); const isAirlift = msg.startsWith("AIRLIFT-CMD"); const isGround = msg.startsWith("GROUND-OPS"); const isLogistics = msg.startsWith("LOGISTICS"); let colorClass = "text-zinc-400"; if (isSystem) colorClass = "text-amber-500"; if (isAirlift) colorClass = "text-cyan-400 drop-shadow-[0_0_5px_rgba(6,182,212,0.6)]"; if (isGround) colorClass = "text-blue-400 font-bold"; if (isLogistics) colorClass = "text-orange-400 font-bold"; // Calculate a deterministic faux timestamp based on index const time = new Date(); time.setSeconds(time.getSeconds() - (steps.length - index)); const timestamp = time.toISOString().split('T')[1].slice(0,8); return (
[{timestamp}] {msg.split(' > ')[0]} {'>'} {msg.split(' > ')[1] || msg}
); })} {autoPlay && visibleCount < steps.length && (
_
)}
{/* Soft gradient at bottom to fade out text if crowded */}
); }