stateful night vision rework

This commit is contained in:
legop3
2026-01-28 13:45:01 -05:00
parent f3cc73f178
commit 7e850dab22
20 changed files with 384 additions and 176 deletions
+18 -3
View File
@@ -312,10 +312,23 @@ export function ControlSystemProvider({ children }) {
[pipeline],
);
const setNightVision = useCallback(
(nightVisionOn) => {
if (!pipeline.nightVision) return;
if (typeof nightVisionOn === 'boolean') {
const action = nightVisionOn ? 'off' : 'on';
pipeline.sendNightVision(action);
} else {
pipeline.sendNightVision('toggle');
}
recordControlIntent();
},
[pipeline, recordControlIntent],
);
const toggleNightVision = useCallback(() => {
pipeline.sendNightVision('toggle');
recordControlIntent();
}, [pipeline, recordControlIntent]);
setNightVision();
}, [setNightVision]);
const setSongNote = useCallback(
(note) => {
@@ -354,6 +367,7 @@ export function ControlSystemProvider({ children }) {
stopAllMotion,
sendOiCommand,
setSensorStream,
setNightVision,
toggleNightVision,
updateKeyBinding,
resetKeyBindings,
@@ -376,6 +390,7 @@ export function ControlSystemProvider({ children }) {
stopAllMotion,
sendOiCommand,
setSensorStream,
setNightVision,
toggleNightVision,
updateKeyBinding,
resetKeyBindings,
+4
View File
@@ -32,6 +32,8 @@ export function useCommandPipeline(options = {}) {
return rosterEntry.nightVision;
}, [rosterEntry]);
const nightVisionState = useMemo(() => rosterEntry?.nightVision?.state ?? null, [rosterEntry]);
const emitCommand = useCallback(
(payload, cb) => {
if (!roverId) return;
@@ -202,6 +204,7 @@ export function useCommandPipeline(options = {}) {
rosterEntry,
servoConfig,
nightVision,
nightVisionState,
emitCommand,
enableSensorStream,
sendDriveDirect,
@@ -217,6 +220,7 @@ export function useCommandPipeline(options = {}) {
rosterEntry,
servoConfig,
nightVision,
nightVisionState,
emitCommand,
enableSensorStream,
sendDriveDirect,
@@ -8,6 +8,7 @@ import {
getPadSignature,
} from './gamepadBindings.js';
import { subscribeGamepadHub } from './gamepadHub.js';
import { isTextEntryActive } from './inputFocusUtils.js';
const SOURCE = 'gamepad';
const ZERO_VECTOR = { x: 0, y: 0, boost: false };
@@ -154,6 +155,21 @@ export default function GamepadInputManager() {
return;
}
if (isTextEntryActive()) {
if (!areVectorsEqual(lastVectorRef.current, ZERO_VECTOR)) {
lastVectorRef.current = ZERO_VECTOR;
setDriveVector(ZERO_VECTOR, { source: SOURCE });
}
if (!areAuxEqual(lastAuxRef.current, ZERO_AUX)) {
lastAuxRef.current = ZERO_AUX;
setAuxMotors(ZERO_AUX);
}
buttonStateRef.current = new Map();
reverseStateRef.current = { main: false, side: false };
registerInputState(SOURCE, { connected: true, blocked: true });
return;
}
ensureProfile(activePad);
const signature = activePad.signature;
const profile =
@@ -3,6 +3,7 @@ import { useControlSystem } from '../ControlContext.jsx';
import { useChat } from '../../context/ChatContext.jsx';
import { useSession } from '../../context/SessionContext.jsx';
import { normalizeKeymapEntries, tokensForEvent } from '../keymapUtils.js';
import { isTextInputElement } from './inputFocusUtils.js';
import { useSettingsNamespace } from '../../settings/index.js';
import { INPUT_SETTINGS_DEFAULTS } from '../../settings/namespaces.js';
import {
@@ -55,15 +56,7 @@ function mapTiltIntervalToSpeed(interval) {
}
function shouldIgnoreEvent(event) {
const target = event.target;
if (!target) return false;
const tag = target.tagName;
return (
tag === 'INPUT' ||
tag === 'TEXTAREA' ||
target.isContentEditable ||
tag === 'SELECT'
);
return isTextInputElement(event?.target);
}
function bindingActive(bindingSet, keys) {
@@ -395,12 +388,12 @@ export default function KeyboardInputManager() {
resetAll();
}
window.addEventListener('keydown', handleKeyDown);
window.addEventListener('keyup', handleKeyUp);
window.addEventListener('keydown', handleKeyDown, { capture: true });
window.addEventListener('keyup', handleKeyUp, { capture: true });
window.addEventListener('blur', handleBlur);
return () => {
window.removeEventListener('keydown', handleKeyDown);
window.removeEventListener('keyup', handleKeyUp);
window.removeEventListener('keydown', handleKeyDown, { capture: true });
window.removeEventListener('keyup', handleKeyUp, { capture: true });
window.removeEventListener('blur', handleBlur);
};
}, [
@@ -0,0 +1,30 @@
const TEXT_INPUT_TYPES = new Set([
'',
'text',
'search',
'email',
'password',
'url',
'tel',
'number',
'date',
'datetime-local',
'month',
'time',
'week',
]);
export function isTextInputElement(target) {
if (!target || target.nodeType !== 1) return false;
const tag = target.tagName;
if (tag === 'TEXTAREA') return true;
if (target.isContentEditable) return true;
if (tag !== 'INPUT') return false;
const type = target.type ? target.type.toLowerCase() : '';
return TEXT_INPUT_TYPES.has(type);
}
export function isTextEntryActive() {
if (typeof document === 'undefined') return false;
return isTextInputElement(document.activeElement);
}