// Hud Overlay // Purpose: Defines the Hud Overlay module and the local helpers/components used in this file. // Scope: Keeps behavior unchanged while isolating this concern into a clear, single-responsibility unit. import React from 'react'; import TopDownMap from '../TopDownMap/index.jsx'; import { roverNameChromeStyle } from '../../lib/roverColor.js'; function HudOverlay({ sensors, label, roverColor = null, status, audioStatus, levelStatus, layoutFormat = 'desktop', variant = 'default', driverLabel = null, showTopDown = false, mobileHud = false, mapPosition = 'top-center', turnTimerText = null, turnTimerFlashActive = false, labelScale = 1, }) { const isMobile = mobileHud; const portraitMobile = layoutFormat === 'mobile-portrait'; const statusTextClass = isMobile ? 'text-[0.45rem]' : 'text-[0.65rem]'; const statusPadClass = isMobile ? 'px-0.25 py-0.25' : 'px-1 py-0.5'; const labelPadClass = isMobile ? 'px-0.25 py-0.25' : 'px-0.5 py-0.5'; const labelTextClass = isMobile ? 'text-[0.55rem]' : 'text-[0.8rem]'; const statusPosClass = isMobile ? 'left-0.5 top-0.5' : 'left-1 top-1'; const timerTextClass = isMobile ? 'text-[0.5rem]' : 'text-[0.7rem]'; const timerPadClass = isMobile ? 'px-0.5 py-0.25' : 'px-1 py-0.5'; const telemetryPosClass = isMobile ? 'left-0.5 top-1/2' : 'left-1 top-1/2'; const labelPosClass = isMobile ? 'bottom-0.5' : 'bottom-0.5'; const labelWrapperStyle = { transform: `translateX(-50%) scale(${labelScale})`, transformOrigin: 'center bottom', }; const mapSize = '240px'; const mapScale = portraitMobile ? 0.3 : isMobile ? 0.33 : 0.7; const mapOpacity = isMobile ? 0.6 : 0.7; const mapStyle = { width: mapSize, height: mapSize, opacity: mapOpacity, transform: mapPosition === 'top-center' ? `translateX(-50%) scale(${mapScale})` : `scale(${mapScale})`, transformOrigin: mapPosition === 'bottom-left' ? 'bottom left' : mapPosition === 'top-center' ? 'top center' : 'top right', ...(mapPosition === 'bottom-left' ? { left: '0.25rem', bottom: '0.25rem' } : mapPosition === 'top-center' ? { left: '50%', top: '0.25rem' } : { right: '0.25rem', top: '0.25rem' }), }; if (variant === 'none') { return null; } if (variant === 'spectator') { const telemetryEntries = [ ['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 || '--'], ]; const docked = Boolean(sensors?.chargingSources?.homeBase); const chargingLabel = sensors?.chargingState?.label || ''; const charging = Boolean(chargingLabel && chargingLabel.toLowerCase() !== 'not charging'); const oiLabel = sensors?.oiMode?.label || 'Unknown'; const oiNormalized = oiLabel.toLowerCase(); const oiTone = oiNormalized === 'full' ? 'bg-emerald-500/80 text-emerald-50' : oiNormalized === 'safe' ? 'bg-amber-400/80 text-amber-950' : oiNormalized === 'passive' ? 'bg-slate-700/80 text-slate-100' : 'bg-slate-700/60 text-slate-200'; const dockTone = docked ? 'bg-emerald-500/80 text-emerald-50' : 'bg-slate-700/70 text-slate-200'; const chargingTone = charging ? 'bg-emerald-500/80 text-emerald-50' : docked ? 'bg-amber-400/80 text-amber-950' : 'bg-slate-700/70 text-slate-200'; return ( <>