big boy webui new new new new new 100 files changed 80 years

This commit is contained in:
legop3
2026-08-18 22:01:25 -04:00
parent 0f0f82e5f6
commit 737760ff56
79 changed files with 2714 additions and 388 deletions
@@ -1,36 +1,85 @@
// Movement Column
// Purpose: Assembles the mobile movement column, which is the right column by default.
// Scope: Integrates drive/dock actions with the mobile control pad without hiding that dependency inside the pad.
// Scope: Owns movement availability while the in-video DockingHud owns every dock/undock action.
import { FaChargingStation } from 'react-icons/fa';
import { useControlSelector } from '../../controls/index.js';
import DriveDockAction from '../DriveDockAction/index.jsx';
import { useDriveDockState } from '../DriveDockAction/driveDockState.js';
import { useSessionSelector } from '../../context/SessionContext.jsx';
import { useTelemetrySelector } from '../../context/TelemetryContext.jsx';
import { dockTelemetryEqual, selectDockTelemetry } from '../../context/telemetryViews.js';
import { useManualDockAssist } from '../../features/manualDockAssist/useManualDockAssist.js';
import useCanControlRover from '../../hooks/useCanControlRover.js';
import { triggerTouchHaptic } from '../../lib/touchHaptics.js';
import ControlPadPanel from './ControlPadPanel.jsx';
function MovementColumnContent({ layout }) {
const roverId = useControlSelector((control) => control.state.roverId);
const driveDockState = useDriveDockState(roverId);
const dockedNotDriving = driveDockState.docked && !driveDockState.driving;
const expandAction = dockedNotDriving || driveDockState.dockingInProgress;
const disabled = !roverId;
const dockTelemetry = useTelemetrySelector(roverId, selectDockTelemetry, dockTelemetryEqual);
const dockAssist = useManualDockAssist();
const canControl = useCanControlRover(roverId);
const batteryState = useSessionSelector((state) => {
const rover = (state.session?.roster || []).find((entry) => String(entry.id) === String(roverId));
return rover?.batteryState || null;
});
const batteryUrgent = Boolean(batteryState?.urgentActive);
const batteryLow = Boolean(batteryState?.warnActive || batteryUrgent);
const docked = Boolean(dockTelemetry?.homeBase);
const drivingMode = String(dockTelemetry?.oiModeLabel || '').toLowerCase() === 'full';
/*
DriveDockAction owns whether the rover is ready to accept movement, while
ControlPadPanel owns only movement intent. Keeping the integration here makes
the column layout explicit and prevents the pad component from knowing about
docking state.
DockingHud is now the only place where a user starts driving or enters docking
assist. This column only decides whether touch movement is safe. Requiring both
an undocked rover and full OI mode keeps the pad inert throughout the undock
macro, including the interval where dock contact and OI mode change separately.
Manual assist is the deliberate exception to the normal OI-mode requirement:
its entire purpose is to let the user drive onto the dock under the assist speed
cap. Actual dock contact still disables movement immediately.
*/
const fillClass = dockedNotDriving ? 'max-h-screen self-start' : '';
const containerClass = `mobile-touch-control flex h-full flex-col gap-0.5 text-slate-100 ${fillClass}`;
const movementDisabled = !roverId
|| !canControl
|| docked
|| (!drivingMode && !dockAssist.active);
// Both controls share the same hardware-availability boundary. Dock assist
// changes their actions, not who is allowed to command the rover.
const dockActionDisabled = movementDisabled;
const handleDockAction = () => {
if (dockActionDisabled) return;
triggerTouchHaptic('button');
// Mobile deliberately enters assist on the first tap. The centered video HUD
// provides the resulting instruction, so a confirmation modal would only add
// friction between intent and the camera-guided docking task.
if (dockAssist.active) {
dockAssist.exitAssist();
} else {
dockAssist.enterAssist();
}
};
return (
<div className={containerClass} data-mobile-layout={layout}>
<DriveDockAction
layout="mobile"
expand={expandAction}
driveDockState={driveDockState}
compactHeightClass="min-h-[5rem]"
/>
{!expandAction ? <ControlPadPanel disabled={disabled} /> : null}
<div className="mobile-touch-control flex h-full flex-col gap-0.5 text-slate-100" data-mobile-layout={layout}>
<button
type="button"
disabled={dockActionDisabled}
onClick={handleDockAction}
className={`mobile-touch-control flex min-h-[4.5rem] shrink-0 items-center justify-center gap-1.5 rounded-xl border-2 px-2 text-base font-semibold shadow-md transition disabled:cursor-not-allowed disabled:opacity-50 ${
dockAssist.active
? 'border-cyan-300/70 bg-cyan-900 text-cyan-50'
: batteryUrgent
? 'border-red-300/80 bg-red-950 text-red-50'
: batteryLow
? 'border-amber-300/80 bg-amber-950 text-amber-50'
: 'border-indigo-300/70 bg-indigo-900 text-indigo-50'
}`}
>
<FaChargingStation className="text-lg" aria-hidden="true" />
{/* The mobile column remains the only mobile entry point into dock assist. Battery
severity makes that existing action more obvious without adding another control. */}
<span>{dockAssist.active ? 'Exit dock assist' : batteryUrgent ? 'Dock now' : batteryLow ? 'Dock and charge soon' : 'Dock and charge'}</span>
</button>
{/* Keeping the pad mounted prevents the mobile columns from changing size while
the centered video HUD explains and performs dock-related transitions. */}
<ControlPadPanel disabled={movementDisabled} />
</div>
);
}