88 physicalInputIsAnalog ,
99} from "../src/engine/input/NormalizedInputRegistry.js" ;
1010
11- const KEYBOARD_MOUSE_INPUTS = Object . freeze ( [ "KeyW" , "KeyA" , "KeyS" , "KeyD" , "Space" , "Enter" , "Escape" , "KeyP" , "MouseButton0" , "MouseButton2" , "MouseWheelUp" , "MouseWheelDown" , "MouseX" , "MouseY" ] ) ;
11+ const DEVICE_POLL_INTERVAL_MS = 1200 ;
12+ const KEYBOARD_INPUTS = Object . freeze ( [ "KeyW" , "KeyA" , "KeyS" , "KeyD" , "Space" , "Enter" , "Escape" , "KeyP" ] ) ;
13+ const MOUSE_INPUTS = Object . freeze ( [ "MouseButton0" , "MouseButton2" , "MouseX" , "MouseY" ] ) ;
1214const SUPPORTED_CONTROL_TYPES = Object . freeze ( [
1315 "Keyboard Key" ,
1416 "Mouse Button" ,
@@ -122,8 +124,10 @@ export class AccountUserControlsPage {
122124 this . inputService = new InputService ( { target : window } ) ;
123125 this . profiles = [ ] ;
124126 this . editingProfile = null ;
127+ this . devicePollingTimer = null ;
125128 this . elements = {
126129 addProfile : root . querySelector ( "[data-account-user-controls-add-profile]" ) ,
130+ defaultList : root . querySelector ( "[data-account-user-controls-default-list]" ) ,
127131 deviceSelect : root . querySelector ( "[data-account-user-controls-device]" ) ,
128132 deviceStatus : root . querySelector ( "[data-account-user-controls-device-status]" ) ,
129133 list : root . querySelector ( "[data-account-user-controls-list]" ) ,
@@ -138,6 +142,7 @@ export class AccountUserControlsPage {
138142 this . inputService . attach ( ) ;
139143 this . profiles = this . readProfiles ( ) ;
140144 this . renderDeviceSelect ( ) ;
145+ this . renderDefaultFallbacks ( ) ;
141146 this . renderTypes ( ) ;
142147 this . renderProfiles ( ) ;
143148 this . elements . refresh ?. addEventListener ( "click" , ( ) => {
@@ -151,6 +156,7 @@ export class AccountUserControlsPage {
151156 this . elements . list ?. addEventListener ( "change" , ( event ) => this . handleListChange ( event ) ) ;
152157 this . elements . list ?. addEventListener ( "input" , ( event ) => this . handleListInput ( event ) ) ;
153158 this . elements . list ?. addEventListener ( "dblclick" , ( event ) => this . handleListDoubleClick ( event ) ) ;
159+ this . startDevicePolling ( ) ;
154160 }
155161
156162 setStatus ( message ) {
@@ -171,14 +177,23 @@ export class AccountUserControlsPage {
171177 }
172178
173179 deviceOptions ( ) {
174- const keyboardMouse = {
175- controllerId : "keyboard-mouse" ,
176- controllerName : "Keyboard/Mouse" ,
177- deviceType : "Keyboard/Mouse" ,
178- inputs : [ ...KEYBOARD_MOUSE_INPUTS ] ,
179- label : "Keyboard/Mouse" ,
180- mappingProfile : "Keyboard/Mouse Profile" ,
181- value : "keyboard-mouse" ,
180+ const keyboard = {
181+ controllerId : "generic-keyboard" ,
182+ controllerName : "Keyboard" ,
183+ deviceType : "Keyboard" ,
184+ inputs : [ ...KEYBOARD_INPUTS ] ,
185+ label : "Keyboard" ,
186+ mappingProfile : "Keyboard Profile" ,
187+ value : "keyboard" ,
188+ } ;
189+ const mouse = {
190+ controllerId : "generic-mouse" ,
191+ controllerName : "Mouse" ,
192+ deviceType : "Mouse" ,
193+ inputs : [ ...MOUSE_INPUTS ] ,
194+ label : "Mouse" ,
195+ mappingProfile : "Mouse Profile" ,
196+ value : "mouse" ,
182197 } ;
183198 const gamepads = this . availableGamepads ( ) . map ( ( gamepad ) => {
184199 const controllerName = gamepad . id || `Gamepad ${ gamepad . index } ` ;
@@ -192,15 +207,15 @@ export class AccountUserControlsPage {
192207 value : `gamepad-${ gamepad . index } ` ,
193208 } ;
194209 } ) ;
195- return [ keyboardMouse , ...gamepads ] ;
210+ return [ keyboard , mouse , ...gamepads ] ;
196211 }
197212
198213 deviceRefreshMessage ( ) {
199214 const gamepadCount = this . availableGamepads ( ) . length ;
200215 if ( gamepadCount > 0 ) {
201- return `PASS: Device list refreshed. Keyboard/ Mouse and ${ gamepadCount } game controller${ gamepadCount === 1 ? "" : "s" } available .` ;
216+ return `PASS: Keyboard and Mouse available. ${ gamepadCount } game controller${ gamepadCount === 1 ? "" : "s" } detected automatically .` ;
202217 }
203- return "PASS: Keyboard/ Mouse available. To enumerate a game controller, connect it, press a button, allow browser gamepad access, then refresh devices ." ;
218+ return "PASS: Keyboard and Mouse available. Gamepads auto-detect after browser exposes them ." ;
204219 }
205220
206221 selectedDevice ( ) {
@@ -301,6 +316,43 @@ export class AccountUserControlsPage {
301316 } ) ) ;
302317 }
303318
319+ renderDefaultFallbacks ( ) {
320+ if ( ! this . elements . defaultList ) {
321+ return ;
322+ }
323+ const systemDefault = this . inputService . getSystemDefaultProfileForDevice ( "Keyboard/Mouse" ) ;
324+ const rows = ( systemDefault . inputMappings || [ ] )
325+ . filter ( ( mapping ) => KEYBOARD_INPUTS . includes ( mapping . physicalInput ) || MOUSE_INPUTS . includes ( mapping . physicalInput ) )
326+ . map ( ( mapping ) => {
327+ const row = document . createElement ( "tr" ) ;
328+ row . dataset . accountUserControlsDefaultRow = mapping . physicalInput ;
329+ const family = MOUSE_INPUTS . includes ( mapping . physicalInput ) ? "Mouse" : "Keyboard" ;
330+ row . append (
331+ tableCell ( family ) ,
332+ tableCell ( mapping . physicalInput ) ,
333+ tableCell ( mapping . normalizedInput || mapping . positiveNormalizedInput || mapping . negativeNormalizedInput || "Unassigned" ) ,
334+ tableCell ( "Visible fallback" ) ,
335+ ) ;
336+ return row ;
337+ } ) ;
338+ this . elements . defaultList . replaceChildren ( ...rows ) ;
339+ }
340+
341+ startDevicePolling ( ) {
342+ if ( this . devicePollingTimer || typeof window . setInterval !== "function" ) {
343+ return ;
344+ }
345+ this . devicePollingTimer = window . setInterval ( ( ) => {
346+ this . renderDeviceSelect ( ) ;
347+ } , DEVICE_POLL_INTERVAL_MS ) ;
348+ window . addEventListener ( "pagehide" , ( ) => {
349+ if ( this . devicePollingTimer ) {
350+ window . clearInterval ( this . devicePollingTimer ) ;
351+ this . devicePollingTimer = null ;
352+ }
353+ } , { once : true } ) ;
354+ }
355+
304356 renderProfiles ( ) {
305357 if ( ! this . elements . list ) {
306358 return ;
0 commit comments