mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-15 17:12:59 -04:00
goop converter
This commit is contained in:
@@ -28,7 +28,7 @@
|
|||||||
# REFACTOR TRACKING
|
# REFACTOR TRACKING
|
||||||
## Current phase
|
## Current phase
|
||||||
- [x] Phase 1: Inventory + usage mapping (server + webui)
|
- [x] Phase 1: Inventory + usage mapping (server + webui)
|
||||||
- [ ] Phase 2: Refactor highest-impact offenders first
|
- [x] Phase 2: Refactor highest-impact offenders first
|
||||||
- [ ] Phase 3: Sweep remaining services/components
|
- [ ] Phase 3: Sweep remaining services/components
|
||||||
- [ ] Phase 4: Dead code/file removal pass
|
- [ ] Phase 4: Dead code/file removal pass
|
||||||
- [ ] Phase 5: Final regression validation
|
- [ ] Phase 5: Final regression validation
|
||||||
@@ -68,7 +68,7 @@
|
|||||||
- [x] mobile controls
|
- [x] mobile controls
|
||||||
- [x] top down map
|
- [x] top down map
|
||||||
- [x] video tile
|
- [x] video tile
|
||||||
- [ ] Sweep `webui` for unused or unneeded files/code with verification
|
- [x] Sweep `webui` for unused or unneeded files/code with verification
|
||||||
|
|
||||||
### COMPLETED COMPONENTS
|
### COMPLETED COMPONENTS
|
||||||
- mini summary app
|
- mini summary app
|
||||||
@@ -91,6 +91,8 @@
|
|||||||
- Split `webui/src/components/GamepadMappingSettings.jsx` into `webui/src/components/GamepadMappingSettings/` with extracted constants/helpers/SliderField modules and removed the standalone component file.
|
- Split `webui/src/components/GamepadMappingSettings.jsx` into `webui/src/components/GamepadMappingSettings/` with extracted constants/helpers/SliderField modules and removed the standalone component file.
|
||||||
- Split `webui/src/components/MobileControls.jsx` into `webui/src/components/MobileControls/` with extracted joystick/aux/constants modules; preserved named exports and moved app import to folder entrypoint.
|
- Split `webui/src/components/MobileControls.jsx` into `webui/src/components/MobileControls/` with extracted joystick/aux/constants modules; preserved named exports and moved app import to folder entrypoint.
|
||||||
- Split `webui/src/components/TopDownMap.jsx` into `webui/src/components/TopDownMap/` with extracted geometry/color helpers and visual SVG primitive modules; updated all consumers to folder entrypoint.
|
- Split `webui/src/components/TopDownMap.jsx` into `webui/src/components/TopDownMap/` with extracted geometry/color helpers and visual SVG primitive modules; updated all consumers to folder entrypoint.
|
||||||
|
- Folderized all remaining top-level files under `webui/src/components/` into per-component `index.jsx` folders and rewired component imports to match the new structure.
|
||||||
|
- Removed unreferenced components `CameraServoPanel` and `ControlSummary` after dependency-map verification and successful rebuild.
|
||||||
|
|
||||||
## Done criteria (per item)
|
## Done criteria (per item)
|
||||||
- [ ] Folderized structure created.
|
- [ ] Folderized structure created.
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -11,8 +11,8 @@
|
|||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||||
<meta name="apple-mobile-web-app-title" content="Multi Roomba Rover" />
|
<meta name="apple-mobile-web-app-title" content="Multi Roomba Rover" />
|
||||||
<title>Multi Roomba Rover</title>
|
<title>Multi Roomba Rover</title>
|
||||||
<script type="module" crossorigin src="/assets/index-BSZxBzcC.js"></script>
|
<script type="module" crossorigin src="/assets/index-D5igcM0e.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-DVTOmRBl.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-BmoiQy4X.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
+23
-23
@@ -1,7 +1,7 @@
|
|||||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import TelemetryPanel from './components/TelemetryPanel.jsx';
|
import TelemetryPanel from './components/TelemetryPanel/index.jsx';
|
||||||
import ReplaySourcesPanel from './components/ReplaySourcesPanel.jsx';
|
import ReplaySourcesPanel from './components/ReplaySourcesPanel/index.jsx';
|
||||||
import AlertFeed from './components/AlertFeed.jsx';
|
import AlertFeed from './components/AlertFeed/index.jsx';
|
||||||
import MobileControls, {
|
import MobileControls, {
|
||||||
MobileActionsColumn,
|
MobileActionsColumn,
|
||||||
MobileDriveColumn,
|
MobileDriveColumn,
|
||||||
@@ -12,31 +12,31 @@ import {
|
|||||||
GamepadInputManager,
|
GamepadInputManager,
|
||||||
useControlSystem,
|
useControlSystem,
|
||||||
} from './controls/index.js';
|
} from './controls/index.js';
|
||||||
import RoomCameraPanel from './components/RoomCameraPanel.jsx';
|
import RoomCameraPanel from './components/RoomCameraPanel/index.jsx';
|
||||||
import LogPanel from './components/LogPanel.jsx';
|
import LogPanel from './components/LogPanel/index.jsx';
|
||||||
import DriverVideoPanel from './components/DriverVideoPanel.jsx';
|
import DriverVideoPanel from './components/DriverVideoPanel/index.jsx';
|
||||||
import RightPaneTabs from './components/RightPaneTabs.jsx';
|
import RightPaneTabs from './components/RightPaneTabs/index.jsx';
|
||||||
import ModeGateOverlay from './components/ModeGateOverlay.jsx';
|
import ModeGateOverlay from './components/ModeGateOverlay/index.jsx';
|
||||||
import HomeAssistantControls from './components/HomeAssistantControls.jsx';
|
import HomeAssistantControls from './components/HomeAssistantControls/index.jsx';
|
||||||
import TurnAlertListener from './components/TurnAlertListener.jsx';
|
import TurnAlertListener from './components/TurnAlertListener/index.jsx';
|
||||||
import RawUserPilePanel from './components/RawUserPilePanel.jsx';
|
import RawUserPilePanel from './components/RawUserPilePanel/index.jsx';
|
||||||
import ChatPanel from './components/ChatPanel.jsx';
|
import ChatPanel from './components/ChatPanel/index.jsx';
|
||||||
import FullscreenPrompt from './components/FullscreenPrompt.jsx';
|
import FullscreenPrompt from './components/FullscreenPrompt/index.jsx';
|
||||||
import FloatingFullscreenButton from './components/FloatingFullscreenButton.jsx';
|
import FloatingFullscreenButton from './components/FloatingFullscreenButton/index.jsx';
|
||||||
import { useFullscreenPrompt } from './hooks/useFullscreenPrompt.js';
|
import { useFullscreenPrompt } from './hooks/useFullscreenPrompt.js';
|
||||||
import { useSettingsNamespace } from './settings/index.js';
|
import { useSettingsNamespace } from './settings/index.js';
|
||||||
import HelpOverlay from './components/HelpOverlay.jsx';
|
import HelpOverlay from './components/HelpOverlay/index.jsx';
|
||||||
import HelpPanel from './components/HelpPanel.jsx';
|
import HelpPanel from './components/HelpPanel/index.jsx';
|
||||||
import SettingsPanel from './components/SettingsPanel.jsx';
|
import SettingsPanel from './components/SettingsPanel/index.jsx';
|
||||||
import Tabs, { Tab, TabList, TabPanel, TabPanels } from './components/Tabs.jsx';
|
import Tabs, { Tab, TabList, TabPanel, TabPanels } from './components/Tabs/index.jsx';
|
||||||
import useDefaultNickname from './hooks/useDefaultNickname.js';
|
import useDefaultNickname from './hooks/useDefaultNickname.js';
|
||||||
import useUserIdentitySync from './hooks/useUserIdentitySync.js';
|
import useUserIdentitySync from './hooks/useUserIdentitySync.js';
|
||||||
import CommunityGoalBanner from './components/CommunityGoalBanner.jsx';
|
import CommunityGoalBanner from './components/CommunityGoalBanner/index.jsx';
|
||||||
import RoverQueuesPanel from './components/RoverQueuesPanel.jsx';
|
import RoverQueuesPanel from './components/RoverQueuesPanel/index.jsx';
|
||||||
import VipPanel from './components/VipPanel.jsx';
|
import VipPanel from './components/VipPanel/index.jsx';
|
||||||
import { useSessionSelector } from './context/SessionContext.jsx';
|
import { useSessionSelector } from './context/SessionContext.jsx';
|
||||||
import ButtonBoxPanel from './components/ButtonBoxPanel.jsx';
|
import ButtonBoxPanel from './components/ButtonBoxPanel/index.jsx';
|
||||||
import RewardRunOverlay from './components/RewardRunOverlay.jsx';
|
import RewardRunOverlay from './components/RewardRunOverlay/index.jsx';
|
||||||
|
|
||||||
function useLayoutMode() {
|
function useLayoutMode() {
|
||||||
const [mode, setMode] = useState(() => {
|
const [mode, setMode] = useState(() => {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// Admin control panel main composition and action handlers.
|
// Admin control panel main composition and action handlers.
|
||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
import { useSession } from '../../context/SessionContext.jsx';
|
import { useSession } from '../../context/SessionContext.jsx';
|
||||||
import RoverRoster from '../RoverRoster.jsx';
|
import RoverRoster from '../RoverRoster/index.jsx';
|
||||||
import LlmCommentaryPanel from './LlmCommentaryPanel.jsx';
|
import LlmCommentaryPanel from './LlmCommentaryPanel.jsx';
|
||||||
import ReplaySnapshotHealth from './ReplaySnapshotHealth.jsx';
|
import ReplaySnapshotHealth from './ReplaySnapshotHealth.jsx';
|
||||||
import AdminIpLogPanel from './AdminIpLogPanel.jsx';
|
import AdminIpLogPanel from './AdminIpLogPanel.jsx';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Admin LLM commentary monitor panel.
|
// Admin LLM commentary monitor panel.
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import ChatMessageRow from '../ChatMessageRow.jsx';
|
import ChatMessageRow from '../ChatMessageRow/index.jsx';
|
||||||
import { buildLlmLargeIndicatorFromState, buildLlmConversationRowsFromMessages } from './llmHelpers.js';
|
import { buildLlmLargeIndicatorFromState, buildLlmConversationRowsFromMessages } from './llmHelpers.js';
|
||||||
|
|
||||||
export default function LlmCommentaryPanel({ state, onClearHistory, clearingHistory }) {
|
export default function LlmCommentaryPanel({ state, onClearHistory, clearingHistory }) {
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
import { useSessionActions, useSessionSelector } from '../context/SessionContext.jsx';
|
import { useSessionActions, useSessionSelector } from '../../context/SessionContext.jsx';
|
||||||
import { useSocket } from '../context/SocketContext.jsx';
|
import { useSocket } from '../../context/SocketContext.jsx';
|
||||||
import ChatMessageRow from './ChatMessageRow.jsx';
|
import ChatMessageRow from '../ChatMessageRow/index.jsx';
|
||||||
import ChatTypingRow from './ChatTypingRow.jsx';
|
import ChatTypingRow from '../ChatTypingRow/index.jsx';
|
||||||
import ButtonBoxTile from './ButtonBoxTile.jsx';
|
import ButtonBoxTile from '../ButtonBoxTile/index.jsx';
|
||||||
|
|
||||||
const LIFETIME_MS = 3000;
|
const LIFETIME_MS = 3000;
|
||||||
const BUTTONBOX_LIFETIME_MS = 3500;
|
const BUTTONBOX_LIFETIME_MS = 3500;
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useSessionActions } from '../context/SessionContext.jsx';
|
import { useSessionActions } from '../../context/SessionContext.jsx';
|
||||||
|
|
||||||
export default function AuthPanel() {
|
export default function AuthPanel() {
|
||||||
const { login, setRole } = useSessionActions();
|
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 WARN_FLASH_MS = 1600;
|
||||||
const URGENT_FLASH_MS = 800;
|
const URGENT_FLASH_MS = 800;
|
||||||
+5
-5
@@ -1,9 +1,9 @@
|
|||||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { useSession } from '../context/SessionContext.jsx';
|
import { useSession } from '../../context/SessionContext.jsx';
|
||||||
import { useSocket } from '../context/SocketContext.jsx';
|
import { useSocket } from '../../context/SocketContext.jsx';
|
||||||
import { useSettingsNamespace } from '../settings/index.js';
|
import { useSettingsNamespace } from '../../settings/index.js';
|
||||||
import { AUDIO_SETTINGS_DEFAULTS } from '../settings/namespaces.js';
|
import { AUDIO_SETTINGS_DEFAULTS } from '../../settings/namespaces.js';
|
||||||
import ButtonBoxTile from './ButtonBoxTile.jsx';
|
import ButtonBoxTile from '../ButtonBoxTile/index.jsx';
|
||||||
|
|
||||||
const FLASH_MS = 420;
|
const FLASH_MS = 420;
|
||||||
const REWARD_FLASH_MS = 1200;
|
const REWARD_FLASH_MS = 1200;
|
||||||
@@ -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
-1
@@ -1,5 +1,5 @@
|
|||||||
import { FaDiscord } from 'react-icons/fa';
|
import { FaDiscord } from 'react-icons/fa';
|
||||||
import { roverBadgeStyle } from '../lib/roverColor.js';
|
import { roverBadgeStyle } from '../../lib/roverColor.js';
|
||||||
|
|
||||||
function roleColors(role) {
|
function roleColors(role) {
|
||||||
switch (role) {
|
switch (role) {
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { useChat } from '../context/ChatContext.jsx';
|
import { useChat } from '../../context/ChatContext.jsx';
|
||||||
import { useSessionSelector } from '../context/SessionContext.jsx';
|
import { useSessionSelector } from '../../context/SessionContext.jsx';
|
||||||
import { useSettingsNamespace } from '../settings/index.js';
|
import { useSettingsNamespace } from '../../settings/index.js';
|
||||||
import ChatMessageRow from './ChatMessageRow.jsx';
|
import ChatMessageRow from '../ChatMessageRow/index.jsx';
|
||||||
import ChatTypingRow from './ChatTypingRow.jsx';
|
import ChatTypingRow from '../ChatTypingRow/index.jsx';
|
||||||
|
|
||||||
const FLITE_VOICES = ['kal', 'rms', 'slt', 'ksp', 'bdl'];
|
const FLITE_VOICES = ['kal', 'rms', 'slt', 'ksp', 'bdl'];
|
||||||
const ESPEAK_PITCHES = Array.from({ length: 10 }, (_, idx) => idx * 10);
|
const ESPEAK_PITCHES = Array.from({ length: 10 }, (_, idx) => idx * 10);
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
import { ChatIdentity, chatRowClass } from './ChatMessageRow.jsx';
|
import { ChatIdentity, chatRowClass } from '../ChatMessageRow/index.jsx';
|
||||||
|
|
||||||
export default function ChatTypingRow({ message }) {
|
export default function ChatTypingRow({ message }) {
|
||||||
return (
|
return (
|
||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
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 MOBILE_DISMISS_MS = 10000;
|
||||||
const MAX_FONT_PX = 28;
|
const MAX_FONT_PX = 28;
|
||||||
@@ -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
-1
@@ -1,5 +1,5 @@
|
|||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { useSessionSelector } from '../context/SessionContext.jsx';
|
import { useSessionSelector } from '../../context/SessionContext.jsx';
|
||||||
|
|
||||||
function getRandomElement(elements) {
|
function getRandomElement(elements) {
|
||||||
if (!elements || !elements.length) return null;
|
if (!elements || !elements.length) return null;
|
||||||
+6
-6
@@ -1,10 +1,10 @@
|
|||||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { useSessionSelector } from '../context/SessionContext.jsx';
|
import { useSessionSelector } from '../../context/SessionContext.jsx';
|
||||||
import { useTelemetryFrame } from '../context/TelemetryContext.jsx';
|
import { useTelemetryFrame } from '../../context/TelemetryContext.jsx';
|
||||||
import { useVideoRequests } from '../hooks/useVideoRequests.js';
|
import { useVideoRequests } from '../../hooks/useVideoRequests.js';
|
||||||
import { useRoverSnapshots } from '../hooks/useRoverSnapshots.js';
|
import { useRoverSnapshots } from '../../hooks/useRoverSnapshots.js';
|
||||||
import { useControlSystem } from '../controls/index.js';
|
import { useControlSystem } from '../../controls/index.js';
|
||||||
import VideoTile from './VideoTile.jsx';
|
import VideoTile from '../VideoTile/index.jsx';
|
||||||
|
|
||||||
export default function DriverVideoPanel({layoutFormat = 'desktop'}) {
|
export default function DriverVideoPanel({layoutFormat = 'desktop'}) {
|
||||||
const session = useSessionSelector((state) => state.session);
|
const session = useSessionSelector((state) => state.session);
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { formatKeyLabel } from '../controls/keymapUtils.js';
|
import { formatKeyLabel } from '../../controls/keymapUtils.js';
|
||||||
import { getHelpContent } from '../help/content.js';
|
import { getHelpContent } from '../../help/content.js';
|
||||||
|
|
||||||
function KeyPill({ actionId, keymap }) {
|
function KeyPill({ actionId, keymap }) {
|
||||||
const value = keymap?.[actionId]?.[0] ?? '';
|
const value = keymap?.[actionId]?.[0] ?? '';
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useControlSystem } from '../controls/index.js';
|
import { useControlSystem } from '../../controls/index.js';
|
||||||
import HelpContentView from './HelpContentView.jsx';
|
import HelpContentView from '../HelpContentView/index.jsx';
|
||||||
|
|
||||||
export default function HelpOverlay({ visible, layout, onClose, showOnLoad, onToggleShowOnLoad }) {
|
export default function HelpOverlay({ visible, layout, onClose, showOnLoad, onToggleShowOnLoad }) {
|
||||||
if (!visible) return null;
|
if (!visible) return null;
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useControlSystem } from '../controls/index.js';
|
import { useControlSystem } from '../../controls/index.js';
|
||||||
import HelpContentView from './HelpContentView.jsx';
|
import HelpContentView from '../HelpContentView/index.jsx';
|
||||||
|
|
||||||
export default function HelpPanel({ layout, onOpenOverlay }) {
|
export default function HelpPanel({ layout, onOpenOverlay }) {
|
||||||
const { state } = useControlSystem();
|
const { state } = useControlSystem();
|
||||||
+3
-3
@@ -1,7 +1,7 @@
|
|||||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { useSessionActions, useSessionSelector } from '../context/SessionContext.jsx';
|
import { useSessionActions, useSessionSelector } from '../../context/SessionContext.jsx';
|
||||||
import { useControlSystem } from '../controls/index.js';
|
import { useControlSystem } from '../../controls/index.js';
|
||||||
import { formatKeyLabel } from '../controls/keymapUtils.js';
|
import { formatKeyLabel } from '../../controls/keymapUtils.js';
|
||||||
|
|
||||||
function StatusBadge({ label, tone = 'muted' }) {
|
function StatusBadge({ label, tone = 'muted' }) {
|
||||||
const styles =
|
const styles =
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { useSettingsNamespace } from '../settings/index.js';
|
import { useSettingsNamespace } from '../../settings/index.js';
|
||||||
import { HORN_SETTINGS_DEFAULTS } from '../settings/namespaces.js';
|
import { HORN_SETTINGS_DEFAULTS } from '../../settings/namespaces.js';
|
||||||
|
|
||||||
function clampFreq(value) {
|
function clampFreq(value) {
|
||||||
const num = Number(value);
|
const num = Number(value);
|
||||||
+6
-6
@@ -1,10 +1,10 @@
|
|||||||
import { useMemo, useState, useEffect, useCallback } from 'react';
|
import { useMemo, useState, useEffect, useCallback } from 'react';
|
||||||
import { useControlSystem } from '../controls/index.js';
|
import { useControlSystem } from '../../controls/index.js';
|
||||||
import { DEFAULT_KEYMAP } from '../controls/constants.js';
|
import { DEFAULT_KEYMAP } from '../../controls/constants.js';
|
||||||
import { canonicalizeKeyInput, formatKeyLabel } from '../controls/keymapUtils.js';
|
import { canonicalizeKeyInput, formatKeyLabel } from '../../controls/keymapUtils.js';
|
||||||
import { setKeyboardCaptureLocked } from '../controls/inputs/keyboardCaptureLock.js';
|
import { setKeyboardCaptureLocked } from '../../controls/inputs/keyboardCaptureLock.js';
|
||||||
import { useSettingsNamespace } from '../settings/index.js';
|
import { useSettingsNamespace } from '../../settings/index.js';
|
||||||
import { INPUT_SETTINGS_DEFAULTS } from '../settings/namespaces.js';
|
import { INPUT_SETTINGS_DEFAULTS } from '../../settings/namespaces.js';
|
||||||
|
|
||||||
const KEY_ACTIONS = [
|
const KEY_ACTIONS = [
|
||||||
{ id: 'driveForward', label: 'Drive Forward', group: 'Driving' },
|
{ 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() {
|
export default function LogPanel() {
|
||||||
const logs = useSessionSelector((state) => state.logs);
|
const logs = useSessionSelector((state) => state.logs);
|
||||||
@@ -3,9 +3,9 @@ import { useCallback, useEffect, useRef } from 'react';
|
|||||||
import { useControlSystem } from '../../controls/index.js';
|
import { useControlSystem } from '../../controls/index.js';
|
||||||
import { clampUnit } from '../../controls/controlMath.js';
|
import { clampUnit } from '../../controls/controlMath.js';
|
||||||
import DriveDockAction, { useDriveDockState } from '../DriveDockAction/index.jsx';
|
import DriveDockAction, { useDriveDockState } from '../DriveDockAction/index.jsx';
|
||||||
import NightVisionControl from '../NightVisionControl.jsx';
|
import NightVisionControl from '../NightVisionControl/index.jsx';
|
||||||
import HornControl from '../HornControl.jsx';
|
import HornControl from '../HornControl/index.jsx';
|
||||||
import CameraTiltControl from '../CameraTiltControl.jsx';
|
import CameraTiltControl from '../CameraTiltControl/index.jsx';
|
||||||
import FloatingJoystick from './FloatingJoystick.jsx';
|
import FloatingJoystick from './FloatingJoystick.jsx';
|
||||||
import MobileAuxButton from './MobileAuxButton.jsx';
|
import MobileAuxButton from './MobileAuxButton.jsx';
|
||||||
import {
|
import {
|
||||||
|
|||||||
+5
-5
@@ -1,9 +1,9 @@
|
|||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
import AuthPanel from './AuthPanel.jsx';
|
import AuthPanel from '../AuthPanel/index.jsx';
|
||||||
import { useSessionSelector } from '../context/SessionContext.jsx';
|
import { useSessionSelector } from '../../context/SessionContext.jsx';
|
||||||
import SocialButton from './SocialButton.jsx';
|
import SocialButton from '../SocialButton/index.jsx';
|
||||||
import ChatPanel from './ChatPanel.jsx';
|
import ChatPanel from '../ChatPanel/index.jsx';
|
||||||
import NicknameForm from './NicknameForm.jsx';
|
import NicknameForm from '../NicknameForm/index.jsx';
|
||||||
|
|
||||||
const PRIVILEGED_ROLES = new Set(['admin', 'lockdown', 'lockdown-admin']);
|
const PRIVILEGED_ROLES = new Set(['admin', 'lockdown', 'lockdown-admin']);
|
||||||
const LOCKDOWN_ROLES = new Set(['lockdown', 'lockdown-admin']);
|
const LOCKDOWN_ROLES = new Set(['lockdown', 'lockdown-admin']);
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useSession } from '../context/SessionContext.jsx';
|
import { useSession } from '../../context/SessionContext.jsx';
|
||||||
import { useSettingsNamespace } from '../settings/index.js';
|
import { useSettingsNamespace } from '../../settings/index.js';
|
||||||
|
|
||||||
export default function NicknameForm({ compact = false }) {
|
export default function NicknameForm({ compact = false }) {
|
||||||
const { session, setNickname } = useSession();
|
const { session, setNickname } = useSession();
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useControlSystem } from '../controls/index.js';
|
import { useControlSystem } from '../../controls/index.js';
|
||||||
import { OVERCURRENT_GROUPS } from '../controls/overcurrentLimiter.js';
|
import { OVERCURRENT_GROUPS } from '../../controls/overcurrentLimiter.js';
|
||||||
|
|
||||||
const GROUP_LABELS = {
|
const GROUP_LABELS = {
|
||||||
drive: 'Drive wheels',
|
drive: 'Drive wheels',
|
||||||
+3
-3
@@ -1,7 +1,7 @@
|
|||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useSession } from '../context/SessionContext.jsx';
|
import { useSession } from '../../context/SessionContext.jsx';
|
||||||
import NicknameForm from './NicknameForm.jsx';
|
import NicknameForm from '../NicknameForm/index.jsx';
|
||||||
import SocialButtonsGrid from './SocialButtonsGrid.jsx';
|
import SocialButtonsGrid from '../SocialButtonsGrid/index.jsx';
|
||||||
|
|
||||||
function roleColors(role) {
|
function roleColors(role) {
|
||||||
switch (role) {
|
switch (role) {
|
||||||
+3
-3
@@ -1,7 +1,7 @@
|
|||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
import { useSession } from '../context/SessionContext.jsx';
|
import { useSession } from '../../context/SessionContext.jsx';
|
||||||
import { useSettingsNamespace } from '../settings/index.js';
|
import { useSettingsNamespace } from '../../settings/index.js';
|
||||||
import { roverNameChromeStyle } from '../lib/roverColor.js';
|
import { roverNameChromeStyle } from '../../lib/roverColor.js';
|
||||||
|
|
||||||
function normalizeSources(list = []) {
|
function normalizeSources(list = []) {
|
||||||
return list
|
return list
|
||||||
+3
-3
@@ -1,7 +1,7 @@
|
|||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import { useSocket } from '../context/SocketContext.jsx';
|
import { useSocket } from '../../context/SocketContext.jsx';
|
||||||
import { useSettingsNamespace } from '../settings/index.js';
|
import { useSettingsNamespace } from '../../settings/index.js';
|
||||||
import { AUDIO_SETTINGS_DEFAULTS } from '../settings/namespaces.js';
|
import { AUDIO_SETTINGS_DEFAULTS } from '../../settings/namespaces.js';
|
||||||
|
|
||||||
const SHOW_MS = 3200;
|
const SHOW_MS = 3200;
|
||||||
|
|
||||||
+22
-22
@@ -1,25 +1,25 @@
|
|||||||
import RoomCameraPanel from './RoomCameraPanel.jsx';
|
import RoomCameraPanel from '../RoomCameraPanel/index.jsx';
|
||||||
import HomeAssistantControls from './HomeAssistantControls.jsx';
|
import HomeAssistantControls from '../HomeAssistantControls/index.jsx';
|
||||||
import SettingsPanel from './SettingsPanel.jsx';
|
import SettingsPanel from '../SettingsPanel/index.jsx';
|
||||||
import HelpPanel from './HelpPanel.jsx';
|
import HelpPanel from '../HelpPanel/index.jsx';
|
||||||
import ChatPanel from './ChatPanel.jsx';
|
import ChatPanel from '../ChatPanel/index.jsx';
|
||||||
import { LinkButtonsPanel, NicknameEntryPanel } from './UserListPanel.jsx';
|
import { LinkButtonsPanel, NicknameEntryPanel } from '../UserListPanel/index.jsx';
|
||||||
import ReplaySourcesPanel from './ReplaySourcesPanel.jsx';
|
import ReplaySourcesPanel from '../ReplaySourcesPanel/index.jsx';
|
||||||
import Tabs, { Tab, TabList, TabPanel, TabPanels } from './Tabs.jsx';
|
import Tabs, { Tab, TabList, TabPanel, TabPanels } from '../Tabs/index.jsx';
|
||||||
import TopDownMap from './TopDownMap/index.jsx';
|
import TopDownMap from '../TopDownMap/index.jsx';
|
||||||
import DriveDockAction, { useDriveDockState } from './DriveDockAction/index.jsx';
|
import DriveDockAction, { useDriveDockState } from '../DriveDockAction/index.jsx';
|
||||||
import { useTelemetryFrame } from '../context/TelemetryContext.jsx';
|
import { useTelemetryFrame } from '../../context/TelemetryContext.jsx';
|
||||||
import { useControlSystem } from '../controls/index.js';
|
import { useControlSystem } from '../../controls/index.js';
|
||||||
import RoverQueuesPanel from './RoverQueuesPanel.jsx';
|
import RoverQueuesPanel from '../RoverQueuesPanel/index.jsx';
|
||||||
import RawUserPilePanel from './RawUserPilePanel.jsx';
|
import RawUserPilePanel from '../RawUserPilePanel/index.jsx';
|
||||||
import { formatKeyLabel } from '../controls/keymapUtils.js';
|
import { formatKeyLabel } from '../../controls/keymapUtils.js';
|
||||||
import NightVisionControl from './NightVisionControl.jsx';
|
import NightVisionControl from '../NightVisionControl/index.jsx';
|
||||||
import HornControl from './HornControl.jsx';
|
import HornControl from '../HornControl/index.jsx';
|
||||||
import CameraTiltControl from './CameraTiltControl.jsx';
|
import CameraTiltControl from '../CameraTiltControl/index.jsx';
|
||||||
import VipPanel from './VipPanel.jsx';
|
import VipPanel from '../VipPanel/index.jsx';
|
||||||
import { useSessionSelector } from '../context/SessionContext.jsx';
|
import { useSessionSelector } from '../../context/SessionContext.jsx';
|
||||||
import { useSettingsNamespace } from '../settings/index.js';
|
import { useSettingsNamespace } from '../../settings/index.js';
|
||||||
import ButtonBoxPanel from './ButtonBoxPanel.jsx';
|
import ButtonBoxPanel from '../ButtonBoxPanel/index.jsx';
|
||||||
|
|
||||||
function TopDownMapPanel() {
|
function TopDownMapPanel() {
|
||||||
const {
|
const {
|
||||||
+4
-4
@@ -1,8 +1,8 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useSession } from '../context/SessionContext.jsx';
|
import { useSession } from '../../context/SessionContext.jsx';
|
||||||
import { useSettingsNamespace } from '../settings/index.js';
|
import { useSettingsNamespace } from '../../settings/index.js';
|
||||||
import { useRoomCameraSnapshots } from '../hooks/useRoomCameraSnapshots.js';
|
import { useRoomCameraSnapshots } from '../../hooks/useRoomCameraSnapshots.js';
|
||||||
import RoomCameraFeed from './RoomCameraFeed.jsx';
|
import RoomCameraFeed from '../RoomCameraFeed/index.jsx';
|
||||||
|
|
||||||
function EmptyState() {
|
function EmptyState() {
|
||||||
return (
|
return (
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
import { useSessionActions, useSessionSelector } from '../context/SessionContext.jsx';
|
import { useSessionActions, useSessionSelector } from '../../context/SessionContext.jsx';
|
||||||
import { roverNameChromeStyle } from '../lib/roverColor.js';
|
import { roverNameChromeStyle } from '../../lib/roverColor.js';
|
||||||
|
|
||||||
function classNames(...values) {
|
function classNames(...values) {
|
||||||
return values.filter(Boolean).join(' ');
|
return values.filter(Boolean).join(' ');
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { roverNameChromeStyle } from '../lib/roverColor.js';
|
import { roverNameChromeStyle } from '../../lib/roverColor.js';
|
||||||
|
|
||||||
function classNames(...values) {
|
function classNames(...values) {
|
||||||
return values.filter(Boolean).join(' ');
|
return values.filter(Boolean).join(' ');
|
||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useSession } from '../context/SessionContext.jsx';
|
import { useSession } from '../../context/SessionContext.jsx';
|
||||||
|
|
||||||
export default function SessionSnapshot() {
|
export default function SessionSnapshot() {
|
||||||
const { session } = useSession();
|
const { session } = useSession();
|
||||||
+13
-13
@@ -1,17 +1,17 @@
|
|||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useControlSystem } from '../controls/index.js';
|
import { useControlSystem } from '../../controls/index.js';
|
||||||
import AuthPanel from './AuthPanel.jsx';
|
import AuthPanel from '../AuthPanel/index.jsx';
|
||||||
import AdminPanel from './AdminPanel/index.jsx';
|
import AdminPanel from '../AdminPanel/index.jsx';
|
||||||
import KeymapSettings from './KeymapSettings.jsx';
|
import KeymapSettings from '../KeymapSettings/index.jsx';
|
||||||
import GamepadMappingSettings from './GamepadMappingSettings/index.jsx';
|
import GamepadMappingSettings from '../GamepadMappingSettings/index.jsx';
|
||||||
import OvercurrentLimiterPanel from './OvercurrentLimiterPanel.jsx';
|
import OvercurrentLimiterPanel from '../OvercurrentLimiterPanel/index.jsx';
|
||||||
import Tabs, { Tab, TabList, TabPanel, TabPanels } from './Tabs.jsx';
|
import Tabs, { Tab, TabList, TabPanel, TabPanels } from '../Tabs/index.jsx';
|
||||||
import SessionSnapshot from './SessionSnapshot.jsx';
|
import SessionSnapshot from '../SessionSnapshot/index.jsx';
|
||||||
import SocketLogPanel from './SocketLogPanel.jsx';
|
import SocketLogPanel from '../SocketLogPanel/index.jsx';
|
||||||
import { useHudMapSetting } from '../hooks/useHudMapSetting.js';
|
import { useHudMapSetting } from '../../hooks/useHudMapSetting.js';
|
||||||
import { useSettingsNamespace } from '../settings/index.js';
|
import { useSettingsNamespace } from '../../settings/index.js';
|
||||||
import { useSocket } from '../context/SocketContext.jsx';
|
import { useSocket } from '../../context/SocketContext.jsx';
|
||||||
import { AUDIO_SETTINGS_DEFAULTS } from '../settings/namespaces.js';
|
import { AUDIO_SETTINGS_DEFAULTS } from '../../settings/namespaces.js';
|
||||||
|
|
||||||
const manualTabs = [
|
const manualTabs = [
|
||||||
{ key: 'start', label: 'Start OI' },
|
{ key: 'start', label: 'Start OI' },
|
||||||
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
import { useSession } from '../context/SessionContext.jsx';
|
import { useSession } from '../../context/SessionContext.jsx';
|
||||||
import SocialButton from './SocialButton.jsx';
|
import SocialButton from '../SocialButton/index.jsx';
|
||||||
|
|
||||||
function normalizeSocials(session) {
|
function normalizeSocials(session) {
|
||||||
const configured = Array.isArray(session?.socials) ? session.socials : null;
|
const configured = Array.isArray(session?.socials) ? session.socials : null;
|
||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
import { useSocket } from '../context/SocketContext.jsx';
|
import { useSocket } from '../../context/SocketContext.jsx';
|
||||||
|
|
||||||
const MAX_ENTRIES = 300;
|
const MAX_ENTRIES = 300;
|
||||||
const MAX_PAYLOAD_LENGTH = 240;
|
const MAX_PAYLOAD_LENGTH = 240;
|
||||||
+3
-3
@@ -1,7 +1,7 @@
|
|||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useSessionSelector } from '../context/SessionContext.jsx';
|
import { useSessionSelector } from '../../context/SessionContext.jsx';
|
||||||
import { useTelemetryFrame } from '../context/TelemetryContext.jsx';
|
import { useTelemetryFrame } from '../../context/TelemetryContext.jsx';
|
||||||
import { useDockIr } from '../hooks/useDockIr.js';
|
import { useDockIr } from '../../hooks/useDockIr.js';
|
||||||
|
|
||||||
function formatMetric(value, fallback = '--') {
|
function formatMetric(value, fallback = '--') {
|
||||||
if (value == null || value === '') return fallback;
|
if (value == null || value === '') return fallback;
|
||||||
+4
-4
@@ -1,8 +1,8 @@
|
|||||||
import { useEffect, useMemo, useRef } from 'react';
|
import { useEffect, useMemo, useRef } from 'react';
|
||||||
import turnSound from '../assets/turn_alert.mp3';
|
import turnSound from '../../assets/turn_alert.mp3';
|
||||||
import { useSessionActions, useSessionSelector } from '../context/SessionContext.jsx';
|
import { useSessionActions, useSessionSelector } from '../../context/SessionContext.jsx';
|
||||||
import { useSettingsNamespace } from '../settings/index.js';
|
import { useSettingsNamespace } from '../../settings/index.js';
|
||||||
import { AUDIO_SETTINGS_DEFAULTS } from '../settings/namespaces.js';
|
import { AUDIO_SETTINGS_DEFAULTS } from '../../settings/namespaces.js';
|
||||||
|
|
||||||
function useAudio(src, volume = 1) {
|
function useAudio(src, volume = 1) {
|
||||||
const audioRef = useRef(null);
|
const audioRef = useRef(null);
|
||||||
+4
-4
@@ -1,8 +1,8 @@
|
|||||||
import { useEffect, useMemo, useState, useCallback } from 'react';
|
import { useEffect, useMemo, useState, useCallback } from 'react';
|
||||||
import { useSession } from '../context/SessionContext.jsx';
|
import { useSession } from '../../context/SessionContext.jsx';
|
||||||
import NicknameForm from './NicknameForm.jsx';
|
import NicknameForm from '../NicknameForm/index.jsx';
|
||||||
import SocialButtonsGrid from './SocialButtonsGrid.jsx';
|
import SocialButtonsGrid from '../SocialButtonsGrid/index.jsx';
|
||||||
import { roverBadgeStyle, roverNameChromeStyle } from '../lib/roverColor.js';
|
import { roverBadgeStyle, roverNameChromeStyle } from '../../lib/roverColor.js';
|
||||||
|
|
||||||
export function NicknameEntryPanel({ compact = false }) {
|
export function NicknameEntryPanel({ compact = false }) {
|
||||||
return (
|
return (
|
||||||
@@ -1,25 +1,25 @@
|
|||||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { WhepPlayer } from '../lib/whepPlayer.js';
|
import { WhepPlayer } from '../../lib/whepPlayer.js';
|
||||||
import { useHudMapSetting } from '../hooks/useHudMapSetting.js';
|
import { useHudMapSetting } from '../../hooks/useHudMapSetting.js';
|
||||||
import { useSessionSelector } from '../context/SessionContext.jsx';
|
import { useSessionSelector } from '../../context/SessionContext.jsx';
|
||||||
import { useSettingsNamespace } from '../settings/index.js';
|
import { useSettingsNamespace } from '../../settings/index.js';
|
||||||
import { AUDIO_SETTINGS_DEFAULTS } from '../settings/namespaces.js';
|
import { AUDIO_SETTINGS_DEFAULTS } from '../../settings/namespaces.js';
|
||||||
import SocialButton from './SocialButton.jsx';
|
import SocialButton from '../SocialButton/index.jsx';
|
||||||
import BatteryBar from './BatteryBar.jsx';
|
import BatteryBar from '../BatteryBar/index.jsx';
|
||||||
import { buildBatteryVisual } from '../lib/battery.js';
|
import { buildBatteryVisual } from '../../lib/battery.js';
|
||||||
import TurnCueOverlay from './VideoTile/TurnCueOverlay.jsx';
|
import TurnCueOverlay from './TurnCueOverlay.jsx';
|
||||||
import HudOverlay from './VideoTile/HudOverlay.jsx';
|
import HudOverlay from './HudOverlay.jsx';
|
||||||
import OvercurrentOverlay from './VideoTile/OvercurrentOverlay.jsx';
|
import OvercurrentOverlay from './OvercurrentOverlay.jsx';
|
||||||
import LowBatteryOverlay from './VideoTile/LowBatteryOverlay.jsx';
|
import LowBatteryOverlay from './LowBatteryOverlay.jsx';
|
||||||
import LightBumpBars from './VideoTile/LightBumpBars.jsx';
|
import LightBumpBars from './LightBumpBars.jsx';
|
||||||
import HudChatInput from './VideoTile/HudChatInput.jsx';
|
import HudChatInput from './HudChatInput.jsx';
|
||||||
import {
|
import {
|
||||||
RESTART_DELAY_MS,
|
RESTART_DELAY_MS,
|
||||||
UNMUTE_RETRY_MS,
|
UNMUTE_RETRY_MS,
|
||||||
AUDIO_RETRY_MS,
|
AUDIO_RETRY_MS,
|
||||||
BRUSH_CURRENT_THRESHOLD_MA,
|
BRUSH_CURRENT_THRESHOLD_MA,
|
||||||
DUCK_RELEASE_FADE_MS,
|
DUCK_RELEASE_FADE_MS,
|
||||||
} from './VideoTile/constants.js';
|
} from './constants.js';
|
||||||
|
|
||||||
export default function VideoTile({
|
export default function VideoTile({
|
||||||
sessionInfo,
|
sessionInfo,
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
import { useSession } from '../context/SessionContext.jsx';
|
import { useSession } from '../../context/SessionContext.jsx';
|
||||||
import { useSettingsNamespace } from '../settings/index.js';
|
import { useSettingsNamespace } from '../../settings/index.js';
|
||||||
import { COOKIE_KEY_REGEX, flowWrapClass } from './vip/constants.js';
|
import { COOKIE_KEY_REGEX, flowWrapClass } from '../vip/constants.js';
|
||||||
import VipAudioUploadCard from './vip/VipAudioUploadCard/index.jsx';
|
import VipAudioUploadCard from '../vip/VipAudioUploadCard/index.jsx';
|
||||||
import VipVerificationCard from './vip/VipVerificationCard.jsx';
|
import VipVerificationCard from '../vip/VipVerificationCard.jsx';
|
||||||
import VipIdentityCard from './vip/VipIdentityCard.jsx';
|
import VipIdentityCard from '../vip/VipIdentityCard.jsx';
|
||||||
import VipPrivateRoverAccessCard from './vip/VipPrivateRoverAccessCard.jsx';
|
import VipPrivateRoverAccessCard from '../vip/VipPrivateRoverAccessCard.jsx';
|
||||||
import VipNeatoCard from './vip/VipNeatoCard.jsx';
|
import VipNeatoCard from '../vip/VipNeatoCard.jsx';
|
||||||
|
|
||||||
export default function VipPanel() {
|
export default function VipPanel() {
|
||||||
const {
|
const {
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import NicknameForm from '../NicknameForm.jsx';
|
import NicknameForm from '../NicknameForm/index.jsx';
|
||||||
import { flowWrapClass, innerFlowClass, fieldClass } from './constants.js';
|
import { flowWrapClass, innerFlowClass, fieldClass } from './constants.js';
|
||||||
|
|
||||||
export default function VipVerificationCard({
|
export default function VipVerificationCard({
|
||||||
|
|||||||
+1
-1
@@ -10,7 +10,7 @@ import { ChatProvider } from './context/ChatContext.jsx'
|
|||||||
import SpectatorApp from './spectate/SpectatorApp.jsx'
|
import SpectatorApp from './spectate/SpectatorApp.jsx'
|
||||||
import MiniSummaryApp from './mini/MiniSummaryApp.jsx'
|
import MiniSummaryApp from './mini/MiniSummaryApp.jsx'
|
||||||
import { SettingsProvider } from './settings/index.js'
|
import { SettingsProvider } from './settings/index.js'
|
||||||
import DeterrenceChaos from './components/DeterrenceChaos.jsx'
|
import DeterrenceChaos from './components/DeterrenceChaos/index.jsx'
|
||||||
|
|
||||||
createRoot(document.getElementById('root')).render(
|
createRoot(document.getElementById('root')).render(
|
||||||
<StrictMode>
|
<StrictMode>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Top-level mini summary app shell with hard-refresh timer and alerts.
|
// Top-level mini summary app shell with hard-refresh timer and alerts.
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import AlertFeed from '../../components/AlertFeed.jsx';
|
import AlertFeed from '../../components/AlertFeed/index.jsx';
|
||||||
import MiniSummaryContent from './MiniSummaryContent.jsx';
|
import MiniSummaryContent from './MiniSummaryContent.jsx';
|
||||||
import { HARD_REFRESH_MS } from './constants.js';
|
import { HARD_REFRESH_MS } from './constants.js';
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { useVideoRequests } from '../../hooks/useVideoRequests.js';
|
|||||||
import { useRoverSnapshots } from '../../hooks/useRoverSnapshots.js';
|
import { useRoverSnapshots } from '../../hooks/useRoverSnapshots.js';
|
||||||
import { useSpectatorMode } from '../../hooks/useSpectatorMode.js';
|
import { useSpectatorMode } from '../../hooks/useSpectatorMode.js';
|
||||||
import useDefaultNickname from '../../hooks/useDefaultNickname.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 FitViewportFrame from './components/FitViewportFrame.jsx';
|
||||||
import InfoColumn from './components/InfoColumn.jsx';
|
import InfoColumn from './components/InfoColumn.jsx';
|
||||||
import { ROTATE_MS } from './constants.js';
|
import { ROTATE_MS } from './constants.js';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Rover information and optional preview column for mini summary UI.
|
// Rover information and optional preview column for mini summary UI.
|
||||||
import VideoTile from '../../../components/VideoTile.jsx';
|
import VideoTile from '../../../components/VideoTile/index.jsx';
|
||||||
import BatteryBar from '../../../components/BatteryBar.jsx';
|
import BatteryBar from '../../../components/BatteryBar/index.jsx';
|
||||||
import { roverNameChromeStyle } from '../../../lib/roverColor.js';
|
import { roverNameChromeStyle } from '../../../lib/roverColor.js';
|
||||||
import { getBatteryVisual } from '../utils.js';
|
import { getBatteryVisual } from '../utils.js';
|
||||||
import AutoFitText from './AutoFitText.jsx';
|
import AutoFitText from './AutoFitText.jsx';
|
||||||
|
|||||||
@@ -5,13 +5,13 @@ import { useVideoRequests } from '../../hooks/useVideoRequests.js';
|
|||||||
import { useRoverSnapshots } from '../../hooks/useRoverSnapshots.js';
|
import { useRoverSnapshots } from '../../hooks/useRoverSnapshots.js';
|
||||||
import { useSpectatorMode } from '../../hooks/useSpectatorMode.js';
|
import { useSpectatorMode } from '../../hooks/useSpectatorMode.js';
|
||||||
import useDefaultNickname from '../../hooks/useDefaultNickname.js';
|
import useDefaultNickname from '../../hooks/useDefaultNickname.js';
|
||||||
import ChatPanel from '../../components/ChatPanel.jsx';
|
import ChatPanel from '../../components/ChatPanel/index.jsx';
|
||||||
import AlertFeed from '../../components/AlertFeed.jsx';
|
import AlertFeed from '../../components/AlertFeed/index.jsx';
|
||||||
import CommunityGoalBanner from '../../components/CommunityGoalBanner.jsx';
|
import CommunityGoalBanner from '../../components/CommunityGoalBanner/index.jsx';
|
||||||
import RoverQueuesPanel from '../../components/RoverQueuesPanel.jsx';
|
import RoverQueuesPanel from '../../components/RoverQueuesPanel/index.jsx';
|
||||||
import RawUserPilePanel from '../../components/RawUserPilePanel.jsx';
|
import RawUserPilePanel from '../../components/RawUserPilePanel/index.jsx';
|
||||||
import ButtonBoxPanel from '../../components/ButtonBoxPanel.jsx';
|
import ButtonBoxPanel from '../../components/ButtonBoxPanel/index.jsx';
|
||||||
import RewardRunOverlay from '../../components/RewardRunOverlay.jsx';
|
import RewardRunOverlay from '../../components/RewardRunOverlay/index.jsx';
|
||||||
import usePortraitLayout from './hooks/usePortraitLayout.js';
|
import usePortraitLayout from './hooks/usePortraitLayout.js';
|
||||||
import RoverRow from './components/RoverRow.jsx';
|
import RoverRow from './components/RoverRow.jsx';
|
||||||
import SecondaryRow from './components/SecondaryRow.jsx';
|
import SecondaryRow from './components/SecondaryRow.jsx';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// Log panel wrapper row for spectator layout.
|
// 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 = '' }) {
|
export default function LogsRow({ className = '' }) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// Single rover spectator card containing live/snapshot tile and HUD.
|
// 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';
|
import { formatDriverLabel } from '../utils.js';
|
||||||
|
|
||||||
export default function RoverSpectatorCard({ rover, frame, sessionInfo, videoMode, snapshotFeed, audioInfo, session }) {
|
export default function RoverSpectatorCard({ rover, frame, sessionInfo, videoMode, snapshotFeed, audioInfo, session }) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// Secondary panel row showing room cameras.
|
// Secondary panel row showing room cameras.
|
||||||
import RoomCameraPanel from '../../../components/RoomCameraPanel.jsx';
|
import RoomCameraPanel from '../../../components/RoomCameraPanel/index.jsx';
|
||||||
|
|
||||||
export default function SecondaryRow() {
|
export default function SecondaryRow() {
|
||||||
return (
|
return (
|
||||||
|
|||||||
Reference in New Issue
Block a user