@@ -29,6 +29,8 @@ export class ToolStarterApp {
2929 this . window = windowRef ;
3030 this . captureMode = "" ;
3131 this . activeGamepadIndex = null ;
32+ this . comboCaptureInputs = [ ] ;
33+ this . rumbleFeedbackEnabled = false ;
3234 this . captureTimeoutMs = 8000 ;
3335 this . captureTimeoutTimer = null ;
3436 this . gamepadPollIntervalMs = 750 ;
@@ -47,8 +49,7 @@ export class ToolStarterApp {
4749 onToolCopyJson : ( ) => {
4850 void this . copyJson ( ) ;
4951 } ,
50- onToolExport : ( ) => this . exportMappings ( ) ,
51- onToolExportToolState : ( ) => this . exportToolState ( ) ,
52+ onToolExport : ( ) => this . exportToolState ( ) ,
5253 onWorkspaceCopyManifest : ( ) => this . statusLog . ok ( "Input Mapping V2 workspace copy action is ready." ) ,
5354 onWorkspaceExportManifest : ( ) => this . statusLog . ok ( "Input Mapping V2 workspace export action is ready." ) ,
5455 onWorkspaceImportManifest : ( ) => this . statusLog . ok ( "Input Mapping V2 workspace import action is ready." )
@@ -57,9 +58,13 @@ export class ToolStarterApp {
5758 onActionChanged : ( actionId ) => this . selectAction ( actionId ) ,
5859 onAddAction : ( label ) => this . addAction ( label ) ,
5960 onClearAction : ( ) => this . clearSelectedAction ( ) ,
60- onDeleteAction : ( ) => this . deleteSelectedAction ( )
61+ onDeleteAction : ( ) => this . deleteSelectedAction ( ) ,
62+ onRumbleFeedbackChanged : ( isEnabled ) => {
63+ void this . setRumbleFeedback ( isEnabled ) ;
64+ }
6165 } ) ;
6266 this . capture . mount ( {
67+ onCaptureCombo : ( ) => this . startComboCapture ( ) ,
6368 onCaptureGamepad : ( gamepadIndex ) => this . startGamepadCapture ( gamepadIndex ) ,
6469 onCaptureKeyboard : ( ) => this . startKeyboardCapture ( ) ,
6570 onCaptureMouse : ( ) => this . startMouseCapture ( ) ,
@@ -127,6 +132,17 @@ export class ToolStarterApp {
127132 this . statusLog . ok ( `Mouse capture armed for ${ this . state . selectedActionLabel ( ) } .` ) ;
128133 }
129134
135+ startComboCapture ( ) {
136+ if ( this . isCaptureActive ( "combo" ) ) {
137+ this . cancelCapture ( "Combo" ) ;
138+ return ;
139+ }
140+ this . comboCaptureInputs = [ ] ;
141+ this . beginCapture ( "combo" ) ;
142+ this . capture . showMessage ( `Press two keys or mouse buttons to bind a combo to ${ this . state . selectedActionLabel ( ) } .` ) ;
143+ this . statusLog . ok ( `Combo capture armed for ${ this . state . selectedActionLabel ( ) } .` ) ;
144+ }
145+
130146 startGamepadCapture ( gamepadIndex ) {
131147 const selectedIndex = Number ( gamepadIndex ) ;
132148 if ( ! Number . isInteger ( selectedIndex ) ) {
@@ -154,6 +170,12 @@ export class ToolStarterApp {
154170 }
155171
156172 handleKeyDown ( event ) {
173+ if ( this . captureMode === "combo" ) {
174+ event . preventDefault ( ) ;
175+ event . stopPropagation ( ) ;
176+ this . recordComboInput ( this . engineInputSources . captureKeyboard ( event ) ) ;
177+ return ;
178+ }
157179 if ( this . captureMode !== "keyboard" ) {
158180 return ;
159181 }
@@ -163,6 +185,12 @@ export class ToolStarterApp {
163185 }
164186
165187 handleMouseDown ( event ) {
188+ if ( this . captureMode === "combo" ) {
189+ event . preventDefault ( ) ;
190+ event . stopPropagation ( ) ;
191+ this . recordComboInput ( this . engineInputSources . captureMouse ( event ) ) ;
192+ return ;
193+ }
166194 if ( this . captureMode !== "mouse" ) {
167195 return ;
168196 }
@@ -190,6 +218,29 @@ export class ToolStarterApp {
190218 return true ;
191219 }
192220
221+ recordComboInput ( input ) {
222+ if ( this . comboCaptureInputs . some ( ( candidate ) => candidate . binding === input . binding ) ) {
223+ this . capture . showMessage ( "Combo capture needs two different inputs." ) ;
224+ this . statusLog . warn ( "Combo capture needs two different inputs." ) ;
225+ return ;
226+ }
227+
228+ this . comboCaptureInputs . push ( input ) ;
229+ if ( this . comboCaptureInputs . length < 2 ) {
230+ this . capture . showMessage ( `Combo capture recorded ${ input . label } . Press one more key or mouse button.` ) ;
231+ return ;
232+ }
233+
234+ const result = this . engineInputSources . captureCombo ( this . comboCaptureInputs ) ;
235+ if ( ! result . ok ) {
236+ this . statusLog . warn ( result . message ) ;
237+ this . clearCapture ( ) ;
238+ this . refreshActions ( ) ;
239+ return ;
240+ }
241+ this . addCapturedInput ( result . input ) ;
242+ }
243+
193244 handleGamepadConnectionChange ( ) {
194245 this . refreshGamepads ( ) ;
195246 }
@@ -234,6 +285,20 @@ export class ToolStarterApp {
234285 this . statusLog . ok ( status . message ) ;
235286 }
236287
288+ async setRumbleFeedback ( isEnabled ) {
289+ this . rumbleFeedbackEnabled = isEnabled ;
290+ if ( ! isEnabled ) {
291+ this . statusLog . ok ( "Gamepad rumble/haptic feedback disabled for this tool session." ) ;
292+ return ;
293+ }
294+
295+ const result = await this . engineInputSources . requestGamepadRumblePreview ( ) ;
296+ this . statusLog [ result . ok ? "ok" : "warn" ] ( result . message ) ;
297+ if ( result . ok ) {
298+ this . statusLog . ok ( "Gamepad rumble/haptic feedback is UI-local because Input Mapping V2 toolState has no options field." ) ;
299+ }
300+ }
301+
237302 addCapturedInput ( input ) {
238303 const result = this . state . addBindingToSelectedAction ( input ) ;
239304 this . clearCapture ( ) ;
@@ -267,6 +332,7 @@ export class ToolStarterApp {
267332 this . captureTimeoutTimer = null ;
268333 this . captureMode = "" ;
269334 this . activeGamepadIndex = null ;
335+ this . comboCaptureInputs = [ ] ;
270336 this . capture . clearActiveCapture ( ) ;
271337 }
272338
@@ -298,6 +364,9 @@ export class ToolStarterApp {
298364 if ( this . captureMode === "mouse" ) {
299365 return "Mouse" ;
300366 }
367+ if ( this . captureMode === "combo" ) {
368+ return "Combo" ;
369+ }
301370 if ( this . captureMode === "gamepad" ) {
302371 return "Gamepad" ;
303372 }
@@ -310,14 +379,9 @@ export class ToolStarterApp {
310379 this . refreshActions ( ) ;
311380 }
312381
313- exportMappings ( ) {
314- this . inspector . showObject ( this . state . payload ( ) ) ;
315- this . statusLog . ok ( "Mapping JSON preview written." ) ;
316- }
317-
318382 exportToolState ( ) {
319383 this . inspector . showObject ( this . state . toolState ( ) ) ;
320- this . statusLog . ok ( "Input Mapping V2 toolState preview written." ) ;
384+ this . statusLog . ok ( "Input Mapping V2 export preview written." ) ;
321385 }
322386
323387 async copyJson ( ) {
0 commit comments