@ankhorage/zora-game 0.4.0 → 0.5.0

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.
Files changed (48) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +1 -1
  3. package/dist/ZORA_GAME_COMPONENT_META.d.ts +52 -1
  4. package/dist/ZORA_GAME_COMPONENT_META.d.ts.map +1 -1
  5. package/dist/ZORA_GAME_PLUGIN.d.ts +52 -1
  6. package/dist/ZORA_GAME_PLUGIN.d.ts.map +1 -1
  7. package/dist/ZORA_PLUGIN_METADATA.d.ts +52 -1
  8. package/dist/ZORA_PLUGIN_METADATA.d.ts.map +1 -1
  9. package/dist/features/game-presentation/adapters/inbound/GameInputZone.d.ts +2 -2
  10. package/dist/features/game-presentation/adapters/inbound/GameInputZone.d.ts.map +1 -1
  11. package/dist/features/game-presentation/adapters/inbound/GameInputZone.js +19 -5
  12. package/dist/features/game-presentation/adapters/inbound/GameInputZone.js.map +1 -1
  13. package/dist/features/game-presentation/adapters/inbound/createGameKeyboardEvent.d.ts +16 -0
  14. package/dist/features/game-presentation/adapters/inbound/createGameKeyboardEvent.d.ts.map +1 -0
  15. package/dist/features/game-presentation/adapters/inbound/createGameKeyboardEvent.js +16 -0
  16. package/dist/features/game-presentation/adapters/inbound/createGameKeyboardEvent.js.map +1 -0
  17. package/dist/features/game-presentation/adapters/inbound/useGameInputZoneResponder.d.ts +6 -0
  18. package/dist/features/game-presentation/adapters/inbound/useGameInputZoneResponder.d.ts.map +1 -1
  19. package/dist/features/game-presentation/adapters/inbound/useGameInputZoneResponder.js +15 -0
  20. package/dist/features/game-presentation/adapters/inbound/useGameInputZoneResponder.js.map +1 -1
  21. package/dist/features/game-presentation/composition/createGameBindingContext.d.ts.map +1 -1
  22. package/dist/features/game-presentation/composition/createGameBindingContext.js +1 -0
  23. package/dist/features/game-presentation/composition/createGameBindingContext.js.map +1 -1
  24. package/dist/features/game-presentation/meta/gameInputZoneMeta.d.ts +52 -1
  25. package/dist/features/game-presentation/meta/gameInputZoneMeta.d.ts.map +1 -1
  26. package/dist/features/game-presentation/meta/gameInputZoneMeta.js +47 -1
  27. package/dist/features/game-presentation/meta/gameInputZoneMeta.js.map +1 -1
  28. package/dist/features/game-presentation/public.d.ts +1 -1
  29. package/dist/features/game-presentation/public.d.ts.map +1 -1
  30. package/dist/features/game-presentation/public.js.map +1 -1
  31. package/dist/index.d.ts +1 -1
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js.map +1 -1
  34. package/dist/types/gamePresentation.d.ts +7 -0
  35. package/dist/types/gamePresentation.d.ts.map +1 -1
  36. package/dist/types/gamePresentation.js.map +1 -1
  37. package/package.json +1 -1
  38. package/src/ZORA_PLUGIN_METADATA.test.ts +19 -10
  39. package/src/features/game-presentation/adapters/inbound/GameInputZone.tsx +36 -9
  40. package/src/features/game-presentation/adapters/inbound/createGameKeyboardEvent.test.ts +51 -0
  41. package/src/features/game-presentation/adapters/inbound/createGameKeyboardEvent.ts +33 -0
  42. package/src/features/game-presentation/adapters/inbound/useGameInputZoneResponder.ts +22 -0
  43. package/src/features/game-presentation/composition/createGameBindingContext.test.ts +20 -1
  44. package/src/features/game-presentation/composition/createGameBindingContext.ts +1 -0
  45. package/src/features/game-presentation/meta/gameInputZoneMeta.ts +47 -1
  46. package/src/features/game-presentation/public.ts +1 -0
  47. package/src/index.ts +1 -0
  48. package/src/types/gamePresentation.ts +8 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # @ankhorage/zora-game
2
2
 
3
+ ## 0.5.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 16cf5a7: Add declarative web keyboard bindings to GameInputZone while preserving its existing native/mobile
8
+ pointer and touch normalization path.
9
+
10
+ ## 0.4.1
11
+
12
+ ### Patch Changes
13
+
14
+ - bba6595: Expose the current local game entities as a repeatable binding array while preserving the canonical
15
+ map-shaped GameSession entity storage.
16
+
3
17
  ## 0.4.0
4
18
 
5
19
  ### Minor Changes
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
  # @ankhorage/zora-game
5
5
 
6
- ![license: MIT](././paradox/badges/license.svg) ![npm: v0.4.0](././paradox/badges/npm.svg) ![runtime: bun](././paradox/badges/runtime.svg) ![typescript: strict](././paradox/badges/typescript.svg) ![eslint: checked](././paradox/badges/eslint.svg) ![prettier: checked](././paradox/badges/prettier.svg) ![build: checked](././paradox/badges/build.svg) ![tests: checked](././paradox/badges/tests.svg) ![docs: paradox](././paradox/badges/docs.svg)
6
+ ![license: MIT](././paradox/badges/license.svg) ![npm: v0.5.0](././paradox/badges/npm.svg) ![runtime: bun](././paradox/badges/runtime.svg) ![typescript: strict](././paradox/badges/typescript.svg) ![eslint: checked](././paradox/badges/eslint.svg) ![prettier: checked](././paradox/badges/prettier.svg) ![build: checked](././paradox/badges/build.svg) ![tests: checked](././paradox/badges/tests.svg) ![docs: paradox](././paradox/badges/docs.svg)
7
7
 
8
8
  Generic game presentation primitives for React Native and React Native Web apps built on ZORA.
9
9
 
@@ -346,7 +346,7 @@ export declare const ZORA_GAME_COMPONENT_META: {
346
346
  readonly GameInputZone: {
347
347
  readonly name: "GameInputZone";
348
348
  readonly category: "component";
349
- readonly description: "Normalizes pointer and touch positions into field-relative percentages and dispatches a generic game event.";
349
+ readonly description: "Normalizes pointer/touch positions and optional keyboard bindings into generic game events.";
350
350
  readonly directManifestNode: true;
351
351
  readonly allowedChildren: readonly [];
352
352
  readonly blueprint: {
@@ -443,6 +443,57 @@ export declare const ZORA_GAME_COMPONENT_META: {
443
443
  readonly authority: "instance";
444
444
  };
445
445
  };
446
+ readonly keyboardBindings: {
447
+ readonly type: "array";
448
+ readonly category: "Input";
449
+ readonly label: "Keyboard bindings";
450
+ readonly default: readonly [];
451
+ readonly itemSchema: readonly [{
452
+ readonly key: "key";
453
+ readonly schema: {
454
+ readonly type: "string";
455
+ readonly category: "Input";
456
+ readonly label: "Key";
457
+ readonly authoring: {
458
+ readonly authority: "instance";
459
+ };
460
+ };
461
+ }, {
462
+ readonly key: "eventType";
463
+ readonly schema: {
464
+ readonly type: "string";
465
+ readonly category: "Input";
466
+ readonly label: "Event type";
467
+ readonly authoring: {
468
+ readonly authority: "instance";
469
+ };
470
+ };
471
+ }, {
472
+ readonly key: "entityId";
473
+ readonly schema: {
474
+ readonly type: "string";
475
+ readonly category: "Input";
476
+ readonly label: "Entity id";
477
+ readonly authoring: {
478
+ readonly authority: "instance";
479
+ };
480
+ };
481
+ }, {
482
+ readonly key: "preventDefault";
483
+ readonly schema: {
484
+ readonly type: "boolean";
485
+ readonly category: "Input";
486
+ readonly label: "Prevent browser default";
487
+ readonly default: false;
488
+ readonly authoring: {
489
+ readonly authority: "instance";
490
+ };
491
+ };
492
+ }];
493
+ readonly authoring: {
494
+ readonly authority: "instance";
495
+ };
496
+ };
446
497
  readonly accessibilityLabel: {
447
498
  readonly type: "string";
448
499
  readonly category: "Accessibility";
@@ -1 +1 @@
1
- {"version":3,"file":"ZORA_GAME_COMPONENT_META.d.ts","sourceRoot":"","sources":["../src/ZORA_GAME_COMPONENT_META.ts"],"names":[],"mappings":"AAMA,8EAA8E;AAC9E,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAM3B,CAAC"}
1
+ {"version":3,"file":"ZORA_GAME_COMPONENT_META.d.ts","sourceRoot":"","sources":["../src/ZORA_GAME_COMPONENT_META.ts"],"names":[],"mappings":"AAMA,8EAA8E;AAC9E,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAM3B,CAAC"}
@@ -361,7 +361,7 @@ export declare const ZORA_GAME_PLUGIN: {
361
361
  readonly GameInputZone: {
362
362
  readonly name: "GameInputZone";
363
363
  readonly category: "component";
364
- readonly description: "Normalizes pointer and touch positions into field-relative percentages and dispatches a generic game event.";
364
+ readonly description: "Normalizes pointer/touch positions and optional keyboard bindings into generic game events.";
365
365
  readonly directManifestNode: true;
366
366
  readonly allowedChildren: readonly [];
367
367
  readonly blueprint: {
@@ -458,6 +458,57 @@ export declare const ZORA_GAME_PLUGIN: {
458
458
  readonly authority: "instance";
459
459
  };
460
460
  };
461
+ readonly keyboardBindings: {
462
+ readonly type: "array";
463
+ readonly category: "Input";
464
+ readonly label: "Keyboard bindings";
465
+ readonly default: readonly [];
466
+ readonly itemSchema: readonly [{
467
+ readonly key: "key";
468
+ readonly schema: {
469
+ readonly type: "string";
470
+ readonly category: "Input";
471
+ readonly label: "Key";
472
+ readonly authoring: {
473
+ readonly authority: "instance";
474
+ };
475
+ };
476
+ }, {
477
+ readonly key: "eventType";
478
+ readonly schema: {
479
+ readonly type: "string";
480
+ readonly category: "Input";
481
+ readonly label: "Event type";
482
+ readonly authoring: {
483
+ readonly authority: "instance";
484
+ };
485
+ };
486
+ }, {
487
+ readonly key: "entityId";
488
+ readonly schema: {
489
+ readonly type: "string";
490
+ readonly category: "Input";
491
+ readonly label: "Entity id";
492
+ readonly authoring: {
493
+ readonly authority: "instance";
494
+ };
495
+ };
496
+ }, {
497
+ readonly key: "preventDefault";
498
+ readonly schema: {
499
+ readonly type: "boolean";
500
+ readonly category: "Input";
501
+ readonly label: "Prevent browser default";
502
+ readonly default: false;
503
+ readonly authoring: {
504
+ readonly authority: "instance";
505
+ };
506
+ };
507
+ }];
508
+ readonly authoring: {
509
+ readonly authority: "instance";
510
+ };
511
+ };
461
512
  readonly accessibilityLabel: {
462
513
  readonly type: "string";
463
514
  readonly category: "Accessibility";
@@ -1 +1 @@
1
- {"version":3,"file":"ZORA_GAME_PLUGIN.d.ts","sourceRoot":"","sources":["../src/ZORA_GAME_PLUGIN.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,oDAAoD,CAAC;AAC1E,OAAO,EAAE,UAAU,EAAE,MAAM,0DAA0D,CAAC;AACtF,OAAO,EAAE,SAAS,EAAE,MAAM,yDAAyD,CAAC;AACpF,OAAO,EAAE,aAAa,EAAE,MAAM,6DAA6D,CAAC;AAC5F,OAAO,EAAE,WAAW,EAAE,MAAM,2DAA2D,CAAC;AAGxF,+FAA+F;AAC/F,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CASnB,CAAC"}
1
+ {"version":3,"file":"ZORA_GAME_PLUGIN.d.ts","sourceRoot":"","sources":["../src/ZORA_GAME_PLUGIN.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,oDAAoD,CAAC;AAC1E,OAAO,EAAE,UAAU,EAAE,MAAM,0DAA0D,CAAC;AACtF,OAAO,EAAE,SAAS,EAAE,MAAM,yDAAyD,CAAC;AACpF,OAAO,EAAE,aAAa,EAAE,MAAM,6DAA6D,CAAC;AAC5F,OAAO,EAAE,WAAW,EAAE,MAAM,2DAA2D,CAAC;AAGxF,+FAA+F;AAC/F,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CASnB,CAAC"}
@@ -349,7 +349,7 @@ export declare const ZORA_PLUGIN_METADATA: {
349
349
  readonly GameInputZone: {
350
350
  readonly name: "GameInputZone";
351
351
  readonly category: "component";
352
- readonly description: "Normalizes pointer and touch positions into field-relative percentages and dispatches a generic game event.";
352
+ readonly description: "Normalizes pointer/touch positions and optional keyboard bindings into generic game events.";
353
353
  readonly directManifestNode: true;
354
354
  readonly allowedChildren: readonly [];
355
355
  readonly blueprint: {
@@ -446,6 +446,57 @@ export declare const ZORA_PLUGIN_METADATA: {
446
446
  readonly authority: "instance";
447
447
  };
448
448
  };
449
+ readonly keyboardBindings: {
450
+ readonly type: "array";
451
+ readonly category: "Input";
452
+ readonly label: "Keyboard bindings";
453
+ readonly default: readonly [];
454
+ readonly itemSchema: readonly [{
455
+ readonly key: "key";
456
+ readonly schema: {
457
+ readonly type: "string";
458
+ readonly category: "Input";
459
+ readonly label: "Key";
460
+ readonly authoring: {
461
+ readonly authority: "instance";
462
+ };
463
+ };
464
+ }, {
465
+ readonly key: "eventType";
466
+ readonly schema: {
467
+ readonly type: "string";
468
+ readonly category: "Input";
469
+ readonly label: "Event type";
470
+ readonly authoring: {
471
+ readonly authority: "instance";
472
+ };
473
+ };
474
+ }, {
475
+ readonly key: "entityId";
476
+ readonly schema: {
477
+ readonly type: "string";
478
+ readonly category: "Input";
479
+ readonly label: "Entity id";
480
+ readonly authoring: {
481
+ readonly authority: "instance";
482
+ };
483
+ };
484
+ }, {
485
+ readonly key: "preventDefault";
486
+ readonly schema: {
487
+ readonly type: "boolean";
488
+ readonly category: "Input";
489
+ readonly label: "Prevent browser default";
490
+ readonly default: false;
491
+ readonly authoring: {
492
+ readonly authority: "instance";
493
+ };
494
+ };
495
+ }];
496
+ readonly authoring: {
497
+ readonly authority: "instance";
498
+ };
499
+ };
449
500
  readonly accessibilityLabel: {
450
501
  readonly type: "string";
451
502
  readonly category: "Accessibility";
@@ -1 +1 @@
1
- {"version":3,"file":"ZORA_PLUGIN_METADATA.d.ts","sourceRoot":"","sources":["../src/ZORA_PLUGIN_METADATA.ts"],"names":[],"mappings":"AAIA,sFAAsF;AACtF,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2BM,CAAC"}
1
+ {"version":3,"file":"ZORA_PLUGIN_METADATA.d.ts","sourceRoot":"","sources":["../src/ZORA_PLUGIN_METADATA.ts"],"names":[],"mappings":"AAIA,sFAAsF;AACtF,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2BM,CAAC"}
@@ -1,4 +1,4 @@
1
1
  import type { GameInputZoneProps } from '../../../../types/gamePresentation';
2
- /*** Capture pointer/touch geometry and dispatch only normalized generic Game events. */
3
- export declare function GameInputZone({ x, y, width, height, zIndex, enabled, continuous, accessibilityLabel, testID, ...inputProps }: GameInputZoneProps): import("react").JSX.Element;
2
+ /*** Capture pointer/touch geometry and optional keyboard input as normalized generic Game events. */
3
+ export declare function GameInputZone({ x, y, width, height, zIndex, enabled, continuous, keyboardBindings, accessibilityLabel, testID, ...inputProps }: GameInputZoneProps): import("react").JSX.Element;
4
4
  //# sourceMappingURL=GameInputZone.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"GameInputZone.d.ts","sourceRoot":"","sources":["../../../../../src/features/game-presentation/adapters/inbound/GameInputZone.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AAI7E,wFAAwF;AACxF,wBAAgB,aAAa,CAAC,EAC5B,CAAK,EACL,CAAK,EACL,KAAW,EACX,MAAY,EACZ,MAAU,EACV,OAAc,EACd,UAAiB,EACjB,kBAAkB,EAClB,MAAM,EACN,GAAG,UAAU,EACd,EAAE,kBAAkB,+BAyBpB"}
1
+ {"version":3,"file":"GameInputZone.d.ts","sourceRoot":"","sources":["../../../../../src/features/game-presentation/adapters/inbound/GameInputZone.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AAI7E,qGAAqG;AACrG,wBAAgB,aAAa,CAAC,EAC5B,CAAK,EACL,CAAK,EACL,KAAW,EACX,MAAY,EACZ,MAAU,EACV,OAAc,EACd,UAAiB,EACjB,gBAAgB,EAChB,kBAAkB,EAClB,MAAM,EACN,GAAG,UAAU,EACd,EAAE,kBAAkB,+BAiCpB"}
@@ -1,9 +1,9 @@
1
- import { StyleSheet, View } from 'react-native';
1
+ import { Platform, StyleSheet, View } from 'react-native';
2
2
  import { toGamePercentage } from '../../utils/toGamePercentage';
3
3
  import { useGameInputZoneResponder } from './useGameInputZoneResponder';
4
- /*** Capture pointer/touch geometry and dispatch only normalized generic Game events. */
5
- export function GameInputZone({ x = 0, y = 0, width = 100, height = 100, zIndex = 0, enabled = true, continuous = true, accessibilityLabel, testID, ...inputProps }) {
6
- const { dispatchPointer, handleLayout, shouldSetResponder } = useGameInputZoneResponder({
4
+ /*** Capture pointer/touch geometry and optional keyboard input as normalized generic Game events. */
5
+ export function GameInputZone({ x = 0, y = 0, width = 100, height = 100, zIndex = 0, enabled = true, continuous = true, keyboardBindings, accessibilityLabel, testID, ...inputProps }) {
6
+ const { dispatchKeyboard, dispatchPointer, handleLayout, shouldSetResponder } = useGameInputZoneResponder({
7
7
  ...inputProps,
8
8
  x,
9
9
  y,
@@ -11,11 +11,25 @@ export function GameInputZone({ x = 0, y = 0, width = 100, height = 100, zIndex
11
11
  height,
12
12
  enabled,
13
13
  continuous,
14
+ ...(keyboardBindings === undefined ? {} : { keyboardBindings }),
14
15
  });
15
- return (<View {...(accessibilityLabel === undefined ? {} : { accessibilityLabel })} {...(testID === undefined ? {} : { testID })} {...(continuous
16
+ const keyboardProps = createGameInputZoneKeyboardProps({
17
+ dispatchKeyboard,
18
+ enabled,
19
+ keyboardBindings,
20
+ });
21
+ return (<View {...keyboardProps} {...(accessibilityLabel === undefined ? {} : { accessibilityLabel })} {...(testID === undefined ? {} : { testID })} {...(continuous
16
22
  ? { onResponderGrant: dispatchPointer, onResponderMove: dispatchPointer }
17
23
  : { onResponderRelease: dispatchPointer })} collapsable={false} onLayout={handleLayout} onStartShouldSetResponder={shouldSetResponder} pointerEvents={enabled ? 'auto' : 'none'} style={createInputZoneStyle({ x, y, width, height, zIndex })}/>);
18
24
  }
25
+ /*** Add a focusable web keyboard target only when the input zone owns key bindings. */
26
+ function createGameInputZoneKeyboardProps({ dispatchKeyboard, enabled, keyboardBindings, }) {
27
+ if (Platform.OS !== 'web' || !enabled || keyboardBindings === undefined)
28
+ return {};
29
+ if (keyboardBindings.length === 0)
30
+ return {};
31
+ return { onKeyDown: dispatchKeyboard, tabIndex: 0 };
32
+ }
19
33
  /*** Convert field-relative input-zone props into one absolute React Native style. */
20
34
  function createInputZoneStyle({ x, y, width, height, zIndex }) {
21
35
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"GameInputZone.js","sourceRoot":"","sources":["../../../../../src/features/game-presentation/adapters/inbound/GameInputZone.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,IAAI,EAAkB,MAAM,cAAc,CAAC;AAGhE,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AAExE,wFAAwF;AACxF,MAAM,UAAU,aAAa,CAAC,EAC5B,CAAC,GAAG,CAAC,EACL,CAAC,GAAG,CAAC,EACL,KAAK,GAAG,GAAG,EACX,MAAM,GAAG,GAAG,EACZ,MAAM,GAAG,CAAC,EACV,OAAO,GAAG,IAAI,EACd,UAAU,GAAG,IAAI,EACjB,kBAAkB,EAClB,MAAM,EACN,GAAG,UAAU,EACM;IACnB,MAAM,EAAE,eAAe,EAAE,YAAY,EAAE,kBAAkB,EAAE,GAAG,yBAAyB,CAAC;QACtF,GAAG,UAAU;QACb,CAAC;QACD,CAAC;QACD,KAAK;QACL,MAAM;QACN,OAAO;QACP,UAAU;KACX,CAAC,CAAC;IAEH,OAAO,CACL,CAAC,IAAI,CACH,IAAI,CAAC,kBAAkB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,kBAAkB,EAAE,CAAC,CAAC,CACrE,IAAI,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAC7C,IAAI,CAAC,UAAU;QACb,CAAC,CAAC,EAAE,gBAAgB,EAAE,eAAe,EAAE,eAAe,EAAE,eAAe,EAAE;QACzE,CAAC,CAAC,EAAE,kBAAkB,EAAE,eAAe,EAAE,CAAC,CAAC,CAC7C,WAAW,CAAC,CAAC,KAAK,CAAC,CACnB,QAAQ,CAAC,CAAC,YAAY,CAAC,CACvB,yBAAyB,CAAC,CAAC,kBAAkB,CAAC,CAC9C,aAAa,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CACzC,KAAK,CAAC,CAAC,oBAAoB,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,EAC7D,CACH,CAAC;AACJ,CAAC;AAUD,qFAAqF;AACrF,SAAS,oBAAoB,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAuB;IAChF,OAAO;QACL,IAAI,EAAE,gBAAgB,CAAC,CAAC,CAAC;QACzB,GAAG,EAAE,gBAAgB,CAAC,CAAC,CAAC;QACxB,KAAK,EAAE,gBAAgB,CAAC,KAAK,CAAC;QAC9B,MAAM,EAAE,gBAAgB,CAAC,MAAM,CAAC;QAChC,MAAM;QACN,GAAG,MAAM,CAAC,IAAI;KACf,CAAC;AACJ,CAAC;AAED,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC;IAC/B,IAAI,EAAE;QACJ,QAAQ,EAAE,UAAU;KACrB;CACF,CAAC,CAAC","sourcesContent":["import { StyleSheet, View, type ViewStyle } from 'react-native';\n\nimport type { GameInputZoneProps } from '../../../../types/gamePresentation';\nimport { toGamePercentage } from '../../utils/toGamePercentage';\nimport { useGameInputZoneResponder } from './useGameInputZoneResponder';\n\n/*** Capture pointer/touch geometry and dispatch only normalized generic Game events. */\nexport function GameInputZone({\n x = 0,\n y = 0,\n width = 100,\n height = 100,\n zIndex = 0,\n enabled = true,\n continuous = true,\n accessibilityLabel,\n testID,\n ...inputProps\n}: GameInputZoneProps) {\n const { dispatchPointer, handleLayout, shouldSetResponder } = useGameInputZoneResponder({\n ...inputProps,\n x,\n y,\n width,\n height,\n enabled,\n continuous,\n });\n\n return (\n <View\n {...(accessibilityLabel === undefined ? {} : { accessibilityLabel })}\n {...(testID === undefined ? {} : { testID })}\n {...(continuous\n ? { onResponderGrant: dispatchPointer, onResponderMove: dispatchPointer }\n : { onResponderRelease: dispatchPointer })}\n collapsable={false}\n onLayout={handleLayout}\n onStartShouldSetResponder={shouldSetResponder}\n pointerEvents={enabled ? 'auto' : 'none'}\n style={createInputZoneStyle({ x, y, width, height, zIndex })}\n />\n );\n}\n\ninterface InputZoneStyleInput {\n readonly x: number;\n readonly y: number;\n readonly width: number;\n readonly height: number;\n readonly zIndex: number;\n}\n\n/*** Convert field-relative input-zone props into one absolute React Native style. */\nfunction createInputZoneStyle({ x, y, width, height, zIndex }: InputZoneStyleInput): ViewStyle {\n return {\n left: toGamePercentage(x),\n top: toGamePercentage(y),\n width: toGamePercentage(width),\n height: toGamePercentage(height),\n zIndex,\n ...styles.root,\n };\n}\n\nconst styles = StyleSheet.create({\n root: {\n position: 'absolute',\n },\n});\n"]}
1
+ {"version":3,"file":"GameInputZone.js","sourceRoot":"","sources":["../../../../../src/features/game-presentation/adapters/inbound/GameInputZone.tsx"],"names":[],"mappings":"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","sourcesContent":["import { Platform, StyleSheet, View, type ViewStyle } from 'react-native';\n\nimport type { GameInputZoneProps } from '../../../../types/gamePresentation';\nimport { toGamePercentage } from '../../utils/toGamePercentage';\nimport { useGameInputZoneResponder } from './useGameInputZoneResponder';\n\n/*** Capture pointer/touch geometry and optional keyboard input as normalized generic Game events. */\nexport function GameInputZone({\n x = 0,\n y = 0,\n width = 100,\n height = 100,\n zIndex = 0,\n enabled = true,\n continuous = true,\n keyboardBindings,\n accessibilityLabel,\n testID,\n ...inputProps\n}: GameInputZoneProps) {\n const { dispatchKeyboard, dispatchPointer, handleLayout, shouldSetResponder } =\n useGameInputZoneResponder({\n ...inputProps,\n x,\n y,\n width,\n height,\n enabled,\n continuous,\n ...(keyboardBindings === undefined ? {} : { keyboardBindings }),\n });\n const keyboardProps = createGameInputZoneKeyboardProps({\n dispatchKeyboard,\n enabled,\n keyboardBindings,\n });\n\n return (\n <View\n {...keyboardProps}\n {...(accessibilityLabel === undefined ? {} : { accessibilityLabel })}\n {...(testID === undefined ? {} : { testID })}\n {...(continuous\n ? { onResponderGrant: dispatchPointer, onResponderMove: dispatchPointer }\n : { onResponderRelease: dispatchPointer })}\n collapsable={false}\n onLayout={handleLayout}\n onStartShouldSetResponder={shouldSetResponder}\n pointerEvents={enabled ? 'auto' : 'none'}\n style={createInputZoneStyle({ x, y, width, height, zIndex })}\n />\n );\n}\n\ninterface GameInputZoneKeyboardPropsInput {\n readonly dispatchKeyboard: (event: { readonly key: string; preventDefault(): void }) => void;\n readonly enabled: boolean;\n readonly keyboardBindings: GameInputZoneProps['keyboardBindings'];\n}\n\n/*** Add a focusable web keyboard target only when the input zone owns key bindings. */\nfunction createGameInputZoneKeyboardProps({\n dispatchKeyboard,\n enabled,\n keyboardBindings,\n}: GameInputZoneKeyboardPropsInput) {\n if (Platform.OS !== 'web' || !enabled || keyboardBindings === undefined) return {};\n if (keyboardBindings.length === 0) return {};\n\n return { onKeyDown: dispatchKeyboard, tabIndex: 0 as const };\n}\n\ninterface InputZoneStyleInput {\n readonly x: number;\n readonly y: number;\n readonly width: number;\n readonly height: number;\n readonly zIndex: number;\n}\n\n/*** Convert field-relative input-zone props into one absolute React Native style. */\nfunction createInputZoneStyle({ x, y, width, height, zIndex }: InputZoneStyleInput): ViewStyle {\n return {\n left: toGamePercentage(x),\n top: toGamePercentage(y),\n width: toGamePercentage(width),\n height: toGamePercentage(height),\n zIndex,\n ...styles.root,\n };\n}\n\nconst styles = StyleSheet.create({\n root: {\n position: 'absolute',\n },\n});\n"]}
@@ -0,0 +1,16 @@
1
+ import type { GameEvent } from '@ankhorage/game';
2
+ import type { GameKeyboardBinding } from '../../../../types/gamePresentation';
3
+ interface CreateGameKeyboardEventInput {
4
+ readonly bindings: readonly GameKeyboardBinding[];
5
+ readonly fallbackEventType: string;
6
+ readonly fallbackEntityId?: string;
7
+ readonly key: string;
8
+ }
9
+ interface GameKeyboardEventResult {
10
+ readonly event: GameEvent;
11
+ readonly preventDefault: boolean;
12
+ }
13
+ /*** Map one configured keyboard key into a generic Game event. */
14
+ export declare function createGameKeyboardEvent(input: CreateGameKeyboardEventInput): GameKeyboardEventResult | undefined;
15
+ export {};
16
+ //# sourceMappingURL=createGameKeyboardEvent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createGameKeyboardEvent.d.ts","sourceRoot":"","sources":["../../../../../src/features/game-presentation/adapters/inbound/createGameKeyboardEvent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAC;AAE9E,UAAU,4BAA4B;IACpC,QAAQ,CAAC,QAAQ,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAClD,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB;AAED,UAAU,uBAAuB;IAC/B,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAC1B,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;CAClC;AAED,kEAAkE;AAClE,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,4BAA4B,GAClC,uBAAuB,GAAG,SAAS,CAarC"}
@@ -0,0 +1,16 @@
1
+ /*** Map one configured keyboard key into a generic Game event. */
2
+ export function createGameKeyboardEvent(input) {
3
+ const binding = input.bindings.find((candidate) => candidate.key === input.key);
4
+ if (binding === undefined)
5
+ return undefined;
6
+ const entityId = binding.entityId ?? input.fallbackEntityId;
7
+ return {
8
+ event: {
9
+ type: binding.eventType ?? input.fallbackEventType,
10
+ ...(entityId === undefined ? {} : { entityId }),
11
+ payload: { key: input.key },
12
+ },
13
+ preventDefault: binding.preventDefault ?? false,
14
+ };
15
+ }
16
+ //# sourceMappingURL=createGameKeyboardEvent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createGameKeyboardEvent.js","sourceRoot":"","sources":["../../../../../src/features/game-presentation/adapters/inbound/createGameKeyboardEvent.ts"],"names":[],"mappings":"AAgBA,kEAAkE;AAClE,MAAM,UAAU,uBAAuB,CACrC,KAAmC;IAEnC,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,KAAK,KAAK,CAAC,GAAG,CAAC,CAAC;IAChF,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAE5C,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,KAAK,CAAC,gBAAgB,CAAC;IAC5D,OAAO;QACL,KAAK,EAAE;YACL,IAAI,EAAE,OAAO,CAAC,SAAS,IAAI,KAAK,CAAC,iBAAiB;YAClD,GAAG,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC;YAC/C,OAAO,EAAE,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE;SAC5B;QACD,cAAc,EAAE,OAAO,CAAC,cAAc,IAAI,KAAK;KAChD,CAAC;AACJ,CAAC","sourcesContent":["import type { GameEvent } from '@ankhorage/game';\n\nimport type { GameKeyboardBinding } from '../../../../types/gamePresentation';\n\ninterface CreateGameKeyboardEventInput {\n readonly bindings: readonly GameKeyboardBinding[];\n readonly fallbackEventType: string;\n readonly fallbackEntityId?: string;\n readonly key: string;\n}\n\ninterface GameKeyboardEventResult {\n readonly event: GameEvent;\n readonly preventDefault: boolean;\n}\n\n/*** Map one configured keyboard key into a generic Game event. */\nexport function createGameKeyboardEvent(\n input: CreateGameKeyboardEventInput,\n): GameKeyboardEventResult | undefined {\n const binding = input.bindings.find((candidate) => candidate.key === input.key);\n if (binding === undefined) return undefined;\n\n const entityId = binding.entityId ?? input.fallbackEntityId;\n return {\n event: {\n type: binding.eventType ?? input.fallbackEventType,\n ...(entityId === undefined ? {} : { entityId }),\n payload: { key: input.key },\n },\n preventDefault: binding.preventDefault ?? false,\n };\n}\n"]}
@@ -1,9 +1,15 @@
1
1
  import type { GestureResponderEvent, LayoutChangeEvent } from 'react-native';
2
2
  import type { GameInputZoneProps } from '../../../../types/gamePresentation';
3
+ interface GameKeyboardInputEvent {
4
+ readonly key: string;
5
+ preventDefault(): void;
6
+ }
3
7
  /*** Own React Native responder measurement and event dispatch for one Game input zone. */
4
8
  export declare function useGameInputZoneResponder(props: GameInputZoneProps): {
9
+ dispatchKeyboard: (event: GameKeyboardInputEvent) => void;
5
10
  dispatchPointer: (event: GestureResponderEvent) => void;
6
11
  handleLayout: (event: LayoutChangeEvent) => void;
7
12
  shouldSetResponder: () => boolean;
8
13
  };
14
+ export {};
9
15
  //# sourceMappingURL=useGameInputZoneResponder.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useGameInputZoneResponder.d.ts","sourceRoot":"","sources":["../../../../../src/features/game-presentation/adapters/inbound/useGameInputZoneResponder.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAE7E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AAS7E,0FAA0F;AAC1F,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,kBAAkB;6BAWvD,qBAAqB;0BANgB,iBAAiB;;EA8BjE"}
1
+ {"version":3,"file":"useGameInputZoneResponder.d.ts","sourceRoot":"","sources":["../../../../../src/features/game-presentation/adapters/inbound/useGameInputZoneResponder.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAE7E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AAK7E,UAAU,sBAAsB;IAC9B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,cAAc,IAAI,IAAI,CAAC;CACxB;AAOD,0FAA0F;AAC1F,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,kBAAkB;8BA+BvD,sBAAsB;6BApBtB,qBAAqB;0BANgB,iBAAiB;;EA8CjE"}
@@ -1,5 +1,6 @@
1
1
  import React from 'react';
2
2
  import { GameRuntimeContext } from '../../composition/GameRuntimeContext';
3
+ import { createGameKeyboardEvent } from './createGameKeyboardEvent';
3
4
  import { createGamePointerEvent } from './createGamePointerEvent';
4
5
  /*** Own React Native responder measurement and event dispatch for one Game input zone. */
5
6
  export function useGameInputZoneResponder(props) {
@@ -28,7 +29,21 @@ export function useGameInputZoneResponder(props) {
28
29
  if (gameEvent !== undefined)
29
30
  runtime.dispatch(gameEvent);
30
31
  }, [props.entityId, props.eventType, props.height, props.width, props.x, props.y, runtime]);
32
+ const dispatchKeyboard = React.useCallback((event) => {
33
+ const result = createGameKeyboardEvent({
34
+ bindings: props.keyboardBindings ?? [],
35
+ fallbackEventType: props.eventType,
36
+ ...(props.entityId === undefined ? {} : { fallbackEntityId: props.entityId }),
37
+ key: event.key,
38
+ });
39
+ if (result === undefined)
40
+ return;
41
+ if (result.preventDefault)
42
+ event.preventDefault();
43
+ runtime.dispatch(result.event);
44
+ }, [props.entityId, props.eventType, props.keyboardBindings, runtime]);
31
45
  return {
46
+ dispatchKeyboard,
32
47
  dispatchPointer,
33
48
  handleLayout,
34
49
  shouldSetResponder: () => props.enabled ?? true,
@@ -1 +1 @@
1
- {"version":3,"file":"useGameInputZoneResponder.js","sourceRoot":"","sources":["../../../../../src/features/game-presentation/adapters/inbound/useGameInputZoneResponder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,OAAO,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAC1E,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAOlE,0FAA0F;AAC1F,MAAM,UAAU,yBAAyB,CAAC,KAAyB;IACjE,MAAM,OAAO,GAAG,KAAK,CAAC,UAAU,CAAC,kBAAkB,CAAC,CAAC;IACrD,IAAI,OAAO,KAAK,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;IAErF,MAAM,eAAe,GAAG,KAAK,CAAC,MAAM,CAAe,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;IAC5E,MAAM,YAAY,GAAG,KAAK,CAAC,WAAW,CAAC,CAAC,KAAwB,EAAE,EAAE;QAClE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC;QACnD,eAAe,CAAC,OAAO,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAC9C,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,eAAe,GAAG,KAAK,CAAC,WAAW,CACvC,CAAC,KAA4B,EAAE,EAAE;QAC/B,MAAM,QAAQ,GAAG,eAAe,CAAC,OAAO,CAAC;QACzC,MAAM,SAAS,GAAG,sBAAsB,CAAC;YACvC,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,GAAG,CAAC,KAAK,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC;YACrE,MAAM,EAAE,KAAK,CAAC,WAAW,CAAC,SAAS;YACnC,MAAM,EAAE,KAAK,CAAC,WAAW,CAAC,SAAS;YACnC,aAAa,EAAE,QAAQ,CAAC,KAAK;YAC7B,cAAc,EAAE,QAAQ,CAAC,MAAM;YAC/B,KAAK,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC;YACnB,KAAK,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC;YACnB,SAAS,EAAE,KAAK,CAAC,KAAK,IAAI,GAAG;YAC7B,UAAU,EAAE,KAAK,CAAC,MAAM,IAAI,GAAG;SAChC,CAAC,CAAC;QACH,IAAI,SAAS,KAAK,SAAS;YAAE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;IAC3D,CAAC,EACD,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,CACxF,CAAC;IAEF,OAAO;QACL,eAAe;QACf,YAAY;QACZ,kBAAkB,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,OAAO,IAAI,IAAI;KAChD,CAAC;AACJ,CAAC","sourcesContent":["import React from 'react';\nimport type { GestureResponderEvent, LayoutChangeEvent } from 'react-native';\n\nimport type { GameInputZoneProps } from '../../../../types/gamePresentation';\nimport { GameRuntimeContext } from '../../composition/GameRuntimeContext';\nimport { createGamePointerEvent } from './createGamePointerEvent';\n\ninterface MeasuredSize {\n readonly width: number;\n readonly height: number;\n}\n\n/*** Own React Native responder measurement and event dispatch for one Game input zone. */\nexport function useGameInputZoneResponder(props: GameInputZoneProps) {\n const runtime = React.useContext(GameRuntimeContext);\n if (runtime === null) throw new Error('GameInputZone must be rendered inside Game.');\n\n const measuredSizeRef = React.useRef<MeasuredSize>({ width: 0, height: 0 });\n const handleLayout = React.useCallback((event: LayoutChangeEvent) => {\n const { width, height } = event.nativeEvent.layout;\n measuredSizeRef.current = { width, height };\n }, []);\n\n const dispatchPointer = React.useCallback(\n (event: GestureResponderEvent) => {\n const measured = measuredSizeRef.current;\n const gameEvent = createGamePointerEvent({\n eventType: props.eventType,\n ...(props.entityId === undefined ? {} : { entityId: props.entityId }),\n localX: event.nativeEvent.locationX,\n localY: event.nativeEvent.locationY,\n measuredWidth: measured.width,\n measuredHeight: measured.height,\n zoneX: props.x ?? 0,\n zoneY: props.y ?? 0,\n zoneWidth: props.width ?? 100,\n zoneHeight: props.height ?? 100,\n });\n if (gameEvent !== undefined) runtime.dispatch(gameEvent);\n },\n [props.entityId, props.eventType, props.height, props.width, props.x, props.y, runtime],\n );\n\n return {\n dispatchPointer,\n handleLayout,\n shouldSetResponder: () => props.enabled ?? true,\n };\n}\n"]}
1
+ {"version":3,"file":"useGameInputZoneResponder.js","sourceRoot":"","sources":["../../../../../src/features/game-presentation/adapters/inbound/useGameInputZoneResponder.ts"],"names":[],"mappings":"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","sourcesContent":["import React from 'react';\nimport type { GestureResponderEvent, LayoutChangeEvent } from 'react-native';\n\nimport type { GameInputZoneProps } from '../../../../types/gamePresentation';\nimport { GameRuntimeContext } from '../../composition/GameRuntimeContext';\nimport { createGameKeyboardEvent } from './createGameKeyboardEvent';\nimport { createGamePointerEvent } from './createGamePointerEvent';\n\ninterface GameKeyboardInputEvent {\n readonly key: string;\n preventDefault(): void;\n}\n\ninterface MeasuredSize {\n readonly width: number;\n readonly height: number;\n}\n\n/*** Own React Native responder measurement and event dispatch for one Game input zone. */\nexport function useGameInputZoneResponder(props: GameInputZoneProps) {\n const runtime = React.useContext(GameRuntimeContext);\n if (runtime === null) throw new Error('GameInputZone must be rendered inside Game.');\n\n const measuredSizeRef = React.useRef<MeasuredSize>({ width: 0, height: 0 });\n const handleLayout = React.useCallback((event: LayoutChangeEvent) => {\n const { width, height } = event.nativeEvent.layout;\n measuredSizeRef.current = { width, height };\n }, []);\n\n const dispatchPointer = React.useCallback(\n (event: GestureResponderEvent) => {\n const measured = measuredSizeRef.current;\n const gameEvent = createGamePointerEvent({\n eventType: props.eventType,\n ...(props.entityId === undefined ? {} : { entityId: props.entityId }),\n localX: event.nativeEvent.locationX,\n localY: event.nativeEvent.locationY,\n measuredWidth: measured.width,\n measuredHeight: measured.height,\n zoneX: props.x ?? 0,\n zoneY: props.y ?? 0,\n zoneWidth: props.width ?? 100,\n zoneHeight: props.height ?? 100,\n });\n if (gameEvent !== undefined) runtime.dispatch(gameEvent);\n },\n [props.entityId, props.eventType, props.height, props.width, props.x, props.y, runtime],\n );\n\n const dispatchKeyboard = React.useCallback(\n (event: GameKeyboardInputEvent) => {\n const result = createGameKeyboardEvent({\n bindings: props.keyboardBindings ?? [],\n fallbackEventType: props.eventType,\n ...(props.entityId === undefined ? {} : { fallbackEntityId: props.entityId }),\n key: event.key,\n });\n if (result === undefined) return;\n if (result.preventDefault) event.preventDefault();\n runtime.dispatch(result.event);\n },\n [props.entityId, props.eventType, props.keyboardBindings, runtime],\n );\n\n return {\n dispatchKeyboard,\n dispatchPointer,\n handleLayout,\n shouldSetResponder: () => props.enabled ?? true,\n };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"createGameBindingContext.d.ts","sourceRoot":"","sources":["../../../../src/features/game-presentation/composition/createGameBindingContext.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAEnD,kFAAkF;AAClF,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAMtF"}
1
+ {"version":3,"file":"createGameBindingContext.d.ts","sourceRoot":"","sources":["../../../../src/features/game-presentation/composition/createGameBindingContext.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAEnD,kFAAkF;AAClF,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAOtF"}
@@ -2,6 +2,7 @@
2
2
  export function createGameBindingContext(session) {
3
3
  return {
4
4
  game: {
5
+ entities: Object.values(session.entities),
5
6
  session,
6
7
  },
7
8
  };
@@ -1 +1 @@
1
- {"version":3,"file":"createGameBindingContext.js","sourceRoot":"","sources":["../../../../src/features/game-presentation/composition/createGameBindingContext.ts"],"names":[],"mappings":"AAEA,kFAAkF;AAClF,MAAM,UAAU,wBAAwB,CAAC,OAAoB;IAC3D,OAAO;QACL,IAAI,EAAE;YACJ,OAAO;SACR;KACF,CAAC;AACJ,CAAC","sourcesContent":["import type { GameSession } from '@ankhorage/game';\n\n/*** Create the canonical Runtime context namespace for one local game session. */\nexport function createGameBindingContext(session: GameSession): Record<string, unknown> {\n return {\n game: {\n session,\n },\n };\n}\n"]}
1
+ {"version":3,"file":"createGameBindingContext.js","sourceRoot":"","sources":["../../../../src/features/game-presentation/composition/createGameBindingContext.ts"],"names":[],"mappings":"AAEA,kFAAkF;AAClF,MAAM,UAAU,wBAAwB,CAAC,OAAoB;IAC3D,OAAO;QACL,IAAI,EAAE;YACJ,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC;YACzC,OAAO;SACR;KACF,CAAC;AACJ,CAAC","sourcesContent":["import type { GameSession } from '@ankhorage/game';\n\n/*** Create the canonical Runtime context namespace for one local game session. */\nexport function createGameBindingContext(session: GameSession): Record<string, unknown> {\n return {\n game: {\n entities: Object.values(session.entities),\n session,\n },\n };\n}\n"]}
@@ -2,7 +2,7 @@
2
2
  export declare const gameInputZoneMeta: {
3
3
  readonly name: "GameInputZone";
4
4
  readonly category: "component";
5
- readonly description: "Normalizes pointer and touch positions into field-relative percentages and dispatches a generic game event.";
5
+ readonly description: "Normalizes pointer/touch positions and optional keyboard bindings into generic game events.";
6
6
  readonly directManifestNode: true;
7
7
  readonly allowedChildren: readonly [];
8
8
  readonly blueprint: {
@@ -99,6 +99,57 @@ export declare const gameInputZoneMeta: {
99
99
  readonly authority: "instance";
100
100
  };
101
101
  };
102
+ readonly keyboardBindings: {
103
+ readonly type: "array";
104
+ readonly category: "Input";
105
+ readonly label: "Keyboard bindings";
106
+ readonly default: readonly [];
107
+ readonly itemSchema: readonly [{
108
+ readonly key: "key";
109
+ readonly schema: {
110
+ readonly type: "string";
111
+ readonly category: "Input";
112
+ readonly label: "Key";
113
+ readonly authoring: {
114
+ readonly authority: "instance";
115
+ };
116
+ };
117
+ }, {
118
+ readonly key: "eventType";
119
+ readonly schema: {
120
+ readonly type: "string";
121
+ readonly category: "Input";
122
+ readonly label: "Event type";
123
+ readonly authoring: {
124
+ readonly authority: "instance";
125
+ };
126
+ };
127
+ }, {
128
+ readonly key: "entityId";
129
+ readonly schema: {
130
+ readonly type: "string";
131
+ readonly category: "Input";
132
+ readonly label: "Entity id";
133
+ readonly authoring: {
134
+ readonly authority: "instance";
135
+ };
136
+ };
137
+ }, {
138
+ readonly key: "preventDefault";
139
+ readonly schema: {
140
+ readonly type: "boolean";
141
+ readonly category: "Input";
142
+ readonly label: "Prevent browser default";
143
+ readonly default: false;
144
+ readonly authoring: {
145
+ readonly authority: "instance";
146
+ };
147
+ };
148
+ }];
149
+ readonly authoring: {
150
+ readonly authority: "instance";
151
+ };
152
+ };
102
153
  readonly accessibilityLabel: {
103
154
  readonly type: "string";
104
155
  readonly category: "Accessibility";
@@ -1 +1 @@
1
- {"version":3,"file":"gameInputZoneMeta.d.ts","sourceRoot":"","sources":["../../../../src/features/game-presentation/meta/gameInputZoneMeta.ts"],"names":[],"mappings":"AAEA,8FAA8F;AAC9F,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0FQ,CAAC"}
1
+ {"version":3,"file":"gameInputZoneMeta.d.ts","sourceRoot":"","sources":["../../../../src/features/game-presentation/meta/gameInputZoneMeta.ts"],"names":[],"mappings":"AAEA,8FAA8F;AAC9F,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwIQ,CAAC"}
@@ -2,7 +2,7 @@
2
2
  export const gameInputZoneMeta = {
3
3
  name: 'GameInputZone',
4
4
  category: 'component',
5
- description: 'Normalizes pointer and touch positions into field-relative percentages and dispatches a generic game event.',
5
+ description: 'Normalizes pointer/touch positions and optional keyboard bindings into generic game events.',
6
6
  directManifestNode: true,
7
7
  allowedChildren: [],
8
8
  blueprint: {
@@ -81,6 +81,52 @@ export const gameInputZoneMeta = {
81
81
  default: true,
82
82
  authoring: { authority: 'instance' },
83
83
  },
84
+ keyboardBindings: {
85
+ type: 'array',
86
+ category: 'Input',
87
+ label: 'Keyboard bindings',
88
+ default: [],
89
+ itemSchema: [
90
+ {
91
+ key: 'key',
92
+ schema: {
93
+ type: 'string',
94
+ category: 'Input',
95
+ label: 'Key',
96
+ authoring: { authority: 'instance' },
97
+ },
98
+ },
99
+ {
100
+ key: 'eventType',
101
+ schema: {
102
+ type: 'string',
103
+ category: 'Input',
104
+ label: 'Event type',
105
+ authoring: { authority: 'instance' },
106
+ },
107
+ },
108
+ {
109
+ key: 'entityId',
110
+ schema: {
111
+ type: 'string',
112
+ category: 'Input',
113
+ label: 'Entity id',
114
+ authoring: { authority: 'instance' },
115
+ },
116
+ },
117
+ {
118
+ key: 'preventDefault',
119
+ schema: {
120
+ type: 'boolean',
121
+ category: 'Input',
122
+ label: 'Prevent browser default',
123
+ default: false,
124
+ authoring: { authority: 'instance' },
125
+ },
126
+ },
127
+ ],
128
+ authoring: { authority: 'instance' },
129
+ },
84
130
  accessibilityLabel: {
85
131
  type: 'string',
86
132
  category: 'Accessibility',
@@ -1 +1 @@
1
- {"version":3,"file":"gameInputZoneMeta.js","sourceRoot":"","sources":["../../../../src/features/game-presentation/meta/gameInputZoneMeta.ts"],"names":[],"mappings":"AAEA,8FAA8F;AAC9F,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,IAAI,EAAE,eAAe;IACrB,QAAQ,EAAE,WAAW;IACrB,WAAW,EACT,6GAA6G;IAC/G,kBAAkB,EAAE,IAAI;IACxB,eAAe,EAAE,EAAE;IACnB,SAAS,EAAE;QACT,KAAK,EAAE,iBAAiB;QACxB,YAAY,EAAE;YACZ,SAAS,EAAE,cAAc;YACzB,CAAC,EAAE,CAAC;YACJ,CAAC,EAAE,CAAC;YACJ,KAAK,EAAE,GAAG;YACV,MAAM,EAAE,GAAG;YACX,MAAM,EAAE,CAAC;YACT,OAAO,EAAE,IAAI;YACb,UAAU,EAAE,IAAI;SACjB;KACF;IACD,KAAK,EAAE;QACL,SAAS,EAAE;YACT,IAAI,EAAE,QAAQ;YACd,QAAQ,EAAE,OAAO;YACjB,KAAK,EAAE,iBAAiB;YACxB,OAAO,EAAE,cAAc;YACvB,SAAS,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE;SACrC;QACD,QAAQ,EAAE;YACR,IAAI,EAAE,QAAQ;YACd,QAAQ,EAAE,OAAO;YACjB,KAAK,EAAE,WAAW;YAClB,SAAS,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE;SACrC;QACD,CAAC,EAAE;YACD,IAAI,EAAE,QAAQ;YACd,QAAQ,EAAE,UAAU;YACpB,KAAK,EAAE,OAAO;YACd,OAAO,EAAE,CAAC;YACV,SAAS,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE;SACrC;QACD,CAAC,EAAE;YACD,IAAI,EAAE,QAAQ;YACd,QAAQ,EAAE,UAAU;YACpB,KAAK,EAAE,OAAO;YACd,OAAO,EAAE,CAAC;YACV,SAAS,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE;SACrC;QACD,KAAK,EAAE;YACL,IAAI,EAAE,QAAQ;YACd,QAAQ,EAAE,MAAM;YAChB,KAAK,EAAE,WAAW;YAClB,OAAO,EAAE,GAAG;YACZ,SAAS,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE;SACrC;QACD,MAAM,EAAE;YACN,IAAI,EAAE,QAAQ;YACd,QAAQ,EAAE,MAAM;YAChB,KAAK,EAAE,YAAY;YACnB,OAAO,EAAE,GAAG;YACZ,SAAS,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE;SACrC;QACD,MAAM,EAAE;YACN,IAAI,EAAE,QAAQ;YACd,QAAQ,EAAE,UAAU;YACpB,KAAK,EAAE,SAAS;YAChB,OAAO,EAAE,CAAC;YACV,SAAS,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE;SACrC;QACD,OAAO,EAAE;YACP,IAAI,EAAE,SAAS;YACf,QAAQ,EAAE,OAAO;YACjB,KAAK,EAAE,SAAS;YAChB,OAAO,EAAE,IAAI;YACb,SAAS,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE;SACrC;QACD,UAAU,EAAE;YACV,IAAI,EAAE,SAAS;YACf,QAAQ,EAAE,OAAO;YACjB,KAAK,EAAE,uBAAuB;YAC9B,OAAO,EAAE,IAAI;YACb,SAAS,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE;SACrC;QACD,kBAAkB,EAAE;YAClB,IAAI,EAAE,QAAQ;YACd,QAAQ,EAAE,eAAe;YACzB,KAAK,EAAE,qBAAqB;YAC5B,SAAS,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE;SACrC;KACF;CACmC,CAAC","sourcesContent":["import type { ZoraComponentMeta } from '@ankhorage/zora/metadata';\n\n/*** Describe a field-relative pointer/touch input adapter for one embeddable Game session. */\nexport const gameInputZoneMeta = {\n name: 'GameInputZone',\n category: 'component',\n description:\n 'Normalizes pointer and touch positions into field-relative percentages and dispatches a generic game event.',\n directManifestNode: true,\n allowedChildren: [],\n blueprint: {\n label: 'Game input zone',\n defaultProps: {\n eventType: 'game.pointer',\n x: 0,\n y: 0,\n width: 100,\n height: 100,\n zIndex: 1,\n enabled: true,\n continuous: true,\n },\n },\n props: {\n eventType: {\n type: 'string',\n category: 'Input',\n label: 'Game event type',\n default: 'game.pointer',\n authoring: { authority: 'instance' },\n },\n entityId: {\n type: 'string',\n category: 'Input',\n label: 'Entity id',\n authoring: { authority: 'instance' },\n },\n x: {\n type: 'number',\n category: 'Position',\n label: 'X (%)',\n default: 0,\n authoring: { authority: 'instance' },\n },\n y: {\n type: 'number',\n category: 'Position',\n label: 'Y (%)',\n default: 0,\n authoring: { authority: 'instance' },\n },\n width: {\n type: 'number',\n category: 'Size',\n label: 'Width (%)',\n default: 100,\n authoring: { authority: 'instance' },\n },\n height: {\n type: 'number',\n category: 'Size',\n label: 'Height (%)',\n default: 100,\n authoring: { authority: 'instance' },\n },\n zIndex: {\n type: 'number',\n category: 'Position',\n label: 'Z index',\n default: 1,\n authoring: { authority: 'instance' },\n },\n enabled: {\n type: 'boolean',\n category: 'State',\n label: 'Enabled',\n default: true,\n authoring: { authority: 'instance' },\n },\n continuous: {\n type: 'boolean',\n category: 'Input',\n label: 'Dispatch while moving',\n default: true,\n authoring: { authority: 'instance' },\n },\n accessibilityLabel: {\n type: 'string',\n category: 'Accessibility',\n label: 'Accessibility label',\n authoring: { authority: 'instance' },\n },\n },\n} as const satisfies ZoraComponentMeta;\n"]}
1
+ {"version":3,"file":"gameInputZoneMeta.js","sourceRoot":"","sources":["../../../../src/features/game-presentation/meta/gameInputZoneMeta.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ZoraComponentMeta } from '@ankhorage/zora/metadata';\n\n/*** Describe a field-relative pointer/touch input adapter for one embeddable Game session. */\nexport const gameInputZoneMeta = {\n name: 'GameInputZone',\n category: 'component',\n description:\n 'Normalizes pointer/touch positions and optional keyboard bindings into generic game events.',\n directManifestNode: true,\n allowedChildren: [],\n blueprint: {\n label: 'Game input zone',\n defaultProps: {\n eventType: 'game.pointer',\n x: 0,\n y: 0,\n width: 100,\n height: 100,\n zIndex: 1,\n enabled: true,\n continuous: true,\n },\n },\n props: {\n eventType: {\n type: 'string',\n category: 'Input',\n label: 'Game event type',\n default: 'game.pointer',\n authoring: { authority: 'instance' },\n },\n entityId: {\n type: 'string',\n category: 'Input',\n label: 'Entity id',\n authoring: { authority: 'instance' },\n },\n x: {\n type: 'number',\n category: 'Position',\n label: 'X (%)',\n default: 0,\n authoring: { authority: 'instance' },\n },\n y: {\n type: 'number',\n category: 'Position',\n label: 'Y (%)',\n default: 0,\n authoring: { authority: 'instance' },\n },\n width: {\n type: 'number',\n category: 'Size',\n label: 'Width (%)',\n default: 100,\n authoring: { authority: 'instance' },\n },\n height: {\n type: 'number',\n category: 'Size',\n label: 'Height (%)',\n default: 100,\n authoring: { authority: 'instance' },\n },\n zIndex: {\n type: 'number',\n category: 'Position',\n label: 'Z index',\n default: 1,\n authoring: { authority: 'instance' },\n },\n enabled: {\n type: 'boolean',\n category: 'State',\n label: 'Enabled',\n default: true,\n authoring: { authority: 'instance' },\n },\n continuous: {\n type: 'boolean',\n category: 'Input',\n label: 'Dispatch while moving',\n default: true,\n authoring: { authority: 'instance' },\n },\n keyboardBindings: {\n type: 'array',\n category: 'Input',\n label: 'Keyboard bindings',\n default: [],\n itemSchema: [\n {\n key: 'key',\n schema: {\n type: 'string',\n category: 'Input',\n label: 'Key',\n authoring: { authority: 'instance' },\n },\n },\n {\n key: 'eventType',\n schema: {\n type: 'string',\n category: 'Input',\n label: 'Event type',\n authoring: { authority: 'instance' },\n },\n },\n {\n key: 'entityId',\n schema: {\n type: 'string',\n category: 'Input',\n label: 'Entity id',\n authoring: { authority: 'instance' },\n },\n },\n {\n key: 'preventDefault',\n schema: {\n type: 'boolean',\n category: 'Input',\n label: 'Prevent browser default',\n default: false,\n authoring: { authority: 'instance' },\n },\n },\n ],\n authoring: { authority: 'instance' },\n },\n accessibilityLabel: {\n type: 'string',\n category: 'Accessibility',\n label: 'Accessibility label',\n authoring: { authority: 'instance' },\n },\n },\n} as const satisfies ZoraComponentMeta;\n"]}
@@ -1,4 +1,4 @@
1
- export type { GameEntityProps, GameFieldProps, GameInputZoneProps, GameOverlayPlacement, GameOverlayProps, GamePointerEvents, GameProps, } from '../../types/gamePresentation';
1
+ export type { GameEntityProps, GameFieldProps, GameInputZoneProps, GameKeyboardBinding, GameOverlayPlacement, GameOverlayProps, GamePointerEvents, GameProps, } from '../../types/gamePresentation';
2
2
  export { Game } from './adapters/inbound/Game';
3
3
  export { GameEntity } from './adapters/inbound/GameEntity';
4
4
  export { GameField } from './adapters/inbound/GameField';
@@ -1 +1 @@
1
- {"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../../src/features/game-presentation/public.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,eAAe,EACf,cAAc,EACd,kBAAkB,EAClB,oBAAoB,EACpB,gBAAgB,EAChB,iBAAiB,EACjB,SAAS,GACV,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC"}
1
+ {"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../../src/features/game-presentation/public.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,eAAe,EACf,cAAc,EACd,kBAAkB,EAClB,mBAAmB,EACnB,oBAAoB,EACpB,gBAAgB,EAChB,iBAAiB,EACjB,SAAS,GACV,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"public.js","sourceRoot":"","sources":["../../../src/features/game-presentation/public.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC","sourcesContent":["export type {\n GameEntityProps,\n GameFieldProps,\n GameInputZoneProps,\n GameOverlayPlacement,\n GameOverlayProps,\n GamePointerEvents,\n GameProps,\n} from '../../types/gamePresentation';\nexport { Game } from './adapters/inbound/Game';\nexport { GameEntity } from './adapters/inbound/GameEntity';\nexport { GameField } from './adapters/inbound/GameField';\nexport { GameInputZone } from './adapters/inbound/GameInputZone';\nexport { GameOverlay } from './adapters/inbound/GameOverlay';\n"]}
1
+ {"version":3,"file":"public.js","sourceRoot":"","sources":["../../../src/features/game-presentation/public.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC","sourcesContent":["export type {\n GameEntityProps,\n GameFieldProps,\n GameInputZoneProps,\n GameKeyboardBinding,\n GameOverlayPlacement,\n GameOverlayProps,\n GamePointerEvents,\n GameProps,\n} from '../../types/gamePresentation';\nexport { Game } from './adapters/inbound/Game';\nexport { GameEntity } from './adapters/inbound/GameEntity';\nexport { GameField } from './adapters/inbound/GameField';\nexport { GameInputZone } from './adapters/inbound/GameInputZone';\nexport { GameOverlay } from './adapters/inbound/GameOverlay';\n"]}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export type { GameEntityProps, GameFieldProps, GameInputZoneProps, GameOverlayPlacement, GameOverlayProps, GamePointerEvents, GameProps, } from './features/game-presentation/public';
1
+ export type { GameEntityProps, GameFieldProps, GameInputZoneProps, GameKeyboardBinding, GameOverlayPlacement, GameOverlayProps, GamePointerEvents, GameProps, } from './features/game-presentation/public';
2
2
  export { Game, GameEntity, GameField, GameInputZone, GameOverlay, } from './features/game-presentation/public';
3
3
  export { ZORA_GAME_PLUGIN } from './ZORA_GAME_PLUGIN';
4
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,eAAe,EACf,cAAc,EACd,kBAAkB,EAClB,oBAAoB,EACpB,gBAAgB,EAChB,iBAAiB,EACjB,SAAS,GACV,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EACL,IAAI,EACJ,UAAU,EACV,SAAS,EACT,aAAa,EACb,WAAW,GACZ,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,eAAe,EACf,cAAc,EACd,kBAAkB,EAClB,mBAAmB,EACnB,oBAAoB,EACpB,gBAAgB,EAChB,iBAAiB,EACjB,SAAS,GACV,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EACL,IAAI,EACJ,UAAU,EACV,SAAS,EACT,aAAa,EACb,WAAW,GACZ,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC"}
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AASA,OAAO,EACL,IAAI,EACJ,UAAU,EACV,SAAS,EACT,aAAa,EACb,WAAW,GACZ,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC","sourcesContent":["export type {\n GameEntityProps,\n GameFieldProps,\n GameInputZoneProps,\n GameOverlayPlacement,\n GameOverlayProps,\n GamePointerEvents,\n GameProps,\n} from './features/game-presentation/public';\nexport {\n Game,\n GameEntity,\n GameField,\n GameInputZone,\n GameOverlay,\n} from './features/game-presentation/public';\nexport { ZORA_GAME_PLUGIN } from './ZORA_GAME_PLUGIN';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAUA,OAAO,EACL,IAAI,EACJ,UAAU,EACV,SAAS,EACT,aAAa,EACb,WAAW,GACZ,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC","sourcesContent":["export type {\n GameEntityProps,\n GameFieldProps,\n GameInputZoneProps,\n GameKeyboardBinding,\n GameOverlayPlacement,\n GameOverlayProps,\n GamePointerEvents,\n GameProps,\n} from './features/game-presentation/public';\nexport {\n Game,\n GameEntity,\n GameField,\n GameInputZone,\n GameOverlay,\n} from './features/game-presentation/public';\nexport { ZORA_GAME_PLUGIN } from './ZORA_GAME_PLUGIN';\n"]}
@@ -26,6 +26,12 @@ export interface GameEntityProps {
26
26
  readonly accessibilityLabel?: string;
27
27
  readonly testID?: string;
28
28
  }
29
+ export interface GameKeyboardBinding {
30
+ readonly key: string;
31
+ readonly eventType?: string;
32
+ readonly entityId?: string;
33
+ readonly preventDefault?: boolean;
34
+ }
29
35
  export interface GameInputZoneProps {
30
36
  readonly eventType: string;
31
37
  readonly entityId?: string;
@@ -36,6 +42,7 @@ export interface GameInputZoneProps {
36
42
  readonly zIndex?: number;
37
43
  readonly enabled?: boolean;
38
44
  readonly continuous?: boolean;
45
+ readonly keyboardBindings?: readonly GameKeyboardBinding[];
39
46
  readonly accessibilityLabel?: string;
40
47
  readonly testID?: string;
41
48
  }
@@ -1 +1 @@
1
- {"version":3,"file":"gamePresentation.d.ts","sourceRoot":"","sources":["../../src/types/gamePresentation.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gamePresentation.d.ts","sourceRoot":"","sources":["../../src/types/gamePresentation.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"gamePresentation.js","sourceRoot":"","sources":["../../src/types/gamePresentation.ts"],"names":[],"mappings":"","sourcesContent":["import type { GameDefinition, GameInput, GameOutput } from '@ankhorage/game';\nimport type React from 'react';\n\ntype GameOverlayCornerPlacement = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';\n\nexport type GamePointerEvents = 'auto' | 'box-none' | 'box-only' | 'none';\nexport type GameOverlayPlacement =\n 'fill' | 'center' | 'top' | 'bottom' | GameOverlayCornerPlacement;\n\nexport interface GameFieldProps {\n readonly children?: React.ReactNode;\n readonly aspectRatio?: number;\n readonly minHeight?: number;\n readonly clip?: boolean;\n readonly accessibilityLabel?: string;\n readonly testID?: string;\n}\n\nexport interface GameEntityProps {\n readonly children?: React.ReactNode;\n readonly x?: number;\n readonly y?: number;\n readonly width?: number;\n readonly height?: number;\n readonly opacity?: number;\n readonly scale?: number;\n readonly rotation?: number;\n readonly zIndex?: number;\n readonly hidden?: boolean;\n readonly pointerEvents?: GamePointerEvents;\n readonly accessibilityLabel?: string;\n readonly testID?: string;\n}\n\nexport interface GameInputZoneProps {\n readonly eventType: string;\n readonly entityId?: string;\n readonly x?: number;\n readonly y?: number;\n readonly width?: number;\n readonly height?: number;\n readonly zIndex?: number;\n readonly enabled?: boolean;\n readonly continuous?: boolean;\n readonly accessibilityLabel?: string;\n readonly testID?: string;\n}\n\nexport interface GameOverlayProps {\n readonly children?: React.ReactNode;\n readonly placement?: GameOverlayPlacement;\n readonly blocking?: boolean;\n readonly padding?: number;\n readonly accessibilityLabel?: string;\n readonly testID?: string;\n}\n\nexport interface GameProps extends GameFieldProps {\n readonly definition: GameDefinition;\n readonly input?: GameInput;\n readonly seed?: number;\n readonly resetKey?: string;\n readonly autoAdvanceTime?: boolean;\n readonly onOutput?: (output: GameOutput) => void;\n}\n"]}
1
+ {"version":3,"file":"gamePresentation.js","sourceRoot":"","sources":["../../src/types/gamePresentation.ts"],"names":[],"mappings":"","sourcesContent":["import type { GameDefinition, GameInput, GameOutput } from '@ankhorage/game';\nimport type React from 'react';\n\ntype GameOverlayCornerPlacement = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';\n\nexport type GamePointerEvents = 'auto' | 'box-none' | 'box-only' | 'none';\nexport type GameOverlayPlacement =\n 'fill' | 'center' | 'top' | 'bottom' | GameOverlayCornerPlacement;\n\nexport interface GameFieldProps {\n readonly children?: React.ReactNode;\n readonly aspectRatio?: number;\n readonly minHeight?: number;\n readonly clip?: boolean;\n readonly accessibilityLabel?: string;\n readonly testID?: string;\n}\n\nexport interface GameEntityProps {\n readonly children?: React.ReactNode;\n readonly x?: number;\n readonly y?: number;\n readonly width?: number;\n readonly height?: number;\n readonly opacity?: number;\n readonly scale?: number;\n readonly rotation?: number;\n readonly zIndex?: number;\n readonly hidden?: boolean;\n readonly pointerEvents?: GamePointerEvents;\n readonly accessibilityLabel?: string;\n readonly testID?: string;\n}\n\nexport interface GameKeyboardBinding {\n readonly key: string;\n readonly eventType?: string;\n readonly entityId?: string;\n readonly preventDefault?: boolean;\n}\n\nexport interface GameInputZoneProps {\n readonly eventType: string;\n readonly entityId?: string;\n readonly x?: number;\n readonly y?: number;\n readonly width?: number;\n readonly height?: number;\n readonly zIndex?: number;\n readonly enabled?: boolean;\n readonly continuous?: boolean;\n readonly keyboardBindings?: readonly GameKeyboardBinding[];\n readonly accessibilityLabel?: string;\n readonly testID?: string;\n}\n\nexport interface GameOverlayProps {\n readonly children?: React.ReactNode;\n readonly placement?: GameOverlayPlacement;\n readonly blocking?: boolean;\n readonly padding?: number;\n readonly accessibilityLabel?: string;\n readonly testID?: string;\n}\n\nexport interface GameProps extends GameFieldProps {\n readonly definition: GameDefinition;\n readonly input?: GameInput;\n readonly seed?: number;\n readonly resetKey?: string;\n readonly autoAdvanceTime?: boolean;\n readonly onOutput?: (output: GameOutput) => void;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ankhorage/zora-game",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "type": "module",
5
5
  "description": "Generic game presentation primitives for React Native and React Native Web apps built on ZORA.",
6
6
  "homepage": "https://github.com/ankhorage/zora-game#readme",
@@ -22,16 +22,6 @@ describe('ZORA game plugin metadata', () => {
22
22
  expect(game.allowedChildren).toContain('GameOverlay');
23
23
  });
24
24
 
25
- test('keeps Game input zones scoped to runtime-owning Game parents', () => {
26
- const catalog = composeZoraPluginMetadata([ZORA_CORE_PLUGIN_METADATA, ZORA_PLUGIN_METADATA]);
27
- const gameInputZone = readComponentMeta(catalog.componentMeta.GameInputZone, 'GameInputZone');
28
-
29
- expect(gameInputZone.props.eventType?.type).toBe('string');
30
- expect(gameInputZone.props.continuous?.type).toBe('boolean');
31
- expect(catalog.componentMeta.Game?.allowedChildren).toContain('GameInputZone');
32
- expect(catalog.componentMeta.GameField?.allowedChildren).not.toContain('GameInputZone');
33
- });
34
-
35
25
  test('describes bindable Game inputs and domain-neutral outputs', () => {
36
26
  const catalog = composeZoraPluginMetadata([ZORA_CORE_PLUGIN_METADATA, ZORA_PLUGIN_METADATA]);
37
27
  const game = readComponentMeta(catalog.componentMeta.Game, 'Game');
@@ -60,6 +50,25 @@ describe('ZORA game plugin metadata', () => {
60
50
  });
61
51
  });
62
52
 
53
+ describe('ZORA game input zone metadata', () => {
54
+ test('keeps input zones scoped to runtime-owning Game parents', () => {
55
+ const catalog = composeZoraPluginMetadata([ZORA_CORE_PLUGIN_METADATA, ZORA_PLUGIN_METADATA]);
56
+ const gameInputZone = readComponentMeta(catalog.componentMeta.GameInputZone, 'GameInputZone');
57
+
58
+ expect(gameInputZone.props.eventType?.type).toBe('string');
59
+ expect(gameInputZone.props.continuous?.type).toBe('boolean');
60
+ expect(gameInputZone.props.keyboardBindings?.type).toBe('array');
61
+ expect(gameInputZone.props.keyboardBindings?.itemSchema?.map(({ key }) => key)).toEqual([
62
+ 'key',
63
+ 'eventType',
64
+ 'entityId',
65
+ 'preventDefault',
66
+ ]);
67
+ expect(catalog.componentMeta.Game?.allowedChildren).toContain('GameInputZone');
68
+ expect(catalog.componentMeta.GameField?.allowedChildren).not.toContain('GameInputZone');
69
+ });
70
+ });
71
+
63
72
  /*** Require one composed component metadata entry for focused assertions. */
64
73
  function readComponentMeta<TValue>(value: TValue | undefined, name: string): TValue {
65
74
  if (value === undefined) throw new Error(`Missing composed component metadata for ${name}.`);
@@ -1,10 +1,10 @@
1
- import { StyleSheet, View, type ViewStyle } from 'react-native';
1
+ import { Platform, StyleSheet, View, type ViewStyle } from 'react-native';
2
2
 
3
3
  import type { GameInputZoneProps } from '../../../../types/gamePresentation';
4
4
  import { toGamePercentage } from '../../utils/toGamePercentage';
5
5
  import { useGameInputZoneResponder } from './useGameInputZoneResponder';
6
6
 
7
- /*** Capture pointer/touch geometry and dispatch only normalized generic Game events. */
7
+ /*** Capture pointer/touch geometry and optional keyboard input as normalized generic Game events. */
8
8
  export function GameInputZone({
9
9
  x = 0,
10
10
  y = 0,
@@ -13,22 +13,31 @@ export function GameInputZone({
13
13
  zIndex = 0,
14
14
  enabled = true,
15
15
  continuous = true,
16
+ keyboardBindings,
16
17
  accessibilityLabel,
17
18
  testID,
18
19
  ...inputProps
19
20
  }: GameInputZoneProps) {
20
- const { dispatchPointer, handleLayout, shouldSetResponder } = useGameInputZoneResponder({
21
- ...inputProps,
22
- x,
23
- y,
24
- width,
25
- height,
21
+ const { dispatchKeyboard, dispatchPointer, handleLayout, shouldSetResponder } =
22
+ useGameInputZoneResponder({
23
+ ...inputProps,
24
+ x,
25
+ y,
26
+ width,
27
+ height,
28
+ enabled,
29
+ continuous,
30
+ ...(keyboardBindings === undefined ? {} : { keyboardBindings }),
31
+ });
32
+ const keyboardProps = createGameInputZoneKeyboardProps({
33
+ dispatchKeyboard,
26
34
  enabled,
27
- continuous,
35
+ keyboardBindings,
28
36
  });
29
37
 
30
38
  return (
31
39
  <View
40
+ {...keyboardProps}
32
41
  {...(accessibilityLabel === undefined ? {} : { accessibilityLabel })}
33
42
  {...(testID === undefined ? {} : { testID })}
34
43
  {...(continuous
@@ -43,6 +52,24 @@ export function GameInputZone({
43
52
  );
44
53
  }
45
54
 
55
+ interface GameInputZoneKeyboardPropsInput {
56
+ readonly dispatchKeyboard: (event: { readonly key: string; preventDefault(): void }) => void;
57
+ readonly enabled: boolean;
58
+ readonly keyboardBindings: GameInputZoneProps['keyboardBindings'];
59
+ }
60
+
61
+ /*** Add a focusable web keyboard target only when the input zone owns key bindings. */
62
+ function createGameInputZoneKeyboardProps({
63
+ dispatchKeyboard,
64
+ enabled,
65
+ keyboardBindings,
66
+ }: GameInputZoneKeyboardPropsInput) {
67
+ if (Platform.OS !== 'web' || !enabled || keyboardBindings === undefined) return {};
68
+ if (keyboardBindings.length === 0) return {};
69
+
70
+ return { onKeyDown: dispatchKeyboard, tabIndex: 0 as const };
71
+ }
72
+
46
73
  interface InputZoneStyleInput {
47
74
  readonly x: number;
48
75
  readonly y: number;
@@ -0,0 +1,51 @@
1
+ import { describe, expect, test } from 'bun:test';
2
+
3
+ import { createGameKeyboardEvent } from './createGameKeyboardEvent';
4
+
5
+ describe('createGameKeyboardEvent', () => {
6
+ test('maps a configured key through fallback event and entity values', () => {
7
+ expect(
8
+ createGameKeyboardEvent({
9
+ bindings: [{ key: 'ArrowLeft', preventDefault: true }],
10
+ fallbackEventType: 'player.move',
11
+ fallbackEntityId: 'player',
12
+ key: 'ArrowLeft',
13
+ }),
14
+ ).toEqual({
15
+ event: {
16
+ type: 'player.move',
17
+ entityId: 'player',
18
+ payload: { key: 'ArrowLeft' },
19
+ },
20
+ preventDefault: true,
21
+ });
22
+ });
23
+
24
+ test('allows one key binding to override the event target', () => {
25
+ expect(
26
+ createGameKeyboardEvent({
27
+ bindings: [{ key: ' ', eventType: 'player.action', entityId: 'actor' }],
28
+ fallbackEventType: 'player.move',
29
+ fallbackEntityId: 'player',
30
+ key: ' ',
31
+ }),
32
+ ).toEqual({
33
+ event: {
34
+ type: 'player.action',
35
+ entityId: 'actor',
36
+ payload: { key: ' ' },
37
+ },
38
+ preventDefault: false,
39
+ });
40
+ });
41
+
42
+ test('ignores keys without a binding', () => {
43
+ expect(
44
+ createGameKeyboardEvent({
45
+ bindings: [{ key: 'ArrowLeft' }],
46
+ fallbackEventType: 'player.move',
47
+ key: 'ArrowRight',
48
+ }),
49
+ ).toBeUndefined();
50
+ });
51
+ });
@@ -0,0 +1,33 @@
1
+ import type { GameEvent } from '@ankhorage/game';
2
+
3
+ import type { GameKeyboardBinding } from '../../../../types/gamePresentation';
4
+
5
+ interface CreateGameKeyboardEventInput {
6
+ readonly bindings: readonly GameKeyboardBinding[];
7
+ readonly fallbackEventType: string;
8
+ readonly fallbackEntityId?: string;
9
+ readonly key: string;
10
+ }
11
+
12
+ interface GameKeyboardEventResult {
13
+ readonly event: GameEvent;
14
+ readonly preventDefault: boolean;
15
+ }
16
+
17
+ /*** Map one configured keyboard key into a generic Game event. */
18
+ export function createGameKeyboardEvent(
19
+ input: CreateGameKeyboardEventInput,
20
+ ): GameKeyboardEventResult | undefined {
21
+ const binding = input.bindings.find((candidate) => candidate.key === input.key);
22
+ if (binding === undefined) return undefined;
23
+
24
+ const entityId = binding.entityId ?? input.fallbackEntityId;
25
+ return {
26
+ event: {
27
+ type: binding.eventType ?? input.fallbackEventType,
28
+ ...(entityId === undefined ? {} : { entityId }),
29
+ payload: { key: input.key },
30
+ },
31
+ preventDefault: binding.preventDefault ?? false,
32
+ };
33
+ }
@@ -3,8 +3,14 @@ import type { GestureResponderEvent, LayoutChangeEvent } from 'react-native';
3
3
 
4
4
  import type { GameInputZoneProps } from '../../../../types/gamePresentation';
5
5
  import { GameRuntimeContext } from '../../composition/GameRuntimeContext';
6
+ import { createGameKeyboardEvent } from './createGameKeyboardEvent';
6
7
  import { createGamePointerEvent } from './createGamePointerEvent';
7
8
 
9
+ interface GameKeyboardInputEvent {
10
+ readonly key: string;
11
+ preventDefault(): void;
12
+ }
13
+
8
14
  interface MeasuredSize {
9
15
  readonly width: number;
10
16
  readonly height: number;
@@ -41,7 +47,23 @@ export function useGameInputZoneResponder(props: GameInputZoneProps) {
41
47
  [props.entityId, props.eventType, props.height, props.width, props.x, props.y, runtime],
42
48
  );
43
49
 
50
+ const dispatchKeyboard = React.useCallback(
51
+ (event: GameKeyboardInputEvent) => {
52
+ const result = createGameKeyboardEvent({
53
+ bindings: props.keyboardBindings ?? [],
54
+ fallbackEventType: props.eventType,
55
+ ...(props.entityId === undefined ? {} : { fallbackEntityId: props.entityId }),
56
+ key: event.key,
57
+ });
58
+ if (result === undefined) return;
59
+ if (result.preventDefault) event.preventDefault();
60
+ runtime.dispatch(result.event);
61
+ },
62
+ [props.entityId, props.eventType, props.keyboardBindings, runtime],
63
+ );
64
+
44
65
  return {
66
+ dispatchKeyboard,
45
67
  dispatchPointer,
46
68
  handleLayout,
47
69
  shouldSetResponder: () => props.enabled ?? true,
@@ -15,6 +15,11 @@ const session: GameSession = {
15
15
  templateId: 'actor',
16
16
  state: { x: 42, y: 73, label: 'Player' },
17
17
  },
18
+ shot: {
19
+ id: 'shot',
20
+ templateId: 'projectile',
21
+ state: { x: 50, y: 60, label: 'A' },
22
+ },
18
23
  },
19
24
  scheduled: [],
20
25
  elapsedMs: 120,
@@ -39,9 +44,23 @@ describe('createGameBindingContext', () => {
39
44
  ).toBe(80);
40
45
  });
41
46
 
47
+ test('exposes current entities as a repeatable array with stable ids', () => {
48
+ const context = createGameBindingContext(session);
49
+ const entities = resolveRuntimeBindingValueSync(
50
+ { source: { kind: 'context', path: 'game.entities' } },
51
+ { context },
52
+ );
53
+
54
+ expect(Array.isArray(entities)).toBe(true);
55
+ expect(entities).toEqual(Object.values(session.entities));
56
+ });
57
+
42
58
  test('owns only the local game namespace', () => {
43
59
  expect(createGameBindingContext(session)).toEqual({
44
- game: { session },
60
+ game: {
61
+ entities: Object.values(session.entities),
62
+ session,
63
+ },
45
64
  });
46
65
  });
47
66
  });
@@ -4,6 +4,7 @@ import type { GameSession } from '@ankhorage/game';
4
4
  export function createGameBindingContext(session: GameSession): Record<string, unknown> {
5
5
  return {
6
6
  game: {
7
+ entities: Object.values(session.entities),
7
8
  session,
8
9
  },
9
10
  };
@@ -5,7 +5,7 @@ export const gameInputZoneMeta = {
5
5
  name: 'GameInputZone',
6
6
  category: 'component',
7
7
  description:
8
- 'Normalizes pointer and touch positions into field-relative percentages and dispatches a generic game event.',
8
+ 'Normalizes pointer/touch positions and optional keyboard bindings into generic game events.',
9
9
  directManifestNode: true,
10
10
  allowedChildren: [],
11
11
  blueprint: {
@@ -84,6 +84,52 @@ export const gameInputZoneMeta = {
84
84
  default: true,
85
85
  authoring: { authority: 'instance' },
86
86
  },
87
+ keyboardBindings: {
88
+ type: 'array',
89
+ category: 'Input',
90
+ label: 'Keyboard bindings',
91
+ default: [],
92
+ itemSchema: [
93
+ {
94
+ key: 'key',
95
+ schema: {
96
+ type: 'string',
97
+ category: 'Input',
98
+ label: 'Key',
99
+ authoring: { authority: 'instance' },
100
+ },
101
+ },
102
+ {
103
+ key: 'eventType',
104
+ schema: {
105
+ type: 'string',
106
+ category: 'Input',
107
+ label: 'Event type',
108
+ authoring: { authority: 'instance' },
109
+ },
110
+ },
111
+ {
112
+ key: 'entityId',
113
+ schema: {
114
+ type: 'string',
115
+ category: 'Input',
116
+ label: 'Entity id',
117
+ authoring: { authority: 'instance' },
118
+ },
119
+ },
120
+ {
121
+ key: 'preventDefault',
122
+ schema: {
123
+ type: 'boolean',
124
+ category: 'Input',
125
+ label: 'Prevent browser default',
126
+ default: false,
127
+ authoring: { authority: 'instance' },
128
+ },
129
+ },
130
+ ],
131
+ authoring: { authority: 'instance' },
132
+ },
87
133
  accessibilityLabel: {
88
134
  type: 'string',
89
135
  category: 'Accessibility',
@@ -2,6 +2,7 @@ export type {
2
2
  GameEntityProps,
3
3
  GameFieldProps,
4
4
  GameInputZoneProps,
5
+ GameKeyboardBinding,
5
6
  GameOverlayPlacement,
6
7
  GameOverlayProps,
7
8
  GamePointerEvents,
package/src/index.ts CHANGED
@@ -2,6 +2,7 @@ export type {
2
2
  GameEntityProps,
3
3
  GameFieldProps,
4
4
  GameInputZoneProps,
5
+ GameKeyboardBinding,
5
6
  GameOverlayPlacement,
6
7
  GameOverlayProps,
7
8
  GamePointerEvents,
@@ -32,6 +32,13 @@ export interface GameEntityProps {
32
32
  readonly testID?: string;
33
33
  }
34
34
 
35
+ export interface GameKeyboardBinding {
36
+ readonly key: string;
37
+ readonly eventType?: string;
38
+ readonly entityId?: string;
39
+ readonly preventDefault?: boolean;
40
+ }
41
+
35
42
  export interface GameInputZoneProps {
36
43
  readonly eventType: string;
37
44
  readonly entityId?: string;
@@ -42,6 +49,7 @@ export interface GameInputZoneProps {
42
49
  readonly zIndex?: number;
43
50
  readonly enabled?: boolean;
44
51
  readonly continuous?: boolean;
52
+ readonly keyboardBindings?: readonly GameKeyboardBinding[];
45
53
  readonly accessibilityLabel?: string;
46
54
  readonly testID?: string;
47
55
  }