mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-17 01:50:47 -04:00
updated /mini again..
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-DYhouiff.js"></script>
|
<script type="module" crossorigin src="/assets/index-CT2FRsnB.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-G3PbGrn6.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-C9Dx14Sy.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
@@ -22,14 +22,6 @@ function formatDriverLabel({ roverId, session }) {
|
|||||||
return mode === 'turns' && turnInfo?.current ? `${label}` : label;
|
return mode === 'turns' && turnInfo?.current ? `${label}` : label;
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatDuration(ms) {
|
|
||||||
if (!Number.isFinite(ms) || ms <= 0) return '0:00';
|
|
||||||
const totalSeconds = Math.ceil(ms / 1000);
|
|
||||||
const minutes = Math.floor(totalSeconds / 60);
|
|
||||||
const seconds = totalSeconds % 60;
|
|
||||||
return `${minutes}:${seconds.toString().padStart(2, '0')}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getBatteryPercent({ rover, frame }) {
|
function getBatteryPercent({ rover, frame }) {
|
||||||
const percentDisplay = rover?.batteryState?.percentDisplay;
|
const percentDisplay = rover?.batteryState?.percentDisplay;
|
||||||
if (percentDisplay != null && Number.isFinite(percentDisplay)) {
|
if (percentDisplay != null && Number.isFinite(percentDisplay)) {
|
||||||
@@ -46,26 +38,6 @@ function getBatteryPercent({ rover, frame }) {
|
|||||||
return Math.round(Math.max(0, Math.min(1, normalized)) * 100);
|
return Math.round(Math.max(0, Math.min(1, normalized)) * 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDockChargeStatus(frame) {
|
|
||||||
const docked = Boolean(frame?.sensors?.chargingSources?.homeBase);
|
|
||||||
const chargingLabel = frame?.sensors?.chargingState?.label || '';
|
|
||||||
const charging = Boolean(chargingLabel && chargingLabel.toLowerCase() !== 'not charging');
|
|
||||||
const dockTone = docked ? 'bg-emerald-500/80 text-emerald-50' : 'bg-slate-700/80 text-slate-200';
|
|
||||||
const chargeTone = charging
|
|
||||||
? 'bg-emerald-500/80 text-emerald-50'
|
|
||||||
: docked
|
|
||||||
? 'bg-amber-400/80 text-amber-950'
|
|
||||||
: 'bg-slate-700/80 text-slate-200';
|
|
||||||
return {
|
|
||||||
docked,
|
|
||||||
charging,
|
|
||||||
dockLabel: docked ? 'Docked' : 'Undocked',
|
|
||||||
chargeLabel: charging ? 'Charging' : 'Not charging',
|
|
||||||
dockTone,
|
|
||||||
chargeTone,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getBatteryFillClass({ rover }) {
|
function getBatteryFillClass({ rover }) {
|
||||||
const urgentActive = rover?.batteryState?.urgentActive ?? false;
|
const urgentActive = rover?.batteryState?.urgentActive ?? false;
|
||||||
const warnActive = rover?.batteryState?.warnActive ?? false;
|
const warnActive = rover?.batteryState?.warnActive ?? false;
|
||||||
@@ -79,93 +51,98 @@ function InfoColumn({
|
|||||||
frame,
|
frame,
|
||||||
driverLabel,
|
driverLabel,
|
||||||
snapshotFeed,
|
snapshotFeed,
|
||||||
showCountdown = false,
|
|
||||||
countdownText = '—',
|
|
||||||
withDivider = false,
|
withDivider = false,
|
||||||
showPreview = true,
|
showPreview = true,
|
||||||
variant = 'stacked',
|
variant = 'stacked',
|
||||||
}) {
|
}) {
|
||||||
const batteryPercent = getBatteryPercent({ rover, frame });
|
const batteryPercent = getBatteryPercent({ rover, frame });
|
||||||
const batteryFillClass = getBatteryFillClass({ rover });
|
const batteryFillClass = getBatteryFillClass({ rover });
|
||||||
const dockStatus = getDockChargeStatus(frame);
|
|
||||||
const isActiveView = variant === 'active';
|
const isActiveView = variant === 'active';
|
||||||
|
const batteryFillStyle = isActiveView
|
||||||
|
? { height: `${batteryPercent == null ? 0 : batteryPercent}%`, bottom: 0, top: 'auto' }
|
||||||
|
: { width: `${batteryPercent == null ? 0 : batteryPercent}%` };
|
||||||
|
const batteryFillClassName = isActiveView
|
||||||
|
? `pointer-events-none absolute bottom-0 left-0 right-0 ${batteryFillClass}`
|
||||||
|
: `pointer-events-none absolute left-0 top-0 bottom-0 ${batteryFillClass}`;
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`flex min-w-0 flex-1 flex-col gap-4 px-0 py-0 ${withDivider ? 'border-r border-slate-700/60' : ''}`}
|
className={`relative flex min-w-0 flex-1 flex-col gap-4 overflow-hidden bg-black px-0 py-0 ${
|
||||||
|
withDivider ? 'border-r border-slate-700/60' : ''
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
<div className="flex min-w-0 flex-col gap-1 text-center">
|
<div className={batteryFillClassName} style={batteryFillStyle} />
|
||||||
<div className="min-w-0 rounded-lg border border-slate-800/80 bg-slate-900/70 px-0 py-0 leading-none">
|
{isActiveView ? (
|
||||||
<AutoFitText className="font-semibold leading-none text-white" maxSize={1000} minSize={18}>
|
<div className="relative z-10 flex min-w-0 flex-1 flex-col justify-between text-center">
|
||||||
{rover.name || rover.id}
|
<div className="min-w-0 bg-transparent px-0 py-0 leading-none">
|
||||||
</AutoFitText>
|
<AutoFitText className="font-semibold leading-none text-white" maxSize={1000} minSize={18}>
|
||||||
</div>
|
{rover.name || rover.id}
|
||||||
{driverLabel ? (
|
|
||||||
<div className="min-w-0 rounded-lg border border-slate-800/80 bg-slate-900/70 px-0 py-0 leading-none">
|
|
||||||
<AutoFitText className="font-semibold leading-none text-slate-200" maxSize={1000} minSize={16}>
|
|
||||||
{driverLabel}
|
|
||||||
</AutoFitText>
|
</AutoFitText>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
{driverLabel ? (
|
||||||
<div className="relative min-w-0 overflow-hidden rounded-lg border border-slate-800/80 bg-slate-900/70 px-0 py-0 leading-none">
|
<div className="min-w-0 bg-transparent px-0 py-0 leading-none">
|
||||||
<div
|
<AutoFitText className="font-semibold leading-none text-white" maxSize={1000} minSize={16}>
|
||||||
className={`absolute inset-y-0 left-0 ${batteryFillClass}`}
|
{driverLabel}
|
||||||
style={{ width: `${batteryPercent == null ? 0 : batteryPercent}%` }}
|
</AutoFitText>
|
||||||
/>
|
</div>
|
||||||
<div className="relative">
|
) : (
|
||||||
<AutoFitText className="font-semibold leading-none text-slate-200" maxSize={1000} minSize={16}>
|
<div />
|
||||||
{batteryPercent == null ? 'Battery --%' : `Battery ${batteryPercent}%`}
|
)}
|
||||||
</AutoFitText>
|
<div className="relative min-w-0 overflow-hidden bg-transparent px-0 py-0 leading-none">
|
||||||
|
<div className="relative">
|
||||||
|
<AutoFitText className="font-semibold leading-none text-white" maxSize={1000} minSize={16}>
|
||||||
|
{batteryPercent == null ? '--%' : `${batteryPercent}%`}
|
||||||
|
</AutoFitText>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="overflow-hidden rounded-lg border border-slate-800/80">
|
) : (
|
||||||
<div className="grid grid-cols-2 text-center text-[clamp(0.85rem,2.4vw,2rem)] font-semibold uppercase tracking-wide leading-none">
|
<>
|
||||||
<div className={`${dockStatus.dockTone} px-0 py-0 leading-none`}>{dockStatus.dockLabel}</div>
|
<div className="relative z-10 flex min-w-0 flex-col gap-1 text-center">
|
||||||
<div className={`${dockStatus.chargeTone} px-0 py-0 leading-none`}>{dockStatus.chargeLabel}</div>
|
<div className="min-w-0 bg-transparent px-0 py-0 leading-none">
|
||||||
|
<AutoFitText className="font-semibold leading-none text-white" maxSize={1000} minSize={18}>
|
||||||
|
{rover.name || rover.id}
|
||||||
|
</AutoFitText>
|
||||||
|
</div>
|
||||||
|
{driverLabel ? (
|
||||||
|
<div className="min-w-0 bg-transparent px-0 py-0 leading-none">
|
||||||
|
<AutoFitText className="font-semibold leading-none text-white" maxSize={1000} minSize={16}>
|
||||||
|
{driverLabel}
|
||||||
|
</AutoFitText>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
<div className="relative min-w-0 overflow-hidden bg-transparent px-0 py-0 leading-none">
|
||||||
|
<div className="relative">
|
||||||
|
<AutoFitText className="font-semibold leading-none text-white" maxSize={1000} minSize={16}>
|
||||||
|
{batteryPercent == null ? '--%' : `${batteryPercent}%`}
|
||||||
|
</AutoFitText>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex min-w-0 flex-1 flex-col gap-4">
|
<div className="relative z-10 flex min-w-0 flex-1 flex-col gap-4">
|
||||||
{isActiveView ? (
|
<div className="flex w-full min-w-0 flex-1 flex-col items-center gap-4">
|
||||||
<div className="relative mt-auto w-full min-w-0">
|
{showPreview ? (
|
||||||
{showCountdown ? (
|
<div className="mt-auto w-full">
|
||||||
<div className="absolute bottom-0 right-0 pb-1 pr-1 text-right">
|
<div className="w-full aspect-[4/3]">
|
||||||
<AutoFitText className="font-semibold leading-none text-slate-200" maxSize={1000} minSize={18}>
|
<VideoTile
|
||||||
{countdownText}
|
sessionInfo={null}
|
||||||
</AutoFitText>
|
videoMode="snapshot"
|
||||||
</div>
|
snapshotFeed={snapshotFeed}
|
||||||
) : null}
|
audioSessionInfo={null}
|
||||||
</div>
|
label={rover.name || rover.id}
|
||||||
) : (
|
telemetryFrame={frame}
|
||||||
<div className="flex w-full min-w-0 flex-1 flex-col items-center gap-4">
|
batteryConfig={rover.battery}
|
||||||
{showPreview ? (
|
layoutFormat="mobile"
|
||||||
<div className="mt-auto w-full">
|
hudVariant="none"
|
||||||
<div className="w-full aspect-[4/3]">
|
fitParent
|
||||||
<VideoTile
|
/>
|
||||||
sessionInfo={null}
|
</div>
|
||||||
videoMode="snapshot"
|
|
||||||
snapshotFeed={snapshotFeed}
|
|
||||||
audioSessionInfo={null}
|
|
||||||
label={rover.name || rover.id}
|
|
||||||
telemetryFrame={frame}
|
|
||||||
batteryConfig={rover.battery}
|
|
||||||
layoutFormat="mobile"
|
|
||||||
hudVariant="none"
|
|
||||||
fitParent
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
) : null}
|
||||||
) : null}
|
</div>
|
||||||
{showCountdown ? (
|
|
||||||
<div className="flex min-w-0 items-center justify-center text-center">
|
|
||||||
<AutoFitText className="font-semibold leading-none text-slate-200" maxSize={1000} minSize={18}>
|
|
||||||
{countdownText}
|
|
||||||
</AutoFitText>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
</>
|
||||||
</div>
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -239,8 +216,6 @@ function MiniSummaryContent() {
|
|||||||
const frames = useTelemetryFrames();
|
const frames = useTelemetryFrames();
|
||||||
const roster = session?.roster ?? [];
|
const roster = session?.roster ?? [];
|
||||||
const [index, setIndex] = useState(0);
|
const [index, setIndex] = useState(0);
|
||||||
const [rotationBase, setRotationBase] = useState(Date.now());
|
|
||||||
const [nowTick, setNowTick] = useState(Date.now());
|
|
||||||
const activeDrivers = session?.activeDrivers || {};
|
const activeDrivers = session?.activeDrivers || {};
|
||||||
const driverRoster = useMemo(
|
const driverRoster = useMemo(
|
||||||
() => roster.filter((rover) => activeDrivers[rover.id]),
|
() => roster.filter((rover) => activeDrivers[rover.id]),
|
||||||
@@ -283,25 +258,16 @@ function MiniSummaryContent() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setIndex(0);
|
setIndex(0);
|
||||||
setRotationBase(Date.now());
|
|
||||||
}, [rotationKey]);
|
}, [rotationKey]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!rotationPool.length) return undefined;
|
if (!rotationPool.length) return undefined;
|
||||||
const timer = setInterval(() => {
|
const timer = setInterval(() => {
|
||||||
setRotationBase(Date.now());
|
|
||||||
setIndex((prev) => (prev + 1) % rotationPool.length);
|
setIndex((prev) => (prev + 1) % rotationPool.length);
|
||||||
}, ROTATE_MS);
|
}, ROTATE_MS);
|
||||||
return () => clearInterval(timer);
|
return () => clearInterval(timer);
|
||||||
}, [rotationPool.length, rotationKey]);
|
}, [rotationPool.length, rotationKey]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const timer = setInterval(() => {
|
|
||||||
setNowTick(Date.now());
|
|
||||||
}, 1000);
|
|
||||||
return () => clearInterval(timer);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const activeEntry = rotationPool.length ? rotationPool[index % rotationPool.length] : null;
|
const activeEntry = rotationPool.length ? rotationPool[index % rotationPool.length] : null;
|
||||||
const activeRover = activeEntry?.type === 'rover' ? activeEntry.rover : null;
|
const activeRover = activeEntry?.type === 'rover' ? activeEntry.rover : null;
|
||||||
|
|
||||||
@@ -309,9 +275,6 @@ function MiniSummaryContent() {
|
|||||||
const activeAudio = activeRover ? audioSources[`${activeRover.id}-audio`] || null : null;
|
const activeAudio = activeRover ? audioSources[`${activeRover.id}-audio`] || null : null;
|
||||||
const activeFrame = activeRover ? frames[activeRover.id] || null : null;
|
const activeFrame = activeRover ? frames[activeRover.id] || null : null;
|
||||||
const driverLabel = activeRover ? formatDriverLabel({ roverId: activeRover.id, session }) : null;
|
const driverLabel = activeRover ? formatDriverLabel({ roverId: activeRover.id, session }) : null;
|
||||||
const rotationRemainingMs =
|
|
||||||
rotationPool.length > 1 ? Math.max(0, rotationBase + ROTATE_MS - nowTick) : null;
|
|
||||||
const rotationCountdown = rotationRemainingMs != null ? formatDuration(rotationRemainingMs) : '—';
|
|
||||||
|
|
||||||
if (inLockdown) {
|
if (inLockdown) {
|
||||||
return (
|
return (
|
||||||
@@ -336,7 +299,6 @@ function MiniSummaryContent() {
|
|||||||
frame={frames[rover.id] || null}
|
frame={frames[rover.id] || null}
|
||||||
driverLabel={null}
|
driverLabel={null}
|
||||||
snapshotFeed={snapshotFeeds[rover.id] || null}
|
snapshotFeed={snapshotFeeds[rover.id] || null}
|
||||||
showCountdown={false}
|
|
||||||
showPreview={true}
|
showPreview={true}
|
||||||
withDivider={false}
|
withDivider={false}
|
||||||
/>
|
/>
|
||||||
@@ -389,8 +351,6 @@ function MiniSummaryContent() {
|
|||||||
frame={activeFrame}
|
frame={activeFrame}
|
||||||
driverLabel={driverLabel}
|
driverLabel={driverLabel}
|
||||||
snapshotFeed={snapshotFeeds[activeRover.id] || null}
|
snapshotFeed={snapshotFeeds[activeRover.id] || null}
|
||||||
showCountdown={true}
|
|
||||||
countdownText={rotationCountdown}
|
|
||||||
showPreview={false}
|
showPreview={false}
|
||||||
variant="active"
|
variant="active"
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in New Issue
Block a user