// Right Pane Tabs // Purpose: Defines the Right Pane Tabs 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 RoomCameraPanel from '../RoomCameraPanel/index.jsx'; import HomeAssistantControls from '../HomeAssistantControls/index.jsx'; import SettingsPanel from '../SettingsPanel/index.jsx'; import HelpPanel from '../HelpPanel/index.jsx'; import ChatPanel from '../ChatPanel/index.jsx'; import { LinkButtonsPanel, NicknameEntryPanel } from '../UserListPanel/index.jsx'; import ReplaySourcesPanel from '../ReplaySourcesPanel/index.jsx'; import Tabs, { Tab, TabList, TabPanel, TabPanels } from '../Tabs/index.jsx'; import TopDownMap from '../TopDownMap/index.jsx'; import DriveDockAction, { useDriveDockState } from '../DriveDockAction/index.jsx'; import { useTelemetryFrame } from '../../context/TelemetryContext.jsx'; import { useControlSystem } from '../../controls/index.js'; import RoverQueuesPanel from '../RoverQueuesPanel/index.jsx'; import RawUserPilePanel from '../RawUserPilePanel/index.jsx'; import { formatKeyLabel } from '../../controls/keymapUtils.js'; import NightVisionControl from '../NightVisionControl/index.jsx'; import HornControl from '../HornControl/index.jsx'; import CameraTiltControl from '../CameraTiltControl/index.jsx'; import VipPanel from '../VipPanel/index.jsx'; import { useSessionSelector } from '../../context/SessionContext.jsx'; import { useSettingsNamespace } from '../../settings/index.js'; import ButtonBoxPanel from '../ButtonBoxPanel/index.jsx'; import { useState } from 'react'; function TopDownMapPanel() { const { state: { roverId }, } = useControlSystem(); const frame = useTelemetryFrame(roverId); const sensors = frame?.sensors || {}; return (
); } function DriveDockPanel() { const { state: { roverId, keymap, camera, horn }, pipeline, actions: { setServoAngle, setNightVision, startHorn, stopHorn }, } = useControlSystem(); const driveDockState = useDriveDockState(roverId); const hideInlineControls = driveDockState.docked && !driveDockState.driving; const config = camera?.config; const cameraEnabled = Boolean(roverId && camera?.enabled && config); const nightVisionAvailable = Boolean(roverId && pipeline?.nightVision); const nightVisionState = pipeline?.nightVisionState; const hornAvailable = Boolean(roverId && pipeline?.horn); const hornBlocked = horn?.overheated; const min = typeof config?.minAngle === 'number' ? config.minAngle : -30; const max = typeof config?.maxAngle === 'number' ? config.maxAngle : 30; const value = typeof camera?.angle === 'number' ? camera.angle : typeof config?.homeAngle === 'number' ? config.homeAngle : (min + max) / 2; const nightVisionLabel = formatKeyLabel(keymap?.nightVisionToggle?.[0]); const hornLabel = formatKeyLabel(keymap?.hornHonk?.[0]); const upLabel = formatKeyLabel(keymap?.cameraUp?.[0]); const downLabel = formatKeyLabel(keymap?.cameraDown?.[0]); return (
{!hideInlineControls ? (
{nightVisionAvailable && ( )} {hornAvailable && ( )} {cameraEnabled && ( )}
) : null}
); } export default function RightPaneTabs({ layout, onOpenHelpOverlay }) { const [activeTab, setActiveTab] = useState('telemetry'); const session = useSessionSelector((state) => state.session); const { state: controlState } = useControlSystem(); const { value: vipAudio } = useSettingsNamespace('vipAudio', { openMicEnabled: false, pttMode: 'live' }); const vipDotClass = session?.isVerified ? 'bg-emerald-400' : 'bg-red-600'; const ownRoverId = String(session?.assignment?.roverId || '').trim(); const ownAudioForward = ownRoverId ? session?.audioForward?.[ownRoverId] : null; const pttActive = Boolean(controlState?.mic?.pttActive); const openMicEnabled = Boolean(vipAudio?.openMicEnabled); const pttMode = vipAudio?.pttMode === 'clip' ? 'clip' : 'live'; const vipMicActive = Boolean( ownRoverId && session?.isVerified && (pttMode === 'clip' ? pttActive : (openMicEnabled || pttActive)), ); const vipClipPlaying = Boolean( ownRoverId && session?.isVerified && pttMode === 'clip' && ownAudioForward?.source === 'upload' && ownAudioForward?.state === 'playing', ); return (
Controls VIP Help Settings
); }