mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-16 01:21:20 -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-title" content="Multi Roomba Rover" />
|
||||
<title>Multi Roomba Rover</title>
|
||||
<script type="module" crossorigin src="/assets/index-DYhouiff.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-G3PbGrn6.css">
|
||||
<script type="module" crossorigin src="/assets/index-CT2FRsnB.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-C9Dx14Sy.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
@@ -22,14 +22,6 @@ function formatDriverLabel({ roverId, session }) {
|
||||
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 }) {
|
||||
const percentDisplay = rover?.batteryState?.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);
|
||||
}
|
||||
|
||||
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 }) {
|
||||
const urgentActive = rover?.batteryState?.urgentActive ?? false;
|
||||
const warnActive = rover?.batteryState?.warnActive ?? false;
|
||||
@@ -79,64 +51,75 @@ function InfoColumn({
|
||||
frame,
|
||||
driverLabel,
|
||||
snapshotFeed,
|
||||
showCountdown = false,
|
||||
countdownText = '—',
|
||||
withDivider = false,
|
||||
showPreview = true,
|
||||
variant = 'stacked',
|
||||
}) {
|
||||
const batteryPercent = getBatteryPercent({ rover, frame });
|
||||
const batteryFillClass = getBatteryFillClass({ rover });
|
||||
const dockStatus = getDockChargeStatus(frame);
|
||||
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 (
|
||||
<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="min-w-0 rounded-lg border border-slate-800/80 bg-slate-900/70 px-0 py-0 leading-none">
|
||||
<div className={batteryFillClassName} style={batteryFillStyle} />
|
||||
{isActiveView ? (
|
||||
<div className="relative z-10 flex min-w-0 flex-1 flex-col justify-between text-center">
|
||||
<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 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}>
|
||||
<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>
|
||||
) : (
|
||||
<div />
|
||||
)}
|
||||
<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 className="relative z-10 flex min-w-0 flex-col gap-1 text-center">
|
||||
<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 rounded-lg border border-slate-800/80 bg-slate-900/70 px-0 py-0 leading-none">
|
||||
<div
|
||||
className={`absolute inset-y-0 left-0 ${batteryFillClass}`}
|
||||
style={{ width: `${batteryPercent == null ? 0 : batteryPercent}%` }}
|
||||
/>
|
||||
<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-slate-200" maxSize={1000} minSize={16}>
|
||||
{batteryPercent == null ? 'Battery --%' : `Battery ${batteryPercent}%`}
|
||||
<AutoFitText className="font-semibold leading-none text-white" maxSize={1000} minSize={16}>
|
||||
{batteryPercent == null ? '--%' : `${batteryPercent}%`}
|
||||
</AutoFitText>
|
||||
</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={`${dockStatus.chargeTone} px-0 py-0 leading-none`}>{dockStatus.chargeLabel}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex min-w-0 flex-1 flex-col gap-4">
|
||||
{isActiveView ? (
|
||||
<div className="relative mt-auto w-full min-w-0">
|
||||
{showCountdown ? (
|
||||
<div className="absolute bottom-0 right-0 pb-1 pr-1 text-right">
|
||||
<AutoFitText className="font-semibold leading-none text-slate-200" maxSize={1000} minSize={18}>
|
||||
{countdownText}
|
||||
</AutoFitText>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
) : (
|
||||
<div className="relative z-10 flex min-w-0 flex-1 flex-col gap-4">
|
||||
<div className="flex w-full min-w-0 flex-1 flex-col items-center gap-4">
|
||||
{showPreview ? (
|
||||
<div className="mt-auto w-full">
|
||||
@@ -156,17 +139,11 @@ function InfoColumn({
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
{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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -239,8 +216,6 @@ function MiniSummaryContent() {
|
||||
const frames = useTelemetryFrames();
|
||||
const roster = session?.roster ?? [];
|
||||
const [index, setIndex] = useState(0);
|
||||
const [rotationBase, setRotationBase] = useState(Date.now());
|
||||
const [nowTick, setNowTick] = useState(Date.now());
|
||||
const activeDrivers = session?.activeDrivers || {};
|
||||
const driverRoster = useMemo(
|
||||
() => roster.filter((rover) => activeDrivers[rover.id]),
|
||||
@@ -283,25 +258,16 @@ function MiniSummaryContent() {
|
||||
|
||||
useEffect(() => {
|
||||
setIndex(0);
|
||||
setRotationBase(Date.now());
|
||||
}, [rotationKey]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!rotationPool.length) return undefined;
|
||||
const timer = setInterval(() => {
|
||||
setRotationBase(Date.now());
|
||||
setIndex((prev) => (prev + 1) % rotationPool.length);
|
||||
}, ROTATE_MS);
|
||||
return () => clearInterval(timer);
|
||||
}, [rotationPool.length, rotationKey]);
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setInterval(() => {
|
||||
setNowTick(Date.now());
|
||||
}, 1000);
|
||||
return () => clearInterval(timer);
|
||||
}, []);
|
||||
|
||||
const activeEntry = rotationPool.length ? rotationPool[index % rotationPool.length] : 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 activeFrame = activeRover ? frames[activeRover.id] || null : 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) {
|
||||
return (
|
||||
@@ -336,7 +299,6 @@ function MiniSummaryContent() {
|
||||
frame={frames[rover.id] || null}
|
||||
driverLabel={null}
|
||||
snapshotFeed={snapshotFeeds[rover.id] || null}
|
||||
showCountdown={false}
|
||||
showPreview={true}
|
||||
withDivider={false}
|
||||
/>
|
||||
@@ -389,8 +351,6 @@ function MiniSummaryContent() {
|
||||
frame={activeFrame}
|
||||
driverLabel={driverLabel}
|
||||
snapshotFeed={snapshotFeeds[activeRover.id] || null}
|
||||
showCountdown={true}
|
||||
countdownText={rotationCountdown}
|
||||
showPreview={false}
|
||||
variant="active"
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user