import { useEffect, useRef, useState } from 'react'; import { SettingsProvider } from '../settings/index.js'; import { useSession } from '../context/SessionContext.jsx'; import { useSpectatorMode } from '../hooks/useSpectatorMode.js'; import { useTelemetryFrames } from '../context/TelemetryContext.jsx'; import { useVideoRequests } from '../hooks/useVideoRequests.js'; import VideoTile from '../components/VideoTile.jsx'; import RoomCameraPanel from '../components/RoomCameraPanel.jsx'; import UserListPanel from '../components/UserListPanel.jsx'; import ChatPanel from '../components/ChatPanel.jsx'; import LogPanel from '../components/LogPanel.jsx'; function TelemetrySummary({ frame }) { const sensors = frame?.sensors || {}; const updated = frame?.receivedAt ? new Date(frame.receivedAt).toLocaleTimeString() : null; const entries = [ ['Voltage', sensors.voltageMv != null ? `${(sensors.voltageMv / 1000).toFixed(2)} V` : '--'], ['Current', sensors.currentMa != null ? `${sensors.currentMa} mA` : '--'], ['Charge', sensors.batteryChargeMah != null ? `${sensors.batteryChargeMah}` : '--'], ['OI', sensors.oiMode?.label || '--'], ]; return (
Telemetry {updated ? `Updated ${updated}` : 'Waiting...'}
{entries.map(([label, value]) => (
{label} {value}
))}
); } function CurrentDriverBadge({ roverId, session }) { const activeDriverId = session?.activeDrivers?.[roverId] || null; const user = (session?.users || []).find((entry) => entry.socketId === activeDriverId); const label = user?.nickname || (activeDriverId ? activeDriverId.slice(0, 6) : 'No driver'); const mode = session?.mode; const turnInfo = session?.turnQueues?.[roverId]; const driverText = mode === 'turns' && turnInfo?.current ? `Driver: ${label} (turns)` : `Driver: ${label}`; return (
{driverText}
); } function RoverSpectatorCard({ rover, frame, videoInfo, session }) { return (

{rover.name}

); } function RoverRow({ roster, frames, videoSources, session }) { if (roster.length === 0) { return

No rovers registered.

; } return (
{roster.map((rover) => ( ))}
); } function SecondaryRow() { const roomRef = useRef(null); const [roomHeight, setRoomHeight] = useState(0); useEffect(() => { const target = roomRef.current; if (!target || typeof ResizeObserver === 'undefined') return undefined; const observer = new ResizeObserver((entries) => { const entry = entries[0]; if (!entry) return; const nextHeight = entry.contentRect?.height || 0; setRoomHeight(nextHeight); }); observer.observe(target); return () => observer.disconnect(); }, []); return (
); } function LogsRow() { return (
); } export default function SpectatorApp() { const { session } = useSession(); useSpectatorMode(); const frames = useTelemetryFrames(); const roster = session?.roster ?? []; const videoSources = useVideoRequests(roster.map((rover) => rover.id)); return (
); }