goop converter

This commit is contained in:
legop3
2026-04-28 19:31:02 -04:00
parent 86eb7e628e
commit 07ad9ab253
63 changed files with 194 additions and 418 deletions
+23 -23
View File
@@ -1,7 +1,7 @@
import { useCallback, useEffect, useMemo, useState } from 'react';
import TelemetryPanel from './components/TelemetryPanel.jsx';
import ReplaySourcesPanel from './components/ReplaySourcesPanel.jsx';
import AlertFeed from './components/AlertFeed.jsx';
import TelemetryPanel from './components/TelemetryPanel/index.jsx';
import ReplaySourcesPanel from './components/ReplaySourcesPanel/index.jsx';
import AlertFeed from './components/AlertFeed/index.jsx';
import MobileControls, {
MobileActionsColumn,
MobileDriveColumn,
@@ -12,31 +12,31 @@ import {
GamepadInputManager,
useControlSystem,
} from './controls/index.js';
import RoomCameraPanel from './components/RoomCameraPanel.jsx';
import LogPanel from './components/LogPanel.jsx';
import DriverVideoPanel from './components/DriverVideoPanel.jsx';
import RightPaneTabs from './components/RightPaneTabs.jsx';
import ModeGateOverlay from './components/ModeGateOverlay.jsx';
import HomeAssistantControls from './components/HomeAssistantControls.jsx';
import TurnAlertListener from './components/TurnAlertListener.jsx';
import RawUserPilePanel from './components/RawUserPilePanel.jsx';
import ChatPanel from './components/ChatPanel.jsx';
import FullscreenPrompt from './components/FullscreenPrompt.jsx';
import FloatingFullscreenButton from './components/FloatingFullscreenButton.jsx';
import RoomCameraPanel from './components/RoomCameraPanel/index.jsx';
import LogPanel from './components/LogPanel/index.jsx';
import DriverVideoPanel from './components/DriverVideoPanel/index.jsx';
import RightPaneTabs from './components/RightPaneTabs/index.jsx';
import ModeGateOverlay from './components/ModeGateOverlay/index.jsx';
import HomeAssistantControls from './components/HomeAssistantControls/index.jsx';
import TurnAlertListener from './components/TurnAlertListener/index.jsx';
import RawUserPilePanel from './components/RawUserPilePanel/index.jsx';
import ChatPanel from './components/ChatPanel/index.jsx';
import FullscreenPrompt from './components/FullscreenPrompt/index.jsx';
import FloatingFullscreenButton from './components/FloatingFullscreenButton/index.jsx';
import { useFullscreenPrompt } from './hooks/useFullscreenPrompt.js';
import { useSettingsNamespace } from './settings/index.js';
import HelpOverlay from './components/HelpOverlay.jsx';
import HelpPanel from './components/HelpPanel.jsx';
import SettingsPanel from './components/SettingsPanel.jsx';
import Tabs, { Tab, TabList, TabPanel, TabPanels } from './components/Tabs.jsx';
import HelpOverlay from './components/HelpOverlay/index.jsx';
import HelpPanel from './components/HelpPanel/index.jsx';
import SettingsPanel from './components/SettingsPanel/index.jsx';
import Tabs, { Tab, TabList, TabPanel, TabPanels } from './components/Tabs/index.jsx';
import useDefaultNickname from './hooks/useDefaultNickname.js';
import useUserIdentitySync from './hooks/useUserIdentitySync.js';
import CommunityGoalBanner from './components/CommunityGoalBanner.jsx';
import RoverQueuesPanel from './components/RoverQueuesPanel.jsx';
import VipPanel from './components/VipPanel.jsx';
import CommunityGoalBanner from './components/CommunityGoalBanner/index.jsx';
import RoverQueuesPanel from './components/RoverQueuesPanel/index.jsx';
import VipPanel from './components/VipPanel/index.jsx';
import { useSessionSelector } from './context/SessionContext.jsx';
import ButtonBoxPanel from './components/ButtonBoxPanel.jsx';
import RewardRunOverlay from './components/RewardRunOverlay.jsx';
import ButtonBoxPanel from './components/ButtonBoxPanel/index.jsx';
import RewardRunOverlay from './components/RewardRunOverlay/index.jsx';
function useLayoutMode() {
const [mode, setMode] = useState(() => {
@@ -1,7 +1,7 @@
// Admin control panel main composition and action handlers.
import { useEffect, useMemo, useState } from 'react';
import { useSession } from '../../context/SessionContext.jsx';
import RoverRoster from '../RoverRoster.jsx';
import RoverRoster from '../RoverRoster/index.jsx';
import LlmCommentaryPanel from './LlmCommentaryPanel.jsx';
import ReplaySnapshotHealth from './ReplaySnapshotHealth.jsx';
import AdminIpLogPanel from './AdminIpLogPanel.jsx';
@@ -1,6 +1,6 @@
// Admin LLM commentary monitor panel.
import { useState } from 'react';
import ChatMessageRow from '../ChatMessageRow.jsx';
import ChatMessageRow from '../ChatMessageRow/index.jsx';
import { buildLlmLargeIndicatorFromState, buildLlmConversationRowsFromMessages } from './llmHelpers.js';
export default function LlmCommentaryPanel({ state, onClearHistory, clearingHistory }) {
@@ -1,9 +1,9 @@
import { useEffect, useMemo, useState } from 'react';
import { useSessionActions, useSessionSelector } from '../context/SessionContext.jsx';
import { useSocket } from '../context/SocketContext.jsx';
import ChatMessageRow from './ChatMessageRow.jsx';
import ChatTypingRow from './ChatTypingRow.jsx';
import ButtonBoxTile from './ButtonBoxTile.jsx';
import { useSessionActions, useSessionSelector } from '../../context/SessionContext.jsx';
import { useSocket } from '../../context/SocketContext.jsx';
import ChatMessageRow from '../ChatMessageRow/index.jsx';
import ChatTypingRow from '../ChatTypingRow/index.jsx';
import ButtonBoxTile from '../ButtonBoxTile/index.jsx';
const LIFETIME_MS = 3000;
const BUTTONBOX_LIFETIME_MS = 3500;
@@ -1,5 +1,5 @@
import { useState } from 'react';
import { useSessionActions } from '../context/SessionContext.jsx';
import { useSessionActions } from '../../context/SessionContext.jsx';
export default function AuthPanel() {
const { login, setRole } = useSessionActions();
@@ -1,4 +1,4 @@
import { WARN_DISPLAY_PERCENT } from '../lib/battery.js';
import { WARN_DISPLAY_PERCENT } from '../../lib/battery.js';
const WARN_FLASH_MS = 1600;
const URGENT_FLASH_MS = 800;
@@ -1,9 +1,9 @@
import { useEffect, useMemo, useRef, useState } from 'react';
import { useSession } from '../context/SessionContext.jsx';
import { useSocket } from '../context/SocketContext.jsx';
import { useSettingsNamespace } from '../settings/index.js';
import { AUDIO_SETTINGS_DEFAULTS } from '../settings/namespaces.js';
import ButtonBoxTile from './ButtonBoxTile.jsx';
import { useSession } from '../../context/SessionContext.jsx';
import { useSocket } from '../../context/SocketContext.jsx';
import { useSettingsNamespace } from '../../settings/index.js';
import { AUDIO_SETTINGS_DEFAULTS } from '../../settings/namespaces.js';
import ButtonBoxTile from '../ButtonBoxTile/index.jsx';
const FLASH_MS = 420;
const REWARD_FLASH_MS = 1200;
-92
View File
@@ -1,92 +0,0 @@
import { useControlSystem } from '../controls/index.js';
import { formatKeyLabel } from '../controls/keymapUtils.js';
import NightVisionControl from './NightVisionControl.jsx';
import HornControl from './HornControl.jsx';
import CameraTiltControl from './CameraTiltControl.jsx';
const SLIDER_THROTTLE_MS = 150;
export default function CameraServoPanel() {
const {
state: { roverId, camera, keymap, horn },
pipeline,
actions: { setServoAngle, setNightVision, startHorn, stopHorn },
} = useControlSystem();
const config = camera?.config;
const enabled = Boolean(roverId && camera?.enabled && config);
const nightVisionAvailable = Boolean(roverId && pipeline?.nightVision);
const nightVisionState = pipeline?.nightVisionState;
const hornAvailable = Boolean(roverId && pipeline?.horn);
const nightVisionKey = formatKeyLabel(keymap?.nightVisionToggle?.[0]);
const hornKey = formatKeyLabel(keymap?.hornHonk?.[0]);
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;
if (!enabled && !nightVisionAvailable && !hornAvailable) return null;
const handleNightVisionToggle = (nextOn) => {
if (!nightVisionAvailable) return;
setNightVision(nextOn);
};
return (
<section className="panel-section space-y-0.5 text-base">
{nightVisionAvailable && (
<NightVisionControl
nightVisionOn={nightVisionState?.nightVisionOn}
disabled={!roverId}
onToggle={handleNightVisionToggle}
keyLabel={nightVisionKey}
/>
)}
{hornAvailable && (
<HornControl
disabled={!roverId || hornBlocked}
onStart={startHorn}
onStop={stopHorn}
keyLabel={hornKey}
active={horn?.active}
heat={horn?.heat}
/>
)}
{enabled && (
<CameraTiltControl
value={value}
min={min}
max={max}
step={0.5}
onChange={setServoAngle}
throttleMs={SLIDER_THROTTLE_MS}
className="space-y-0.5"
labelRowClass="text-sm text-slate-300"
valueClass="font-mono text-sm text-slate-100"
sliderClass="w-full"
accentClass="accent-emerald-400"
endpointClass="text-xs text-slate-400"
endpointLabelClass=""
/>
)}
{/* <div className="flex gap-0.5 text-sm">
<button type="button" className="flex-1 button-dark" onClick={() => handleNudge(-1)}>
Tilt Down
</button>
<button type="button" className="flex-1 button-dark" onClick={() => goServoHome()}>
Center
</button>
<button type="button" className="flex-1 button-dark" onClick={() => handleNudge(1)}>
Tilt Up
</button>
</div> */}
{/* <p className="text-xs text-slate-400">
Step: {formatDegrees(step)} · Pin GPIO {config?.pin}
</p> */}
</section>
);
}
@@ -1,5 +1,5 @@
import { FaDiscord } from 'react-icons/fa';
import { roverBadgeStyle } from '../lib/roverColor.js';
import { roverBadgeStyle } from '../../lib/roverColor.js';
function roleColors(role) {
switch (role) {
@@ -1,9 +1,9 @@
import { useEffect, useMemo, useRef, useState } from 'react';
import { useChat } from '../context/ChatContext.jsx';
import { useSessionSelector } from '../context/SessionContext.jsx';
import { useSettingsNamespace } from '../settings/index.js';
import ChatMessageRow from './ChatMessageRow.jsx';
import ChatTypingRow from './ChatTypingRow.jsx';
import { useChat } from '../../context/ChatContext.jsx';
import { useSessionSelector } from '../../context/SessionContext.jsx';
import { useSettingsNamespace } from '../../settings/index.js';
import ChatMessageRow from '../ChatMessageRow/index.jsx';
import ChatTypingRow from '../ChatTypingRow/index.jsx';
const FLITE_VOICES = ['kal', 'rms', 'slt', 'ksp', 'bdl'];
const ESPEAK_PITCHES = Array.from({ length: 10 }, (_, idx) => idx * 10);
@@ -1,4 +1,4 @@
import { ChatIdentity, chatRowClass } from './ChatMessageRow.jsx';
import { ChatIdentity, chatRowClass } from '../ChatMessageRow/index.jsx';
export default function ChatTypingRow({ message }) {
return (
@@ -1,5 +1,5 @@
import { useEffect, useMemo, useRef, useState } from 'react';
import { useSession } from '../context/SessionContext.jsx';
import { useSession } from '../../context/SessionContext.jsx';
const MOBILE_DISMISS_MS = 10000;
const MAX_FONT_PX = 28;
-134
View File
@@ -1,134 +0,0 @@
import { useMemo, useState } from 'react';
import { useSession } from '../context/SessionContext.jsx';
import { useTelemetryFrame } from '../context/TelemetryContext.jsx';
import { useControlSystem } from '../controls/index.js';
import { formatKeyLabel } from '../controls/keymapUtils.js';
import TopDownMap from './TopDownMap/index.jsx';
import RoverRoster from './RoverRoster.jsx';
import DriveDockAction, { useDriveDockState } from './DriveDockAction/index.jsx';
import NightVisionControl from './NightVisionControl.jsx';
import HornControl from './HornControl.jsx';
import CameraTiltControl from './CameraTiltControl.jsx';
export function RoverRosterPanel({ title = 'Rovers' }) {
const { session, requestControl } = useSession();
const [pending, setPending] = useState({});
const canRequest = useMemo(() => session?.role && session.role !== 'spectator', [session?.role]);
async function handleRequest(targetRoverId) {
if (!targetRoverId) return;
setPending((prev) => ({ ...prev, [targetRoverId]: true }));
try {
await requestControl(targetRoverId);
} catch (err) {
alert(err.message);
} finally {
setPending((prev) => ({ ...prev, [targetRoverId]: false }));
}
}
return (
<RoverRoster
title={title}
roster={session?.roster ?? []}
renderActions={(rover) =>
canRequest ? (
<button
type="button"
onClick={() => handleRequest(rover.id)}
disabled={pending[rover.id]}
className="button-dark disabled:opacity-40"
>
{pending[rover.id] ? '...' : 'request'}
</button>
) : null
}
/>
);
}
export default function ControlSummary() {
const {
state: { roverId, keymap, camera, horn },
pipeline,
actions: { setServoAngle, setNightVision, startHorn, stopHorn },
} = useControlSystem();
const frame = useTelemetryFrame(roverId);
const sensors = frame?.sensors || {};
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 (
<section className="panel-section">
<div className="grid items-stretch gap-0.5 md:grid-cols-[minmax(0,1.1fr)_minmax(0,1fr)] md:min-h-[18rem]">
<div className="flex h-full w-full items-stretch justify-center">
<div className="aspect-square h-full w-full">
<TopDownMap sensors={sensors} />
</div>
</div>
<div className="grid h-full min-h-0 grid-rows-[minmax(0,1fr)_auto] gap-0.5">
<DriveDockAction layout="desktop" expand driveDockState={driveDockState} />
{!hideInlineControls ? (
<div className="surface space-y-0.5 p-0 text-sm text-slate-200">
{nightVisionAvailable && (
<NightVisionControl
nightVisionOn={nightVisionState?.nightVisionOn}
disabled={!roverId}
onToggle={setNightVision}
keyLabel={nightVisionLabel}
/>
)}
{hornAvailable && (
<HornControl
disabled={!roverId || hornBlocked}
onStart={startHorn}
onStop={stopHorn}
keyLabel={hornLabel}
active={horn?.active}
heat={horn?.heat}
/>
)}
{cameraEnabled && (
<CameraTiltControl
value={value}
min={min}
max={max}
onChange={setServoAngle}
keyDownLabel={downLabel}
keyUpLabel={upLabel}
className="space-y-0.5 px-1 py-1"
labelRowClass="text-xs text-slate-300"
labelClass=""
valueClass="font-mono text-slate-100"
sliderClass="w-full"
accentClass="accent-emerald-400"
endpointClass="text-[0.7rem] text-slate-400"
/>
)}
</div>
) : null}
</div>
</div>
</section>
);
}
@@ -1,5 +1,5 @@
import { useEffect } from 'react';
import { useSessionSelector } from '../context/SessionContext.jsx';
import { useSessionSelector } from '../../context/SessionContext.jsx';
function getRandomElement(elements) {
if (!elements || !elements.length) return null;
@@ -1,10 +1,10 @@
import { useEffect, useMemo, useRef, useState } from 'react';
import { useSessionSelector } from '../context/SessionContext.jsx';
import { useTelemetryFrame } from '../context/TelemetryContext.jsx';
import { useVideoRequests } from '../hooks/useVideoRequests.js';
import { useRoverSnapshots } from '../hooks/useRoverSnapshots.js';
import { useControlSystem } from '../controls/index.js';
import VideoTile from './VideoTile.jsx';
import { useSessionSelector } from '../../context/SessionContext.jsx';
import { useTelemetryFrame } from '../../context/TelemetryContext.jsx';
import { useVideoRequests } from '../../hooks/useVideoRequests.js';
import { useRoverSnapshots } from '../../hooks/useRoverSnapshots.js';
import { useControlSystem } from '../../controls/index.js';
import VideoTile from '../VideoTile/index.jsx';
export default function DriverVideoPanel({layoutFormat = 'desktop'}) {
const session = useSessionSelector((state) => state.session);
@@ -1,6 +1,6 @@
import { useMemo } from 'react';
import { formatKeyLabel } from '../controls/keymapUtils.js';
import { getHelpContent } from '../help/content.js';
import { formatKeyLabel } from '../../controls/keymapUtils.js';
import { getHelpContent } from '../../help/content.js';
function KeyPill({ actionId, keymap }) {
const value = keymap?.[actionId]?.[0] ?? '';
@@ -1,5 +1,5 @@
import { useControlSystem } from '../controls/index.js';
import HelpContentView from './HelpContentView.jsx';
import { useControlSystem } from '../../controls/index.js';
import HelpContentView from '../HelpContentView/index.jsx';
export default function HelpOverlay({ visible, layout, onClose, showOnLoad, onToggleShowOnLoad }) {
if (!visible) return null;
@@ -1,5 +1,5 @@
import { useControlSystem } from '../controls/index.js';
import HelpContentView from './HelpContentView.jsx';
import { useControlSystem } from '../../controls/index.js';
import HelpContentView from '../HelpContentView/index.jsx';
export default function HelpPanel({ layout, onOpenOverlay }) {
const { state } = useControlSystem();
@@ -1,7 +1,7 @@
import { useEffect, useMemo, useRef, useState } from 'react';
import { useSessionActions, useSessionSelector } from '../context/SessionContext.jsx';
import { useControlSystem } from '../controls/index.js';
import { formatKeyLabel } from '../controls/keymapUtils.js';
import { useSessionActions, useSessionSelector } from '../../context/SessionContext.jsx';
import { useControlSystem } from '../../controls/index.js';
import { formatKeyLabel } from '../../controls/keymapUtils.js';
function StatusBadge({ label, tone = 'muted' }) {
const styles =
@@ -1,6 +1,6 @@
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useSettingsNamespace } from '../settings/index.js';
import { HORN_SETTINGS_DEFAULTS } from '../settings/namespaces.js';
import { useSettingsNamespace } from '../../settings/index.js';
import { HORN_SETTINGS_DEFAULTS } from '../../settings/namespaces.js';
function clampFreq(value) {
const num = Number(value);
@@ -1,10 +1,10 @@
import { useMemo, useState, useEffect, useCallback } from 'react';
import { useControlSystem } from '../controls/index.js';
import { DEFAULT_KEYMAP } from '../controls/constants.js';
import { canonicalizeKeyInput, formatKeyLabel } from '../controls/keymapUtils.js';
import { setKeyboardCaptureLocked } from '../controls/inputs/keyboardCaptureLock.js';
import { useSettingsNamespace } from '../settings/index.js';
import { INPUT_SETTINGS_DEFAULTS } from '../settings/namespaces.js';
import { useControlSystem } from '../../controls/index.js';
import { DEFAULT_KEYMAP } from '../../controls/constants.js';
import { canonicalizeKeyInput, formatKeyLabel } from '../../controls/keymapUtils.js';
import { setKeyboardCaptureLocked } from '../../controls/inputs/keyboardCaptureLock.js';
import { useSettingsNamespace } from '../../settings/index.js';
import { INPUT_SETTINGS_DEFAULTS } from '../../settings/namespaces.js';
const KEY_ACTIONS = [
{ id: 'driveForward', label: 'Drive Forward', group: 'Driving' },
@@ -1,4 +1,4 @@
import { useSessionSelector } from '../context/SessionContext.jsx';
import { useSessionSelector } from '../../context/SessionContext.jsx';
export default function LogPanel() {
const logs = useSessionSelector((state) => state.logs);
@@ -3,9 +3,9 @@ import { useCallback, useEffect, useRef } from 'react';
import { useControlSystem } from '../../controls/index.js';
import { clampUnit } from '../../controls/controlMath.js';
import DriveDockAction, { useDriveDockState } from '../DriveDockAction/index.jsx';
import NightVisionControl from '../NightVisionControl.jsx';
import HornControl from '../HornControl.jsx';
import CameraTiltControl from '../CameraTiltControl.jsx';
import NightVisionControl from '../NightVisionControl/index.jsx';
import HornControl from '../HornControl/index.jsx';
import CameraTiltControl from '../CameraTiltControl/index.jsx';
import FloatingJoystick from './FloatingJoystick.jsx';
import MobileAuxButton from './MobileAuxButton.jsx';
import {
@@ -1,9 +1,9 @@
import { useEffect, useMemo, useState } from 'react';
import AuthPanel from './AuthPanel.jsx';
import { useSessionSelector } from '../context/SessionContext.jsx';
import SocialButton from './SocialButton.jsx';
import ChatPanel from './ChatPanel.jsx';
import NicknameForm from './NicknameForm.jsx';
import AuthPanel from '../AuthPanel/index.jsx';
import { useSessionSelector } from '../../context/SessionContext.jsx';
import SocialButton from '../SocialButton/index.jsx';
import ChatPanel from '../ChatPanel/index.jsx';
import NicknameForm from '../NicknameForm/index.jsx';
const PRIVILEGED_ROLES = new Set(['admin', 'lockdown', 'lockdown-admin']);
const LOCKDOWN_ROLES = new Set(['lockdown', 'lockdown-admin']);
@@ -1,6 +1,6 @@
import { useEffect, useState } from 'react';
import { useSession } from '../context/SessionContext.jsx';
import { useSettingsNamespace } from '../settings/index.js';
import { useSession } from '../../context/SessionContext.jsx';
import { useSettingsNamespace } from '../../settings/index.js';
export default function NicknameForm({ compact = false }) {
const { session, setNickname } = useSession();
@@ -1,6 +1,6 @@
import { useMemo } from 'react';
import { useControlSystem } from '../controls/index.js';
import { OVERCURRENT_GROUPS } from '../controls/overcurrentLimiter.js';
import { useControlSystem } from '../../controls/index.js';
import { OVERCURRENT_GROUPS } from '../../controls/overcurrentLimiter.js';
const GROUP_LABELS = {
drive: 'Drive wheels',
@@ -1,7 +1,7 @@
import { useMemo } from 'react';
import { useSession } from '../context/SessionContext.jsx';
import NicknameForm from './NicknameForm.jsx';
import SocialButtonsGrid from './SocialButtonsGrid.jsx';
import { useSession } from '../../context/SessionContext.jsx';
import NicknameForm from '../NicknameForm/index.jsx';
import SocialButtonsGrid from '../SocialButtonsGrid/index.jsx';
function roleColors(role) {
switch (role) {
@@ -1,7 +1,7 @@
import { useEffect, useMemo, useState } from 'react';
import { useSession } from '../context/SessionContext.jsx';
import { useSettingsNamespace } from '../settings/index.js';
import { roverNameChromeStyle } from '../lib/roverColor.js';
import { useSession } from '../../context/SessionContext.jsx';
import { useSettingsNamespace } from '../../settings/index.js';
import { roverNameChromeStyle } from '../../lib/roverColor.js';
function normalizeSources(list = []) {
return list
@@ -1,7 +1,7 @@
import { useEffect, useRef, useState } from 'react';
import { useSocket } from '../context/SocketContext.jsx';
import { useSettingsNamespace } from '../settings/index.js';
import { AUDIO_SETTINGS_DEFAULTS } from '../settings/namespaces.js';
import { useSocket } from '../../context/SocketContext.jsx';
import { useSettingsNamespace } from '../../settings/index.js';
import { AUDIO_SETTINGS_DEFAULTS } from '../../settings/namespaces.js';
const SHOW_MS = 3200;
@@ -1,25 +1,25 @@
import RoomCameraPanel from './RoomCameraPanel.jsx';
import HomeAssistantControls from './HomeAssistantControls.jsx';
import SettingsPanel from './SettingsPanel.jsx';
import HelpPanel from './HelpPanel.jsx';
import ChatPanel from './ChatPanel.jsx';
import { LinkButtonsPanel, NicknameEntryPanel } from './UserListPanel.jsx';
import ReplaySourcesPanel from './ReplaySourcesPanel.jsx';
import Tabs, { Tab, TabList, TabPanel, TabPanels } from './Tabs.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.jsx';
import RawUserPilePanel from './RawUserPilePanel.jsx';
import { formatKeyLabel } from '../controls/keymapUtils.js';
import NightVisionControl from './NightVisionControl.jsx';
import HornControl from './HornControl.jsx';
import CameraTiltControl from './CameraTiltControl.jsx';
import VipPanel from './VipPanel.jsx';
import { useSessionSelector } from '../context/SessionContext.jsx';
import { useSettingsNamespace } from '../settings/index.js';
import ButtonBoxPanel from './ButtonBoxPanel.jsx';
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';
function TopDownMapPanel() {
const {
@@ -1,8 +1,8 @@
import { useEffect, useState } from 'react';
import { useSession } from '../context/SessionContext.jsx';
import { useSettingsNamespace } from '../settings/index.js';
import { useRoomCameraSnapshots } from '../hooks/useRoomCameraSnapshots.js';
import RoomCameraFeed from './RoomCameraFeed.jsx';
import { useSession } from '../../context/SessionContext.jsx';
import { useSettingsNamespace } from '../../settings/index.js';
import { useRoomCameraSnapshots } from '../../hooks/useRoomCameraSnapshots.js';
import RoomCameraFeed from '../RoomCameraFeed/index.jsx';
function EmptyState() {
return (
@@ -1,6 +1,6 @@
import { useEffect, useMemo, useState } from 'react';
import { useSessionActions, useSessionSelector } from '../context/SessionContext.jsx';
import { roverNameChromeStyle } from '../lib/roverColor.js';
import { useSessionActions, useSessionSelector } from '../../context/SessionContext.jsx';
import { roverNameChromeStyle } from '../../lib/roverColor.js';
function classNames(...values) {
return values.filter(Boolean).join(' ');
@@ -1,4 +1,4 @@
import { roverNameChromeStyle } from '../lib/roverColor.js';
import { roverNameChromeStyle } from '../../lib/roverColor.js';
function classNames(...values) {
return values.filter(Boolean).join(' ');
@@ -1,5 +1,5 @@
import { useMemo } from 'react';
import { useSession } from '../context/SessionContext.jsx';
import { useSession } from '../../context/SessionContext.jsx';
export default function SessionSnapshot() {
const { session } = useSession();
@@ -1,17 +1,17 @@
import { useMemo } from 'react';
import { useControlSystem } from '../controls/index.js';
import AuthPanel from './AuthPanel.jsx';
import AdminPanel from './AdminPanel/index.jsx';
import KeymapSettings from './KeymapSettings.jsx';
import GamepadMappingSettings from './GamepadMappingSettings/index.jsx';
import OvercurrentLimiterPanel from './OvercurrentLimiterPanel.jsx';
import Tabs, { Tab, TabList, TabPanel, TabPanels } from './Tabs.jsx';
import SessionSnapshot from './SessionSnapshot.jsx';
import SocketLogPanel from './SocketLogPanel.jsx';
import { useHudMapSetting } from '../hooks/useHudMapSetting.js';
import { useSettingsNamespace } from '../settings/index.js';
import { useSocket } from '../context/SocketContext.jsx';
import { AUDIO_SETTINGS_DEFAULTS } from '../settings/namespaces.js';
import { useControlSystem } from '../../controls/index.js';
import AuthPanel from '../AuthPanel/index.jsx';
import AdminPanel from '../AdminPanel/index.jsx';
import KeymapSettings from '../KeymapSettings/index.jsx';
import GamepadMappingSettings from '../GamepadMappingSettings/index.jsx';
import OvercurrentLimiterPanel from '../OvercurrentLimiterPanel/index.jsx';
import Tabs, { Tab, TabList, TabPanel, TabPanels } from '../Tabs/index.jsx';
import SessionSnapshot from '../SessionSnapshot/index.jsx';
import SocketLogPanel from '../SocketLogPanel/index.jsx';
import { useHudMapSetting } from '../../hooks/useHudMapSetting.js';
import { useSettingsNamespace } from '../../settings/index.js';
import { useSocket } from '../../context/SocketContext.jsx';
import { AUDIO_SETTINGS_DEFAULTS } from '../../settings/namespaces.js';
const manualTabs = [
{ key: 'start', label: 'Start OI' },
@@ -1,5 +1,5 @@
import { useSession } from '../context/SessionContext.jsx';
import SocialButton from './SocialButton.jsx';
import { useSession } from '../../context/SessionContext.jsx';
import SocialButton from '../SocialButton/index.jsx';
function normalizeSocials(session) {
const configured = Array.isArray(session?.socials) ? session.socials : null;
@@ -1,5 +1,5 @@
import { useEffect, useMemo, useState } from 'react';
import { useSocket } from '../context/SocketContext.jsx';
import { useSocket } from '../../context/SocketContext.jsx';
const MAX_ENTRIES = 300;
const MAX_PAYLOAD_LENGTH = 240;
@@ -1,7 +1,7 @@
import { useMemo } from 'react';
import { useSessionSelector } from '../context/SessionContext.jsx';
import { useTelemetryFrame } from '../context/TelemetryContext.jsx';
import { useDockIr } from '../hooks/useDockIr.js';
import { useSessionSelector } from '../../context/SessionContext.jsx';
import { useTelemetryFrame } from '../../context/TelemetryContext.jsx';
import { useDockIr } from '../../hooks/useDockIr.js';
function formatMetric(value, fallback = '--') {
if (value == null || value === '') return fallback;
@@ -1,8 +1,8 @@
import { useEffect, useMemo, useRef } from 'react';
import turnSound from '../assets/turn_alert.mp3';
import { useSessionActions, useSessionSelector } from '../context/SessionContext.jsx';
import { useSettingsNamespace } from '../settings/index.js';
import { AUDIO_SETTINGS_DEFAULTS } from '../settings/namespaces.js';
import turnSound from '../../assets/turn_alert.mp3';
import { useSessionActions, useSessionSelector } from '../../context/SessionContext.jsx';
import { useSettingsNamespace } from '../../settings/index.js';
import { AUDIO_SETTINGS_DEFAULTS } from '../../settings/namespaces.js';
function useAudio(src, volume = 1) {
const audioRef = useRef(null);
@@ -1,8 +1,8 @@
import { useEffect, useMemo, useState, useCallback } from 'react';
import { useSession } from '../context/SessionContext.jsx';
import NicknameForm from './NicknameForm.jsx';
import SocialButtonsGrid from './SocialButtonsGrid.jsx';
import { roverBadgeStyle, roverNameChromeStyle } from '../lib/roverColor.js';
import { useSession } from '../../context/SessionContext.jsx';
import NicknameForm from '../NicknameForm/index.jsx';
import SocialButtonsGrid from '../SocialButtonsGrid/index.jsx';
import { roverBadgeStyle, roverNameChromeStyle } from '../../lib/roverColor.js';
export function NicknameEntryPanel({ compact = false }) {
return (
@@ -1,25 +1,25 @@
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { WhepPlayer } from '../lib/whepPlayer.js';
import { useHudMapSetting } from '../hooks/useHudMapSetting.js';
import { useSessionSelector } from '../context/SessionContext.jsx';
import { useSettingsNamespace } from '../settings/index.js';
import { AUDIO_SETTINGS_DEFAULTS } from '../settings/namespaces.js';
import SocialButton from './SocialButton.jsx';
import BatteryBar from './BatteryBar.jsx';
import { buildBatteryVisual } from '../lib/battery.js';
import TurnCueOverlay from './VideoTile/TurnCueOverlay.jsx';
import HudOverlay from './VideoTile/HudOverlay.jsx';
import OvercurrentOverlay from './VideoTile/OvercurrentOverlay.jsx';
import LowBatteryOverlay from './VideoTile/LowBatteryOverlay.jsx';
import LightBumpBars from './VideoTile/LightBumpBars.jsx';
import HudChatInput from './VideoTile/HudChatInput.jsx';
import { WhepPlayer } from '../../lib/whepPlayer.js';
import { useHudMapSetting } from '../../hooks/useHudMapSetting.js';
import { useSessionSelector } from '../../context/SessionContext.jsx';
import { useSettingsNamespace } from '../../settings/index.js';
import { AUDIO_SETTINGS_DEFAULTS } from '../../settings/namespaces.js';
import SocialButton from '../SocialButton/index.jsx';
import BatteryBar from '../BatteryBar/index.jsx';
import { buildBatteryVisual } from '../../lib/battery.js';
import TurnCueOverlay from './TurnCueOverlay.jsx';
import HudOverlay from './HudOverlay.jsx';
import OvercurrentOverlay from './OvercurrentOverlay.jsx';
import LowBatteryOverlay from './LowBatteryOverlay.jsx';
import LightBumpBars from './LightBumpBars.jsx';
import HudChatInput from './HudChatInput.jsx';
import {
RESTART_DELAY_MS,
UNMUTE_RETRY_MS,
AUDIO_RETRY_MS,
BRUSH_CURRENT_THRESHOLD_MA,
DUCK_RELEASE_FADE_MS,
} from './VideoTile/constants.js';
} from './constants.js';
export default function VideoTile({
sessionInfo,
@@ -1,12 +1,12 @@
import { useMemo, useState } from 'react';
import { useSession } from '../context/SessionContext.jsx';
import { useSettingsNamespace } from '../settings/index.js';
import { COOKIE_KEY_REGEX, flowWrapClass } from './vip/constants.js';
import VipAudioUploadCard from './vip/VipAudioUploadCard/index.jsx';
import VipVerificationCard from './vip/VipVerificationCard.jsx';
import VipIdentityCard from './vip/VipIdentityCard.jsx';
import VipPrivateRoverAccessCard from './vip/VipPrivateRoverAccessCard.jsx';
import VipNeatoCard from './vip/VipNeatoCard.jsx';
import { useSession } from '../../context/SessionContext.jsx';
import { useSettingsNamespace } from '../../settings/index.js';
import { COOKIE_KEY_REGEX, flowWrapClass } from '../vip/constants.js';
import VipAudioUploadCard from '../vip/VipAudioUploadCard/index.jsx';
import VipVerificationCard from '../vip/VipVerificationCard.jsx';
import VipIdentityCard from '../vip/VipIdentityCard.jsx';
import VipPrivateRoverAccessCard from '../vip/VipPrivateRoverAccessCard.jsx';
import VipNeatoCard from '../vip/VipNeatoCard.jsx';
export default function VipPanel() {
const {
@@ -1,5 +1,5 @@
import { useState } from 'react';
import NicknameForm from '../NicknameForm.jsx';
import NicknameForm from '../NicknameForm/index.jsx';
import { flowWrapClass, innerFlowClass, fieldClass } from './constants.js';
export default function VipVerificationCard({
+1 -1
View File
@@ -10,7 +10,7 @@ import { ChatProvider } from './context/ChatContext.jsx'
import SpectatorApp from './spectate/SpectatorApp.jsx'
import MiniSummaryApp from './mini/MiniSummaryApp.jsx'
import { SettingsProvider } from './settings/index.js'
import DeterrenceChaos from './components/DeterrenceChaos.jsx'
import DeterrenceChaos from './components/DeterrenceChaos/index.jsx'
createRoot(document.getElementById('root')).render(
<StrictMode>
@@ -1,6 +1,6 @@
// Top-level mini summary app shell with hard-refresh timer and alerts.
import { useEffect } from 'react';
import AlertFeed from '../../components/AlertFeed.jsx';
import AlertFeed from '../../components/AlertFeed/index.jsx';
import MiniSummaryContent from './MiniSummaryContent.jsx';
import { HARD_REFRESH_MS } from './constants.js';
@@ -6,7 +6,7 @@ import { useVideoRequests } from '../../hooks/useVideoRequests.js';
import { useRoverSnapshots } from '../../hooks/useRoverSnapshots.js';
import { useSpectatorMode } from '../../hooks/useSpectatorMode.js';
import useDefaultNickname from '../../hooks/useDefaultNickname.js';
import VideoTile from '../../components/VideoTile.jsx';
import VideoTile from '../../components/VideoTile/index.jsx';
import FitViewportFrame from './components/FitViewportFrame.jsx';
import InfoColumn from './components/InfoColumn.jsx';
import { ROTATE_MS } from './constants.js';
@@ -1,6 +1,6 @@
// Rover information and optional preview column for mini summary UI.
import VideoTile from '../../../components/VideoTile.jsx';
import BatteryBar from '../../../components/BatteryBar.jsx';
import VideoTile from '../../../components/VideoTile/index.jsx';
import BatteryBar from '../../../components/BatteryBar/index.jsx';
import { roverNameChromeStyle } from '../../../lib/roverColor.js';
import { getBatteryVisual } from '../utils.js';
import AutoFitText from './AutoFitText.jsx';
@@ -5,13 +5,13 @@ import { useVideoRequests } from '../../hooks/useVideoRequests.js';
import { useRoverSnapshots } from '../../hooks/useRoverSnapshots.js';
import { useSpectatorMode } from '../../hooks/useSpectatorMode.js';
import useDefaultNickname from '../../hooks/useDefaultNickname.js';
import ChatPanel from '../../components/ChatPanel.jsx';
import AlertFeed from '../../components/AlertFeed.jsx';
import CommunityGoalBanner from '../../components/CommunityGoalBanner.jsx';
import RoverQueuesPanel from '../../components/RoverQueuesPanel.jsx';
import RawUserPilePanel from '../../components/RawUserPilePanel.jsx';
import ButtonBoxPanel from '../../components/ButtonBoxPanel.jsx';
import RewardRunOverlay from '../../components/RewardRunOverlay.jsx';
import ChatPanel from '../../components/ChatPanel/index.jsx';
import AlertFeed from '../../components/AlertFeed/index.jsx';
import CommunityGoalBanner from '../../components/CommunityGoalBanner/index.jsx';
import RoverQueuesPanel from '../../components/RoverQueuesPanel/index.jsx';
import RawUserPilePanel from '../../components/RawUserPilePanel/index.jsx';
import ButtonBoxPanel from '../../components/ButtonBoxPanel/index.jsx';
import RewardRunOverlay from '../../components/RewardRunOverlay/index.jsx';
import usePortraitLayout from './hooks/usePortraitLayout.js';
import RoverRow from './components/RoverRow.jsx';
import SecondaryRow from './components/SecondaryRow.jsx';
@@ -1,5 +1,5 @@
// Log panel wrapper row for spectator layout.
import LogPanel from '../../../components/LogPanel.jsx';
import LogPanel from '../../../components/LogPanel/index.jsx';
export default function LogsRow({ className = '' }) {
return (
@@ -1,5 +1,5 @@
// Single rover spectator card containing live/snapshot tile and HUD.
import VideoTile from '../../../components/VideoTile.jsx';
import VideoTile from '../../../components/VideoTile/index.jsx';
import { formatDriverLabel } from '../utils.js';
export default function RoverSpectatorCard({ rover, frame, sessionInfo, videoMode, snapshotFeed, audioInfo, session }) {
@@ -1,5 +1,5 @@
// Secondary panel row showing room cameras.
import RoomCameraPanel from '../../../components/RoomCameraPanel.jsx';
import RoomCameraPanel from '../../../components/RoomCameraPanel/index.jsx';
export default function SecondaryRow() {
return (