import React, { useState, useEffect, useRef } from 'react'; export default function DevConsole({ actions, onSendCommand }) { const [cmd, setCmd] = useState(""); const logEndRef = useRef(null); // Auto-scroll to bottom of logs useEffect(() => { if (logEndRef.current) { logEndRef.current.scrollIntoView({ behavior: "smooth" }); } }, [actions]); const handleSend = () => { if (cmd.trim()) { if (onSendCommand) { onSendCommand(cmd); } else { console.log("Sending command to backend:", cmd); } setCmd(""); } }; const formatEvent = (act) => { if (typeof act === 'string') return act; const type = act.type || "INFO"; const body = act.msg || act.message || JSON.stringify(act); const preview = typeof body === 'string' && body.length > 240 ? `${body.slice(0, 240)}…` : body; // Custom formatting for specific types if (type === "SPAWN") return `[SPAWN] ${act.callsign} (${act.weight_class} ${act.ac_type}) entered airspace`; if (type === "LANDING") return `[LAND] ${act.callsign} landed successfully (+${act.reward})`; if (type === "CRASH") return `[CRASH] ${act.callsign} !! CRITICAL SEPARATION LOSS !!`; if (type === "RUNWAY_CHANGE") return `[RWY] Selection changed to: ${act.to?.join(', ')}`; if (type === "WAYPOINT_CREATED") return `[INFRA] New waypoint created: ${act.name}`; if (type === "RUNWAY_CREATED") return `[INFRA] New runway created: ${act.id}`; if (type === "AIRPORT_CREATED") return `[INFRA] New airport created: ${act.name} (${act.code})`; if (type === "ATC") return `[ATC] ${body}`; if (type === "ERROR") return `[ERROR] !! ${body} !!`; if (type === "INFO" && body.startsWith("CMD:")) return `[SIM] ${body.replace("CMD: ", "")}`; if (type === "RL_TASK") return `[RL] ${act.phase || 'step'} task=${act.task} model=${act.model} step=${act.step}`; if (type === "RL_PROMPT") return `[PROMPT] ${preview}`; if (type === "RL_RESPONSE") return `[MODEL] ${preview}`; if (type === "RL_ACTION") return `[ACTION] ${act.action}`; if (type === "RL_REWARD") return `[REWARD] step=${act.step} reward=${act.reward} cumulative=${act.cumulative_reward} done=${act.done}`; if (type === "RL_ERROR") return `[RL ERROR] ${body}`; return `[${type}] ${body}`; }; return (

Live Simulation Log

{actions.map((act, i) => { const timestamp = act.timestamp ? new Date(act.timestamp * 1000).toLocaleTimeString([], {hour: '2-digit', minute:'2-digit', second:'2-digit'}) : new Date().toLocaleTimeString([], {hour: '2-digit', minute:'2-digit', second:'2-digit'}); const eventType = act?.type || "INFO"; const eventBody = act?.msg || act?.message || ""; const eventKey = `${act?.timestamp ?? "no-ts"}-${eventType}-${eventBody}-${i}`; const isError = act.type === "ERROR"; const isATC = act.type === "ATC"; return (
[{timestamp}] {formatEvent(act)}
); })}
setCmd(e.target.value)} onKeyDown={e => { if (e.key === 'Enter') handleSend(); }} placeholder="Enter SIM/ATC Command..." style={{ flex: 1, padding: '8px', background: '#1a1a1a', color: '#fff', border: '1px solid #444', borderRadius: '4px' }} />
); }