mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-16 01:21:20 -04:00
top-down in spectator
This commit is contained in:
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -11,7 +11,7 @@
|
|||||||
<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-C6EqAuKU.js"></script>
|
<script type="module" crossorigin src="/assets/index-BBUSDixK.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-DkBejE8J.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-DkBejE8J.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -52,6 +52,8 @@ export default function VideoTile({
|
|||||||
hudVariant = 'default',
|
hudVariant = 'default',
|
||||||
driverLabel = null,
|
driverLabel = null,
|
||||||
songNote = null,
|
songNote = null,
|
||||||
|
hudForceMap = false,
|
||||||
|
hudMapPosition = 'top-right',
|
||||||
}) {
|
}) {
|
||||||
const videoRef = useRef(null);
|
const videoRef = useRef(null);
|
||||||
const audioRef = useRef(null);
|
const audioRef = useRef(null);
|
||||||
@@ -70,7 +72,7 @@ export default function VideoTile({
|
|||||||
const desktopLayout = layoutFormat === 'desktop';
|
const desktopLayout = layoutFormat === 'desktop';
|
||||||
const mobileHud = !desktopLayout;
|
const mobileHud = !desktopLayout;
|
||||||
const [showHudMapDesktop, setShowHudMapDesktop] = useHudMapSetting();
|
const [showHudMapDesktop, setShowHudMapDesktop] = useHudMapSetting();
|
||||||
const showHudMap = mobileHud ? true : showHudMapDesktop;
|
const showHudMap = hudForceMap ? true : mobileHud ? true : showHudMapDesktop;
|
||||||
const batteryVisual = buildBatteryVisual(batteryCharge, batteryConfig);
|
const batteryVisual = buildBatteryVisual(batteryCharge, batteryConfig);
|
||||||
// console.log('[BatteryBarDebug]', {
|
// console.log('[BatteryBarDebug]', {
|
||||||
// frameSensors: sensors,
|
// frameSensors: sensors,
|
||||||
@@ -284,6 +286,7 @@ export default function VideoTile({
|
|||||||
songNote={songNote}
|
songNote={songNote}
|
||||||
showTopDown={showHudMap}
|
showTopDown={showHudMap}
|
||||||
mobileHud={mobileHud}
|
mobileHud={mobileHud}
|
||||||
|
mapPosition={hudMapPosition}
|
||||||
/>
|
/>
|
||||||
<OvercurrentOverlay motors={overcurrentMotors} />
|
<OvercurrentOverlay motors={overcurrentMotors} />
|
||||||
<LowBatteryOverlay charge={batteryCharge} config={batteryConfig} />
|
<LowBatteryOverlay charge={batteryCharge} config={batteryConfig} />
|
||||||
@@ -348,6 +351,7 @@ function HudOverlay({
|
|||||||
songNote = null,
|
songNote = null,
|
||||||
showTopDown = false,
|
showTopDown = false,
|
||||||
mobileHud = false,
|
mobileHud = false,
|
||||||
|
mapPosition = 'top-right',
|
||||||
}) {
|
}) {
|
||||||
const bumps = sensors?.bumpsAndWheelDrops || {};
|
const bumps = sensors?.bumpsAndWheelDrops || {};
|
||||||
const [now, setNow] = useState(() => Date.now());
|
const [now, setNow] = useState(() => Date.now());
|
||||||
@@ -358,6 +362,16 @@ function HudOverlay({
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const pulse = frame?.receivedAt ? now - frame.receivedAt < 200 : false;
|
const pulse = frame?.receivedAt ? now - frame.receivedAt < 200 : false;
|
||||||
|
const mapStyle = {
|
||||||
|
width: '240px',
|
||||||
|
height: '240px',
|
||||||
|
opacity: 0.7,
|
||||||
|
transform: `scale(${mobileHud ? 0.55 : 0.7})`,
|
||||||
|
transformOrigin: mapPosition === 'bottom-left' ? 'bottom left' : 'top right',
|
||||||
|
...(mapPosition === 'bottom-left'
|
||||||
|
? { left: '0.25rem', bottom: '0.25rem' }
|
||||||
|
: { right: '0.25rem', top: '0.25rem' }),
|
||||||
|
};
|
||||||
|
|
||||||
if (variant === 'spectator') {
|
if (variant === 'spectator') {
|
||||||
const telemetryEntries = [
|
const telemetryEntries = [
|
||||||
@@ -378,7 +392,8 @@ function HudOverlay({
|
|||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
<div className="absolute left-1 top-1/2 flex -translate-y-1/2 flex-col gap-0.25 bg-black/70 px-1 py-0.75 text-[0.65rem] text-slate-100">
|
<div className="absolute left-1 top-1/2 flex -translate-y-1/2 flex-col gap-0.5 bg-black/70 px-1 py-0.75 text-[0.65rem] text-slate-100">
|
||||||
|
<div className="space-y-0.25">
|
||||||
<span className="text-[0.6rem] uppercase tracking-wide text-slate-400">Telemetry</span>
|
<span className="text-[0.6rem] uppercase tracking-wide text-slate-400">Telemetry</span>
|
||||||
{telemetryEntries.map(([labelText, value]) => (
|
{telemetryEntries.map(([labelText, value]) => (
|
||||||
<span key={labelText} className="flex items-center justify-between gap-0.5">
|
<span key={labelText} className="flex items-center justify-between gap-0.5">
|
||||||
@@ -387,26 +402,28 @@ function HudOverlay({
|
|||||||
</span>
|
</span>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
{showTopDown ? (
|
||||||
|
<div className="flex items-center justify-center">
|
||||||
|
<div
|
||||||
|
className="pointer-events-none rounded"
|
||||||
|
style={{
|
||||||
|
width: '160px',
|
||||||
|
height: '160px',
|
||||||
|
opacity: 0.8,
|
||||||
|
transform: 'scale(0.7)',
|
||||||
|
transformOrigin: 'top left',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<TopDownMap sensors={sensors} size={200} overlay />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="absolute bottom-0.5 left-1/2 flex -translate-x-1/2 items-center gap-1 bg-black/80 px-1 py-0.5 text-[0.8rem] text-slate-100">
|
<div className="absolute bottom-0.5 left-1/2 flex -translate-x-1/2 items-center gap-1 bg-black/80 px-1 py-0.5 text-[0.8rem] text-slate-100">
|
||||||
<span className="font-semibold text-white">{label || 'Unnamed Rover'}</span>
|
<span className="font-semibold text-white">{label || 'Unnamed Rover'}</span>
|
||||||
{driverLabel ? <span className="text-slate-300">• {driverLabel}</span> : null}
|
{driverLabel ? <span className="text-slate-300">• {driverLabel}</span> : null}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{showTopDown ? (
|
|
||||||
<div
|
|
||||||
className="pointer-events-none absolute right-1 top-1"
|
|
||||||
style={{
|
|
||||||
width: '240px',
|
|
||||||
height: '240px',
|
|
||||||
opacity: 0.7,
|
|
||||||
transform: `scale(${mobileHud ? 0.55 : 0.7})`,
|
|
||||||
transformOrigin: 'top right',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<TopDownMap sensors={sensors} size={240} overlay />
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -427,15 +444,13 @@ function HudOverlay({
|
|||||||
{/* <span>{pulse ? 'Sensors active' : 'No recent sensors'}</span> */}
|
{/* <span>{pulse ? 'Sensors active' : 'No recent sensors'}</span> */}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{showTopDown ? (
|
{showTopDown && variant !== 'spectator' ? (
|
||||||
<div
|
<div
|
||||||
className="pointer-events-none absolute right-1 top-1"
|
className="pointer-events-none absolute rounded"
|
||||||
style={{
|
style={{
|
||||||
width: '240px',
|
...mapStyle,
|
||||||
height: '240px',
|
padding: '0.1rem',
|
||||||
opacity: 0.7,
|
background: 'rgba(0,0,0,0.6)',
|
||||||
transform: `scale(${mobileHud ? 0.55 : 0.7})`,
|
|
||||||
transformOrigin: 'top right',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<TopDownMap sensors={sensors} size={240} overlay />
|
<TopDownMap sensors={sensors} size={240} overlay />
|
||||||
|
|||||||
@@ -1,28 +1,10 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useSettingsNamespace } from '../settings/index.js';
|
||||||
|
|
||||||
export function useHudMapSetting() {
|
export function useHudMapSetting() {
|
||||||
const [enabled, setEnabled] = useState(() => {
|
const { value, save } = useSettingsNamespace('page', { hudMapDesktop: false });
|
||||||
if (typeof window === 'undefined') return false;
|
const enabled = Boolean(value?.hudMapDesktop);
|
||||||
return window.localStorage.getItem('hudMapDesktop') === '1';
|
const setEnabled = (next) => {
|
||||||
});
|
save({ hudMapDesktop: Boolean(next) });
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (typeof window === 'undefined') return;
|
|
||||||
window.localStorage.setItem('hudMapDesktop', enabled ? '1' : '0');
|
|
||||||
const event = new CustomEvent('hudMapDesktopChanged', { detail: enabled });
|
|
||||||
window.dispatchEvent(event);
|
|
||||||
}, [enabled]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (typeof window === 'undefined') return undefined;
|
|
||||||
const handler = (event) => {
|
|
||||||
if (typeof event.detail === 'boolean') {
|
|
||||||
setEnabled(event.detail);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
window.addEventListener('hudMapDesktopChanged', handler);
|
|
||||||
return () => window.removeEventListener('hudMapDesktopChanged', handler);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return [enabled, setEnabled];
|
return [enabled, setEnabled];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ function RoverSpectatorCard({ rover, frame, videoInfo, audioInfo, session }) {
|
|||||||
batteryConfig={rover.battery}
|
batteryConfig={rover.battery}
|
||||||
hudVariant="spectator"
|
hudVariant="spectator"
|
||||||
driverLabel={driverLabel}
|
driverLabel={driverLabel}
|
||||||
|
hudForceMap
|
||||||
|
hudMapPosition="bottom-left"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
@@ -54,6 +56,8 @@ function RoverRow({ roster, frames, videoSources, session }) {
|
|||||||
videoInfo={videoSources[rover.id]}
|
videoInfo={videoSources[rover.id]}
|
||||||
audioInfo={videoSources[`${rover.id}-audio`]}
|
audioInfo={videoSources[`${rover.id}-audio`]}
|
||||||
session={session}
|
session={session}
|
||||||
|
showHudMap
|
||||||
|
hudMapPosition="bottom-left"
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
Reference in New Issue
Block a user