@the-inclusionist/engine 9.0.0 → 11.0.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 (532) hide show
  1. package/README.md +120 -78
  2. package/app/css/style.css +761 -377
  3. package/app/public/vendor/fonts/playwrite-cu.woff2 +0 -0
  4. package/app/public/vendor/fonts/playwrite-es-deco.woff2 +0 -0
  5. package/app/public/vendor/fonts/playwrite-es.woff2 +0 -0
  6. package/app/public/vendor/fonts/playwrite-gb-j.woff2 +0 -0
  7. package/app/public/vendor/fonts/playwrite-gb-s.woff2 +0 -0
  8. package/app/public/vendor/fonts/playwrite-pe.woff2 +0 -0
  9. package/app/public/vendor/fonts/playwrite-pt.woff2 +0 -0
  10. package/app/public/vendor/fonts-licences/NOTICE.txt +166 -0
  11. package/app/public/vendor/fonts-licences/OFL-1.1.txt +86 -0
  12. package/app/public/vendor/fonts.css +36 -84
  13. package/dist-pkg/boot/create-game.d.ts +499 -219
  14. package/dist-pkg/boot/create-game.js +3944 -562
  15. package/dist-pkg/core/a11y-sr.d.ts +25 -3
  16. package/dist-pkg/core/a11y-sr.js +38 -19
  17. package/dist-pkg/core/accessible-label.d.ts +17 -0
  18. package/dist-pkg/core/accessible-label.js +37 -0
  19. package/dist-pkg/core/accommodation-subjects.d.ts +26 -0
  20. package/dist-pkg/core/accommodation-subjects.js +32 -0
  21. package/dist-pkg/core/accommodations.d.ts +125 -0
  22. package/dist-pkg/core/accommodations.js +195 -0
  23. package/dist-pkg/core/actions.d.ts +108 -55
  24. package/dist-pkg/core/actions.js +122 -71
  25. package/dist-pkg/core/calm-mode.d.ts +31 -0
  26. package/dist-pkg/core/calm-mode.js +47 -0
  27. package/dist-pkg/core/camera-cycle.d.ts +15 -0
  28. package/dist-pkg/core/camera-cycle.js +7 -0
  29. package/dist-pkg/core/caption-duration.d.ts +15 -0
  30. package/dist-pkg/core/caption-duration.js +19 -0
  31. package/dist-pkg/core/cartridge-problems.d.ts +34 -0
  32. package/dist-pkg/core/cartridge-problems.js +53 -0
  33. package/dist-pkg/core/constants.d.ts +0 -54
  34. package/dist-pkg/core/constants.js +9 -76
  35. package/dist-pkg/core/contract.d.ts +142 -164
  36. package/dist-pkg/core/contract.js +222 -208
  37. package/dist-pkg/core/dom-query.d.ts +4 -4
  38. package/dist-pkg/core/dom-query.js +12 -20
  39. package/dist-pkg/core/entity.d.ts +62 -77
  40. package/dist-pkg/core/entity.js +18 -32
  41. package/dist-pkg/core/escape-html.d.ts +9 -10
  42. package/dist-pkg/core/escape-html.js +16 -20
  43. package/dist-pkg/core/flash-threshold.d.ts +17 -0
  44. package/dist-pkg/core/flash-threshold.js +113 -0
  45. package/dist-pkg/core/game-speed.d.ts +11 -0
  46. package/dist-pkg/core/game-speed.js +16 -0
  47. package/dist-pkg/core/genres.d.ts +20 -0
  48. package/dist-pkg/core/genres.js +44 -0
  49. package/dist-pkg/core/i18n.d.ts +71 -60
  50. package/dist-pkg/core/i18n.js +186 -200
  51. package/dist-pkg/core/loop.d.ts +17 -6
  52. package/dist-pkg/core/loop.js +26 -26
  53. package/dist-pkg/core/pause-icon-catalogue.d.ts +15 -0
  54. package/dist-pkg/core/pause-icon-catalogue.js +54 -0
  55. package/dist-pkg/core/ring.d.ts +17 -0
  56. package/dist-pkg/core/ring.js +29 -0
  57. package/dist-pkg/core/rng.d.ts +7 -12
  58. package/dist-pkg/core/rng.js +23 -53
  59. package/dist-pkg/core/route.d.ts +26 -26
  60. package/dist-pkg/core/route.js +126 -121
  61. package/dist-pkg/core/scenes.d.ts +37 -40
  62. package/dist-pkg/core/scenes.js +41 -43
  63. package/dist-pkg/core/screens.d.ts +4 -4
  64. package/dist-pkg/core/screens.js +11 -16
  65. package/dist-pkg/core/session-clock.d.ts +23 -0
  66. package/dist-pkg/core/session-clock.js +37 -0
  67. package/dist-pkg/core/setting-defaults.d.ts +61 -0
  68. package/dist-pkg/core/setting-defaults.js +66 -0
  69. package/dist-pkg/core/speech-rate.d.ts +29 -0
  70. package/dist-pkg/core/speech-rate.js +47 -0
  71. package/dist-pkg/core/state.d.ts +118 -118
  72. package/dist-pkg/core/state.js +322 -297
  73. package/dist-pkg/core/visual-cycles.d.ts +30 -0
  74. package/dist-pkg/core/visual-cycles.js +52 -0
  75. package/dist-pkg/core/visual-state.d.ts +13 -0
  76. package/dist-pkg/core/visual-state.js +6 -0
  77. package/dist-pkg/educational/activities-registry.d.ts +1 -1
  78. package/dist-pkg/educational/activities-registry.js +9 -8
  79. package/dist-pkg/educational/adaptive-engine.d.ts +11 -11
  80. package/dist-pkg/educational/adaptive-engine.js +17 -17
  81. package/dist-pkg/educational/segment-bar.d.ts +12 -12
  82. package/dist-pkg/educational/segment-bar.js +9 -9
  83. package/dist-pkg/i18n/en.js +324 -223
  84. package/dist-pkg/i18n/es.js +323 -223
  85. package/dist-pkg/i18n/pt.js +348 -226
  86. package/dist-pkg/input/default-bindings.d.ts +37 -38
  87. package/dist-pkg/input/default-bindings.js +127 -144
  88. package/dist-pkg/input/devices.d.ts +7 -7
  89. package/dist-pkg/input/devices.js +17 -16
  90. package/dist-pkg/input/edges.d.ts +23 -32
  91. package/dist-pkg/input/edges.js +23 -28
  92. package/dist-pkg/input/empathy-filter.d.ts +22 -0
  93. package/dist-pkg/input/empathy-filter.js +34 -0
  94. package/dist-pkg/input/face-map.d.ts +96 -0
  95. package/dist-pkg/input/face-map.js +255 -0
  96. package/dist-pkg/input/face-signals.d.ts +40 -0
  97. package/dist-pkg/input/face-signals.js +48 -0
  98. package/dist-pkg/input/gamepad.d.ts +195 -145
  99. package/dist-pkg/input/gamepad.js +383 -556
  100. package/dist-pkg/input/gaze-cycle.d.ts +64 -0
  101. package/dist-pkg/input/gaze-cycle.js +133 -0
  102. package/dist-pkg/input/gaze-relative.d.ts +68 -0
  103. package/dist-pkg/input/gaze-relative.js +142 -0
  104. package/dist-pkg/input/hand-map.d.ts +28 -0
  105. package/dist-pkg/input/hand-map.js +142 -0
  106. package/dist-pkg/input/input-cooldown.d.ts +17 -0
  107. package/dist-pkg/input/input-cooldown.js +39 -0
  108. package/dist-pkg/input/key-default.d.ts +30 -0
  109. package/dist-pkg/input/key-default.js +60 -0
  110. package/dist-pkg/input/keyboard-runtime.d.ts +15 -21
  111. package/dist-pkg/input/keyboard-runtime.js +23 -27
  112. package/dist-pkg/input/keyboard.d.ts +68 -63
  113. package/dist-pkg/input/keyboard.js +83 -105
  114. package/dist-pkg/input/keydown.d.ts +123 -137
  115. package/dist-pkg/input/keydown.js +284 -289
  116. package/dist-pkg/input/latch-edge.d.ts +33 -21
  117. package/dist-pkg/input/latch-edge.js +34 -35
  118. package/dist-pkg/input/latch-scope.d.ts +51 -50
  119. package/dist-pkg/input/latch-scope.js +61 -82
  120. package/dist-pkg/input/latch-store.d.ts +26 -30
  121. package/dist-pkg/input/latch-store.js +42 -50
  122. package/dist-pkg/input/latch-sync.d.ts +29 -29
  123. package/dist-pkg/input/latch-sync.js +25 -25
  124. package/dist-pkg/input/latch.d.ts +14 -17
  125. package/dist-pkg/input/latch.js +29 -37
  126. package/dist-pkg/input/pad-defaults.d.ts +13 -16
  127. package/dist-pkg/input/pad-defaults.js +21 -28
  128. package/dist-pkg/input/pad-reading.d.ts +76 -0
  129. package/dist-pkg/input/pad-reading.js +169 -0
  130. package/dist-pkg/input/pad-wizard.d.ts +99 -0
  131. package/dist-pkg/input/pad-wizard.js +256 -0
  132. package/dist-pkg/input/pointer-space.d.ts +19 -20
  133. package/dist-pkg/input/pointer-space.js +27 -32
  134. package/dist-pkg/input/pointer.d.ts +39 -40
  135. package/dist-pkg/input/pointer.js +42 -45
  136. package/dist-pkg/input/state.d.ts +101 -89
  137. package/dist-pkg/input/state.js +54 -134
  138. package/dist-pkg/input/switch-scan.d.ts +59 -0
  139. package/dist-pkg/input/switch-scan.js +67 -0
  140. package/dist-pkg/input/synthetic-source.d.ts +42 -0
  141. package/dist-pkg/input/synthetic-source.js +57 -0
  142. package/dist-pkg/input/touch-bindings.d.ts +127 -116
  143. package/dist-pkg/input/touch-bindings.js +158 -173
  144. package/dist-pkg/input/touch.d.ts +147 -72
  145. package/dist-pkg/input/touch.js +362 -126
  146. package/dist-pkg/input/transport-in-use.d.ts +107 -0
  147. package/dist-pkg/input/transport-in-use.js +130 -0
  148. package/dist-pkg/input/transports.d.ts +90 -117
  149. package/dist-pkg/input/transports.js +104 -106
  150. package/dist-pkg/input/virtual-controller.d.ts +82 -0
  151. package/dist-pkg/input/virtual-controller.js +69 -0
  152. package/dist-pkg/input/vocabulary-migration.d.ts +50 -49
  153. package/dist-pkg/input/vocabulary-migration.js +80 -79
  154. package/dist-pkg/input/voice-map.d.ts +59 -0
  155. package/dist-pkg/input/voice-map.js +123 -0
  156. package/dist-pkg/platform/audio-ambient.d.ts +13 -2
  157. package/dist-pkg/platform/audio-ambient.js +14 -14
  158. package/dist-pkg/platform/audio-earcons.d.ts +20 -19
  159. package/dist-pkg/platform/audio-earcons.js +51 -42
  160. package/dist-pkg/platform/audio-jingles.js +9 -10
  161. package/dist-pkg/platform/audio-mixer.d.ts +14 -2
  162. package/dist-pkg/platform/audio-mixer.js +31 -39
  163. package/dist-pkg/platform/audio-sonar.d.ts +49 -152
  164. package/dist-pkg/platform/audio-sonar.js +124 -302
  165. package/dist-pkg/platform/audio.d.ts +62 -19
  166. package/dist-pkg/platform/audio.js +193 -168
  167. package/dist-pkg/platform/choose-by-voice.d.ts +70 -0
  168. package/dist-pkg/platform/choose-by-voice.js +157 -0
  169. package/dist-pkg/platform/flash-sampler.d.ts +19 -0
  170. package/dist-pkg/platform/flash-sampler.js +79 -0
  171. package/dist-pkg/platform/font-library.d.ts +77 -0
  172. package/dist-pkg/platform/font-library.js +98 -0
  173. package/dist-pkg/platform/font-library.json +721 -0
  174. package/dist-pkg/platform/heavy-catalogue.d.ts +77 -0
  175. package/dist-pkg/platform/heavy-catalogue.js +256 -0
  176. package/dist-pkg/platform/heavy-mirror.d.ts +20 -0
  177. package/dist-pkg/platform/heavy-mirror.js +73 -0
  178. package/dist-pkg/platform/heavy.d.ts +136 -0
  179. package/dist-pkg/platform/heavy.js +324 -0
  180. package/dist-pkg/platform/interruptible-speech.d.ts +23 -16
  181. package/dist-pkg/platform/interruptible-speech.js +77 -54
  182. package/dist-pkg/platform/kokoro-port.d.ts +27 -0
  183. package/dist-pkg/platform/kokoro-port.js +50 -0
  184. package/dist-pkg/platform/kokoro-runtime.d.ts +20 -0
  185. package/dist-pkg/platform/kokoro-runtime.js +52 -0
  186. package/dist-pkg/platform/kokoro.d.ts +70 -0
  187. package/dist-pkg/platform/kokoro.js +133 -0
  188. package/dist-pkg/platform/listener-scope.d.ts +12 -0
  189. package/dist-pkg/platform/listener-scope.js +90 -0
  190. package/dist-pkg/platform/locale-host.d.ts +14 -0
  191. package/dist-pkg/platform/locale-host.js +45 -0
  192. package/dist-pkg/platform/microphone.d.ts +51 -0
  193. package/dist-pkg/platform/microphone.js +118 -0
  194. package/dist-pkg/platform/onnx-runtime.d.ts +34 -0
  195. package/dist-pkg/platform/onnx-runtime.js +42 -0
  196. package/dist-pkg/platform/reading-in-worker.d.ts +64 -0
  197. package/dist-pkg/platform/reading-in-worker.js +125 -0
  198. package/dist-pkg/platform/reading-model.d.ts +94 -0
  199. package/dist-pkg/platform/reading-model.js +272 -0
  200. package/dist-pkg/platform/reading-runtime.d.ts +24 -0
  201. package/dist-pkg/platform/reading-runtime.js +164 -0
  202. package/dist-pkg/platform/reading-worker.d.ts +41 -0
  203. package/dist-pkg/platform/reading-worker.js +58 -0
  204. package/dist-pkg/platform/reading.d.ts +80 -0
  205. package/dist-pkg/platform/reading.js +136 -0
  206. package/dist-pkg/platform/speech-recognition.d.ts +60 -0
  207. package/dist-pkg/platform/speech-recognition.js +104 -0
  208. package/dist-pkg/platform/speech.d.ts +23 -1
  209. package/dist-pkg/platform/speech.js +29 -18
  210. package/dist-pkg/platform/storage-keys.d.ts +96 -0
  211. package/dist-pkg/platform/storage-keys.js +106 -0
  212. package/dist-pkg/platform/storage.d.ts +46 -111
  213. package/dist-pkg/platform/storage.js +104 -169
  214. package/dist-pkg/platform/tts.d.ts +85 -39
  215. package/dist-pkg/platform/tts.js +447 -127
  216. package/dist-pkg/platform/vision-loop.d.ts +23 -0
  217. package/dist-pkg/platform/vision-loop.js +56 -0
  218. package/dist-pkg/platform/vision.d.ts +132 -0
  219. package/dist-pkg/platform/vision.js +117 -0
  220. package/dist-pkg/platform/voice-listener.d.ts +40 -0
  221. package/dist-pkg/platform/voice-listener.js +88 -0
  222. package/dist-pkg/platform/voice-plan.d.ts +15 -98
  223. package/dist-pkg/platform/voice-plan.js +56 -143
  224. package/dist-pkg/platform/vosk-runtime.d.ts +96 -0
  225. package/dist-pkg/platform/vosk-runtime.js +151 -0
  226. package/dist-pkg/platform/vosk-vocabulary.d.ts +6 -0
  227. package/dist-pkg/platform/vosk-vocabulary.js +178 -0
  228. package/dist-pkg/render/canvas.d.ts +13 -7
  229. package/dist-pkg/render/canvas.js +13 -13
  230. package/dist-pkg/render/crt.d.ts +48 -12
  231. package/dist-pkg/render/crt.js +78 -80
  232. package/dist-pkg/render/cvd-matrices.d.ts +22 -18
  233. package/dist-pkg/render/cvd-matrices.js +37 -38
  234. package/dist-pkg/render/hc-role-data.d.ts +7 -7
  235. package/dist-pkg/render/hc-role-data.js +14 -14
  236. package/dist-pkg/render/high-contrast.d.ts +67 -56
  237. package/dist-pkg/render/high-contrast.js +226 -217
  238. package/dist-pkg/render/low-vision-drawing.d.ts +5 -0
  239. package/dist-pkg/render/low-vision-drawing.js +46 -0
  240. package/dist-pkg/render/lq-filter.d.ts +32 -20
  241. package/dist-pkg/render/lq-filter.js +59 -65
  242. package/dist-pkg/render/port.d.ts +86 -88
  243. package/dist-pkg/render/port.js +17 -23
  244. package/dist-pkg/render/screen-pipeline.d.ts +42 -42
  245. package/dist-pkg/render/screen-pipeline.js +80 -87
  246. package/dist-pkg/render/sprite-fx.d.ts +2 -1
  247. package/dist-pkg/render/sprite-fx.js +16 -14
  248. package/dist-pkg/render/viewports.d.ts +20 -15
  249. package/dist-pkg/render/viewports.js +83 -114
  250. package/dist-pkg/render/viz-axes-labels.d.ts +47 -0
  251. package/dist-pkg/render/viz-axes-labels.js +88 -0
  252. package/dist-pkg/render/viz-axes.d.ts +89 -86
  253. package/dist-pkg/render/viz-axes.js +128 -116
  254. package/dist-pkg/render/viz-modes.d.ts +28 -29
  255. package/dist-pkg/render/viz-modes.js +40 -45
  256. package/dist-pkg/render/viz-refusal.d.ts +32 -0
  257. package/dist-pkg/render/viz-refusal.js +57 -0
  258. package/dist-pkg/render/viz-setters.d.ts +104 -66
  259. package/dist-pkg/render/viz-setters.js +182 -191
  260. package/dist-pkg/ui/aac-sets.d.ts +45 -0
  261. package/dist-pkg/ui/aac-sets.js +41 -0
  262. package/dist-pkg/ui/audio-choices.d.ts +68 -0
  263. package/dist-pkg/ui/audio-choices.js +98 -0
  264. package/dist-pkg/ui/audio-rows-that-apply.d.ts +12 -0
  265. package/dist-pkg/ui/audio-rows-that-apply.js +43 -0
  266. package/dist-pkg/ui/camera-control.d.ts +53 -0
  267. package/dist-pkg/ui/camera-control.js +48 -0
  268. package/dist-pkg/ui/changed-mark.d.ts +9 -8
  269. package/dist-pkg/ui/changed-mark.js +22 -53
  270. package/dist-pkg/ui/control-choices.d.ts +37 -0
  271. package/dist-pkg/ui/control-choices.js +84 -0
  272. package/dist-pkg/ui/debug-panel.d.ts +59 -43
  273. package/dist-pkg/ui/debug-panel.js +68 -69
  274. package/dist-pkg/ui/declared-words.d.ts +21 -0
  275. package/dist-pkg/ui/declared-words.js +66 -0
  276. package/dist-pkg/ui/dom.d.ts +10 -14
  277. package/dist-pkg/ui/dom.js +10 -47
  278. package/dist-pkg/ui/drawing-problems.d.ts +25 -0
  279. package/dist-pkg/ui/drawing-problems.js +94 -0
  280. package/dist-pkg/ui/eye-control.d.ts +36 -0
  281. package/dist-pkg/ui/eye-control.js +178 -0
  282. package/dist-pkg/ui/face-control.d.ts +25 -0
  283. package/dist-pkg/ui/face-control.js +183 -0
  284. package/dist-pkg/ui/focus-trap.d.ts +39 -49
  285. package/dist-pkg/ui/focus-trap.js +56 -65
  286. package/dist-pkg/ui/fonts.d.ts +157 -68
  287. package/dist-pkg/ui/fonts.js +305 -115
  288. package/dist-pkg/ui/footer-scroll-driver.d.ts +14 -0
  289. package/dist-pkg/ui/footer-scroll-driver.js +201 -0
  290. package/dist-pkg/ui/footer-scroll.d.ts +38 -0
  291. package/dist-pkg/ui/footer-scroll.js +55 -0
  292. package/dist-pkg/ui/game-options.d.ts +47 -0
  293. package/dist-pkg/ui/game-options.js +158 -0
  294. package/dist-pkg/ui/gaze-overlay.d.ts +64 -0
  295. package/dist-pkg/ui/gaze-overlay.js +165 -0
  296. package/dist-pkg/ui/hand-control.d.ts +25 -0
  297. package/dist-pkg/ui/hand-control.js +143 -0
  298. package/dist-pkg/ui/help-panel.d.ts +87 -0
  299. package/dist-pkg/ui/help-panel.js +242 -0
  300. package/dist-pkg/ui/hud-bands.d.ts +62 -0
  301. package/dist-pkg/ui/hud-bands.js +146 -0
  302. package/dist-pkg/ui/hud-row.d.ts +34 -0
  303. package/dist-pkg/ui/hud-row.js +80 -0
  304. package/dist-pkg/ui/hud.d.ts +73 -73
  305. package/dist-pkg/ui/hud.js +94 -98
  306. package/dist-pkg/ui/item-announcement.d.ts +13 -12
  307. package/dist-pkg/ui/item-announcement.js +25 -26
  308. package/dist-pkg/ui/layout.d.ts +103 -28
  309. package/dist-pkg/ui/layout.js +117 -99
  310. package/dist-pkg/ui/libras-avatar-clip.d.ts +30 -0
  311. package/dist-pkg/ui/libras-avatar-clip.js +86 -0
  312. package/dist-pkg/ui/libras-avatar-load.d.ts +52 -0
  313. package/dist-pkg/ui/libras-avatar-load.js +134 -0
  314. package/dist-pkg/ui/libras-avatar-plan.d.ts +102 -0
  315. package/dist-pkg/ui/libras-avatar-plan.js +211 -0
  316. package/dist-pkg/ui/libras-avatar-player.d.ts +28 -0
  317. package/dist-pkg/ui/libras-avatar-player.js +226 -0
  318. package/dist-pkg/ui/libras-avatar-stage.d.ts +37 -0
  319. package/dist-pkg/ui/libras-avatar-stage.js +208 -0
  320. package/dist-pkg/ui/libras-glosses.d.ts +28 -0
  321. package/dist-pkg/ui/libras-glosses.js +161 -0
  322. package/dist-pkg/ui/locale-flags.d.ts +16 -0
  323. package/dist-pkg/ui/locale-flags.js +30 -0
  324. package/dist-pkg/ui/loop-crash.d.ts +17 -17
  325. package/dist-pkg/ui/loop-crash.js +33 -62
  326. package/dist-pkg/ui/menu-intent.d.ts +61 -0
  327. package/dist-pkg/ui/menu-intent.js +88 -0
  328. package/dist-pkg/ui/menu-items.d.ts +20 -0
  329. package/dist-pkg/ui/menu-items.js +51 -0
  330. package/dist-pkg/ui/menu-nav.d.ts +98 -92
  331. package/dist-pkg/ui/menu-nav.js +398 -286
  332. package/dist-pkg/ui/mobility-choices.d.ts +26 -0
  333. package/dist-pkg/ui/mobility-choices.js +41 -0
  334. package/dist-pkg/ui/motion-choices.d.ts +31 -0
  335. package/dist-pkg/ui/motion-choices.js +68 -0
  336. package/dist-pkg/ui/motion-scene.d.ts +24 -19
  337. package/dist-pkg/ui/motion-scene.js +29 -61
  338. package/dist-pkg/ui/mount-panel.d.ts +81 -0
  339. package/dist-pkg/ui/mount-panel.js +61 -0
  340. package/dist-pkg/ui/panel-shell.d.ts +65 -31
  341. package/dist-pkg/ui/panel-shell.js +92 -66
  342. package/dist-pkg/ui/panel-widgets.d.ts +150 -0
  343. package/dist-pkg/ui/panel-widgets.js +294 -0
  344. package/dist-pkg/ui/pause-buttons.d.ts +35 -0
  345. package/dist-pkg/ui/pause-buttons.js +66 -0
  346. package/dist-pkg/ui/pause-icons.d.ts +261 -347
  347. package/dist-pkg/ui/pause-icons.js +671 -601
  348. package/dist-pkg/ui/pause-markup.d.ts +86 -0
  349. package/dist-pkg/ui/pause-markup.js +125 -0
  350. package/dist-pkg/ui/reach-notice.d.ts +22 -23
  351. package/dist-pkg/ui/reach-notice.js +55 -58
  352. package/dist-pkg/ui/scan-overlay.d.ts +23 -0
  353. package/dist-pkg/ui/scan-overlay.js +50 -0
  354. package/dist-pkg/ui/screen-text.d.ts +25 -0
  355. package/dist-pkg/ui/screen-text.js +152 -0
  356. package/dist-pkg/ui/session-clock.d.ts +32 -0
  357. package/dist-pkg/ui/session-clock.js +67 -0
  358. package/dist-pkg/ui/settings-aac.d.ts +70 -0
  359. package/dist-pkg/ui/settings-aac.js +208 -0
  360. package/dist-pkg/ui/settings-audio.d.ts +97 -114
  361. package/dist-pkg/ui/settings-audio.js +493 -425
  362. package/dist-pkg/ui/settings-controls.d.ts +53 -96
  363. package/dist-pkg/ui/settings-controls.js +143 -215
  364. package/dist-pkg/ui/settings-empathy.d.ts +39 -30
  365. package/dist-pkg/ui/settings-empathy.js +53 -60
  366. package/dist-pkg/ui/settings-mobility.d.ts +192 -0
  367. package/dist-pkg/ui/settings-mobility.js +332 -0
  368. package/dist-pkg/ui/settings-motion.d.ts +91 -70
  369. package/dist-pkg/ui/settings-motion.js +287 -198
  370. package/dist-pkg/ui/settings-panel.d.ts +58 -45
  371. package/dist-pkg/ui/settings-panel.js +189 -94
  372. package/dist-pkg/ui/settings-typo.d.ts +29 -78
  373. package/dist-pkg/ui/settings-typo.js +158 -129
  374. package/dist-pkg/ui/settings-visual.d.ts +29 -87
  375. package/dist-pkg/ui/settings-visual.js +240 -193
  376. package/dist-pkg/ui/shell.d.ts +112 -135
  377. package/dist-pkg/ui/shell.js +148 -217
  378. package/dist-pkg/ui/simulation-list.d.ts +27 -0
  379. package/dist-pkg/ui/simulation-list.js +76 -0
  380. package/dist-pkg/ui/simulation-over-the-world.d.ts +23 -0
  381. package/dist-pkg/ui/simulation-over-the-world.js +131 -0
  382. package/dist-pkg/ui/simulation-refusal.d.ts +5 -31
  383. package/dist-pkg/ui/simulation-refusal.js +8 -56
  384. package/dist-pkg/ui/switchable-control.d.ts +11 -0
  385. package/dist-pkg/ui/switchable-control.js +19 -0
  386. package/dist-pkg/ui/title.d.ts +3 -12
  387. package/dist-pkg/ui/title.js +49 -18
  388. package/dist-pkg/ui/top-band.d.ts +26 -0
  389. package/dist-pkg/ui/top-band.js +96 -0
  390. package/dist-pkg/ui/typo-choices.d.ts +71 -0
  391. package/dist-pkg/ui/typo-choices.js +85 -0
  392. package/dist-pkg/ui/visual-axes-panel.d.ts +11 -47
  393. package/dist-pkg/ui/visual-axes-panel.js +12 -94
  394. package/dist-pkg/ui/visual-choices.d.ts +77 -0
  395. package/dist-pkg/ui/visual-choices.js +107 -0
  396. package/dist-pkg/ui/vlibras.d.ts +77 -10
  397. package/dist-pkg/ui/vlibras.js +130 -81
  398. package/dist-pkg/ui/voice-control.d.ts +68 -0
  399. package/dist-pkg/ui/voice-control.js +276 -0
  400. package/dist-pkg/ui/voice-settings.d.ts +95 -0
  401. package/dist-pkg/ui/voice-settings.js +352 -0
  402. package/dist-pkg/ui/where-the-child-is.d.ts +31 -0
  403. package/dist-pkg/ui/where-the-child-is.js +70 -0
  404. package/docs/CREDITS.md +300 -44
  405. package/docs/LICENSES.md +189 -145
  406. package/package.json +51 -27
  407. package/scripts/check-cartridge.mjs +78 -0
  408. package/scripts/game-build.d.mts +25 -0
  409. package/scripts/game-build.mjs +144 -0
  410. package/scripts/heavy-into-the-delivery.mjs +388 -0
  411. package/scripts/libras-avatar.json +680 -0
  412. package/scripts/libras-avatar.mjs +246 -0
  413. package/scripts/libras-glosses/gloss.py +95 -0
  414. package/scripts/libras-glosses/pyproject.toml +32 -0
  415. package/scripts/libras-glosses/uv.lock +781 -0
  416. package/scripts/libras-glosses.mjs +351 -0
  417. package/scripts/licences/Apache-2.0.txt +176 -0
  418. package/scripts/licences/GPL-3.0.txt +674 -0
  419. package/scripts/licences/MIT.txt +17 -0
  420. package/scripts/licences/OFL-1.1.txt +86 -0
  421. package/scripts/licences/UFL-1.0.txt +96 -0
  422. package/scripts/licences/fonts.mjs +389 -0
  423. package/scripts/licences/third-party.mjs +301 -0
  424. package/scripts/licences/vosk-browser.NOTICE.txt +268 -0
  425. package/app/public/vendor/fonts/comicneue-400.woff2 +0 -0
  426. package/app/public/vendor/fonts/comicneue-700.woff2 +0 -0
  427. package/app/public/vendor/fonts/fondamento-400-ext.woff2 +0 -0
  428. package/app/public/vendor/fonts/fondamento-400.woff2 +0 -0
  429. package/app/public/vendor/fonts/inter-400.woff2 +0 -0
  430. package/app/public/vendor/fonts/inter-700.woff2 +0 -0
  431. package/app/public/vendor/fonts/lato-400.woff2 +0 -0
  432. package/app/public/vendor/fonts/lato-700.woff2 +0 -0
  433. package/app/public/vendor/fonts/literata-400.woff2 +0 -0
  434. package/app/public/vendor/fonts/literata-700.woff2 +0 -0
  435. package/app/public/vendor/fonts/newsreader-400.woff2 +0 -0
  436. package/app/public/vendor/fonts/newsreader-700.woff2 +0 -0
  437. package/app/public/vendor/fonts/opendyslexic-400.woff2 +0 -0
  438. package/app/public/vendor/fonts/opensans-400.woff2 +0 -0
  439. package/app/public/vendor/fonts/opensans-700.woff2 +0 -0
  440. package/app/public/vendor/fonts/pinyon-400.woff2 +0 -0
  441. package/app/public/vendor/fonts/pressstart-400.woff2 +0 -0
  442. package/app/public/vendor/fonts/quattro-400.woff2 +0 -0
  443. package/app/public/vendor/fonts/quattro-700.woff2 +0 -0
  444. package/app/public/vendor/fonts/sourcesans-400.woff2 +0 -0
  445. package/app/public/vendor/fonts/sourcesans-700.woff2 +0 -0
  446. package/app/public/vendor/fonts/sourceserif-400.woff2 +0 -0
  447. package/app/public/vendor/fonts/sourceserif-700.woff2 +0 -0
  448. package/app/public/vendor/fonts/ufmag-400.woff2 +0 -0
  449. package/dist-pkg/core/anel.d.ts +0 -17
  450. package/dist-pkg/core/anel.js +0 -31
  451. package/dist-pkg/core/collision.d.ts +0 -21
  452. package/dist-pkg/core/collision.js +0 -62
  453. package/dist-pkg/core/layers.d.ts +0 -63
  454. package/dist-pkg/core/layers.js +0 -95
  455. package/dist-pkg/core/letter-grid.d.ts +0 -55
  456. package/dist-pkg/core/letter-grid.js +0 -116
  457. package/dist-pkg/core/password.d.ts +0 -34
  458. package/dist-pkg/core/password.js +0 -205
  459. package/dist-pkg/core/rotulo-acessivel.d.ts +0 -17
  460. package/dist-pkg/core/rotulo-acessivel.js +0 -41
  461. package/dist-pkg/core/run-state.d.ts +0 -115
  462. package/dist-pkg/core/run-state.js +0 -40
  463. package/dist-pkg/core/tiles.d.ts +0 -12
  464. package/dist-pkg/core/tiles.js +0 -61
  465. package/dist-pkg/core/world.d.ts +0 -4
  466. package/dist-pkg/core/world.js +0 -58
  467. package/dist-pkg/input/origem-sintetica.d.ts +0 -44
  468. package/dist-pkg/input/origem-sintetica.js +0 -60
  469. package/dist-pkg/input/transporte-em-uso.d.ts +0 -101
  470. package/dist-pkg/input/transporte-em-uso.js +0 -130
  471. package/dist-pkg/platform/audio-nav.d.ts +0 -51
  472. package/dist-pkg/platform/audio-nav.js +0 -99
  473. package/dist-pkg/platform/guide-intensity.d.ts +0 -40
  474. package/dist-pkg/platform/guide-intensity.js +0 -73
  475. package/dist-pkg/platform/pesados-catalogo.d.ts +0 -14
  476. package/dist-pkg/platform/pesados-catalogo.js +0 -177
  477. package/dist-pkg/platform/pesados.d.ts +0 -31
  478. package/dist-pkg/platform/pesados.js +0 -75
  479. package/dist-pkg/platform/vozes-prontas.d.ts +0 -28
  480. package/dist-pkg/platform/vozes-prontas.js +0 -61
  481. package/dist-pkg/render/camera.d.ts +0 -58
  482. package/dist-pkg/render/camera.js +0 -105
  483. package/dist-pkg/render/cenario-data.d.ts +0 -127
  484. package/dist-pkg/render/cenario-data.js +0 -145
  485. package/dist-pkg/render/city-tex.d.ts +0 -63
  486. package/dist-pkg/render/city-tex.js +0 -228
  487. package/dist-pkg/render/city-tiles.d.ts +0 -11
  488. package/dist-pkg/render/city-tiles.js +0 -140
  489. package/dist-pkg/render/draw.d.ts +0 -149
  490. package/dist-pkg/render/draw.js +0 -231
  491. package/dist-pkg/render/fx.d.ts +0 -63
  492. package/dist-pkg/render/fx.js +0 -123
  493. package/dist-pkg/render/minimap.d.ts +0 -11
  494. package/dist-pkg/render/minimap.js +0 -94
  495. package/dist-pkg/render/parallax.d.ts +0 -85
  496. package/dist-pkg/render/parallax.js +0 -158
  497. package/dist-pkg/render/player-anim.d.ts +0 -71
  498. package/dist-pkg/render/player-anim.js +0 -152
  499. package/dist-pkg/render/recycling-tex.d.ts +0 -48
  500. package/dist-pkg/render/recycling-tex.js +0 -164
  501. package/dist-pkg/render/scene-city.d.ts +0 -77
  502. package/dist-pkg/render/scene-city.js +0 -181
  503. package/dist-pkg/render/scene-parallax.d.ts +0 -72
  504. package/dist-pkg/render/scene-parallax.js +0 -426
  505. package/dist-pkg/render/scene-sky.d.ts +0 -126
  506. package/dist-pkg/render/scene-sky.js +0 -295
  507. package/dist-pkg/render/set-cenario.d.ts +0 -54
  508. package/dist-pkg/render/set-cenario.js +0 -103
  509. package/dist-pkg/render/textures.d.ts +0 -75
  510. package/dist-pkg/render/textures.js +0 -240
  511. package/dist-pkg/render/title-scene.d.ts +0 -46
  512. package/dist-pkg/render/title-scene.js +0 -75
  513. package/dist-pkg/render/weather.d.ts +0 -96
  514. package/dist-pkg/render/weather.js +0 -171
  515. package/dist-pkg/render/wheelchair-sprites.d.ts +0 -32
  516. package/dist-pkg/render/wheelchair-sprites.js +0 -69
  517. package/dist-pkg/render/world-tex.d.ts +0 -18
  518. package/dist-pkg/render/world-tex.js +0 -87
  519. package/dist-pkg/ui/activities-menu.d.ts +0 -258
  520. package/dist-pkg/ui/activities-menu.js +0 -648
  521. package/dist-pkg/ui/caa-sets.d.ts +0 -39
  522. package/dist-pkg/ui/caa-sets.js +0 -65
  523. package/dist-pkg/ui/latch-refusal.d.ts +0 -32
  524. package/dist-pkg/ui/latch-refusal.js +0 -60
  525. package/dist-pkg/ui/map-hub.d.ts +0 -56
  526. package/dist-pkg/ui/map-hub.js +0 -138
  527. package/dist-pkg/ui/settings-caa.d.ts +0 -45
  528. package/dist-pkg/ui/settings-caa.js +0 -137
  529. package/dist-pkg/ui/settings-motor.d.ts +0 -175
  530. package/dist-pkg/ui/settings-motor.js +0 -342
  531. package/dist-pkg/ui/webcam.d.ts +0 -7
  532. package/dist-pkg/ui/webcam.js +0 -93
@@ -0,0 +1,76 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ import type { PadTable } from './pad-defaults.js';
3
+ export interface PadButtonLike {
4
+ pressed: boolean;
5
+ }
6
+ export interface PadLike {
7
+ id: string;
8
+ index: number;
9
+ mapping: string;
10
+ buttons: readonly (PadButtonLike | null | undefined)[];
11
+ axes: readonly number[];
12
+ }
13
+ /** Adapter for `navigator.getGamepads()` — the DI point that lets tests feed a fake pad without a browser. */
14
+ export type GetGamepads = () => readonly (PadLike | null | undefined)[] | null | undefined;
15
+ /** One binding captured by the wizard: a digital button, a signed analog threshold, or an exact hat/POV value.
16
+ * Loosely-shaped (all fields optional) rather than a strict union: bindings round-trip through JSON in
17
+ * localStorage, so `bindActive` must stay defensive against malformed/partial saved data, same as the original. */
18
+ export interface PadBinding {
19
+ b?: number;
20
+ ax?: number;
21
+ s?: number;
22
+ av?: number;
23
+ v?: number;
24
+ }
25
+ /** action -> binding, keyed by the 9 wizard steps (left/right/up/down/jump/run/swap/especial/start), PLUS the
26
+ * `_skip: true` sentinel meaning "user cancelled the wizard for this model — use the default mapping, don't
27
+ * ask again this session" (not persisted). A flat index signature (not `Partial<Record<..>> & {_skip}`) so the
28
+ * boolean `_skip` and the PadBinding action values can coexist under TS's index-signature rule; `bindingAt`
29
+ * narrows a lookup back down to a PadBinding for `bindActive`. */
30
+ export type PadMap = Record<string, PadBinding | boolean | undefined>;
31
+ export type ActionKey = 'left' | 'right' | 'up' | 'down' | 'action2' | 'action1' | 'action4' | 'action3';
32
+ export interface Dirs {
33
+ left: boolean;
34
+ right: boolean;
35
+ up: boolean;
36
+ down: boolean;
37
+ }
38
+ export interface PadActions extends Dirs {
39
+ [key: string]: boolean;
40
+ action2: boolean;
41
+ action1: boolean;
42
+ action4: boolean;
43
+ action3: boolean;
44
+ _start: boolean;
45
+ _pause: boolean;
46
+ }
47
+ /** Directions from the STANDARD SOURCES: stick 0/1 (dead zone PAD_DEAD), D-pad 12-15, and the "hat" on axes >=6
48
+ * (DirectInput's POV). A pad with both directionals mapped keeps both alive (a thumb on the stick does not kill the D-pad). */
49
+ export declare function stdDirs(gp: PadLike): Dirs;
50
+ /** Is binding `bd` active now on this gamepad? Digital = pressed; analogue ({ax,s}) = threshold by sign (half the
51
+ * travel); hat ({av,v}) = the step's exact value (±0.13 — the 8 steps are ~0.286 apart). */
52
+ export declare function bindActive(gp: PadLike, bd: PadBinding | null | undefined): boolean;
53
+ /** This frame's actions for this gamepad. `custom` = the map the wizard saved for this `gp.id` (null/`_skip` = the
54
+ * declared table). ⚠️ The table decides only in the second branch, which is right: a game's default does not override
55
+ * a choice the child recorded. */
56
+ export declare function padActions(gp: PadLike, custom: PadMap | null, table?: PadTable): PadActions;
57
+ /**
58
+ * ONE BUTTON AT A TIME, APPLIED TO THE PAD — the missing half of the motor empathy simulation (issue #120).
59
+ *
60
+ * ⚠️ IT USED TO HOLD ONLY ON THE KEYBOARD. The keyboard transport releases every other game key when a new one arrives
61
+ * with the simulation on; the pad polling had no equivalent. A child who turned it on with a pad in hand **was not in
62
+ * it** — no error, no warning, no symptom, because the settings kept saying it was on.
63
+ *
64
+ * ⚠️ THE DIRECTIONS COUNT, which is what makes the rule faithful to the keyboard: there, the game keys include the four
65
+ * directions — walking and jumping do not coexist. A filter that spared the directions would be more comfortable and
66
+ * would be simulating another disability.
67
+ *
68
+ * ⚠️ AND WHO SURVIVES DIFFERS FROM THE KEYBOARD, BY NECESSITY. On the keyboard the new arrival wins, because there IS an
69
+ * arrival: the event says which. A pad is read by POLLING — what arrives is a snapshot, without order. So the one that
70
+ * already held stays, and only when it lets go does the next take over. That keeps the run button from being cut because
71
+ * the thumb brushed another, and it is the reading of "holding" ADR-0077 gives.
72
+ *
73
+ * The POLICY is the keyboard's; the IMPLEMENTATION cannot be shared today because the state's shapes differ — there a
74
+ * `Set` of key codes, here a snapshot of booleans by position.
75
+ */
76
+ export declare function oneButtonAtOnce(wasDown: Readonly<Record<string, boolean | undefined>>, reading: PadActions, on: boolean): PadActions;
@@ -0,0 +1,169 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // input/pad-reading — WHAT A GAMEPAD IS DOING RIGHT NOW, and nothing else.
3
+ //
4
+ // 🔴 WHY IT IS ITS OWN MODULE. `input/gamepad` carried two jobs under one name: this one — buttons and axes in,
5
+ // positions out, no host, no state, no clock — and the DI runtime that polls the pads every frame and decides
6
+ // where the reading goes (title, pause, seat, play, wizard). The second is the engine's wiring and can only be
7
+ // read with a ctx in hand; the first is arithmetic over a snapshot, and every one of its rules is a promise to a
8
+ // child whose controller the browser does not recognise: a D-pad on a DirectInput POV hat, an analogue axis used
9
+ // as a button, a map the child recorded in the wizard.
10
+ //
11
+ // 📌 Nothing here reads or writes state. The frame-to-frame memory (`padCur`, `padPrevAct`) belongs to whoever
12
+ // polls — one root's `input/state` (ADR-0232 D4).
13
+ import { GAMEPAD_STANDARD } from './default-bindings.js';
14
+ /**
15
+ * THE STICK'S DEAD ZONE = the first HALF of its travel (ergonomics — the Dev's decision). A constant, and so it lives
16
+ * here with the reading that uses it, not in `input/state`, which is a factory now (ADR-0232 D4). Internal: nothing
17
+ * outside this reading asks for it, in this repository or in the games.
18
+ */
19
+ const PAD_DEAD = 0.5;
20
+ function bindingAt(map, key) {
21
+ const v = map[key];
22
+ return typeof v === 'object' && v !== null ? v : undefined;
23
+ }
24
+ // [axisValue, up, down, left, right] — the 8 steps of a "POV hat" D-pad (DirectInput's high axis), resting at ~1.286.
25
+ const HAT_STEPS = [
26
+ [-1, 1, 0, 0, 0], [-0.7143, 1, 0, 0, 1], [-0.4286, 0, 0, 0, 1], [-0.1429, 0, 1, 0, 1],
27
+ [0.1429, 0, 1, 0, 0], [0.4286, 0, 1, 1, 0], [0.7143, 0, 0, 1, 0], [1, 1, 0, 1, 0],
28
+ ];
29
+ /** The hat step this axis value IS, or `null` — the eight are ~0.286 apart, and the tolerance is ±0.09. */
30
+ function hatStepAt(v) {
31
+ if (typeof v !== 'number' || Math.abs(v) > 1.001)
32
+ return null; // the hat's rest, outside the steps
33
+ return HAT_STEPS.find(([hv]) => Math.abs(v - hv) <= 0.09) ?? null;
34
+ }
35
+ /** A hat step LIGHTS directions and never puts them out — which keeps the stick and the D-pad alive at once. */
36
+ function applyHatStep(d, step) {
37
+ const [, up, down, left, right] = step;
38
+ if (up)
39
+ d.up = true;
40
+ if (down)
41
+ d.down = true;
42
+ if (left)
43
+ d.left = true;
44
+ if (right)
45
+ d.right = true;
46
+ }
47
+ /** Directions from the STANDARD SOURCES: stick 0/1 (dead zone PAD_DEAD), D-pad 12-15, and the "hat" on axes >=6
48
+ * (DirectInput's POV). A pad with both directionals mapped keeps both alive (a thumb on the stick does not kill the D-pad). */
49
+ export function stdDirs(gp) {
50
+ const b = (i) => !!(gp.buttons[i] && gp.buttons[i].pressed);
51
+ const ax = (i) => gp.axes[i] || 0;
52
+ const d = { left: ax(0) < -PAD_DEAD || b(14), right: ax(0) > PAD_DEAD || b(15), up: ax(1) < -PAD_DEAD || b(12), down: ax(1) > PAD_DEAD || b(13) };
53
+ for (let i = 6; i < gp.axes.length; i++) {
54
+ const step = hatStepAt(gp.axes[i]);
55
+ if (step)
56
+ applyHatStep(d, step);
57
+ }
58
+ return d;
59
+ }
60
+ /** Is binding `bd` active now on this gamepad? Digital = pressed; analogue ({ax,s}) = threshold by sign (half the
61
+ * travel); hat ({av,v}) = the step's exact value (±0.13 — the 8 steps are ~0.286 apart). */
62
+ export function bindActive(gp, bd) {
63
+ if (!bd)
64
+ return false;
65
+ if (bd.b != null)
66
+ return !!(gp.buttons[bd.b] && gp.buttons[bd.b].pressed);
67
+ if (bd.ax != null)
68
+ return ((gp.axes[bd.ax] || 0) * (bd.s ?? 0)) > 0.5;
69
+ if (bd.av != null)
70
+ return Math.abs((gp.axes[bd.av] || 0) - (bd.v ?? 0)) <= 0.13;
71
+ return false;
72
+ }
73
+ /** The snapshot when the CHILD recorded a map in the wizard: their directions fall back to the standard ones when the
74
+ * binding is not active, so the D-pad and the stick stay alive beside what they chose.
75
+ * ⚠️ `start` and `select` are read AS POSITIONS, as the table's reading does: the wizard asks them and stores them, and a
76
+ * map read without `select` left a mapped pad with no door to the menus — not even as the one-button scan's switch
77
+ * (ADR-0155, ADR-0218 §4). */
78
+ function actionsFromTheSavedMap(gp, custom) {
79
+ const A = (k) => bindActive(gp, bindingAt(custom, k));
80
+ const sd = stdDirs(gp);
81
+ return {
82
+ left: A('left') || sd.left, right: A('right') || sd.right, up: A('up') || sd.up, down: A('down') || sd.down,
83
+ action2: A('action2'), action1: A('action1'), action4: A('action4'), action3: A('action3'),
84
+ start: A('start'), select: A('select'),
85
+ _start: A('action2') || A('start'), _pause: A('start'),
86
+ };
87
+ }
88
+ /**
89
+ * The snapshot by the declared TABLE — the engine's factory with this game's default on top (ADR-0115), resolved in
90
+ * `input/pad-defaults`.
91
+ *
92
+ * ⚠️ THE INDICES COME FROM THE TABLE, not from literals here. While they were literals, this line and
93
+ * `input/default-bindings` DISAGREED and nothing noticed — the defect the touch gate caught: two tables agreeing with
94
+ * each other prove nothing about a third that reads them.
95
+ *
96
+ * ⚠️ AND THE DISAGREEMENT WAS REAL: here `action1` was `b(2) || b(5) || b(7)` — X, R1 and R2 all running — while the table
97
+ * declares R1 as `rightShoulder` and R2 as `rightTrigger`. ADR-0086 recorded it as the asterisk of its "zero movement": no
98
+ * VERB changes button, but that one loses two of its three.
99
+ */
100
+ function actionsFromTheTable(gp, table) {
101
+ const b = (i) => !!(gp.buttons[i] && gp.buttons[i].pressed);
102
+ const sd = stdDirs(gp);
103
+ const at = (a) => { const i = table[a]; return typeof i === 'number' ? b(i) : false; };
104
+ return {
105
+ left: sd.left, right: sd.right, up: sd.up, down: sd.down,
106
+ action1: at('action1'), action2: at('action2'), action3: at('action3'), action4: at('action4'),
107
+ leftShoulder: at('leftShoulder'), leftTrigger: at('leftTrigger'),
108
+ rightShoulder: at('rightShoulder'), rightTrigger: at('rightTrigger'),
109
+ // ⚠️ `start` AS A POSITION, not only as the derived ones below. It was missing, and the table's gate found it:
110
+ // whoever wanted to know "is START pressed?" had to read `_start`, which starts with an underscore and means
111
+ // something else (closes a dialog, and accepts action 2 too).
112
+ start: at('start'), select: at('select'),
113
+ // `_start` and `_pause` are DERIVED, not positions: "close dialog" accepts action 2 or START, "pause" only START.
114
+ // They are written here because they describe what the root does with two positions, not a third one.
115
+ _start: at('action2') || at('start'), _pause: at('start'),
116
+ };
117
+ }
118
+ /** This frame's actions for this gamepad. `custom` = the map the wizard saved for this `gp.id` (null/`_skip` = the
119
+ * declared table). ⚠️ The table decides only in the second branch, which is right: a game's default does not override
120
+ * a choice the child recorded. */
121
+ export function padActions(gp, custom, table = GAMEPAD_STANDARD) {
122
+ return custom && !custom._skip ? actionsFromTheSavedMap(gp, custom) : actionsFromTheTable(gp, table);
123
+ }
124
+ /**
125
+ * THE POSITIONS THE ONE-BUTTON SIMULATION DOES NOT CUT. Pausing is the WAY OUT, not a move.
126
+ *
127
+ * ⚠️ Cutting START would trap the child inside the match — the reasoning that puts "the way out first" in ADR-0044 and
128
+ * made the focus trap exist in ADR-0090. An accommodation that locks is not an accommodation. The derived ones (`_start`,
129
+ * `_pause`) pass too: they describe what the root does with these two positions, not a third.
130
+ */
131
+ const OUTSIDE_CUT = new Set(['start', 'select', '_start', '_pause']);
132
+ /**
133
+ * ONE BUTTON AT A TIME, APPLIED TO THE PAD — the missing half of the motor empathy simulation (issue #120).
134
+ *
135
+ * ⚠️ IT USED TO HOLD ONLY ON THE KEYBOARD. The keyboard transport releases every other game key when a new one arrives
136
+ * with the simulation on; the pad polling had no equivalent. A child who turned it on with a pad in hand **was not in
137
+ * it** — no error, no warning, no symptom, because the settings kept saying it was on.
138
+ *
139
+ * ⚠️ THE DIRECTIONS COUNT, which is what makes the rule faithful to the keyboard: there, the game keys include the four
140
+ * directions — walking and jumping do not coexist. A filter that spared the directions would be more comfortable and
141
+ * would be simulating another disability.
142
+ *
143
+ * ⚠️ AND WHO SURVIVES DIFFERS FROM THE KEYBOARD, BY NECESSITY. On the keyboard the new arrival wins, because there IS an
144
+ * arrival: the event says which. A pad is read by POLLING — what arrives is a snapshot, without order. So the one that
145
+ * already held stays, and only when it lets go does the next take over. That keeps the run button from being cut because
146
+ * the thumb brushed another, and it is the reading of "holding" ADR-0077 gives.
147
+ *
148
+ * The POLICY is the keyboard's; the IMPLEMENTATION cannot be shared today because the state's shapes differ — there a
149
+ * `Set` of key codes, here a snapshot of booleans by position.
150
+ */
151
+ export function oneButtonAtOnce(
152
+ // ⚠️ THE PREVIOUS FRAME IS TYPED BY WHAT THIS FUNCTION READS, not by `PadActions`: the loop's previous-frame state is
153
+ // looser, and demanding the full shape would force the caller into a mould that does not describe what happens here —
154
+ // the only question is "was this key down?".
155
+ wasDown, reading, on) {
156
+ if (!on)
157
+ return reading;
158
+ const cuttable = Object.keys(reading).filter((k) => !OUTSIDE_CUT.has(k));
159
+ const active = cuttable.filter((k) => reading[k] === true);
160
+ if (active.length <= 1)
161
+ return reading;
162
+ // The one that already held has priority; with none, the snapshot's first takes over.
163
+ const kept = active.find((k) => wasDown[k] === true) ?? active[0];
164
+ const onlyOne = { ...reading };
165
+ for (const k of active)
166
+ if (k !== kept)
167
+ onlyOne[k] = false;
168
+ return onlyOne;
169
+ }
@@ -0,0 +1,99 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ import type { Translate } from '../core/i18n.js';
3
+ import type { Store } from '../platform/storage.js';
4
+ interface PadLike {
5
+ readonly id: string;
6
+ readonly index: number;
7
+ readonly buttons: readonly ({
8
+ pressed: boolean;
9
+ } | null | undefined)[];
10
+ readonly axes: readonly number[];
11
+ }
12
+ type GetGamepads = () => readonly (PadLike | null | undefined)[] | null | undefined;
13
+ interface PadBinding {
14
+ b?: number;
15
+ ax?: number;
16
+ s?: number;
17
+ av?: number;
18
+ v?: number;
19
+ }
20
+ type PadMap = Record<string, PadBinding | boolean | undefined>;
21
+ /** This pad's resting pose: which buttons were already down and where each axis rested. */
22
+ interface Baseline {
23
+ b: boolean[];
24
+ a: number[];
25
+ }
26
+ interface WizState {
27
+ gi: number;
28
+ id: string;
29
+ step: number;
30
+ base: Baseline | null;
31
+ map: PadMap;
32
+ release: boolean;
33
+ baseWait: boolean;
34
+ axTrack: {
35
+ i: number;
36
+ v: number;
37
+ last: number;
38
+ changes: number;
39
+ ticks: number;
40
+ } | null;
41
+ timer: ReturnType<typeof setInterval> | null;
42
+ }
43
+ export declare const PADWIZ_ORDER: readonly string[];
44
+ /** What this module reads and writes the maps through: the page's store, built by the root (ADR-0232, issue #207). */
45
+ export type PadMapStore = Pick<Store, 'getJSON' | 'setJSON'>;
46
+ /** ONE ROOT'S STORED PAD MAPS, per pad id — what `createPadMaps` returns. */
47
+ export interface PadMaps {
48
+ /**
49
+ * The stored map of pad `id`, or `null`. Read through the vocabulary translator: a map saved before ADR-0086 has the old
50
+ * action keys, and a custom pad would otherwise stop answering with no word said.
51
+ */
52
+ padMap(id: string): PadMap | null;
53
+ /** Stores the map of pad `id` and makes it the one read from now on. */
54
+ store(id: string, map: PadMap): void;
55
+ /** A cancelled wizard: the DEFAULT map for this session, not stored, so the wizard does not reopen in a loop. */
56
+ skip(id: string): void;
57
+ }
58
+ /**
59
+ * THE CACHE OF STORED MAPS, one per ROOT (ADR-0232 D4). ⚠️ Not safe to share: two roots on two stores would read each
60
+ * other's map, and the one the page's first wizard saved would answer in a root whose child never recorded it. The root
61
+ * builds ONE and hands it to both readers — its own wizard and `initGamepad` — so a map saved by the wizard is the one the
62
+ * pad reads on the next frame, not a copy cached before.
63
+ */
64
+ export declare function createPadMaps(store: PadMapStore): PadMaps;
65
+ export interface PadWizardCtx {
66
+ /** Translates in the page's language — the root's translator (ADR-0232 D3). REQUIRED: text built from nowhere is a raw key. */
67
+ t: Translate;
68
+ /** Where the finished map is stored — this root's maps (`createPadMaps`). Required: a wizard that saved nowhere would ask
69
+ * the child the fourteen questions again at every visit, in silence. */
70
+ maps: PadMaps;
71
+ getGamepads: GetGamepads;
72
+ /**
73
+ * The position's name in the GAME's word and the language of now; `null` = the game does not use it (the step is skipped).
74
+ * ⚠️ Not asked about `start` and `select`: the wizard always asks them, in the engine's words (`SYSTEM_WORDS`).
75
+ */
76
+ actionLabel: (action: string) => string | null;
77
+ /** Shows and says the wizard's sentence. */
78
+ say: (phrase: string) => void;
79
+ /** Shows what is mapped so far. */
80
+ progress: (text: string) => void;
81
+ srAlert: (phrase: string) => void;
82
+ /** The step that starts (`null` while waiting for a pad) — the host's demonstration, when it has one. */
83
+ onStep?: (action: string | null) => void;
84
+ /** Every tick while open — the host's animation, when it has one. */
85
+ onTick?: () => void;
86
+ /** After closing: the pad index mapped (-1 if none was identified) and whether the map was saved. */
87
+ onClose?: (gi: number, saved: boolean) => void;
88
+ }
89
+ export interface PadWizard {
90
+ /** Opens waiting for any pad to press a button. */
91
+ open(): void;
92
+ /** Opens for a pad already identified. */
93
+ openFor(gp: PadLike): void;
94
+ close(save: boolean): void;
95
+ tick(): void;
96
+ state(): WizState | null;
97
+ }
98
+ export declare function createPadWizard(ctx: PadWizardCtx): PadWizard;
99
+ export {};
@@ -0,0 +1,256 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ import { SYSTEM } from '../core/actions.js';
3
+ import { migrateControlMap } from './vocabulary-migration.js';
4
+ import { buttonName } from './touch.js';
5
+ export const PADWIZ_ORDER = [
6
+ // Directions first: they are what the child finds without thinking, and getting all four right gives confidence for
7
+ // the other ten.
8
+ 'up', 'down', 'left', 'right',
9
+ // The diamond, in the order the finger walks it in this project (ADR-0086 §2).
10
+ 'action2', 'action1', 'action4', 'action3',
11
+ // The four shoulders: the wizard exists FOR pads that are not "standard" — generic, adapted, one-handed — and a game
12
+ // that declares `leftShoulder` needs a way for the child to map it.
13
+ 'leftShoulder', 'leftTrigger', 'rightShoulder', 'rightTrigger',
14
+ // System last: `start` and `select` are usually the smallest and most hidden buttons.
15
+ 'start', 'select',
16
+ ];
17
+ /**
18
+ * 🔴 THE SYSTEM'S TWO ARE ALWAYS ASKED, IN THE ENGINE'S WORDS (ADR-0122, ADR-0144 §1 and its erratum of 2026-09-26, ADR-0155).
19
+ * A game's preset may not name `start` or `select` (ADR-0144 §4, ADR-0155 §4 — refused at boot), so a wizard that asked only
20
+ * what the game names never asked them, and every pad it mapped had no quick pause and no menus: the pause no game may decline,
21
+ * declined by the pad. They are the system's, and `core/actions` (`SYSTEM`) says the engine may name them — the legend printed
22
+ * on the pad. The host's labeller is not asked about them.
23
+ * 📌 Each carries its QUESTION and its NAME: the name is the word the question says (START, SELECT), and it is how the progress
24
+ * line lists the position once mapped — never its id (ADR-0074: no abstract name reaches a person).
25
+ */
26
+ const SYSTEM_WORDS = {
27
+ start: { question: 'pad.wiz.stepStart', name: 'touch.start' },
28
+ select: { question: 'pad.wiz.stepSelect', name: 'touch.select' },
29
+ };
30
+ const systemWords = (action) => (SYSTEM.includes(action) ? SYSTEM_WORDS[action] : null);
31
+ /**
32
+ * 🔴 THE DEFAULT POSITIONS (ADR-0144, erratum of 2026-09-26 — the Dev: «as posições padrões são up, down, left, right, 1, 2, 3, 4,
33
+ * L1, L2, R1, R2, START e SELECT»). A game that names no position still gets a pad the child can map: these fourteen, in this
34
+ * order, each asked by its button's name (`input/touch.buttonName`, ADR-0165 — the names printed on the virtual pad).
35
+ */
36
+ const DEFAULT_ORDER = [
37
+ 'up', 'down', 'left', 'right', 'action1', 'action2', 'action3', 'action4',
38
+ 'leftShoulder', 'leftTrigger', 'rightShoulder', 'rightTrigger', 'start', 'select',
39
+ ];
40
+ const KEY = (id) => 'incl_padmap_' + id;
41
+ /**
42
+ * THE CACHE OF STORED MAPS, one per ROOT (ADR-0232 D4). ⚠️ Not safe to share: two roots on two stores would read each
43
+ * other's map, and the one the page's first wizard saved would answer in a root whose child never recorded it. The root
44
+ * builds ONE and hands it to both readers — its own wizard and `initGamepad` — so a map saved by the wizard is the one the
45
+ * pad reads on the next frame, not a copy cached before.
46
+ */
47
+ export function createPadMaps(store) {
48
+ const maps = new Map();
49
+ return {
50
+ padMap(id) {
51
+ if (!maps.has(id))
52
+ maps.set(id, migrateControlMap(store.getJSON(KEY(id), null)));
53
+ return maps.get(id) ?? null;
54
+ },
55
+ store(id, map) {
56
+ store.setJSON(KEY(id), map);
57
+ maps.set(id, map);
58
+ },
59
+ skip(id) {
60
+ if (!maps.get(id))
61
+ maps.set(id, { _skip: true });
62
+ },
63
+ };
64
+ }
65
+ export function createPadWizard(ctx) {
66
+ const { t } = ctx;
67
+ let padWiz = null;
68
+ /**
69
+ * THIS WIZARD'S STEPS, decided when it opens: the game's positions (`PADWIZ_ORDER`, those it names) and then START and SELECT;
70
+ * or, where the game names none, the fourteen defaults (`DEFAULT_ORDER`), all asked, in the engine's words.
71
+ */
72
+ let defaults = false;
73
+ let order = PADWIZ_ORDER;
74
+ const gameNamesAnyPosition = () => PADWIZ_ORDER.some((action) => systemWords(action) === null && !!ctx.actionLabel(action));
75
+ /** Is this step asked? Every default; else the game's positions when it names them; the system's two always (`SYSTEM_WORDS`). */
76
+ const asked = (action) => defaults || systemWords(action) !== null || !!ctx.actionLabel(action);
77
+ /**
78
+ * A position's word, the one its question said: the engine's for START and SELECT and for every default, the game's for the
79
+ * rest. ⚠️ A position the game no longer names (a cartridge mounted mid-wizard) has no word, and is left out rather than shown
80
+ * by its id.
81
+ */
82
+ const wordFor = (action) => {
83
+ const system = systemWords(action);
84
+ if (system)
85
+ return t(system.name);
86
+ return defaults ? buttonName(ctx.t, action) : ctx.actionLabel(action);
87
+ };
88
+ /**
89
+ * Walks to the next step asked, or closes if there is none left. ONE function: after the last step the wizard does not stay
90
+ * open pointing at a position nobody asks.
91
+ */
92
+ function advance() {
93
+ if (!padWiz)
94
+ return;
95
+ while (padWiz.step < order.length && !asked(order[padWiz.step]))
96
+ padWiz.step++;
97
+ if (padWiz.step >= order.length)
98
+ closeWizard(true);
99
+ }
100
+ function ask() {
101
+ if (!padWiz)
102
+ return;
103
+ advance();
104
+ if (!padWiz)
105
+ return; // it closed while advancing
106
+ const action = order[padWiz.step];
107
+ const n = padWiz.step + 1, total = order.length;
108
+ const system = systemWords(action);
109
+ ctx.say(t(system?.question ?? 'pad.wiz.step', { n, total, acao: system ? '' : wordFor(action) }));
110
+ ctx.onStep?.(action);
111
+ // What is mapped, in the words it was asked with (ADR-0074) — the map's keys are position ids. The empty list's dash stays
112
+ // raw on purpose: it is punctuation, not language.
113
+ const words = Object.keys(padWiz.map).map(wordFor).filter((word) => !!word);
114
+ ctx.progress(t('pad.wiz.mapped', { lista: words.join(' · ') || '—' }));
115
+ }
116
+ function wire(bd) {
117
+ if (!padWiz)
118
+ return;
119
+ padWiz.map[order[padWiz.step]] = bd;
120
+ padWiz.step++;
121
+ padWiz.release = true; // requires letting go before the next step
122
+ advance();
123
+ }
124
+ function begin(gi, id, phrase) {
125
+ defaults = !gameNamesAnyPosition();
126
+ order = defaults ? DEFAULT_ORDER : PADWIZ_ORDER;
127
+ padWiz = { gi, id, step: -1, base: null, map: {}, release: false, baseWait: gi >= 0, axTrack: null, timer: null };
128
+ ctx.say(phrase);
129
+ ctx.onStep?.(null);
130
+ ctx.progress('');
131
+ padWiz.timer = setInterval(tickWizard, 30);
132
+ }
133
+ function openAny() { begin(-1, '', t('pad.wiz.pressAny')); }
134
+ function openForPad(gp) { begin(gp.index, gp.id, t('pad.wiz.detected', { id: gp.id })); }
135
+ function closeWizard(save) {
136
+ if (!padWiz)
137
+ return;
138
+ if (padWiz.timer != null)
139
+ clearInterval(padWiz.timer);
140
+ if (save && padWiz.id) {
141
+ ctx.maps.store(padWiz.id, padWiz.map);
142
+ ctx.srAlert(t('sr.pad.mapSaved', { id: padWiz.id }));
143
+ }
144
+ else if (padWiz.id) {
145
+ ctx.maps.skip(padWiz.id);
146
+ }
147
+ const gi = padWiz.gi;
148
+ const saved = save && !!padWiz.id;
149
+ padWiz = null;
150
+ ctx.onClose?.(gi, saved);
151
+ }
152
+ /* ===================== the five moments of a frame =====================
153
+ *
154
+ * 🔴 This is a STATE MACHINE, and the order below is what it is: no pad adopted · waiting for the resting pose ·
155
+ * waiting for the hand to let go · an axis under watch · reading what moved. Each moment is a function named for what
156
+ * it waits for, and `tickWizard` is their list.
157
+ *
158
+ * ⚠️ AND IT IS NOT A TABLE, unlike `input/keydown`'s chain, because the moments are not symmetric: the first runs
159
+ * WITHOUT a pad in hand (it is the one that picks it) and the other four need one. A list of equal rows would have to
160
+ * pretend the first receives what does not exist yet.
161
+ *
162
+ * 📌 Each moment receives the state instead of reaching for it, which is what lets them be read one at a time — and
163
+ * what removes the non-null assertions a closure looking at `padWiz` would need.
164
+ */
165
+ /** 1 · No pad adopted: the FIRST button pressed on any pad picks the pad the hand is holding. */
166
+ function adoptTheHandsPad(w, pads) {
167
+ for (const gp of pads) {
168
+ if (gp && gp.buttons.some((b) => b && b.pressed)) {
169
+ w.gi = gp.index;
170
+ w.id = gp.id;
171
+ w.baseWait = true;
172
+ ctx.say(t('pad.wiz.releaseAll', { id: gp.id }));
173
+ break;
174
+ }
175
+ }
176
+ }
177
+ /** 2 · This pad's RESTING pose, measured on the one frame in which nothing is pressed. */
178
+ function takeTheRestingPose(w, gp) {
179
+ if (gp.buttons.some((b) => b && b.pressed))
180
+ return;
181
+ w.baseWait = false;
182
+ w.base = { b: gp.buttons.map((x) => !!(x && x.pressed)), a: gp.axes.slice() };
183
+ w.step = 0;
184
+ ask();
185
+ }
186
+ /** 3 · The hand has to LET GO before the next question — and letting go has two halves, the buttons and the axes. */
187
+ function waitForTheHandToLetGo(w, gp, base) {
188
+ const idle = !gp.buttons.some((b, i) => b && b.pressed && !base.b[i]) && gp.axes.every((v, i) => Math.abs((v || 0) - base.a[i]) < 0.35);
189
+ if (idle) {
190
+ w.release = false;
191
+ ask();
192
+ }
193
+ }
194
+ /**
195
+ * 4 · An axis under watch (~240 ms): classified by BEHAVIOUR — varies continuously = analogue (threshold by sign);
196
+ * jumps and STAYS CONSTANT = a D-pad/POV hat (exact value, ±0.13).
197
+ */
198
+ function classifyTheWatchedAxis(w, gp, base) {
199
+ const tr = w.axTrack;
200
+ const v = gp.axes[tr.i] || 0;
201
+ if (Math.abs(v - tr.last) > 0.03)
202
+ tr.changes++;
203
+ tr.last = v;
204
+ if (Math.abs(v - base.a[tr.i]) > Math.abs(tr.v - base.a[tr.i]))
205
+ tr.v = v;
206
+ if (++tr.ticks < 8)
207
+ return;
208
+ const pv = tr.v;
209
+ w.axTrack = null;
210
+ wire(tr.changes >= 2 ? { ax: tr.i, s: pv > 0 ? 1 : -1 } : { av: tr.i, v: Math.round(pv * 10000) / 10000 });
211
+ }
212
+ /** 5 · What moved since rest: a button first, and only then an axis that really left. */
213
+ function readWhatMoved(w, gp, base) {
214
+ for (let i = 0; i < gp.buttons.length; i++) {
215
+ if (gp.buttons[i] && gp.buttons[i].pressed && !base.b[i]) {
216
+ wire({ b: i });
217
+ return;
218
+ }
219
+ }
220
+ for (let i = 0; i < gp.axes.length; i++) {
221
+ const v = gp.axes[i] || 0;
222
+ if (Math.abs(v - base.a[i]) > 0.45) {
223
+ w.axTrack = { i, v, last: v, changes: 0, ticks: 0 };
224
+ return;
225
+ }
226
+ }
227
+ }
228
+ function tickWizard() {
229
+ if (!padWiz)
230
+ return;
231
+ ctx.onTick?.();
232
+ const pads = ctx.getGamepads() ?? [];
233
+ if (padWiz.gi < 0) {
234
+ adoptTheHandsPad(padWiz, pads);
235
+ return;
236
+ }
237
+ const gp = pads[padWiz.gi];
238
+ if (!gp)
239
+ return; // pad disconnected (or its index not populated yet): freezes until it comes back
240
+ if (padWiz.baseWait) {
241
+ takeTheRestingPose(padWiz, gp);
242
+ return;
243
+ }
244
+ const base = padWiz.base;
245
+ if (padWiz.release) {
246
+ waitForTheHandToLetGo(padWiz, gp, base);
247
+ return;
248
+ }
249
+ if (padWiz.axTrack) {
250
+ classifyTheWatchedAxis(padWiz, gp, base);
251
+ return;
252
+ }
253
+ readWhatMoved(padWiz, gp, base);
254
+ }
255
+ return { open: openAny, openFor: openForPad, close: closeWizard, tick: tickWizard, state: () => padWiz };
256
+ }