From 936701134f4d4d4e1d2467d2415738b684826c61 Mon Sep 17 00:00:00 2001 From: tariknz Date: Fri, 25 Jul 2025 09:46:54 +1200 Subject: [PATCH 1/4] feat: add steering input --- src/app/storage/defaultDashboard.ts | 3 + .../InputContainer/InputContainer.stories.tsx | 12 ++ .../Input/InputContainer/InputContainer.tsx | 26 +---- .../Input/InputSteer/InputSteer.stories.tsx | 14 ++- .../Input/InputSteer/InputSteer.tsx | 32 +++++- .../Input/hooks/useInputSettings.tsx | 4 +- .../components/Input/hooks/useInputs.tsx | 3 +- .../Settings/sections/InputSettings.tsx | 104 +++++++++++++++++- src/frontend/components/Settings/types.ts | 27 ++++- 9 files changed, 185 insertions(+), 40 deletions(-) diff --git a/src/app/storage/defaultDashboard.ts b/src/app/storage/defaultDashboard.ts index 9f9e900..31c2efd 100644 --- a/src/app/storage/defaultDashboard.ts +++ b/src/app/storage/defaultDashboard.ts @@ -45,6 +45,9 @@ export const defaultDashboard: DashboardLayout = { enabled: true, unit: 'auto', }, + steer: { + enabled: true, + }, }, }, { diff --git a/src/frontend/components/Input/InputContainer/InputContainer.stories.tsx b/src/frontend/components/Input/InputContainer/InputContainer.stories.tsx index 968b7cb..151c6fb 100644 --- a/src/frontend/components/Input/InputContainer/InputContainer.stories.tsx +++ b/src/frontend/components/Input/InputContainer/InputContainer.stories.tsx @@ -15,6 +15,7 @@ const RandomTraces = () => { const [clutch, setClutch] = useState(0); const [gear] = useState(2); const [speed] = useState(122); + const [steer, setSteer] = useState(0); useEffect(() => { const interval = setInterval(() => { @@ -29,6 +30,13 @@ const RandomTraces = () => { setClutch((value) => Math.max(0, Math.min(1, value + Math.random() * 0.1 - 0.05)) ); + + setSteer((value) => + Math.max( + -Math.PI, + Math.min(Math.PI, value + Math.random() * 0.2 - 0.1), + ) + ); }, 1000 / 60); return () => clearInterval(interval); }, []); @@ -39,6 +47,7 @@ const RandomTraces = () => { clutch={clutch} gear={gear} speed={speed} + steer={steer} settings={{ trace: { enabled: true, @@ -55,6 +64,9 @@ const RandomTraces = () => { enabled: true, unit: 'auto', }, + steer: { + enabled: true, + }, }} /> ); diff --git a/src/frontend/components/Input/InputContainer/InputContainer.tsx b/src/frontend/components/Input/InputContainer/InputContainer.tsx index 2ac1afb..83feeac 100644 --- a/src/frontend/components/Input/InputContainer/InputContainer.tsx +++ b/src/frontend/components/Input/InputContainer/InputContainer.tsx @@ -1,5 +1,7 @@ +import { InputWidgetSettings } from '../../Settings/types'; import { InputBar } from '../InputBar/InputBar'; import { InputGear } from '../InputGear/InputGear'; +import { InputSteer } from '../InputSteer/InputSteer'; import { InputTrace } from '../InputTrace/InputTrace'; export interface InputProps { @@ -9,25 +11,8 @@ export interface InputProps { gear?: number; speed?: number; unit?: number; - settings?: InputSettings; -} - -export interface InputSettings { - trace: { - enabled: boolean; - includeThrottle: boolean; - includeBrake: boolean; - }; - bar: { - enabled: boolean; - includeClutch: boolean; - includeBrake: boolean; - includeThrottle: boolean; - }; - gear: { - enabled: boolean; - unit: 'mph' | 'km/h' | 'auto'; - }; + steer?: number; + settings?: InputWidgetSettings['config']; } export const InputContainer = ({ @@ -36,6 +21,7 @@ export const InputContainer = ({ clutch, gear, speed, + steer, unit, settings, }: InputProps) => { @@ -44,7 +30,7 @@ export const InputContainer = ({ {settings?.trace.enabled && } {settings?.bar.enabled && } {settings?.gear.enabled && } - {/* */} {/* WIP */} + {settings?.steer?.enabled && } ); }; diff --git a/src/frontend/components/Input/InputSteer/InputSteer.stories.tsx b/src/frontend/components/Input/InputSteer/InputSteer.stories.tsx index bd2adf1..6528459 100644 --- a/src/frontend/components/Input/InputSteer/InputSteer.stories.tsx +++ b/src/frontend/components/Input/InputSteer/InputSteer.stories.tsx @@ -3,10 +3,22 @@ import { InputSteer } from './InputSteer'; export default { component: InputSteer, + argTypes: { + angleRad: { + control: { + type: 'range', + min: -3.14, + max: 3.14, + step: 0.01, + }, + }, + }, } as Meta; type Story = StoryObj; export const Primary: Story = { - args: {}, + args: { + angleRad: 0, + }, }; diff --git a/src/frontend/components/Input/InputSteer/InputSteer.tsx b/src/frontend/components/Input/InputSteer/InputSteer.tsx index a0414d5..4806a56 100644 --- a/src/frontend/components/Input/InputSteer/InputSteer.tsx +++ b/src/frontend/components/Input/InputSteer/InputSteer.tsx @@ -1,11 +1,31 @@ -import { SteeringWheelIcon } from '@phosphor-icons/react'; +export interface InputSteerProps { + angleRad?: number; +} -export type InputSteerProps = object; // TODO - -export const InputSteer = () => { +export const InputSteer = ({ angleRad = 0 }: InputSteerProps) => { return ( -
- +
+ + + + + +
); }; diff --git a/src/frontend/components/Input/hooks/useInputSettings.tsx b/src/frontend/components/Input/hooks/useInputSettings.tsx index 676f6a3..1a7d4fb 100644 --- a/src/frontend/components/Input/hooks/useInputSettings.tsx +++ b/src/frontend/components/Input/hooks/useInputSettings.tsx @@ -1,5 +1,5 @@ import { useDashboard } from '@irdashies/context' -import { InputSettings } from '../InputContainer/InputContainer'; +import { InputWidgetSettings } from '../../Settings/types'; export const useInputSettings = () => { const { currentDashboard } = useDashboard(); @@ -18,7 +18,7 @@ export const useInputSettings = () => { typeof inputSettings.bar === 'object' && typeof inputSettings.gear === 'object' ) { - return inputSettings as unknown as InputSettings; + return inputSettings as unknown as InputWidgetSettings['config']; } return undefined; diff --git a/src/frontend/components/Input/hooks/useInputs.tsx b/src/frontend/components/Input/hooks/useInputs.tsx index 2ec23ad..da9fa2e 100644 --- a/src/frontend/components/Input/hooks/useInputs.tsx +++ b/src/frontend/components/Input/hooks/useInputs.tsx @@ -7,6 +7,7 @@ export const useInputs = () => { const gear = useTelemetryValue('Gear'); const speed = useTelemetryValue('Speed'); const unit = useTelemetryValue('DisplayUnits'); + const steer = useTelemetryValue('SteeringWheelAngle'); - return { brake, throttle, clutch, gear, speed, unit }; + return { brake, throttle, clutch, gear, speed, unit, steer }; }; diff --git a/src/frontend/components/Settings/sections/InputSettings.tsx b/src/frontend/components/Settings/sections/InputSettings.tsx index d050fc6..5891d7c 100644 --- a/src/frontend/components/Settings/sections/InputSettings.tsx +++ b/src/frontend/components/Settings/sections/InputSettings.tsx @@ -3,15 +3,87 @@ import { BaseSettingsSection } from '../components/BaseSettingsSection'; import { InputWidgetSettings } from '../types'; import { useDashboard } from '@irdashies/context'; import { ToggleSwitch } from '../components/ToggleSwitch'; -import { InputSettings as InputSettingsType } from '../../Input/InputContainer/InputContainer'; + +const SETTING_ID = 'input'; + +const defaultConfig: InputWidgetSettings['config'] = { + trace: { + enabled: true, + includeThrottle: true, + includeBrake: true, + }, + bar: { + enabled: true, + includeClutch: true, + includeBrake: true, + includeThrottle: true, + }, + gear: { + enabled: true, + unit: 'auto', + }, + steer: { + enabled: true, + }, +}; + +// Migration function to handle missing properties in the new config format +const migrateConfig = ( + savedConfig: unknown, +): InputWidgetSettings['config'] => { + if (!savedConfig || typeof savedConfig !== 'object') return defaultConfig; + + const config = savedConfig as Record; + + return { + trace: { + enabled: + (config.trace as { enabled?: boolean })?.enabled ?? + defaultConfig.trace.enabled, + includeThrottle: + (config.trace as { includeThrottle?: boolean })?.includeThrottle ?? + defaultConfig.trace.includeThrottle, + includeBrake: + (config.trace as { includeBrake?: boolean })?.includeBrake ?? + defaultConfig.trace.includeBrake, + }, + bar: { + enabled: + (config.bar as { enabled?: boolean })?.enabled ?? defaultConfig.bar.enabled, + includeClutch: + (config.bar as { includeClutch?: boolean })?.includeClutch ?? + defaultConfig.bar.includeClutch, + includeBrake: + (config.bar as { includeBrake?: boolean })?.includeBrake ?? + defaultConfig.bar.includeBrake, + includeThrottle: + (config.bar as { includeThrottle?: boolean })?.includeThrottle ?? + defaultConfig.bar.includeThrottle, + }, + gear: { + enabled: + (config.gear as { enabled?: boolean })?.enabled ?? + defaultConfig.gear.enabled, + unit: + (config.gear as { unit?: 'mph' | 'km/h' | 'auto' })?.unit ?? + defaultConfig.gear.unit, + }, + steer: { + enabled: + (config.steer as { enabled?: boolean })?.enabled ?? + defaultConfig.steer.enabled, + }, + }; +}; export const InputSettings = () => { const { currentDashboard } = useDashboard(); + const savedSettings = currentDashboard?.widgets.find( + (w) => w.id === SETTING_ID, + ); const [settings, setSettings] = useState({ - enabled: - currentDashboard?.widgets.find((w) => w.id === 'input')?.enabled ?? false, - config: ((currentDashboard?.widgets.find((w) => w.id === 'input') - ?.config as unknown) as InputSettingsType), + enabled: savedSettings?.enabled ?? false, + config: migrateConfig(savedSettings?.config), }); if (!currentDashboard) { @@ -21,7 +93,7 @@ export const InputSettings = () => { const config = settings.config; return ( - + { )}
+ {/* Steer Settings */} +
+
+

+ Steer Settings +

+
+ + Enable Steer Display + + + handleConfigChange({ steer: { ...config.steer, enabled } }) + } + /> +
+
+
+ {/* Gear Settings */}
diff --git a/src/frontend/components/Settings/types.ts b/src/frontend/components/Settings/types.ts index a71a867..af17238 100644 --- a/src/frontend/components/Settings/types.ts +++ b/src/frontend/components/Settings/types.ts @@ -1,6 +1,3 @@ -import { InputSettings } from '../Input/InputContainer/InputContainer'; - -/* eslint-disable @typescript-eslint/no-empty-object-type */ export interface BaseWidgetSettings> { enabled: boolean; config: T; @@ -42,8 +39,30 @@ export interface TrackMapWidgetSettings extends BaseWidgetSettings { }; } -export type InputWidgetSettings = BaseWidgetSettings; +export interface InputWidgetSettings extends BaseWidgetSettings { + config: { + trace: { + enabled: boolean; + includeThrottle: boolean; + includeBrake: boolean; + }; + bar: { + enabled: boolean; + includeClutch: boolean; + includeBrake: boolean; + includeThrottle: boolean; + }; + gear: { + enabled: boolean; + unit: 'mph' | 'km/h' | 'auto'; + }; + steer: { + enabled: boolean; + }; + }; +} +/* eslint-disable @typescript-eslint/no-empty-object-type */ export interface AdvancedSettings extends BaseWidgetSettings { // Add specific advanced settings here } \ No newline at end of file From d130dc9053a4cf93d874d426216f43956318c980 Mon Sep 17 00:00:00 2001 From: tariknz Date: Fri, 25 Jul 2025 09:48:33 +1200 Subject: [PATCH 2/4] use tailwind colour for yellow --- src/frontend/components/Input/InputSteer/InputSteer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/components/Input/InputSteer/InputSteer.tsx b/src/frontend/components/Input/InputSteer/InputSteer.tsx index 4806a56..362c2df 100644 --- a/src/frontend/components/Input/InputSteer/InputSteer.tsx +++ b/src/frontend/components/Input/InputSteer/InputSteer.tsx @@ -22,7 +22,7 @@ export const InputSteer = ({ angleRad = 0 }: InputSteerProps) => { From ca22809e135edd372fd84957e4803d46400a8c3a Mon Sep 17 00:00:00 2001 From: Tarik Alani Date: Fri, 25 Jul 2025 10:15:22 +1200 Subject: [PATCH 3/4] fix steering angle --- src/frontend/components/Input/InputSteer/InputSteer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/components/Input/InputSteer/InputSteer.tsx b/src/frontend/components/Input/InputSteer/InputSteer.tsx index 362c2df..4e478c1 100644 --- a/src/frontend/components/Input/InputSteer/InputSteer.tsx +++ b/src/frontend/components/Input/InputSteer/InputSteer.tsx @@ -14,7 +14,7 @@ export const InputSteer = ({ angleRad = 0 }: InputSteerProps) => { > Date: Fri, 25 Jul 2025 12:29:53 +1200 Subject: [PATCH 4/4] tidy up coalescing settings --- .../components/Input/InputBar/InputBar.tsx | 15 +++++++++--- .../Input/InputContainer/InputContainer.tsx | 24 +++++++++++++++---- .../components/Input/InputGear/InputGear.tsx | 12 +++++++--- .../Input/InputTrace/InputTrace.tsx | 13 ++++++---- 4 files changed, 49 insertions(+), 15 deletions(-) diff --git a/src/frontend/components/Input/InputBar/InputBar.tsx b/src/frontend/components/Input/InputBar/InputBar.tsx index ab3777c..01bb0e6 100644 --- a/src/frontend/components/Input/InputBar/InputBar.tsx +++ b/src/frontend/components/Input/InputBar/InputBar.tsx @@ -8,18 +8,27 @@ const INPUT_CONFIG = [ { key: 'throttle', color: getColor('green') } ] as const; -export interface InputTraceProps { +export interface InputBarProps { brake?: number; throttle?: number; clutch?: number; - settings: { + settings?: { includeClutch: boolean; includeBrake: boolean; includeThrottle: boolean; }; } -export const InputBar = ({ brake, throttle, clutch, settings }: InputTraceProps) => { +export const InputBar = ({ + brake, + throttle, + clutch, + settings = { + includeClutch: true, + includeBrake: true, + includeThrottle: true, + }, +}: InputBarProps) => { const svgRef = useRef(null); useEffect(() => { diff --git a/src/frontend/components/Input/InputContainer/InputContainer.tsx b/src/frontend/components/Input/InputContainer/InputContainer.tsx index 83feeac..be8b37a 100644 --- a/src/frontend/components/Input/InputContainer/InputContainer.tsx +++ b/src/frontend/components/Input/InputContainer/InputContainer.tsx @@ -27,10 +27,26 @@ export const InputContainer = ({ }: InputProps) => { return (
- {settings?.trace.enabled && } - {settings?.bar.enabled && } - {settings?.gear.enabled && } - {settings?.steer?.enabled && } + {(settings?.trace?.enabled ?? true) && ( + + )} + {(settings?.bar?.enabled ?? true) && ( + + )} + {(settings?.gear?.enabled ?? true) && ( + + )} + {(settings?.steer?.enabled ?? true) && }
); }; diff --git a/src/frontend/components/Input/InputGear/InputGear.tsx b/src/frontend/components/Input/InputGear/InputGear.tsx index 256a3aa..eee560c 100644 --- a/src/frontend/components/Input/InputGear/InputGear.tsx +++ b/src/frontend/components/Input/InputGear/InputGear.tsx @@ -2,13 +2,19 @@ export interface InputGearProps { gear?: number; speedMs?: number; unit?: number; - settings: { + settings?: { unit: 'mph' | 'km/h' | 'auto'; }; } -export const InputGear = ({ gear, speedMs, unit, settings }: InputGearProps) => { - const isMetric = (unit === 1 && settings.unit === 'auto') || settings.unit === 'km/h'; +export const InputGear = ({ + gear, + speedMs, + unit, + settings = { unit: 'auto' }, +}: InputGearProps) => { + const isMetric = + (unit === 1 && settings.unit === 'auto') || settings.unit === 'km/h'; const speed = (speedMs ?? 0) * (isMetric ? 3.6 : 2.23694); const displayUnit = isMetric ? 'km/h' : 'mph'; let gearText = ''; diff --git a/src/frontend/components/Input/InputTrace/InputTrace.tsx b/src/frontend/components/Input/InputTrace/InputTrace.tsx index 7d77605..0b56b4e 100644 --- a/src/frontend/components/Input/InputTrace/InputTrace.tsx +++ b/src/frontend/components/Input/InputTrace/InputTrace.tsx @@ -1,22 +1,25 @@ import * as d3 from 'd3'; import { useEffect, useRef, useState } from 'react'; -import tailwindColors from 'tailwindcss/colors'; +import { getColor } from '@irdashies/utils/colors'; -const BRAKE_COLOR = tailwindColors.red['500']; -const THROTTLE_COLOR = tailwindColors.green['500']; +const BRAKE_COLOR = getColor('red'); +const THROTTLE_COLOR = getColor('green'); export interface InputTraceProps { input: { brake?: number; throttle?: number; }; - settings: { + settings?: { includeThrottle?: boolean; includeBrake?: boolean; }; } -export const InputTrace = ({ input, settings }: InputTraceProps) => { +export const InputTrace = ({ + input, + settings = { includeThrottle: true, includeBrake: true }, +}: InputTraceProps) => { const { includeThrottle, includeBrake } = settings; const svgRef = useRef(null); const { width, height } = { width: 400, height: 100 };