arm powered steam link ??

This commit is contained in:
legop3
2026-08-11 20:50:15 -04:00
parent dfd674a447
commit e5830533ba
10 changed files with 59 additions and 18 deletions
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
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
@@ -12,8 +12,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" />
<!-- site-metadata:inject --> <!-- site-metadata:inject -->
<!-- analytics:inject --> <!-- analytics:inject -->
<script type="module" crossorigin src="/assets/index-hzmph71J.js"></script> <script type="module" crossorigin src="/assets/index-CTIt0so7.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-DKoVL79I.css"> <link rel="stylesheet" crossorigin href="/assets/index-CaoGM6_n.css">
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>
+25
View File
@@ -71,6 +71,7 @@ function isGenericAlert(alert) {
if (alert?.kind === 'chat' && alert.payload) return false; if (alert?.kind === 'chat' && alert.payload) return false;
if (alert?.kind === 'chat-typing' && alert.payload) return false; if (alert?.kind === 'chat-typing' && alert.payload) return false;
if (alert?.kind === 'barcode-scan' && alert.payload) return false; if (alert?.kind === 'barcode-scan' && alert.payload) return false;
if (alert?.kind === 'undocked-exit-warning') return false;
return true; return true;
} }
@@ -481,6 +482,27 @@ function BarcodeScanToast({ payload }) {
); );
} }
function UndockedExitWarningToast() {
return (
<div
/*
This warning deliberately keeps AlertFeed's existing dark shell and
compact rounded geometry while giving a safety-critical instruction
enough type size and breathing room to be unmistakable. The responsive
width leaves space for the feed shell's separate dismiss column on
narrow screens instead of allowing the card to overflow the viewport.
*/
className="w-[min(32rem,calc(80vw-1.25rem))] border-l-4 border-amber-400 bg-amber-950/90 px-4 py-3 text-left text-amber-50"
role="alert"
>
<p className="text-xl font-bold leading-tight text-amber-100">Please dock your rover</p>
<p className="mt-1 text-base font-medium leading-snug text-amber-50/90">
Your rover is still undocked. Please dock it before leaving the page.
</p>
</div>
);
}
function AlertToast({ alert }) { function AlertToast({ alert }) {
if (alert.kind === 'buttonbox-active' && alert.payload) { if (alert.kind === 'buttonbox-active' && alert.payload) {
const payload = alert.payload; const payload = alert.payload;
@@ -515,6 +537,9 @@ function AlertToast({ alert }) {
if (alert.kind === 'barcode-scan' && alert.payload) { if (alert.kind === 'barcode-scan' && alert.payload) {
return <BarcodeScanToast payload={alert.payload} />; return <BarcodeScanToast payload={alert.payload} />;
} }
if (alert.kind === 'undocked-exit-warning') {
return <UndockedExitWarningToast />;
}
return ( return (
<div <div
/* /*
@@ -1,10 +1,13 @@
// Undocked Page Exit Guard // Undocked Page Exit Guard
// Purpose: Asks the browser to confirm document-level navigation while the current driver's rover is undocked. // Purpose: Asks the browser to confirm document-level navigation while the current driver's rover is undocked.
// Scope: Owns only the native beforeunload lifecycle; it does not render UI or attempt to replace browser safety dialogs. // Scope: Owns the native beforeunload lifecycle and queues the matching AlertFeed explanation when leaving is attempted.
import { useEffect } from 'react'; import { useEffect } from 'react';
import { useControlSelector } from '../../controls/index.js'; import { useControlSelector } from '../../controls/index.js';
import { useSessionActions } from '../../context/SessionContext.jsx';
import { useTelemetrySelector } from '../../context/TelemetryContext.jsx'; import { useTelemetrySelector } from '../../context/TelemetryContext.jsx';
const UNDOCKED_EXIT_ALERT_LIFETIME_MS = 15 * 1000;
function selectHomeBaseState(frame) { function selectHomeBaseState(frame) {
const homeBase = frame?.sensors?.chargingSources?.homeBase; const homeBase = frame?.sensors?.chargingSources?.homeBase;
@@ -19,6 +22,7 @@ function selectHomeBaseState(frame) {
export default function UndockedPageExitGuard() { export default function UndockedPageExitGuard() {
const roverId = useControlSelector((control) => control.state.roverId); const roverId = useControlSelector((control) => control.state.roverId);
const homeBaseState = useTelemetrySelector(roverId, selectHomeBaseState); const homeBaseState = useTelemetrySelector(roverId, selectHomeBaseState);
const { pushAlert } = useSessionActions();
const shouldConfirmExit = Boolean(roverId) && homeBaseState === false; const shouldConfirmExit = Boolean(roverId) && homeBaseState === false;
useEffect(() => { useEffect(() => {
@@ -33,6 +37,18 @@ export default function UndockedPageExitGuard() {
*/ */
event.preventDefault(); event.preventDefault();
event.returnValue = true; event.returnValue = true;
/*
If the driver cancels the browser-owned confirmation, React remains
mounted and AlertFeed presents the actionable reason for the warning.
A stable id refreshes this one warning lane on another exit attempt
instead of stacking duplicate cards over the driving interface.
*/
pushAlert({
id: 'undocked-page-exit-warning',
kind: 'undocked-exit-warning',
lifetimeMs: UNDOCKED_EXIT_ALERT_LIFETIME_MS,
});
} }
window.addEventListener('beforeunload', handleBeforeUnload); window.addEventListener('beforeunload', handleBeforeUnload);
@@ -44,7 +60,7 @@ export default function UndockedPageExitGuard() {
*/ */
window.removeEventListener('beforeunload', handleBeforeUnload); window.removeEventListener('beforeunload', handleBeforeUnload);
}; };
}, [shouldConfirmExit]); }, [pushAlert, shouldConfirmExit]);
return null; return null;
} }