mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-16 17:40:46 -04:00
made mobile HUD smaller
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
File diff suppressed because one or more lines are too long
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-D6SoVC4Y.js"></script>
|
<script type="module" crossorigin src="/assets/index-IngetVG6.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-Blc6K3HE.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-DCrU2sZz.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
+63
-30
@@ -2,7 +2,6 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
|
|||||||
import TelemetryPanel from './components/TelemetryPanel.jsx';
|
import TelemetryPanel from './components/TelemetryPanel.jsx';
|
||||||
import ControlSummary from './components/ControlSummary.jsx';
|
import ControlSummary from './components/ControlSummary.jsx';
|
||||||
import AlertFeed from './components/AlertFeed.jsx';
|
import AlertFeed from './components/AlertFeed.jsx';
|
||||||
import AdminPanel from './components/AdminPanel.jsx';
|
|
||||||
import MobileControls, {
|
import MobileControls, {
|
||||||
MobileLandscapeAuxColumn,
|
MobileLandscapeAuxColumn,
|
||||||
MobileLandscapeControlColumn,
|
MobileLandscapeControlColumn,
|
||||||
@@ -11,11 +10,9 @@ import { ControlSystemProvider, KeyboardInputManager, GamepadInputManager } from
|
|||||||
import { SettingsProvider } from './settings/index.js';
|
import { SettingsProvider } from './settings/index.js';
|
||||||
import RoomCameraPanel from './components/RoomCameraPanel.jsx';
|
import RoomCameraPanel from './components/RoomCameraPanel.jsx';
|
||||||
import LogPanel from './components/LogPanel.jsx';
|
import LogPanel from './components/LogPanel.jsx';
|
||||||
import AuthPanel from './components/AuthPanel.jsx';
|
|
||||||
import DriverVideoPanel from './components/DriverVideoPanel.jsx';
|
import DriverVideoPanel from './components/DriverVideoPanel.jsx';
|
||||||
import RightPaneTabs from './components/RightPaneTabs.jsx';
|
import RightPaneTabs from './components/RightPaneTabs.jsx';
|
||||||
import ModeGateOverlay from './components/ModeGateOverlay.jsx';
|
import ModeGateOverlay from './components/ModeGateOverlay.jsx';
|
||||||
import SessionSnapshot from './components/SessionSnapshot.jsx';
|
|
||||||
import HomeAssistantControls from './components/HomeAssistantControls.jsx';
|
import HomeAssistantControls from './components/HomeAssistantControls.jsx';
|
||||||
import TurnAlertListener from './components/TurnAlertListener.jsx';
|
import TurnAlertListener from './components/TurnAlertListener.jsx';
|
||||||
import UserListPanel from './components/UserListPanel.jsx';
|
import UserListPanel from './components/UserListPanel.jsx';
|
||||||
@@ -24,6 +21,9 @@ import FullscreenPrompt from './components/FullscreenPrompt.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.jsx';
|
||||||
|
import HelpPanel from './components/HelpPanel.jsx';
|
||||||
|
import SettingsPanel from './components/SettingsPanel.jsx';
|
||||||
|
import Tabs, { Tab, TabList, TabPanel, TabPanels } from './components/Tabs.jsx';
|
||||||
|
|
||||||
function useLayoutMode() {
|
function useLayoutMode() {
|
||||||
const [mode, setMode] = useState(() => {
|
const [mode, setMode] = useState(() => {
|
||||||
@@ -78,25 +78,66 @@ function DesktopLayout({ layout, onOpenHelpOverlay }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function MobilePortraitLayout() {
|
function MobileFeatureTabs({
|
||||||
|
layout,
|
||||||
|
onOpenHelpOverlay,
|
||||||
|
roomPanelId,
|
||||||
|
showTelemetry = true,
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<section className="panel text-base">
|
||||||
|
<Tabs defaultTab="chat">
|
||||||
|
<TabList>
|
||||||
|
<Tab id="chat">Chat</Tab>
|
||||||
|
<Tab id="roomcontrols">Room Controls</Tab>
|
||||||
|
<Tab id="help">Help</Tab>
|
||||||
|
<Tab id="settings">Settings</Tab>
|
||||||
|
</TabList>
|
||||||
|
<TabPanels>
|
||||||
|
<TabPanel id="chat">
|
||||||
|
<div className="space-y-0.5">
|
||||||
|
<ChatPanel />
|
||||||
|
<UserListPanel />
|
||||||
|
</div>
|
||||||
|
</TabPanel>
|
||||||
|
<TabPanel id="roomcontrols">
|
||||||
|
<div className="space-y-0.5">
|
||||||
|
{showTelemetry ? <TelemetryPanel /> : null}
|
||||||
|
<HomeAssistantControls />
|
||||||
|
<RoomCameraPanel panelId={roomPanelId} />
|
||||||
|
</div>
|
||||||
|
</TabPanel>
|
||||||
|
<TabPanel id="help">
|
||||||
|
<HelpPanel layout={layout} onOpenOverlay={onOpenHelpOverlay} />
|
||||||
|
</TabPanel>
|
||||||
|
<TabPanel id="settings">
|
||||||
|
<div className="space-y-0.5">
|
||||||
|
<SettingsPanel />
|
||||||
|
<LogPanel />
|
||||||
|
</div>
|
||||||
|
</TabPanel>
|
||||||
|
</TabPanels>
|
||||||
|
</Tabs>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function MobilePortraitLayout({ onOpenHelpOverlay }) {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-0.5">
|
<div className="flex flex-col gap-0.5">
|
||||||
<DriverVideoPanel layoutFormat='mobile'/>
|
<DriverVideoPanel layoutFormat='mobile'/>
|
||||||
<MobileControls />
|
<MobileControls />
|
||||||
<ControlSummary />
|
{/* <ControlSummary /> */}
|
||||||
<ChatPanel />
|
<MobileFeatureTabs
|
||||||
<UserListPanel />
|
layout="mobile-portrait"
|
||||||
<TelemetryPanel />
|
onOpenHelpOverlay={onOpenHelpOverlay}
|
||||||
<AuthPanel />
|
roomPanelId="mobile-portrait-room"
|
||||||
<AdminPanel />
|
/>
|
||||||
<HomeAssistantControls />
|
|
||||||
<RoomCameraPanel panelId="mobile-portrait-room" />
|
|
||||||
<LogPanel />
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function MobileLandscapeLayout() {
|
function MobileLandscapeLayout({ onOpenHelpOverlay }) {
|
||||||
return (
|
return (
|
||||||
<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">
|
||||||
@@ -108,20 +149,12 @@ function MobileLandscapeLayout() {
|
|||||||
<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">
|
||||||
<section className="grid grid-cols-[minmax(0,1fr)_minmax(0,1fr)] gap-0.5">
|
<MobileFeatureTabs
|
||||||
<div className="flex flex-col gap-0.5">
|
layout="mobile-landscape"
|
||||||
{/* <AuthPanel /> */}
|
onOpenHelpOverlay={onOpenHelpOverlay}
|
||||||
{/* <AdminPanel /> */}
|
roomPanelId="mobile-landscape-room"
|
||||||
<UserListPanel />
|
showTelemetry={false}
|
||||||
</div>
|
/>
|
||||||
<div className="flex flex-col gap-0.5">
|
|
||||||
{/* <LogPanel /> */}
|
|
||||||
<ChatPanel />
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<HomeAssistantControls />
|
|
||||||
<RoomCameraPanel panelId="mobile-landscape-room" />
|
|
||||||
{/* <DrivePanel /> */}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -180,8 +213,8 @@ function AppWithProviders({ layout, isDesktop, fullscreen }) {
|
|||||||
isDesktop
|
isDesktop
|
||||||
? <DesktopLayout layout={layout} onOpenHelpOverlay={openHelp} />
|
? <DesktopLayout layout={layout} onOpenHelpOverlay={openHelp} />
|
||||||
: layout === 'mobile-landscape'
|
: layout === 'mobile-landscape'
|
||||||
? <MobileLandscapeLayout />
|
? <MobileLandscapeLayout onOpenHelpOverlay={openHelp} />
|
||||||
: <MobilePortraitLayout />,
|
: <MobilePortraitLayout onOpenHelpOverlay={openHelp} />,
|
||||||
[isDesktop, layout, openHelp],
|
[isDesktop, layout, openHelp],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -501,11 +501,19 @@ function HudOverlay({
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const pulse = frame?.receivedAt ? now - frame.receivedAt < 200 : false;
|
const pulse = frame?.receivedAt ? now - frame.receivedAt < 200 : false;
|
||||||
|
const isMobile = mobileHud;
|
||||||
|
const statusTextClass = isMobile ? 'text-[0.55rem]' : 'text-[0.65rem]';
|
||||||
|
const statusPadClass = isMobile ? 'px-0.5 py-0.25' : 'px-1 py-0.5';
|
||||||
|
const labelPadClass = isMobile ? 'px-0.5 py-0.25' : 'px-0.5 py-0.5';
|
||||||
|
const labelTextClass = isMobile ? 'text-[0.7rem]' : 'text-[0.8rem]';
|
||||||
|
const mapSize = isMobile ? '240px' : '240px';
|
||||||
|
const mapScale = isMobile ? 0.45 : 0.7;
|
||||||
|
const mapOpacity = isMobile ? 0.85 : 0.7;
|
||||||
const mapStyle = {
|
const mapStyle = {
|
||||||
width: '240px',
|
width: mapSize,
|
||||||
height: '240px',
|
height: mapSize,
|
||||||
opacity: 0.7,
|
opacity: mapOpacity,
|
||||||
transform: `scale(${mobileHud ? 0.55 : 0.7})`,
|
transform: `scale(${mapScale})`,
|
||||||
transformOrigin: mapPosition === 'bottom-left' ? 'bottom left' : 'top right',
|
transformOrigin: mapPosition === 'bottom-left' ? 'bottom left' : 'top right',
|
||||||
...(mapPosition === 'bottom-left'
|
...(mapPosition === 'bottom-left'
|
||||||
? { left: '0.25rem', bottom: '0.25rem' }
|
? { left: '0.25rem', bottom: '0.25rem' }
|
||||||
@@ -521,19 +529,27 @@ function HudOverlay({
|
|||||||
];
|
];
|
||||||
return (
|
return (
|
||||||
<div className="pointer-events-none absolute inset-0 flex items-center justify-center">
|
<div className="pointer-events-none absolute inset-0 flex items-center justify-center">
|
||||||
<div className="absolute left-1 top-1 bg-black/70 px-1 py-0.5 text-[0.65rem] font-medium text-slate-100">
|
<div
|
||||||
|
className={`absolute left-1 top-1 bg-black/70 font-medium text-slate-100 ${statusTextClass} ${statusPadClass}`}
|
||||||
|
>
|
||||||
<span>Status: {status}</span>
|
<span>Status: {status}</span>
|
||||||
{audioStatus ? <div>Audio: {audioStatus}</div> : null}
|
{audioStatus ? <div>Audio: {audioStatus}</div> : null}
|
||||||
</div>
|
</div>
|
||||||
{songNote != null ? (
|
{songNote != null ? (
|
||||||
<div className="absolute right-1 top-1 rounded bg-black/70 px-1 py-0.25 text-[0.65rem] font-semibold text-emerald-200">
|
<div
|
||||||
|
className={`absolute right-1 top-1 rounded bg-black/70 font-semibold text-emerald-200 ${statusTextClass} ${statusPadClass}`}
|
||||||
|
>
|
||||||
Song {formatNoteLabel(songNote)} <span className="text-slate-400">({songNote})</span>
|
Song {formatNoteLabel(songNote)} <span className="text-slate-400">({songNote})</span>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
<div className="absolute left-1 top-1/2 flex -translate-y-1/2 flex-col gap-0.35 bg-black/70 px-1 py-0.35 text-[0.65rem] text-slate-100">
|
<div
|
||||||
|
className={`absolute left-1 top-1/2 flex -translate-y-1/2 flex-col gap-0.35 bg-black/70 text-slate-100 ${statusTextClass} ${statusPadClass}`}
|
||||||
|
>
|
||||||
<div className="space-y-0.1 leading-tight">
|
<div className="space-y-0.1 leading-tight">
|
||||||
<span className="text-[0.6rem] uppercase tracking-wide text-slate-400">Telemetry</span>
|
<span className={`${isMobile ? 'text-[0.55rem]' : '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">
|
||||||
<span className="text-slate-400">{labelText}</span>
|
<span className="text-slate-400">{labelText}</span>
|
||||||
@@ -546,20 +562,22 @@ function HudOverlay({
|
|||||||
<div
|
<div
|
||||||
className="pointer-events-none rounded"
|
className="pointer-events-none rounded"
|
||||||
style={{
|
style={{
|
||||||
width: '130px',
|
width: isMobile ? '110px' : '130px',
|
||||||
height: '130px',
|
height: isMobile ? '110px' : '130px',
|
||||||
opacity: 0.9,
|
opacity: isMobile ? 0.85 : 0.9,
|
||||||
transform: 'scale(0.85)',
|
transform: isMobile ? 'scale(0.7)' : 'scale(0.85)',
|
||||||
transformOrigin: 'top left',
|
transformOrigin: 'top left',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<TopDownMap sensors={sensors} size={160} overlay />
|
<TopDownMap sensors={sensors} size={isMobile ? 160 : 160} overlay />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</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 text-slate-100 ${labelPadClass} ${labelTextClass}`}
|
||||||
|
>
|
||||||
<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>
|
||||||
@@ -569,17 +587,23 @@ function HudOverlay({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="pointer-events-none absolute inset-0 flex items-center justify-center">
|
<div className="pointer-events-none absolute inset-0 flex items-center justify-center">
|
||||||
<div className="absolute left-1 top-1 bg-black/70 px-1 py-0.5 text-[0.65rem] font-medium text-slate-100">
|
<div
|
||||||
|
className={`absolute left-1 top-1 bg-black/70 font-medium text-slate-100 ${statusTextClass} ${statusPadClass}`}
|
||||||
|
>
|
||||||
<span>Status: {status}</span>
|
<span>Status: {status}</span>
|
||||||
{audioStatus ? <div>Audio: {audioStatus}</div> : null}
|
{audioStatus ? <div>Audio: {audioStatus}</div> : null}
|
||||||
</div>
|
</div>
|
||||||
{songNote != null ? (
|
{songNote != null ? (
|
||||||
<div className="absolute bottom-1 right-1 rounded bg-black/70 px-1 py-0.25 text-[0.65rem] font-semibold text-emerald-200">
|
<div
|
||||||
|
className={`absolute bottom-1 right-1 rounded bg-black/70 font-semibold text-emerald-200 ${statusTextClass} ${statusPadClass}`}
|
||||||
|
>
|
||||||
Song {formatNoteLabel(songNote)} <span className="text-slate-400">({songNote})</span>
|
Song {formatNoteLabel(songNote)} <span className="text-slate-400">({songNote})</span>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
<div className="absolute bottom-0.5 left-1/2 flex -translate-x-1/2 gap-0.5 bg-black/80 px-0.5 py-0.5 text-slate-100">
|
<div
|
||||||
|
className={`absolute bottom-0.5 left-1/2 flex -translate-x-1/2 gap-0.5 bg-black/80 text-slate-100 ${labelPadClass} ${labelTextClass}`}
|
||||||
|
>
|
||||||
<span>Rover: "{label || 'Unnamed Rover'}"</span>
|
<span>Rover: "{label || 'Unnamed Rover'}"</span>
|
||||||
{/* <span>{pulse ? 'Sensors active' : 'No recent sensors'}</span> */}
|
{/* <span>{pulse ? 'Sensors active' : 'No recent sensors'}</span> */}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user