@utsp/input 0.9.0 → 0.10.0-nightly.20251213135145.115c488

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,572 @@
1
+ import { Vector2, Vector3, IMobileVibrationProcessor, VibrationPattern } from '@utsp/types';
2
+ export { InputDeviceType, TouchInput, Vector2, VibrationPattern } from '@utsp/types';
3
+
4
+ /**
5
+ * Base interface for all input management systems
6
+ * This provides a common API for keyboard, mouse, gamepad, and touch inputs
7
+ */
8
+ interface IInputs {
9
+ /**
10
+ * Start listening to input events
11
+ */
12
+ start(): void;
13
+ /**
14
+ * Stop listening to input events and cleanup
15
+ */
16
+ stop(): void;
17
+ /**
18
+ * Reset all input states to default
19
+ */
20
+ reset(): void;
21
+ /**
22
+ * Check if the input system is currently active/listening
23
+ */
24
+ isListening(): boolean;
25
+ /**
26
+ * Cleanup and destroy the instance, removing all event listeners
27
+ */
28
+ destroy(): void;
29
+ /**
30
+ * Reset delta values (call at the end of each frame/update)
31
+ */
32
+ resetDelta(): void;
33
+ /**
34
+ * Set or update event callbacks
35
+ */
36
+ setCallbacks(callbacks: unknown): void;
37
+ /**
38
+ * Remove all event callbacks
39
+ */
40
+ clearCallbacks(): void;
41
+ /**
42
+ * Check if a key is currently pressed (Keyboard)
43
+ */
44
+ isKeyPressed?(key: string): boolean;
45
+ /**
46
+ * Check if left mouse button is pressed (Mouse)
47
+ */
48
+ isLeftMousePressed?(): boolean;
49
+ /**
50
+ * Get mouse position (Mouse)
51
+ */
52
+ getMousePosition?(): Vector2 | null;
53
+ /**
54
+ * Get mouse movement delta (Mouse)
55
+ */
56
+ getMouseDelta?(): Vector2 | null;
57
+ /**
58
+ * Get wheel delta (Mouse)
59
+ */
60
+ getWheelDelta?(): Vector2 | null;
61
+ /**
62
+ * Check if a button is pressed (Gamepad, TVRemote)
63
+ */
64
+ isButtonPressed?(button: string | number): boolean;
65
+ /**
66
+ * Get axis value (Gamepad, TVRemote)
67
+ */
68
+ getAxis?(axis: number): number | null;
69
+ /**
70
+ * Get motion/gyroscope data (TVRemote, Mobile)
71
+ */
72
+ getMotionData?(): MotionData | null;
73
+ }
74
+ /**
75
+ * Motion/Gyroscope data interface
76
+ */
77
+ interface MotionData extends Vector3 {
78
+ timestamp?: number;
79
+ }
80
+ /**
81
+ * Common button state interface
82
+ */
83
+ interface ButtonState {
84
+ pressed: boolean;
85
+ justPressed: boolean;
86
+ justReleased: boolean;
87
+ timestamp?: number;
88
+ }
89
+ /**
90
+ * Input event types enumeration
91
+ */
92
+ declare enum InputEventType {
93
+ KeyDown = "keydown",
94
+ KeyUp = "keyup",
95
+ MouseDown = "mousedown",
96
+ MouseUp = "mouseup",
97
+ MouseMove = "mousemove",
98
+ MouseWheel = "mousewheel",
99
+ TouchStart = "touchstart",
100
+ TouchEnd = "touchend",
101
+ TouchMove = "touchmove",
102
+ GamepadConnected = "gamepadconnected",
103
+ GamepadDisconnected = "gamepaddisconnected",
104
+ GamepadButton = "gamepadbutton",
105
+ GamepadAxis = "gamepadaxis"
106
+ }
107
+ /**
108
+ * Configuration options for input systems
109
+ */
110
+ interface InputConfig {
111
+ /**
112
+ * Target element to attach listeners to (default: window)
113
+ */
114
+ targetElement?: HTMLElement | Window;
115
+ /**
116
+ * Enable/disable specific input types
117
+ */
118
+ enabled?: boolean;
119
+ /**
120
+ * Prevent default browser behaviors
121
+ */
122
+ preventDefault?: boolean;
123
+ /**
124
+ * Stop event propagation
125
+ */
126
+ stopPropagation?: boolean;
127
+ /**
128
+ * Enable debug logging
129
+ */
130
+ debug?: boolean;
131
+ }
132
+ /**
133
+ * Abstract base class for input systems
134
+ * All input classes should extend this to maintain consistency
135
+ */
136
+ declare abstract class BaseInputs implements IInputs {
137
+ protected isActive: boolean;
138
+ protected targetElement: HTMLElement | Window;
139
+ protected config: InputConfig;
140
+ protected callbacks: Record<string, ((...args: any[]) => void)[]>;
141
+ constructor(targetElement?: HTMLElement | Window, config?: InputConfig);
142
+ abstract start(): void;
143
+ abstract stop(): void;
144
+ abstract reset(): void;
145
+ abstract resetDelta(): void;
146
+ isListening(): boolean;
147
+ destroy(): void;
148
+ setCallbacks(callbacks: unknown): void;
149
+ clearCallbacks(): void;
150
+ /**
151
+ * Get the target element
152
+ */
153
+ getTargetElement(): HTMLElement | Window;
154
+ /**
155
+ * Enable the input system
156
+ */
157
+ enable(): void;
158
+ /**
159
+ * Disable the input system
160
+ */
161
+ disable(): void;
162
+ /**
163
+ * Check if the input system is enabled
164
+ */
165
+ isEnabled(): boolean;
166
+ /**
167
+ * Emit an event to all registered callbacks
168
+ */
169
+ protected emit(eventType: string, ...args: unknown[]): void;
170
+ /**
171
+ * Log debug messages if debug mode is enabled
172
+ */
173
+ protected log(...args: unknown[]): void;
174
+ }
175
+
176
+ /**
177
+ * MobileInputs - Touch and mobile device input management
178
+ * Handles touch events with support for multi-touch, gestures, and orientation
179
+ */
180
+
181
+ interface TouchPoint {
182
+ id: number;
183
+ nativeId: number;
184
+ position: Vector2;
185
+ startPosition: Vector2;
186
+ active: boolean;
187
+ justTouched: boolean;
188
+ justReleased: boolean;
189
+ }
190
+ interface MobileCallbacks {
191
+ onTouchStart?: (touches: TouchPoint[], event: TouchEvent) => void;
192
+ onTouchEnd?: (touches: TouchPoint[], event: TouchEvent) => void;
193
+ onTouchMove?: (touches: TouchPoint[], event: TouchEvent) => void;
194
+ onTouchCancel?: (touches: TouchPoint[], event: TouchEvent) => void;
195
+ onOrientationChange?: (orientation: number) => void;
196
+ }
197
+ interface MobileInputsConfig extends InputConfig {
198
+ /** Prevent default browser behavior (scroll, zoom) - Default: true */
199
+ preventDefault?: boolean;
200
+ /** Use passive event listeners for better performance - Default: false */
201
+ passive?: boolean;
202
+ /** Maximum number of simultaneous touches - Default: 10 */
203
+ maxTouches?: number;
204
+ }
205
+ declare class MobileInputs extends BaseInputs {
206
+ private touches;
207
+ private nativeToInternal;
208
+ private mobileCallbacks;
209
+ private mobileConfig;
210
+ private maxTouches;
211
+ private lastLogTime;
212
+ private boundHandlers;
213
+ constructor(targetElement?: HTMLElement | Window, config?: MobileInputsConfig);
214
+ start(): void;
215
+ stop(): void;
216
+ reset(): void;
217
+ resetDelta(): void;
218
+ /**
219
+ * Poll - Reset transient states (justTouched/justReleased)
220
+ * Should be called once per frame AFTER collecting input
221
+ */
222
+ poll(): void;
223
+ /**
224
+ * Check if a touch is active (touchId 0-9)
225
+ *
226
+ * @param touchId - Touch identifier (0-9)
227
+ * @returns True if touch is active, false otherwise
228
+ *
229
+ * @example
230
+ * ```typescript
231
+ * if (mobile.isTouchActive(0)) {
232
+ * const pos = mobile.getTouchPosition(0);
233
+ * console.log('First touch at', pos);
234
+ * }
235
+ * ```
236
+ */
237
+ isTouchActive(touchId: number): boolean;
238
+ /**
239
+ * Get the position of a touch (touchId 0-9)
240
+ *
241
+ * @param touchId - Touch identifier (0-9)
242
+ * @returns Vector2 with current position, or null if touch is not active
243
+ *
244
+ * @example
245
+ * ```typescript
246
+ * const pos = mobile.getTouchPosition(0);
247
+ * if (pos) {
248
+ * player.moveTo(pos.x, pos.y);
249
+ * }
250
+ * ```
251
+ */
252
+ getTouchPosition(touchId: number): Vector2 | null;
253
+ /**
254
+ * Get the starting position of a touch (touchId 0-9)
255
+ *
256
+ * @param touchId - Touch identifier (0-9)
257
+ * @returns Vector2 with start position, or null if touch is not active
258
+ *
259
+ * @example
260
+ * ```typescript
261
+ * const start = mobile.getTouchStartPosition(0);
262
+ * const current = mobile.getTouchPosition(0);
263
+ * if (start && current) {
264
+ * const swipeDistance = current.subtract(start).length();
265
+ * if (swipeDistance > 100) {
266
+ * console.log('Swipe detected!');
267
+ * }
268
+ * }
269
+ * ```
270
+ */
271
+ getTouchStartPosition(touchId: number): Vector2 | null;
272
+ /**
273
+ * Get the delta since the beginning of the touch
274
+ *
275
+ * @param touchId - Touch identifier (0-9)
276
+ * @returns Vector2 with delta from start position, or null if touch is not active
277
+ *
278
+ * @example
279
+ * ```typescript
280
+ * const delta = mobile.getTouchDelta(0);
281
+ * if (delta && delta.x > 50) {
282
+ * console.log('Swiped right!');
283
+ * }
284
+ * ```
285
+ */
286
+ getTouchDelta(touchId: number): Vector2 | null;
287
+ /**
288
+ * Get the number of active touches
289
+ *
290
+ * @returns Number of simultaneous touches (0-10)
291
+ *
292
+ * @example
293
+ * ```typescript
294
+ * const count = mobile.getTouchCount();
295
+ * if (count === 2) {
296
+ * console.log('Pinch gesture possible');
297
+ * }
298
+ * ```
299
+ */
300
+ getTouchCount(): number;
301
+ /**
302
+ * Check if a touch just started this frame
303
+ *
304
+ * @param touchId - Touch identifier (0-9)
305
+ * @returns True if touch just started, false otherwise
306
+ *
307
+ * @example
308
+ * ```typescript
309
+ * if (mobile.isTouchJustStarted(0)) {
310
+ * playSound('tap');
311
+ * }
312
+ * ```
313
+ */
314
+ isTouchJustStarted(touchId: number): boolean;
315
+ /**
316
+ * Check if a touch just ended this frame
317
+ *
318
+ * @param touchId - Touch identifier (0-9)
319
+ * @returns True if touch just ended, false otherwise
320
+ *
321
+ * @example
322
+ * ```typescript
323
+ * if (mobile.isTouchJustReleased(0)) {
324
+ * releaseDragObject();
325
+ * }
326
+ * ```
327
+ */
328
+ isTouchJustReleased(touchId: number): boolean;
329
+ /**
330
+ * Check if any touch just started this frame
331
+ */
332
+ isAnyTouchJustStarted(): boolean;
333
+ /**
334
+ * Get all active touches
335
+ *
336
+ * @returns Array of all active TouchPoint objects
337
+ *
338
+ * @example
339
+ * ```typescript
340
+ * const touches = mobile.getAllTouches();
341
+ * touches.forEach(touch => {
342
+ * console.log(`Touch ${touch.id} at`, touch.position);
343
+ * });
344
+ * ```
345
+ */
346
+ getAllTouches(): TouchPoint[];
347
+ /**
348
+ * Set mobile-specific callbacks
349
+ *
350
+ * @param callbacks - Object containing callback functions
351
+ *
352
+ * @example
353
+ * ```typescript
354
+ * mobile.setMobileCallbacks({
355
+ * onTouchStart: (touches, event) => {
356
+ * console.log(`${touches.length} touches started`);
357
+ * },
358
+ * onTouchMove: (touches, event) => {
359
+ * touches.forEach(t => console.log(`Touch ${t.id}:`, t.position));
360
+ * },
361
+ * onOrientationChange: (orientation) => {
362
+ * console.log('Orientation:', orientation);
363
+ * },
364
+ * });
365
+ * ```
366
+ */
367
+ setMobileCallbacks(callbacks: MobileCallbacks): void;
368
+ clearMobileCallbacks(): void;
369
+ setCallbacks(callbacks: MobileCallbacks | unknown): void;
370
+ private isMobileCallbacks;
371
+ private handleTouchStart;
372
+ private handleTouchEnd;
373
+ private handleTouchMove;
374
+ private handleTouchCancel;
375
+ private handleOrientationChange;
376
+ }
377
+ /**
378
+ * React hook for mobile inputs
379
+ *
380
+ * ⚠️ IMPORTANT: This function is NOT a proper React hook!
381
+ * It creates a new MobileInputs instance on every render, causing memory leaks.
382
+ *
383
+ * To use correctly in React, wrap with useMemo and useEffect:
384
+ *
385
+ * @example
386
+ * ```typescript
387
+ * import { useMemo, useEffect } from 'react';
388
+ *
389
+ * const inputs = useMemo(() => new MobileInputs(canvasRef.current, config), [config]);
390
+ *
391
+ * useEffect(() => {
392
+ * if (callbacks) inputs.setCallbacks(callbacks);
393
+ * inputs.start();
394
+ * return () => inputs.stop();
395
+ * }, [inputs, callbacks]);
396
+ * ```
397
+ *
398
+ * @deprecated Use the pattern above instead of this function
399
+ */
400
+ declare function useMobileInputs(targetElement?: HTMLElement | Window, callbacks?: MobileCallbacks, config?: MobileInputsConfig): MobileInputs;
401
+
402
+ /**
403
+ * MobileVibration - Vibration feedback for mobile devices
404
+ *
405
+ * Provides vibration feedback using the Navigator Vibration API.
406
+ * Requires user interaction before first use (browser security requirement).
407
+ *
408
+ * @example
409
+ * ```typescript
410
+ * const vibration = new MobileVibration();
411
+ *
412
+ * // Simple vibration
413
+ * vibration.vibrate(100); // 100ms vibration
414
+ *
415
+ * // Pattern vibration
416
+ * vibration.vibrate([100, 50, 100]); // vibrate 100ms, pause 50ms, vibrate 100ms
417
+ *
418
+ * // Predefined patterns
419
+ * vibration.tap(); // Light tap feedback
420
+ * vibration.success(); // Success confirmation
421
+ * vibration.error(); // Error notification
422
+ * vibration.warning(); // Warning notification
423
+ * ```
424
+ */
425
+
426
+ /**
427
+ * Configuration options for MobileVibration
428
+ */
429
+ interface MobileVibrationConfig {
430
+ /** Enable vibration (default: true) */
431
+ enabled?: boolean;
432
+ /** Enable debug logging (default: false) */
433
+ debug?: boolean;
434
+ /** Global intensity multiplier 0.0-1.0 (default: 1.0) */
435
+ intensity?: number;
436
+ }
437
+ /**
438
+ * Predefined vibration patterns for common use cases
439
+ */
440
+ declare const VibrationPatterns: {
441
+ /** Light tap - single short pulse */
442
+ readonly tap: 10;
443
+ /** Medium tap - slightly longer pulse */
444
+ readonly mediumTap: 25;
445
+ /** Heavy tap - strong pulse */
446
+ readonly heavyTap: 50;
447
+ /** Success - two quick pulses */
448
+ readonly success: readonly [30, 50, 30];
449
+ /** Error - three rapid pulses */
450
+ readonly error: readonly [50, 30, 50, 30, 50];
451
+ /** Warning - one long pulse */
452
+ readonly warning: readonly [100];
453
+ /** Selection change - very light feedback */
454
+ readonly selection: 5;
455
+ /** Impact light */
456
+ readonly impactLight: 15;
457
+ /** Impact medium */
458
+ readonly impactMedium: 30;
459
+ /** Impact heavy */
460
+ readonly impactHeavy: 50;
461
+ /** Notification - attention-grabbing pattern */
462
+ readonly notification: readonly [100, 100, 100, 100, 100];
463
+ /** SOS pattern in Morse code */
464
+ readonly sos: readonly [100, 30, 100, 30, 100, 30, 200, 30, 200, 30, 200, 30, 100, 30, 100, 30, 100];
465
+ };
466
+ /**
467
+ * MobileVibration manager - implements IMobileVibrationProcessor
468
+ */
469
+ declare class MobileVibration implements IMobileVibrationProcessor {
470
+ private config;
471
+ private supported;
472
+ private userActivated;
473
+ constructor(config?: MobileVibrationConfig);
474
+ /**
475
+ * Check if vibration is supported on this device
476
+ */
477
+ isSupported(): boolean;
478
+ /**
479
+ * Check if vibration is enabled
480
+ */
481
+ isEnabled(): boolean;
482
+ /**
483
+ * Enable or disable vibration
484
+ */
485
+ setEnabled(enabled: boolean): void;
486
+ /**
487
+ * Set global intensity multiplier (0.0 to 1.0)
488
+ * This scales the duration of all vibrations
489
+ */
490
+ setIntensity(intensity: number): void;
491
+ /**
492
+ * Get current intensity setting
493
+ */
494
+ getIntensity(): number;
495
+ /**
496
+ * Vibrate with a pattern or duration
497
+ *
498
+ * @param pattern - Duration in ms, or array of [vibrate, pause, vibrate, pause, ...]
499
+ * @returns true if vibration was triggered, false otherwise
500
+ *
501
+ * @example
502
+ * ```typescript
503
+ * vibration.vibrate(100); // Vibrate for 100ms
504
+ * vibration.vibrate([100, 50, 100]); // Vibrate, pause, vibrate
505
+ * ```
506
+ */
507
+ vibrate(pattern: VibrationPattern): boolean;
508
+ /**
509
+ * Stop any ongoing vibration
510
+ */
511
+ cancel(): void;
512
+ /**
513
+ * Check if user has activated vibration (first successful vibration)
514
+ * This can be useful for UI feedback
515
+ */
516
+ hasUserActivation(): boolean;
517
+ /**
518
+ * Light tap feedback - for button presses, selections
519
+ */
520
+ tap(): boolean;
521
+ /**
522
+ * Medium tap feedback
523
+ */
524
+ mediumTap(): boolean;
525
+ /**
526
+ * Heavy tap feedback - for important actions
527
+ */
528
+ heavyTap(): boolean;
529
+ /**
530
+ * Success feedback - for completed actions
531
+ */
532
+ success(): boolean;
533
+ /**
534
+ * Error feedback - for failed actions
535
+ */
536
+ error(): boolean;
537
+ /**
538
+ * Warning feedback - for important notices
539
+ */
540
+ warning(): boolean;
541
+ /**
542
+ * Selection feedback - very light, for UI navigation
543
+ */
544
+ selection(): boolean;
545
+ /**
546
+ * Light impact feedback
547
+ */
548
+ impactLight(): boolean;
549
+ /**
550
+ * Medium impact feedback
551
+ */
552
+ impactMedium(): boolean;
553
+ /**
554
+ * Heavy impact feedback
555
+ */
556
+ impactHeavy(): boolean;
557
+ /**
558
+ * Notification feedback - attention-grabbing pattern
559
+ */
560
+ notification(): boolean;
561
+ /**
562
+ * Scale a vibration pattern by intensity
563
+ */
564
+ private scalePattern;
565
+ }
566
+ /**
567
+ * Get the default MobileVibration instance (singleton)
568
+ */
569
+ declare function getMobileVibration(): MobileVibration;
570
+
571
+ export { BaseInputs, InputEventType, MobileInputs, MobileVibration, VibrationPatterns, getMobileVibration, useMobileInputs };
572
+ export type { ButtonState, IInputs, InputConfig, MobileCallbacks, MobileInputsConfig, MobileVibrationConfig, MotionData, TouchPoint };
package/dist/touch.mjs ADDED
@@ -0,0 +1 @@
1
+ var M=Object.defineProperty;var w=(l,o,e)=>o in l?M(l,o,{enumerable:!0,configurable:!0,writable:!0,value:e}):l[o]=e;var h=(l,o)=>M(l,"name",{value:o,configurable:!0});var a=(l,o,e)=>(w(l,typeof o!="symbol"?o+"":o,e),e);import{Vector2 as W}from"@utsp/types";import{InputDeviceType as U,TouchInput as B}from"@utsp/types";var E=(c=>(c.KeyDown="keydown",c.KeyUp="keyup",c.MouseDown="mousedown",c.MouseUp="mouseup",c.MouseMove="mousemove",c.MouseWheel="mousewheel",c.TouchStart="touchstart",c.TouchEnd="touchend",c.TouchMove="touchmove",c.GamepadConnected="gamepadconnected",c.GamepadDisconnected="gamepaddisconnected",c.GamepadButton="gamepadbutton",c.GamepadAxis="gamepadaxis",c))(E||{}),v=class v{constructor(o=window,e={}){a(this,"isActive",!1);a(this,"targetElement");a(this,"config");a(this,"callbacks",{});this.targetElement=o,this.config={enabled:!0,preventDefault:!1,stopPropagation:!1,debug:!1,...e}}isListening(){return this.isActive}destroy(){this.stop(),this.clearCallbacks()}setCallbacks(o){typeof o=="object"&&o!==null&&Object.assign(this.callbacks,o)}clearCallbacks(){this.callbacks={}}getTargetElement(){return this.targetElement}enable(){this.config.enabled=!0,this.isActive||this.start()}disable(){this.config.enabled=!1,this.isActive&&this.stop()}isEnabled(){return this.config.enabled??!0}emit(o,...e){this.callbacks[o]&&this.callbacks[o].forEach(t=>{try{t(...e)}catch(n){this.config.debug&&console.error(`Error in ${o} callback:`,n)}})}log(...o){this.config.debug&&console.warn("[InputSystem]",...o)}};h(v,"BaseInputs");var b=v;import{Vector2 as y}from"@utsp/types";var m=class m extends b{constructor(e=window,t={}){super(e,t);a(this,"touches",new Map);a(this,"nativeToInternal",new Map);a(this,"mobileCallbacks",{});a(this,"mobileConfig");a(this,"maxTouches",10);a(this,"lastLogTime",0);a(this,"boundHandlers");this.mobileConfig={targetElement:e,preventDefault:t.preventDefault??!0,passive:t.passive??!1,maxTouches:t.maxTouches??10,enabled:t.enabled??!0,debug:t.debug??!1,stopPropagation:t.stopPropagation??!1},this.maxTouches=this.mobileConfig.maxTouches,this.mobileConfig.preventDefault&&this.mobileConfig.passive&&(console.warn("[MobileInputs] passive=true prevents preventDefault() from working! Setting passive=false."),this.mobileConfig.passive=!1),this.boundHandlers={touchStart:this.handleTouchStart.bind(this),touchEnd:this.handleTouchEnd.bind(this),touchMove:this.handleTouchMove.bind(this),touchCancel:this.handleTouchCancel.bind(this),orientationChange:this.handleOrientationChange.bind(this)}}start(){if(this.isActive)return;let e={passive:!this.mobileConfig.preventDefault&&this.mobileConfig.passive};this.targetElement.addEventListener("touchstart",this.boundHandlers.touchStart,e),this.targetElement.addEventListener("touchend",this.boundHandlers.touchEnd,e),this.targetElement.addEventListener("touchmove",this.boundHandlers.touchMove,e),this.targetElement.addEventListener("touchcancel",this.boundHandlers.touchCancel,e),window.addEventListener("orientationchange",this.boundHandlers.orientationChange),this.isActive=!0}stop(){this.isActive&&(this.targetElement.removeEventListener("touchstart",this.boundHandlers.touchStart),this.targetElement.removeEventListener("touchend",this.boundHandlers.touchEnd),this.targetElement.removeEventListener("touchmove",this.boundHandlers.touchMove),this.targetElement.removeEventListener("touchcancel",this.boundHandlers.touchCancel),window.removeEventListener("orientationchange",this.boundHandlers.orientationChange),this.isActive=!1)}reset(){this.touches.clear(),this.nativeToInternal.clear()}resetDelta(){}poll(){for(let e of this.touches.values())e.justTouched=!1,e.justReleased=!1}isTouchActive(e){return e<0||e>=this.maxTouches?!1:this.touches.has(e)}getTouchPosition(e){if(e<0||e>=this.maxTouches)return null;let t=this.touches.get(e);return t?t.position.clone():null}getTouchStartPosition(e){if(e<0||e>=this.maxTouches)return null;let t=this.touches.get(e);return t?t.startPosition.clone():null}getTouchDelta(e){if(e<0||e>=this.maxTouches)return null;let t=this.touches.get(e);return t?t.position.subtract(t.startPosition):null}getTouchCount(){return this.touches.size}isTouchJustStarted(e){if(e<0||e>=this.maxTouches)return!1;let t=this.touches.get(e);return t?t.justTouched:!1}isTouchJustReleased(e){if(e<0||e>=this.maxTouches)return!1;let t=this.touches.get(e);return t?t.justReleased:!1}isAnyTouchJustStarted(){for(let e of this.touches.values())if(e.justTouched)return!0;return!1}getAllTouches(){return Array.from(this.touches.values())}setMobileCallbacks(e){this.mobileCallbacks={...this.mobileCallbacks,...e}}clearMobileCallbacks(){this.mobileCallbacks={}}setCallbacks(e){this.isMobileCallbacks(e)&&this.setMobileCallbacks(e)}isMobileCallbacks(e){return typeof e=="object"&&e!==null&&("onTouchStart"in e||"onTouchEnd"in e||"onTouchMove"in e||"onTouchCancel"in e||"onOrientationChange"in e)}handleTouchStart(e){try{this.mobileConfig.preventDefault&&e.preventDefault();let t=Array.from(e.changedTouches),n=[];for(let s of t){if(this.touches.size>=this.maxTouches)break;let i=-1;for(let d=0;d<this.maxTouches;d++)if(!this.touches.has(d)){i=d;break}if(i===-1)continue;let r=new y(s.clientX,s.clientY),C={id:i,nativeId:s.identifier,position:r.clone(),startPosition:r.clone(),active:!0,justTouched:!0,justReleased:!1};this.touches.set(i,C),this.nativeToInternal.set(s.identifier,i),n.push(C),console.warn(`\u{1F446} Touch START - ID: ${i}`)}this.mobileCallbacks.onTouchStart?.(n,e)}catch(t){console.error("Error in touch start handler:",t)}}handleTouchEnd(e){try{this.mobileConfig.preventDefault&&e.preventDefault();let t=Array.from(e.changedTouches),n=[];for(let s of t){let i=this.nativeToInternal.get(s.identifier);if(i!==void 0){let r=this.touches.get(i);r&&(r.justReleased=!0,r.justTouched=!1,n.push({...r}))}}for(let s of t){let i=this.nativeToInternal.get(s.identifier);i!==void 0&&(this.touches.delete(i),this.nativeToInternal.delete(s.identifier),console.warn(`\u{1F447} Touch END - ID: ${i}`))}this.mobileCallbacks.onTouchEnd?.(n,e)}catch(t){console.error("Error in touch end handler:",t)}}handleTouchMove(e){try{let t=Array.from(e.changedTouches);this.mobileConfig.debug&&(!this.lastLogTime||Date.now()-this.lastLogTime>200)&&(console.warn(`\u{1F504} TouchMove event - ${t.length} touches changed`),this.lastLogTime=Date.now());for(let n of t){let s=this.nativeToInternal.get(n.identifier);if(s!==void 0){let i=this.touches.get(s);if(i){let r=i.position.clone();i.position=new y(n.clientX,n.clientY),this.mobileConfig.debug&&(!this.lastLogTime||Date.now()-this.lastLogTime>200)&&console.warn(`\u{1F504} Updated touch ${i.id} (native ${n.identifier}) - ${r.x.toFixed(1)},${r.y.toFixed(1)} \u2192 ${n.clientX.toFixed(1)},${n.clientY.toFixed(1)}`)}}else this.mobileConfig.debug&&console.warn(`\u26A0\uFE0F Touch move for unknown native ID ${n.identifier}`)}this.mobileCallbacks.onTouchMove?.(this.getAllTouches(),e)}catch(t){console.error("Error in touch move handler:",t)}}handleTouchCancel(e){try{this.mobileConfig.preventDefault&&e.preventDefault();let t=[],n=Array.from(e.changedTouches);for(let s of n){let i=this.nativeToInternal.get(s.identifier);if(i!==void 0){let r=this.touches.get(i);r&&(r.justReleased=!0,r.justTouched=!1,t.push({...r}))}}for(let s of n){let i=this.nativeToInternal.get(s.identifier);i!==void 0&&(this.touches.delete(i),this.nativeToInternal.delete(s.identifier),this.mobileConfig.debug&&console.warn(`\u274C Touch CANCEL - ID: ${i}, nativeId: ${s.identifier}`))}this.mobileCallbacks.onTouchCancel?.(t,e)}catch(t){console.error("Error in touch cancel handler:",t)}}handleOrientationChange(e){try{let t=(window.screen.orientation&&window.screen.orientation.angle)??window.orientation??0;this.mobileCallbacks.onOrientationChange?.(t)}catch(t){console.error("Error in orientation change handler:",t)}}};h(m,"MobileInputs");var p=m;function x(l,o,e){console.warn("[MobileInputs] useMobileInputs() is deprecated and causes memory leaks. Use useMemo + useEffect instead.");let t=new p(l,e);return o&&t.setCallbacks(o),t.start(),t}h(x,"useMobileInputs");var u={tap:10,mediumTap:25,heavyTap:50,success:[30,50,30],error:[50,30,50,30,50],warning:[100],selection:5,impactLight:15,impactMedium:30,impactHeavy:50,notification:[100,100,100,100,100],sos:[100,30,100,30,100,30,200,30,200,30,200,30,100,30,100,30,100]},T=class T{constructor(o={}){a(this,"config");a(this,"supported",!1);a(this,"userActivated",!1);this.config={enabled:o.enabled??!0,debug:o.debug??!1,intensity:Math.max(0,Math.min(1,o.intensity??1))},this.supported=typeof navigator<"u"&&"vibrate"in navigator,this.config.debug&&console.log(`[MobileVibration] Vibration API supported: ${this.supported}`)}isSupported(){return this.supported}isEnabled(){return this.config.enabled&&this.supported}setEnabled(o){this.config.enabled=o,this.config.debug&&console.log(`[MobileVibration] Enabled: ${o}`)}setIntensity(o){this.config.intensity=Math.max(0,Math.min(1,o)),this.config.debug&&console.log(`[MobileVibration] Intensity set to: ${this.config.intensity}`)}getIntensity(){return this.config.intensity}vibrate(o){if(!this.isEnabled())return!1;try{let e=this.scalePattern(o),t=typeof e=="number"?e:[...e],n=navigator.vibrate(t);return this.config.debug&&console.log(`[MobileVibration] Vibrate: ${JSON.stringify(e)} -> ${n}`),n&&(this.userActivated=!0),n}catch(e){return this.config.debug&&console.error("[MobileVibration] Vibration failed:",e),!1}}cancel(){this.supported&&(navigator.vibrate(0),this.config.debug&&console.log("[MobileVibration] Vibration cancelled"))}hasUserActivation(){return this.userActivated}tap(){return this.vibrate(u.tap)}mediumTap(){return this.vibrate(u.mediumTap)}heavyTap(){return this.vibrate(u.heavyTap)}success(){return this.vibrate(u.success)}error(){return this.vibrate(u.error)}warning(){return this.vibrate(u.warning)}selection(){return this.vibrate(u.selection)}impactLight(){return this.vibrate(u.impactLight)}impactMedium(){return this.vibrate(u.impactMedium)}impactHeavy(){return this.vibrate(u.impactHeavy)}notification(){return this.vibrate(u.notification)}scalePattern(o){return this.config.intensity===1?o:typeof o=="number"?Math.round(o*this.config.intensity):o.map((e,t)=>t%2===0?Math.round(e*this.config.intensity):e)}};h(T,"MobileVibration");var f=T,g=null;function k(){return g||(g=new f),g}h(k,"getMobileVibration");export{b as BaseInputs,U as InputDeviceType,E as InputEventType,p as MobileInputs,f as MobileVibration,B as TouchInput,W as Vector2,u as VibrationPatterns,k as getMobileVibration,x as useMobileInputs};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@utsp/input",
3
- "version": "0.9.0",
3
+ "version": "0.10.0-nightly.20251213135145.115c488",
4
4
  "description": "Comprehensive input management system for UTSP - keyboard, mouse, gamepad, and touch support with unified routing",
5
5
  "author": "THP Software",
6
6
  "license": "MIT",
@@ -13,6 +13,58 @@
13
13
  "types": "./dist/index.d.ts",
14
14
  "import": "./dist/index.mjs",
15
15
  "require": "./dist/index.cjs"
16
+ },
17
+ "./core": {
18
+ "types": "./dist/core.d.ts",
19
+ "import": "./dist/core.mjs",
20
+ "require": "./dist/core.cjs"
21
+ },
22
+ "./keyboard": {
23
+ "types": "./dist/keyboard.d.ts",
24
+ "import": "./dist/keyboard.mjs",
25
+ "require": "./dist/keyboard.cjs"
26
+ },
27
+ "./mouse": {
28
+ "types": "./dist/mouse.d.ts",
29
+ "import": "./dist/mouse.mjs",
30
+ "require": "./dist/mouse.cjs"
31
+ },
32
+ "./touch": {
33
+ "types": "./dist/touch.d.ts",
34
+ "import": "./dist/touch.mjs",
35
+ "require": "./dist/touch.cjs"
36
+ },
37
+ "./gamepad": {
38
+ "types": "./dist/gamepad.d.ts",
39
+ "import": "./dist/gamepad.mjs",
40
+ "require": "./dist/gamepad.cjs"
41
+ },
42
+ "./desktop": {
43
+ "types": "./dist/desktop.d.ts",
44
+ "import": "./dist/desktop.mjs",
45
+ "require": "./dist/desktop.cjs"
46
+ }
47
+ },
48
+ "typesVersions": {
49
+ "*": {
50
+ "core": [
51
+ "./dist/core.d.ts"
52
+ ],
53
+ "keyboard": [
54
+ "./dist/keyboard.d.ts"
55
+ ],
56
+ "mouse": [
57
+ "./dist/mouse.d.ts"
58
+ ],
59
+ "touch": [
60
+ "./dist/touch.d.ts"
61
+ ],
62
+ "gamepad": [
63
+ "./dist/gamepad.d.ts"
64
+ ],
65
+ "desktop": [
66
+ "./dist/desktop.d.ts"
67
+ ]
16
68
  }
17
69
  },
18
70
  "keywords": [
@@ -52,7 +104,7 @@
52
104
  "access": "public"
53
105
  },
54
106
  "dependencies": {
55
- "@utsp/types": "0.9.0"
107
+ "@utsp/types": "0.10.0-nightly.20251213135145.115c488"
56
108
  },
57
109
  "devDependencies": {
58
110
  "typescript": "^5.6.3"