diff --git a/webui/src/App.jsx b/webui/src/App.jsx index b2e86378..a251b6aa 100644 --- a/webui/src/App.jsx +++ b/webui/src/App.jsx @@ -81,7 +81,7 @@ function DesktopLayout({ layout, onOpenHelpOverlay }) {
- + {/* */}
diff --git a/webui/src/components/AdminPanel/AdminIpLogPanel.jsx b/webui/src/components/AdminPanel/AdminIpLogPanel.jsx index 1adbe1a6..ba10522c 100644 --- a/webui/src/components/AdminPanel/AdminIpLogPanel.jsx +++ b/webui/src/components/AdminPanel/AdminIpLogPanel.jsx @@ -1,15 +1,13 @@ // Admin Ip Log Panel // Purpose: Defines the Admin Ip Log Panel module and the local helpers/components used in this file. // Scope: Keeps behavior unchanged while isolating this concern into a clear, single-responsibility unit. +import CardFrame from '../CardFrame/index.jsx'; + export default function AdminIpLogPanel({ entries }) { const logs = entries || []; return ( -
-
- Admin IP log - {logs.length} -
-
+ +
{logs.length === 0 ? (

No admin log entries yet.

) : ( @@ -17,7 +15,7 @@ export default function AdminIpLogPanel({ entries }) { .slice() .reverse() .map((entry) => ( -
+
{entry.ts ? new Date(entry.ts).toLocaleTimeString() : '--'}{' '} {entry.label && [{entry.label}]}{' '} {entry.message}{' '} @@ -28,6 +26,6 @@ export default function AdminIpLogPanel({ entries }) { )}

Admin-only log stream; IPs never appear in user data.

-
+ ); } diff --git a/webui/src/components/AdminPanel/AdminPanelContent.jsx b/webui/src/components/AdminPanel/AdminPanelContent.jsx index 8c3bc79f..6c165e77 100644 --- a/webui/src/components/AdminPanel/AdminPanelContent.jsx +++ b/webui/src/components/AdminPanel/AdminPanelContent.jsx @@ -8,6 +8,7 @@ import LlmCommentaryPanel from './LlmCommentaryPanel.jsx'; import OverseerControlPanel from './OverseerControlPanel.jsx'; import ReplaySnapshotHealth from './ReplaySnapshotHealth.jsx'; import AdminIpLogPanel from './AdminIpLogPanel.jsx'; +import CardFrame from '../CardFrame/index.jsx'; const MODES = [ { key: 'open', label: 'Open' }, @@ -279,10 +280,7 @@ export default function AdminPanelContent() { if (!isAdmin) return null; - return ( -
-
- Admin controls + const actions = ( -
+ ); + + return ( +
+ ); } diff --git a/webui/src/components/AuthPanel/index.jsx b/webui/src/components/AuthPanel/index.jsx index 98e5b93e..f5d68aed 100644 --- a/webui/src/components/AuthPanel/index.jsx +++ b/webui/src/components/AuthPanel/index.jsx @@ -3,9 +3,10 @@ // Scope: Keeps behavior unchanged while isolating this concern into a clear, single-responsibility unit. import { useState } from 'react'; import { useSessionActions } from '../../context/SessionContext.jsx'; +import CardFrame from '../CardFrame/index.jsx'; export default function AuthPanel() { - const { login, setRole } = useSessionActions(); + const { login } = useSessionActions(); const [username, setUsername] = useState(''); const [password, setPassword] = useState(''); const [loading, setLoading] = useState(false); @@ -25,8 +26,7 @@ export default function AuthPanel() { } return ( -
-

Admin login

+
*/} -
+
); } diff --git a/webui/src/components/CameraTiltControl/index.jsx b/webui/src/components/CameraTiltControl/index.jsx index 0e130545..59bfce5e 100644 --- a/webui/src/components/CameraTiltControl/index.jsx +++ b/webui/src/components/CameraTiltControl/index.jsx @@ -10,7 +10,7 @@ function formatDegrees(value) { function KeyPill({ label }) { if (!label) return null; - return {label}; + return {label}; } export default function CameraTiltControl({ @@ -143,10 +143,14 @@ export default function CameraTiltControl({ } return ( -
+
{label} - {showValue ? {formatDegrees(value)} : null} + {showValue ? ( + + {formatDegrees(value)} + + ) : null}
{slider} diff --git a/webui/src/components/CardFrame/index.jsx b/webui/src/components/CardFrame/index.jsx index ddbee32a..7c97efeb 100644 --- a/webui/src/components/CardFrame/index.jsx +++ b/webui/src/components/CardFrame/index.jsx @@ -53,14 +53,14 @@ export default function CardFrame({ const cardStyle = accentRgb ? { borderColor: rgba(accentRgb, 0.35) } : undefined; const headerStyle = accentRgb ? { - backgroundImage: `linear-gradient(90deg, rgba(23,23,23,0.96) 0%, rgba(38,38,38,0.94) 58%, ${rgba(accentRgb, 0.18)} 100%)`, + backgroundImage: `linear-gradient(90deg, rgba(23,23,23,0.96) 0%, rgba(38,38,38,0.94) 30%, ${rgba(accentRgb, 0.28)} 100%)`, } : undefined; return (
-
+
{sorted.length === 0 && typingRows.length === 0 ? (

No messages yet.

) : ( diff --git a/webui/src/components/DriverVideo/index.jsx b/webui/src/components/DriverVideo/index.jsx index 375fef1c..e06d0a54 100644 --- a/webui/src/components/DriverVideo/index.jsx +++ b/webui/src/components/DriverVideo/index.jsx @@ -10,6 +10,7 @@ import OvercurrentOverlay from '../HudOverlays/OvercurrentOverlay/index.jsx'; import LowBatteryOverlay from '../HudOverlays/LowBatteryOverlay/index.jsx'; import DriverBottomStrip from '../HudOverlays/DriverBottomStrip/index.jsx'; import HudChatInput from '../HudOverlays/HudChatInput/index.jsx'; +import CardFrame from '../CardFrame/index.jsx'; export default function DriverVideo({ layoutFormat = 'desktop' }) { const roverId = useSessionSelector((state) => state.session?.assignment?.roverId ?? null); @@ -20,7 +21,7 @@ export default function DriverVideo({ layoutFormat = 'desktop' }) { if (!roverId) { return ( -
+

You are not assigned to a rover.

@@ -29,13 +30,13 @@ export default function DriverVideo({ layoutFormat = 'desktop' }) {

-
+ ); } const mobileHud = layoutFormat !== 'desktop'; return ( -
+
@@ -58,6 +59,6 @@ export default function DriverVideo({ layoutFormat = 'desktop' }) {
-
+ ); } diff --git a/webui/src/components/GamepadMappingSettings/GamepadMappingSettingsContent.jsx b/webui/src/components/GamepadMappingSettings/GamepadMappingSettingsContent.jsx index 4d3ee2c5..3dc34dd8 100644 --- a/webui/src/components/GamepadMappingSettings/GamepadMappingSettingsContent.jsx +++ b/webui/src/components/GamepadMappingSettings/GamepadMappingSettingsContent.jsx @@ -9,6 +9,7 @@ import { createProfileForPad, } from '../../controls/inputs/gamepadBindings.js'; import { useGamepadHubState } from '../../controls/inputs/gamepadHub.js'; +import CardFrame from '../CardFrame/index.jsx'; import SliderField from './SliderField.jsx'; import { ACTIONS, NUMBER_FORMAT } from './constants.js'; import { @@ -222,20 +223,16 @@ export default function GamepadMappingSettings() { }, [activePad, activeProfile]); return ( -
-
-
-

Controller

-

- {activePad ? 'Move sticks or press buttons to bind' : 'Connect a controller to configure.'} -

- {captureAction && ( -

Capturing {captureAction.label}…

- )} -
-
+ + {captureAction && ( +

Capturing {captureAction.label}…

+ )} -
+

Connected controller

{hubState.pads.length === 0 ? (

No controller detected.

@@ -257,7 +254,7 @@ export default function GamepadMappingSettings() { )}
-
+

Calibration

updateCalibration({ auxSideScale: value })} /> -
+ ); } diff --git a/webui/src/components/OvercurrentLimiterPanel/index.jsx b/webui/src/components/OvercurrentLimiterPanel/index.jsx index 93b83409..2d8d9fd9 100644 --- a/webui/src/components/OvercurrentLimiterPanel/index.jsx +++ b/webui/src/components/OvercurrentLimiterPanel/index.jsx @@ -4,6 +4,7 @@ import { useMemo } from 'react'; import { useControlSystem } from '../../controls/index.js'; import { OVERCURRENT_GROUPS } from '../../controls/overcurrentLimiter.js'; +import CardFrame from '../CardFrame/index.jsx'; const GROUP_LABELS = { drive: 'Drive wheels', @@ -32,11 +33,11 @@ export default function OvercurrentLimiterPanel() { const groups = useMemo(() => OVERCURRENT_GROUPS.map((group) => group.key), []); return ( -
-
- Overcurrent limiter - {overcurrentLimiter?.adminImmune ? 'Admin immune' : 'Active'} -
+ {!roverId ? (

Assign a rover to view limiter status.

) : ( @@ -46,7 +47,7 @@ export default function OvercurrentLimiterPanel() { const over = overcurrentLimiter?.overcurrent?.groups?.[key] ?? false; const scale = overcurrentLimiter?.scales?.perGroup?.[key] ?? 1; return ( -
+
{GROUP_LABELS[key] || key} @@ -67,13 +68,13 @@ export default function OvercurrentLimiterPanel() {
); })} -
+
{`Down rate ${overcurrentLimiter?.config?.downRatePerSec}/s · Up rate ${overcurrentLimiter?.config?.upRatePerSec}/s`}
{`Release delay ${overcurrentLimiter?.config?.releaseDelaySec}s`}
{`Output rate ${overcurrentLimiter?.config?.outputRateMs}ms`}
)} -
+ ); } diff --git a/webui/src/components/RawUserPilePanel/index.jsx b/webui/src/components/RawUserPilePanel/index.jsx index 0161ebee..01dc0265 100644 --- a/webui/src/components/RawUserPilePanel/index.jsx +++ b/webui/src/components/RawUserPilePanel/index.jsx @@ -70,7 +70,7 @@ export default function RawUserPilePanel({
-
+
@@ -82,7 +82,7 @@ export default function RawUserPilePanel({
{sorted.length === 0 ? (

Waiting for users…

@@ -90,7 +90,7 @@ export default function RawUserPilePanel({ sorted.map((user) => ( {formatLabel(user, selfId)} diff --git a/webui/src/components/RightPaneTabs/index.jsx b/webui/src/components/RightPaneTabs/index.jsx index c398c930..5cf720ba 100644 --- a/webui/src/components/RightPaneTabs/index.jsx +++ b/webui/src/components/RightPaneTabs/index.jsx @@ -76,10 +76,15 @@ function DriveDockPanel() { const cameraDisabled = Boolean(!roverId || dockAssist.cameraLocked); return ( -
+ {!hideInlineControls ? ( -
+
{nightVisionAvailable && ( )}
) : null} -
+ ); } diff --git a/webui/src/components/RoomCameraPanel/index.jsx b/webui/src/components/RoomCameraPanel/index.jsx index a609d9de..a740cf54 100644 --- a/webui/src/components/RoomCameraPanel/index.jsx +++ b/webui/src/components/RoomCameraPanel/index.jsx @@ -10,10 +10,10 @@ import CardFrame from '../CardFrame/index.jsx'; function EmptyState() { return ( -
+

No room cameras configured.

Add entries to server config to populate this list.

-
+ ); } @@ -97,7 +97,7 @@ export default function RoomCameraPanel({ {cameras.map((camera) => { const feed = feedMap[camera.id] || null; return ( -
+
{/*

{camera.name || camera.id}

{camera.description &&

{camera.description}

} diff --git a/webui/src/components/SessionSnapshot/index.jsx b/webui/src/components/SessionSnapshot/index.jsx index 0064566a..b9bc3a35 100644 --- a/webui/src/components/SessionSnapshot/index.jsx +++ b/webui/src/components/SessionSnapshot/index.jsx @@ -3,16 +3,16 @@ // Scope: Keeps behavior unchanged while isolating this concern into a clear, single-responsibility unit. import { useMemo } from 'react'; import { useSession } from '../../context/SessionContext.jsx'; +import CardFrame from '../CardFrame/index.jsx'; export default function SessionSnapshot() { const { session } = useSession(); const payload = useMemo(() => JSON.stringify(session ?? {}, null, 2), [session]); return ( -
-

Session snapshot

-
+    
+      
         {payload}
       
-
+ ); } diff --git a/webui/src/components/SettingsPanel/index.jsx b/webui/src/components/SettingsPanel/index.jsx index ad811c0e..cb6f50a9 100644 --- a/webui/src/components/SettingsPanel/index.jsx +++ b/webui/src/components/SettingsPanel/index.jsx @@ -11,6 +11,7 @@ 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 CardFrame from '../CardFrame/index.jsx'; import { useHudMapSetting } from '../../hooks/useHudMapSetting.js'; import { useSettingsNamespace } from '../../settings/index.js'; import { useSocket } from '../../context/SocketContext.jsx'; @@ -121,8 +122,7 @@ export default function SettingsPanel() {
-
-

HUD

+

Mobile HUD keeps the map on by default.

-
-
-

Mobile controls

+ + -
-
-

Macros

+ + -
-
-

Audio

+ +
-
-

Connection

+ +