fixing up mobile layouts

This commit is contained in:
legop3
2025-11-22 19:10:50 -05:00
parent 3be9e17ecc
commit 930f411549
10 changed files with 62 additions and 53 deletions
+4 -4
View File
@@ -14,14 +14,14 @@
<!-- 2. pi-side sensor throttle (1/5th) --> <!-- 2. pi-side sensor throttle (1/5th) -->
<!-- 3. convert rover roster to a reusable component, unbake it from telemetry and admin panels --> <!-- 3. convert rover roster to a reusable component, unbake it from telemetry and admin panels -->
<!-- 4. room cameras --> <!-- 4. room cameras -->
4. fix gamepad input - add manual inversions? 4. fix gamepad input - more complex axis assignment? division, combined axes?
<!-- 5. home assistant controls --> <!-- 5. home assistant controls -->
<!-- 6. nicknames --> <!-- 6. nicknames -->
<!-- 7. discord bot --> <!-- 7. discord bot -->
8. turns mode display <!-- 8. turns mode display -->
<!-- 9. online user list --> <!-- 9. online user list -->
<!-- 10. chat --> <!-- 10. chat -->
9. discord invite button <!-- 9. discord invite button -->
10. redo both mobile layouts 10. redo both mobile layouts
11. redo spectator view (last) 11. redo spectator view (last)
21. finally.. set the favicon 21. finally.. set the favicon and title
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -5,8 +5,8 @@
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> <link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>webui</title> <title>webui</title>
<script type="module" crossorigin src="/assets/index-D4k56rvM.js"></script> <script type="module" crossorigin src="/assets/index-CoT66lYm.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-DUduiIaK.css"> <link rel="stylesheet" crossorigin href="/assets/index-DQGzoV_h.css">
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>
+15 -9
View File
@@ -72,8 +72,10 @@ function DesktopLayout({ layout }) {
function MobilePortraitLayout() { function MobilePortraitLayout() {
return ( return (
<div className="flex flex-col gap-0.5"> <div className="flex flex-col gap-0.5">
<DriverVideoPanel /> <DriverVideoPanel layoutFormat='mobile'/>
<MobileControls /> <MobileControls />
<ChatPanel />
<UserListPanel />
<DrivePanel /> <DrivePanel />
<TelemetryPanel /> <TelemetryPanel />
<AuthPanel /> <AuthPanel />
@@ -90,23 +92,27 @@ function MobileLandscapeLayout() {
<div className="flex flex-col gap-0.5"> <div className="flex flex-col gap-0.5">
<section className="grid min-h-screen grid-cols-[minmax(0,0.7fr)_minmax(0,2.1fr)_minmax(0,0.7fr)] gap-0.5"> <section className="grid min-h-screen grid-cols-[minmax(0,0.7fr)_minmax(0,2.1fr)_minmax(0,0.7fr)] gap-0.5">
<MobileLandscapeAuxColumn /> <MobileLandscapeAuxColumn />
<DriverVideoPanel /> <div>
<DriverVideoPanel layoutFormat='mobile'/>
<TelemetryPanel />
</div>
<MobileLandscapeControlColumn /> <MobileLandscapeControlColumn />
</section> </section>
<div className="flex flex-col gap-0.5 pb-0.5"> <div className="flex flex-col gap-0.5 pb-0.5">
<RoomCameraPanel />
<HomeAssistantControls />
<DrivePanel />
<section className="grid grid-cols-[minmax(0,1fr)_minmax(0,1fr)] gap-0.5"> <section className="grid grid-cols-[minmax(0,1fr)_minmax(0,1fr)] gap-0.5">
<div className="flex flex-col gap-0.5"> <div className="flex flex-col gap-0.5">
<AuthPanel /> {/* <AuthPanel /> */}
<AdminPanel /> {/* <AdminPanel /> */}
<UserListPanel />
</div> </div>
<div className="flex flex-col gap-0.5"> <div className="flex flex-col gap-0.5">
<LogPanel /> {/* <LogPanel /> */}
<TelemetryPanel /> <ChatPanel />
</div> </div>
</section> </section>
<RoomCameraPanel />
<HomeAssistantControls />
{/* <DrivePanel /> */}
</div> </div>
</div> </div>
); );
+2 -2
View File
@@ -3,7 +3,7 @@ import { useTelemetryFrame } from '../context/TelemetryContext.jsx';
import { useVideoRequests } from '../hooks/useVideoRequests.js'; import { useVideoRequests } from '../hooks/useVideoRequests.js';
import VideoTile from './VideoTile.jsx'; import VideoTile from './VideoTile.jsx';
export default function DriverVideoPanel() { export default function DriverVideoPanel({layoutFormat = 'desktop'}) {
const { session } = useSession(); const { session } = useSession();
const roverId = session?.assignment?.roverId; const roverId = session?.assignment?.roverId;
const sources = useVideoRequests(roverId ? [roverId] : []); const sources = useVideoRequests(roverId ? [roverId] : []);
@@ -20,7 +20,7 @@ export default function DriverVideoPanel() {
return ( return (
<section className="panel"> <section className="panel">
{roverId ? ( {roverId ? (
<VideoTile sessionInfo={info} label={roverLabel} telemetryFrame={frame} batteryConfig={batteryConfig} /> <VideoTile sessionInfo={info} label={roverLabel} telemetryFrame={frame} batteryConfig={batteryConfig} layoutFormat={layoutFormat}/>
) : ( ) : (
<div className="panel-muted content-center text-center text-sm text-slate-400 aspect-video"> <div className="panel-muted content-center text-center text-sm text-slate-400 aspect-video">
<p>You are not assigned to a rover.</p> <p>You are not assigned to a rover.</p>
+18 -16
View File
@@ -7,13 +7,13 @@ const SOURCE = 'mobile-joystick';
const JOYSTICK_RADIUS = 80; const JOYSTICK_RADIUS = 80;
const JOYSTICK_SMOOTHING = 0.15; const JOYSTICK_SMOOTHING = 0.15;
const AUX_BUTTONS = [ const AUX_BUTTONS = [
{ id: 'main-forward', label: 'Main +', values: { main: 127 }, hold: true, color: 'bg-emerald-600' }, { id: 'main-forward', label: 'Main Brush +', values: { main: 127 }, hold: true, color: 'bg-emerald-600' },
{ id: 'main-reverse', label: 'Main -', values: { main: -127 }, hold: true, color: 'bg-emerald-800' }, { id: 'main-reverse', label: 'Main Brush -', values: { main: -127 }, hold: true, color: 'bg-emerald-800' },
{ id: 'side-forward', label: 'Side +', values: { side: 127 }, hold: true, color: 'bg-cyan-600' }, { id: 'all-forward', label: 'All Motors +', values: { main: 127, side: 127, vacuum: 127 }, hold: true, color: 'bg-fuchsia-600' },
{ id: 'side-reverse', label: 'Side -', values: { side: -70 }, hold: true, color: 'bg-cyan-800' }, { id: 'side-forward', label: 'Side Brush +', values: { side: 127 }, hold: true, color: 'bg-cyan-600' },
{ id: 'side-reverse', label: 'Side Brush -', values: { side: -70 }, hold: true, color: 'bg-cyan-800' },
{ id: 'vacuum-fast', label: 'Vacuum Max', values: { vacuum: 127 }, hold: true, color: 'bg-amber-500 text-amber-950' }, { id: 'vacuum-fast', label: 'Vacuum Max', values: { vacuum: 127 }, hold: true, color: 'bg-amber-500 text-amber-950' },
{ id: 'vacuum-slow', label: 'Vacuum Low', values: { vacuum: 50 }, hold: true, color: 'bg-amber-700' }, { id: 'vacuum-slow', label: 'Vacuum Low', values: { vacuum: 50 }, hold: true, color: 'bg-amber-700' },
{ id: 'all-forward', label: 'All +', values: { main: 127, side: 127, vacuum: 127 }, hold: true, color: 'bg-fuchsia-600' },
{ id: 'stop-all', label: 'Stop', values: { main: 0, side: 0, vacuum: 0 }, hold: false, color: 'bg-slate-900' }, { id: 'stop-all', label: 'Stop', values: { main: 0, side: 0, vacuum: 0 }, hold: false, color: 'bg-slate-900' },
]; ];
@@ -196,17 +196,8 @@ function MobileJoystickPanel({ layout }) {
return ( return (
<div className="flex flex-col gap-0.5 text-slate-100"> <div className="flex flex-col gap-0.5 text-slate-100">
<DriveModeToggle size="compact" /> <DriveModeToggle size="compact" />
<FloatingJoystick
disabled={disabled}
layout={layout}
radius={joystickRadius}
onMove={handleMove}
onStop={handleStop}
/>
<button type="button" onClick={stopAllMotion} disabled={disabled} className="button-danger w-full disabled:opacity-40">
Panic Stop
</button>
{cameraEnabled && ( {cameraEnabled && (
<div className="bg-zinc-950 p-0.5 text-xs"> <div className="bg-zinc-950 p-0.5 text-xs">
<div className="flex items-center justify-between text-[0.75rem] text-slate-400"> <div className="flex items-center justify-between text-[0.75rem] text-slate-400">
@@ -224,6 +215,17 @@ function MobileJoystickPanel({ layout }) {
/> />
</div> </div>
)} )}
<FloatingJoystick
disabled={disabled}
layout={layout}
radius={joystickRadius}
onMove={handleMove}
onStop={handleStop}
/>
<button type="button" onClick={stopAllMotion} disabled={disabled} className="button-danger w-full disabled:opacity-40">
Panic Stop
</button>
</div> </div>
); );
} }
@@ -280,7 +282,7 @@ function AuxMotorPanel({ orientation }) {
onPointerLeave={() => handleRelease(button)} onPointerLeave={() => handleRelease(button)}
onPointerCancel={() => handleRelease(button)} onPointerCancel={() => handleRelease(button)}
onContextMenu={(event) => event.preventDefault()} onContextMenu={(event) => event.preventDefault()}
className={`px-0.5 py-0.5 text-left text-sm font-semibold text-white transition ${button.color} hover:brightness-110 disabled:opacity-30`} className={`px-0.5 py-0.5 text-left text-sm font-semibold text-white transition ${button.color} hover:brightness-110 disabled:opacity-30 h-10`}
> >
{button.label} {button.label}
</button> </button>
+9 -8
View File
@@ -4,7 +4,7 @@ import { WhepPlayer } from '../lib/whepPlayer.js';
const RESTART_DELAY_MS = 2000; const RESTART_DELAY_MS = 2000;
const UNMUTE_RETRY_MS = 3000; const UNMUTE_RETRY_MS = 3000;
export default function VideoTile({ sessionInfo, label, forceMute = false, telemetryFrame, batteryConfig }) { export default function VideoTile({ sessionInfo, label, forceMute = false, telemetryFrame, batteryConfig, layoutFormat = 'desktop' }) {
const videoRef = useRef(null); const videoRef = useRef(null);
const restartTimer = useRef(null); const restartTimer = useRef(null);
const unmuteTimer = useRef(null); const unmuteTimer = useRef(null);
@@ -14,6 +14,7 @@ export default function VideoTile({ sessionInfo, label, forceMute = false, telem
const [muted, setMuted] = useState(true); const [muted, setMuted] = useState(true);
const sensors = telemetryFrame?.sensors; const sensors = telemetryFrame?.sensors;
const batteryCharge = sensors?.batteryChargeMah ?? null; const batteryCharge = sensors?.batteryChargeMah ?? null;
const desktopLayout = layoutFormat === 'desktop';
// console.log('[BatteryBarDebug]', { // console.log('[BatteryBarDebug]', {
// frameSensors: sensors, // frameSensors: sensors,
// batteryCharge, // batteryCharge,
@@ -157,7 +158,7 @@ export default function VideoTile({ sessionInfo, label, forceMute = false, telem
controls={false} controls={false}
className="h-full w-full object-contain" className="h-full w-full object-contain"
/> />
<HudOverlay frame={telemetryFrame} label={label} status={renderedStatus} /> <HudOverlay frame={telemetryFrame} label={label} status={renderedStatus} desktopLayout={desktopLayout}/>
<OvercurrentOverlay motors={overcurrentMotors} /> <OvercurrentOverlay motors={overcurrentMotors} />
<LowBatteryOverlay charge={batteryCharge} config={batteryConfig} /> <LowBatteryOverlay charge={batteryCharge} config={batteryConfig} />
</div> </div>
@@ -200,7 +201,7 @@ function BatteryBar({ charge, config }) {
); );
} }
function HudOverlay({ frame, label, status }) { function HudOverlay({ frame, label, status, desktopLayout = true }) {
const sensors = frame?.sensors; const sensors = frame?.sensors;
const bumps = sensors?.bumpsAndWheelDrops || {}; const bumps = sensors?.bumpsAndWheelDrops || {};
const [now, setNow] = useState(() => Date.now()); const [now, setNow] = useState(() => Date.now());
@@ -224,11 +225,11 @@ function HudOverlay({ frame, label, status }) {
{/* bump and wheel drops bar */} {/* bump and wheel drops bar */}
<div className="absolute top-0.5 left-1/2 flex -translate-x-1/2 gap-1 bg-black/70 px-0.5 py-0.5 text-[0.6rem] font-medium text-slate-200"> <div className="absolute top-0.5 left-1/2 flex -translate-x-1/2 gap-1 bg-black/70 text-[0.6rem] font-medium text-slate-200">
<div className={`px-1 py-0.5 ${bumps.bumpLeft ? 'bg-red-600 text-white animate-pulse' : 'text-slate-500'}`}>Left bump</div> <div className={`${desktopLayout ? 'px-1 py-0.5':'px-0.5 py-0 text-nowrap'} ${bumps.bumpLeft ? 'bg-red-600 text-white animate-pulse' : 'text-slate-500'}`}>Left bump</div>
<div className={`px-1 py-0.5 ${bumps.wheelDropLeft ? 'bg-red-600 text-white animate-pulse' : 'text-slate-500'}`}>Left wheel drop</div> <div className={`${desktopLayout ? 'px-1 py-0.5':'px-0.5 py-0 text-nowrap'} ${bumps.wheelDropLeft ? 'bg-red-600 text-white animate-pulse' : 'text-slate-500'}`}>Left wheel drop</div>
<div className={`px-1 py-0.5 ${bumps.wheelDropRight ? 'bg-red-600 text-white animate-pulse' : 'text-slate-500'}`}>Right wheel drop</div> <div className={`${desktopLayout ? 'px-1 py-0.5':'px-0.5 py-0 text-nowrap'} ${bumps.wheelDropRight ? 'bg-red-600 text-white animate-pulse' : 'text-slate-500'}`}>Right wheel drop</div>
<div className={`px-1 py-0.5 ${bumps.bumpRight ? 'bg-red-600 text-white animate-pulse' : 'text-slate-500'}`}>Right bump</div> <div className={`${desktopLayout ? 'px-1 py-0.5':'px-0.5 py-0 text-nowrap'} ${bumps.bumpRight ? 'bg-red-600 text-white animate-pulse' : 'text-slate-500'}`}>Right bump</div>
</div> </div>
</div> </div>
); );