@@ -11,8 +11,8 @@ import {
1111
1212const DEVICE_POLL_INTERVAL_MS = 1200 ;
1313const INPUT_CAPTURE_TIMEOUT_MS = 5000 ;
14- const KEYBOARD_INPUTS = Object . freeze ( [ "KeyW" , "KeyA" , "KeyS" , "KeyD" , "Space" , "Enter" , "Escape " , "KeyP" ] ) ;
15- const MOUSE_INPUTS = Object . freeze ( [ "MouseButton0" , "MouseButton2" , "MouseX" , "MouseY" ] ) ;
14+ const KEYBOARD_INPUTS = Object . freeze ( [ "KeyW" , "KeyA" , "KeyS" , "KeyD" , "Space" , "ShiftLeft" , "ControlLeft" , " Enter", "Backspace " , "KeyP" ] ) ;
15+ const MOUSE_INPUTS = Object . freeze ( [ "MouseButton0" , "MouseButton2" , "MouseButton1" , "MouseWheelUp" , "MouseWheelDown" , " MouseX- ", "MouseX+" , " MouseY-" , "MouseY+ "] ) ;
1616const KEYBOARD_MOUSE_EXCLUDED_NORMALIZED_PREFIXES = Object . freeze ( [ "dpad." , "trigger." ] ) ;
1717const SUPPORTED_CONTROL_TYPES = Object . freeze ( [
1818 "Keyboard Key" ,
@@ -99,8 +99,13 @@ function rangeValueLabel(value, unit = "") {
9999 return `${ value } ${ unit } ` ;
100100}
101101
102- function physicalInputSupportsTuning ( physicalInput ) {
103- return physicalInputIsAnalog ( physicalInput ) || Boolean ( physicalInputSensitivityDescriptor ( physicalInput ) ) ;
102+ function physicalInputSupportsDeadzoneInvert ( physicalInput ) {
103+ const normalizedInput = normalizeText ( physicalInput ) ;
104+ const lowerName = normalizedInput . toLowerCase ( ) ;
105+ return physicalInputIsAnalog ( normalizedInput )
106+ || lowerName . includes ( "trigger" )
107+ || normalizedInput === "LT"
108+ || normalizedInput === "RT" ;
104109}
105110
106111function createSliderControl ( { ariaLabel, dataName, defaultValue, index, max, min, step, unit, value } ) {
@@ -315,7 +320,7 @@ export class AccountUserControlsPage {
315320 this . profiles = result . profiles . map ( ( profile ) => this . normalizeProfile ( profile ) ) ;
316321 return true ;
317322 }
318- this . setStatus ( result ?. message || "WARN: Account user controls could not reach the shared DB adapter." ) ;
323+ this . setStatus ( result ?. message || "WARN: User Controls could not reach the shared DB adapter." ) ;
319324 return false ;
320325 }
321326
@@ -330,6 +335,43 @@ export class AccountUserControlsPage {
330335 } ) ;
331336 }
332337
338+ uniqueProfileForDevice ( device ) {
339+ const family = this . profileListFamily ( device ) ;
340+ const baseControllerName = normalizeText ( device . controllerName ) || family ;
341+ const baseProfileName = normalizeText ( device . mappingProfile ) || `${ baseControllerName } Profile` ;
342+ const profileNames = new Set ( this . profiles
343+ . filter ( ( profile ) => this . profileListFamily ( profile ) === family )
344+ . map ( ( profile ) => normalizeText ( profile . mappingProfile ) . toLowerCase ( ) ) ) ;
345+ let controllerName = baseControllerName ;
346+ let mappingProfile = baseProfileName ;
347+ let suffix = 2 ;
348+ while ( profileNames . has ( mappingProfile . toLowerCase ( ) ) ) {
349+ controllerName = `${ baseControllerName } ${ suffix } ` ;
350+ mappingProfile = `${ baseControllerName } ${ suffix } Profile` ;
351+ suffix += 1 ;
352+ }
353+ return this . normalizeProfile ( {
354+ controllerId : device . controllerId ,
355+ controllerName,
356+ deviceType : device . deviceType ,
357+ inputMappings : normalizeProfileInputMappings ( device . inputs ) ,
358+ inputs : device . inputs ,
359+ mappingProfile,
360+ } ) ;
361+ }
362+
363+ createProfile ( device ) {
364+ const profile = this . uniqueProfileForDevice ( device ) ;
365+ if ( ! this . saveProfiles ( [ profile , ...this . profiles ] ) ) {
366+ this . setStatus ( "FAIL: User Controls could not reach the shared DB adapter." ) ;
367+ return ;
368+ }
369+ this . editingProfile = null ;
370+ this . viewingDefaultFamily = "" ;
371+ this . renderProfiles ( ) ;
372+ this . setStatus ( `PASS: Created ${ profile . mappingProfile } . Use Edit to change it.` ) ;
373+ }
374+
333375 renderDeviceSelect ( ) {
334376 if ( ! this . elements . deviceSelect ) {
335377 return ;
@@ -661,8 +703,8 @@ export class AccountUserControlsPage {
661703 return row ;
662704 }
663705 row . append (
664- tableCell ( physicalInputSupportsTuning ( inputMapping . physicalInput ) ? String ( inputMapping . deadzone ) : "N/A" ) ,
665- tableCell ( physicalInputSupportsTuning ( inputMapping . physicalInput ) && inputMapping . invert ? "On" : physicalInputSupportsTuning ( inputMapping . physicalInput ) ? "Off" : "N/A" ) ,
706+ tableCell ( physicalInputSupportsDeadzoneInvert ( inputMapping . physicalInput ) ? String ( inputMapping . deadzone ) : "N/A" ) ,
707+ tableCell ( physicalInputSupportsDeadzoneInvert ( inputMapping . physicalInput ) && inputMapping . invert ? "On" : physicalInputSupportsDeadzoneInvert ( inputMapping . physicalInput ) ? "Off" : "N/A" ) ,
666708 tableCell ( physicalInputSensitivityDescriptor ( inputMapping . physicalInput )
667709 ? rangeValueLabel ( inputMapping . sensitivity ?? physicalInputSensitivityDescriptor ( inputMapping . physicalInput ) . defaultValue , physicalInputSensitivityDescriptor ( inputMapping . physicalInput ) . unit )
668710 : "N/A" ) ,
@@ -830,13 +872,6 @@ export class AccountUserControlsPage {
830872 } ) ;
831873 positiveSelect . dataset . accountUserControlsInputPositive = String ( index ) ;
832874 stack . append ( labeledControl ( "Negative" , negativeSelect ) , labeledControl ( "Positive" , positiveSelect ) ) ;
833- } else if ( family === "Keyboard" ) {
834- const value = normalizeText ( inputMapping . normalizedInput ) || "Unassigned" ;
835- const readonlyControl = document . createElement ( "span" ) ;
836- readonlyControl . className = "status" ;
837- readonlyControl . dataset . accountUserControlsInputNormalizedReadonly = String ( index ) ;
838- readonlyControl . textContent = value ;
839- stack . append ( readonlyControl ) ;
840875 } else {
841876 const select = selectControl ( {
842877 ariaLabel : `${ inputMapping . physicalInput } normalized control` ,
@@ -857,7 +892,7 @@ export class AccountUserControlsPage {
857892 }
858893
859894 const deadzoneCell = document . createElement ( "td" ) ;
860- if ( physicalInputSupportsTuning ( inputMapping . physicalInput ) ) {
895+ if ( physicalInputSupportsDeadzoneInvert ( inputMapping . physicalInput ) ) {
861896 const deadzone = document . createElement ( "input" ) ;
862897 deadzone . type = "number" ;
863898 deadzone . min = "0" ;
@@ -871,7 +906,7 @@ export class AccountUserControlsPage {
871906 }
872907
873908 const invertCell = document . createElement ( "td" ) ;
874- if ( physicalInputSupportsTuning ( inputMapping . physicalInput ) ) {
909+ if ( physicalInputSupportsDeadzoneInvert ( inputMapping . physicalInput ) ) {
875910 const invert = document . createElement ( "input" ) ;
876911 invert . type = "checkbox" ;
877912 invert . checked = Boolean ( inputMapping . invert ) ;
@@ -984,37 +1019,13 @@ export class AccountUserControlsPage {
9841019 this . setStatus ( "WARN: Choose a physical controller before creating a user control profile." ) ;
9851020 return ;
9861021 }
987- const existing = this . profiles . find ( ( profile ) => profile . deviceType === device . deviceType && profile . controllerId === device . controllerId ) ;
988- if ( existing ) {
989- this . editingProfile = { id : existing . id , values : existing } ;
990- this . viewingDefaultFamily = "" ;
991- this . renderProfiles ( ) ;
992- this . setStatus ( `Editing existing ${ existing . mappingProfile } .` ) ;
993- return ;
994- }
995- const profile = this . profileFromDevice ( device ) ;
996- this . editingProfile = { id : profile . id , values : profile } ;
997- this . viewingDefaultFamily = "" ;
998- this . renderProfiles ( ) ;
999- this . setStatus ( `Review ${ profile . mappingProfile } before saving.` ) ;
1022+ this . createProfile ( device ) ;
10001023 }
10011024
10021025 editFamilyMappings ( family ) {
10031026 const normalizedFamily = family === "Mouse" ? "Mouse" : "Keyboard" ;
10041027 const device = this . familyDevice ( normalizedFamily ) ;
1005- const existing = this . profiles . find ( ( profile ) => profile . deviceType === device . deviceType && profile . controllerId === device . controllerId ) ;
1006- if ( existing ) {
1007- this . editingProfile = { id : existing . id , values : existing } ;
1008- this . viewingDefaultFamily = "" ;
1009- this . renderProfiles ( ) ;
1010- this . setStatus ( `Editing existing ${ existing . mappingProfile } .` ) ;
1011- return ;
1012- }
1013- const profile = this . profileFromDevice ( device ) ;
1014- this . editingProfile = { id : profile . id , values : profile } ;
1015- this . viewingDefaultFamily = "" ;
1016- this . renderProfiles ( ) ;
1017- this . setStatus ( `Review ${ profile . mappingProfile } before saving.` ) ;
1028+ this . createProfile ( device ) ;
10181029 }
10191030
10201031 profileFromEditingRow ( ) {
@@ -1113,6 +1124,19 @@ export class AccountUserControlsPage {
11131124 } ;
11141125 }
11151126
1127+ duplicateProfileName ( profile ) {
1128+ const profileName = normalizeText ( profile . mappingProfile ) . toLowerCase ( ) ;
1129+ if ( ! profileName ) {
1130+ return null ;
1131+ }
1132+ const family = this . profileListFamily ( profile ) ;
1133+ return this . profiles . find ( ( candidate ) =>
1134+ candidate . id !== profile . id
1135+ && this . profileListFamily ( candidate ) === family
1136+ && normalizeText ( candidate . mappingProfile ) . toLowerCase ( ) === profileName ,
1137+ ) || null ;
1138+ }
1139+
11161140 renderInputValidation ( validation ) {
11171141 const invalidIndexes = new Set ( validation . invalidIndexes || [ ] ) ;
11181142 this . root . querySelectorAll ( "[data-account-user-controls-input-validation]" ) . forEach ( ( status ) => {
@@ -1207,11 +1231,15 @@ export class AccountUserControlsPage {
12071231 this . setStatus ( `FAIL: ${ validation . message } ` ) ;
12081232 return ;
12091233 }
1234+ if ( this . duplicateProfileName ( profile ) ) {
1235+ this . setStatus ( `FAIL: ${ profile . mappingProfile } already exists for ${ this . profileListFamily ( profile ) } .` ) ;
1236+ return ;
1237+ }
12101238 const nextProfiles = this . editingProfile ?. id && this . profiles . some ( ( candidate ) => candidate . id === this . editingProfile . id )
12111239 ? this . profiles . map ( ( candidate ) => ( candidate . id === this . editingProfile . id ? profile : candidate ) )
12121240 : [ profile , ...this . profiles ] ;
12131241 if ( ! this . saveProfiles ( nextProfiles ) ) {
1214- this . setStatus ( "FAIL: Account user controls could not reach the shared DB adapter." ) ;
1242+ this . setStatus ( "FAIL: User Controls could not reach the shared DB adapter." ) ;
12151243 return ;
12161244 }
12171245 this . editingProfile = null ;
0 commit comments