keyboard speed controls

This commit is contained in:
legop3
2025-12-06 01:07:01 -05:00
parent 2b1122dce9
commit b72c58452e
10 changed files with 126 additions and 19 deletions
+79
View File
@@ -2,6 +2,8 @@ import { useMemo, useState, useEffect, useCallback } from 'react';
import { useControlSystem } from '../controls/index.js';
import { DEFAULT_KEYMAP } from '../controls/constants.js';
import { canonicalizeKeyInput, formatKeyLabel } from '../controls/keymapUtils.js';
import { useSettingsNamespace } from '../settings/index.js';
import { INPUT_SETTINGS_DEFAULTS } from '../settings/namespaces.js';
const KEY_ACTIONS = [
{ id: 'driveForward', label: 'Drive Forward', group: 'Driving' },
@@ -33,6 +35,12 @@ function groupActions(actions) {
}, {});
}
function clampSpeed(value, fallback) {
const num = Number(value);
if (!Number.isFinite(num)) return fallback;
return Math.max(0, Math.min(500, num));
}
function useKeyCapture(onCapture) {
const [active, setActive] = useState(null);
@@ -57,11 +65,44 @@ function useKeyCapture(onCapture) {
return { active, startCapture: setActive, cancel: () => setActive(null) };
}
function SpeedField({ label, description, value, onChange }) {
return (
<label className="surface-muted block p-0.5">
<div className="flex items-center justify-between text-xs text-slate-300">
<span className="font-semibold text-slate-100">{label}</span>
<input
type="number"
min={0}
max={500}
value={value}
onChange={(event) => onChange(event.target.value)}
className="w-16 rounded border border-slate-700 bg-slate-900 px-1 py-[2px] text-right text-[0.75rem] font-mono text-slate-100"
/>
</div>
{description && <p className="text-[0.65rem] text-slate-500">{description}</p>}
<div className="mt-0.5 flex items-center gap-0.5">
<input
type="range"
min={0}
max={500}
step={5}
value={value}
onChange={(event) => onChange(event.target.value)}
className="h-2 flex-1 accent-emerald-400"
/>
<span className="w-12 text-right text-[0.75rem] font-mono text-slate-400">{value}</span>
</div>
</label>
);
}
export default function KeymapSettings() {
const {
state: { keymap },
actions: { updateKeyBinding, resetKeyBindings },
} = useControlSystem();
const { value: inputSettings, save: saveInputSettings } = useSettingsNamespace('inputs', INPUT_SETTINGS_DEFAULTS);
const keyboardSpeeds = inputSettings.keyboard ?? INPUT_SETTINGS_DEFAULTS.keyboard;
const grouped = useMemo(() => groupActions(KEY_ACTIONS), []);
const { active, startCapture, cancel } = useKeyCapture((actionId, value) => {
updateKeyBinding(actionId, value);
@@ -72,6 +113,21 @@ export default function KeymapSettings() {
[keymap],
);
const updateKeyboardSpeed = useCallback(
(key, nextValue) => {
const defaults = INPUT_SETTINGS_DEFAULTS.keyboard;
const clamped = clampSpeed(nextValue, defaults[key]);
saveInputSettings((prev) => ({
...(prev ?? {}),
keyboard: {
...(prev?.keyboard ?? defaults),
[key]: clamped,
},
}));
},
[saveInputSettings],
);
return (
<section className="panel-section space-y-0.5 text-sm">
<div className="flex items-center justify-between">
@@ -83,6 +139,29 @@ export default function KeymapSettings() {
Reset defaults
</button>
</div>
<div className="space-y-0.5 surface">
<p className="text-[0.7rem] text-slate-500">Keyboard speeds</p>
<div className="space-y-0.5">
<SpeedField
label="Base speed"
description="Normal driving speed"
value={keyboardSpeeds.baseSpeed}
onChange={(value) => updateKeyboardSpeed('baseSpeed', value)}
/>
<SpeedField
label="Turbo speed"
description="Used when holding the boost modifier"
value={keyboardSpeeds.turboSpeed}
onChange={(value) => updateKeyboardSpeed('turboSpeed', value)}
/>
<SpeedField
label="Precision speed"
description="Used when holding the precision modifier"
value={keyboardSpeeds.precisionSpeed}
onChange={(value) => updateKeyboardSpeed('precisionSpeed', value)}
/>
</div>
</div>
<div className="space-y-0.5">
{Object.entries(grouped).map(([group, actions]) => (
<div key={group} className="space-y-0.5 surface">