@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
@@ -1,125 +1,126 @@
1
1
  // SPDX-License-Identifier: AGPL-3.0-or-later
2
- // input/vocabulary-migration — O ÚNICO SÍTIO DA ENGINE QUE PODE DIZER `jump`, E POR QUANTO TEMPO.
2
+ // input/vocabulary-migration — THE ONLY PLACE IN THE ENGINE ALLOWED TO SAY `jump`, AND FOR HOW LONG.
3
3
  //
4
- // ========================= POR QUE ESTE MÓDULO EXISTE SEPARADO =========================
5
- // O gate `action-vocabulary-boundary` reprova a engine por nomear os verbos do jogo, e estava certo ao
6
- // reprovar esta tabela quando ela nasceu dentro de `input/keyboard.ts`. Só que a tabela PRECISA de os nomear:
7
- // traduzir o vocabulário antigo é literalmente a função dela.
4
+ // ========================= WHY THIS MODULE STANDS APART =========================
5
+ // The `action-vocabulary-boundary` gate fails the engine for naming the game's verbs, and it was right to fail
6
+ // this table when it was born inside `input/keyboard.ts`. But the table HAS to name them: translating the old
7
+ // vocabulary is literally its job.
8
8
  //
9
- // ⚠️ E A SAÍDA NÃO FOI LEVANTAR O TETO. Levantar um teto que "só encolhe" é o afrouxamento que o gate existe
10
- // para impedir, e uma exceção sem endereço vira precedente para a próxima. A saída foi QUARENTENAR: o
11
- // acoplamento inteiro mora aqui, num ficheiro cujo nome diz que ele é histórico, com um teto próprio e uma
12
- // data de morte.
9
+ // ⚠️ AND THE WAY OUT WAS NOT RAISING THE CEILING. Raising a ceiling that "only shrinks" is the loosening the gate
10
+ // exists to prevent, and an exception with no address becomes a precedent for the next one. The way out was
11
+ // QUARANTINE: the whole coupling lives here, in a file whose name says it is historical, with a ceiling of its
12
+ // own and a date of death.
13
13
  //
14
- // ⚠️ QUANDO ISTO SE APAGA: quando não restar dado salvo no formato antigo. Não há como saber isso do lado do
15
- // código — o dado está no navegador de cada criança —, então o critério é de tempo e é do Dev. Enquanto
16
- // houver, apagar este ficheiro apaga o remapeamento de quem o fez.
14
+ // ⚠️ WHEN THIS GETS DELETED: when no data saved in the old format remains. The code side has no way to know that
15
+ // — the data is in each child's browser — so the criterion is time, and it is the Dev's. While any remains,
16
+ // deleting this file deletes the remapping of whoever made one.
17
17
  //
18
- // ========================= O QUE SE PERDE SE ISTO ESTIVER ERRADO =========================
19
- // Quem remapeou teclas normalmente remapeou por NECESSIDADE — alcance de mão, dedo que não estica, teclado
20
- // sem numpad. Um esquema salvo que deixe de casar não dá erro: as teclas simplesmente param de responder, e
21
- // a criança conclui que o jogo quebrou. É perda de uma adaptação, não de uma preferência.
18
+ // ========================= WHAT IS LOST IF THIS IS WRONG =========================
19
+ // Whoever remapped keys usually remapped out of NEED — hand reach, a finger that does not stretch, a keyboard
20
+ // without a numpad. A saved scheme that stops matching raises no error: the keys simply stop responding, and the
21
+ // child concludes the game is broken. It is the loss of an adaptation, not of a preference.
22
22
  /**
23
- * Nome de plataforma → posição abstrata. **ADR-0086 §2**, e não o ADR-0074.
23
+ * Platform name → abstract position. **ADR-0086 §2**, not ADR-0074.
24
24
  *
25
- * ⚠️ A DIFERENÇA ENTRE OS DOIS REGISTROS É A TECLA DA CRIANÇA. O ADR-0074 dizia `jump → action1` e
26
- * `run → action4`; o ADR-0086 corrigiu para `run → action1` e `jump → action2`, medindo que a correção é a
27
- * leitura CONSERVADORA — ela deixa cada verbo na tecla e no botão que já ocupava. Traduzir por engano pela
28
- * tabela do 0074 não daria erro nenhum: moveria o pulo de `J` para `U` em silêncio.
25
+ * ⚠️ THE DIFFERENCE BETWEEN THE TWO RECORDS IS THE CHILD'S KEY. ADR-0074 said `jump → action1` and
26
+ * `run → action4`; ADR-0086 corrected it to `run → action1` and `jump → action2`, having measured that the
27
+ * correction is the CONSERVATIVE reading — it leaves each verb on the key and the button it already held.
28
+ * Translating by mistake with 0074's table would raise no error at all: it would move jump from `J` to `U`
29
+ * silently.
29
30
  */
30
- export const VOCABULARIO_ANTIGO = Object.freeze({
31
+ export const OLD_VOCABULARY = Object.freeze({
31
32
  run: 'action1',
32
33
  jump: 'action2',
33
34
  especial: 'action3',
34
35
  swap: 'action4',
35
- // ⚠️ E UMA QUINTA ENTRADA QUE NÃO É UM VERBO DE PLATAFORMA. A camada de toque chamava `pause` a posição
36
- // que todo o resto chama `start` — duas palavras para a mesma coisa, e a do toque era a única que não
37
- // existia no conjunto abstrato. Um mapa de toque gravado antes disto tem `start: 'pause'` no slot do
38
- // START, e sem esta linha esse botão deixaria de pausar: `decide()` passa a procurar `'start'` e
39
- // receberia `'pause'`, que já não é nada — sem erro, sem aviso, e o único botão de pausa de um tablet.
36
+ // ⚠️ AND A FIFTH ENTRY THAT IS NOT A PLATFORM VERB. The touch layer called `pause` the position everything
37
+ // else calls `start` — two words for the same thing, and the touch one was the only one missing from the
38
+ // abstract set. A touch map saved before this has `start: 'pause'` in the START slot, and without this line
39
+ // that button would stop pausing: `decide()` now looks for `'start'` and would get `'pause'`, which is no
40
+ // longer anything — no error, no warning, and the only pause button a tablet has.
40
41
  pause: 'start',
41
42
  });
42
43
  /**
43
- * ⚠️ O SEGUNDO DADO SALVO, e ele quase passou. O mapa de toque (`incl_touchmap`) guarda SLOT → AÇÃO, ou seja
44
- * o nome da ação está no VALOR e não na chave: `{ b0: 'jump', b1: 'especial' }`. O tradutor de esquemas de
45
- * teclado, que traduz CHAVES, passaria por cima dele sem tocar em nada.
44
+ * ⚠️ THE SECOND SAVED DATA, and it nearly slipped through. The touch map (`incl_touchmap`) stores SLOT → ACTION,
45
+ * so the action name is in the VALUE and not the key: `{ b0: 'jump', b1: 'especial' }`. The keyboard scheme
46
+ * translator, which translates KEYS, would pass over it without touching anything.
46
47
  *
47
- * ⚠️ E O DANO SERIA PIOR DO QUE NO TECLADO. `normalizeTouchMap` funde o guardado SOBRE o padrão, então um
48
- * `b0: 'jump'` gravado sobrescreveria o `b0: 'action2'` correto — e o botão da tela deixaria de fazer
49
- * qualquer coisa. Num tablet de escola pública o toque não é o caminho alternativo: é o único.
48
+ * ⚠️ AND THE DAMAGE WOULD BE WORSE THAN ON THE KEYBOARD. `normalizeTouchMap` merges what is stored OVER the
49
+ * default, so a saved `b0: 'jump'` would overwrite the correct `b0: 'action2'` — and the on-screen button would
50
+ * stop doing anything. On a public-school tablet, touch is not the alternative path: it is the only one.
50
51
  *
51
- * Foi um teste de NAVEGADOR que o encontrou (`tests/touch.browser.test.js`), depois de a suíte `node` já
52
- * estar verde — o que é o argumento para os dois projetos existirem.
52
+ * A BROWSER test found it (`tests/touch.browser.test.js`), after the `node` suite was already green — which is
53
+ * the argument for having both projects.
53
54
  */
54
- export function migrarMapaDeToque(mapa) {
55
- if (!mapa)
55
+ export function migrateTouchMap(touchMap) {
56
+ if (!touchMap)
56
57
  return null;
57
- const saida = {};
58
- for (const [slot, acao] of Object.entries(mapa)) {
59
- saida[slot] = VOCABULARIO_ANTIGO[acao] ?? acao;
58
+ const migrated = {};
59
+ for (const [slot, action] of Object.entries(touchMap)) {
60
+ migrated[slot] = OLD_VOCABULARY[action] ?? action;
60
61
  }
61
- return saida;
62
+ return migrated;
62
63
  }
63
64
  /**
64
- * ⚠️ O TERCEIRO DADO SALVO, encontrado por varredura e não por acidente. Depois de o segundo aparecer num
65
- * teste de navegador, a pergunta certa deixou de ser «este está migrado?» e passou a ser «QUANTOS formatos
66
- * persistidos existem?». São três, e este é o do assistente de controle: `incl_padmap_<id>` guarda
67
- * AÇÃO → BINDING FÍSICO, `{ jump: { b: 0 }, run: { b: 2 } }`, um por modelo de controle.
65
+ * ⚠️ THE THIRD SAVED DATA, found by a sweep and not by accident. After the second turned up in a browser test,
66
+ * the right question stopped being "is this one migrated?" and became "HOW MANY persisted formats exist?". There
67
+ * are three, and this is the pad wizard's: `incl_padmap_<id>` stores ACTION → PHYSICAL BINDING,
68
+ * `{ jump: { b: 0 }, run: { b: 2 } }`, one per pad model.
68
69
  *
69
- * ⚠️ E ELE É O MAIS CARO DE PERDER DOS TRÊS. Um mapa desses existe porque a criança (ou quem a acompanha)
70
- * passou por um assistente de nove passos apertando botão a botão, provavelmente porque o controle dela não
71
- * é «standard» — controles genéricos e adaptados raramente são. Perdê-lo manda essa pessoa de volta ao
72
- * assistente inteiro.
70
+ * ⚠️ AND IT IS THE MOST EXPENSIVE OF THE THREE TO LOSE. Such a map exists because the child (or whoever is with
71
+ * them) went through the wizard pressing button after button, one step per position the game names — probably
72
+ * because their pad is not "standard", and generic and adapted pads rarely are. Losing it sends that person back
73
+ * through the whole wizard.
73
74
  *
74
- * `_skip` e qualquer chave desconhecida atravessam, pela mesma razão das outras duas migrações.
75
+ * `_skip` and any unknown key pass through, for the same reason as the other two migrations.
75
76
  */
76
- export function migrarMapaDeControle(mapa) {
77
- if (!mapa)
77
+ export function migrateControlMap(touchMap) {
78
+ if (!touchMap)
78
79
  return null;
79
- const saida = {};
80
- for (const [chave, valor] of Object.entries(mapa)) {
81
- saida[VOCABULARIO_ANTIGO[chave] ?? chave] = valor;
80
+ const migrated = {};
81
+ for (const [key, value] of Object.entries(touchMap)) {
82
+ migrated[OLD_VOCABULARY[key] ?? key] = value;
82
83
  }
83
- return saida;
84
+ return migrated;
84
85
  }
85
86
  /**
86
- * Traduz UM esquema salvo do vocabulário antigo para o abstrato.
87
+ * Translates ONE saved scheme from the old vocabulary to the abstract one.
87
88
  *
88
- * ⚠️ CHAVE DESCONHECIDA ATRAVESSA INTACTA, e é decisão e não descuido: `up`, `down`, `left` e `right` nunca
89
- * mudaram de nome, e um esquema pode carregar uma chave que este código não conhece — dado de uma versão
90
- * futura, ou lixo. Apagá-la seria destruir dado que não entendemos, e é o que faria as quatro direções
91
- * sumirem. É também o que torna esta função IDEMPOTENTE: aplicada sobre um esquema já migrado, nenhuma chave
92
- * casa e o resultado é igual à entrada, o que importa porque `loadKB` pode correr mais de uma vez na sessão.
89
+ * ⚠️ AN UNKNOWN KEY PASSES THROUGH UNTOUCHED, and that is a decision, not carelessness: `up`, `down`, `left` and
90
+ * `right` never changed names, and a scheme may carry a key this code does not know — data from a future
91
+ * version, or garbage. Deleting it would destroy data we do not understand, and it is what would make the four
92
+ * directions vanish. It is also what makes this function IDEMPOTENT: applied to an already migrated scheme, no
93
+ * key matches and the result equals the input, which matters because `loadKB` may run more than once a session.
93
94
  */
94
- export function migrarEsquema(esquema) {
95
- if (!esquema)
95
+ export function migrateScheme(saved) {
96
+ if (!saved)
96
97
  return null;
97
- const saida = {};
98
- for (const [chave, teclas] of Object.entries(esquema)) {
99
- const nova = VOCABULARIO_ANTIGO[chave] ?? chave;
100
- // ⚠️ Esquema MEIO migrado (as duas chaves presentes): a UNIÃO, nunca a sobreposição. Perder uma tecla é
101
- // o dano que este módulo existe para impedir; ter a mesma tecla duas vezes não é dano nenhum.
102
- saida[nova] = saida[nova] ? [...new Set([...saida[nova], ...teclas])] : [...teclas];
98
+ const migrated = {};
99
+ for (const [key, keys] of Object.entries(saved)) {
100
+ const newKey = OLD_VOCABULARY[key] ?? key;
101
+ // ⚠️ A HALF-migrated scheme (both keys present): the UNION, never the overwrite. Losing a key is the damage
102
+ // this module exists to prevent; having the same key twice is no damage at all.
103
+ migrated[newKey] = migrated[newKey] ? [...new Set([...migrated[newKey], ...keys])] : [...keys];
103
104
  }
104
- return saida;
105
+ return migrated;
105
106
  }
106
- /** Traduz o objeto salvo inteiro — o esquema solo e as listas por contagem de jogadores. */
107
- export function migrarSalvo(s) {
107
+ /** Translates the whole saved object — the solo scheme and the lists per player count. */
108
+ export function migrateSaved(s) {
108
109
  if (!s)
109
110
  return null;
110
- const lista = (arr) => (Array.isArray(arr) ? arr.map((m) => migrarEsquema(m)) : undefined);
111
+ const list = (arr) => (Array.isArray(arr) ? arr.map((m) => migrateScheme(m)) : undefined);
111
112
  const out = {};
112
- const solo = migrarEsquema(s.solo);
113
+ const solo = migrateScheme(s.solo);
113
114
  if (solo)
114
115
  out.solo = solo;
115
116
  for (const g of ['p2', 'p3', 'p4']) {
116
- const v = lista(s[g]);
117
+ const v = list(s[g]);
117
118
  if (v)
118
119
  out[g] = v;
119
120
  }
120
- // `p34` migra de VOCABULÁRIO aqui e de FORMA em `loadKB`, que já o fazia antes deste módulo existir.
121
- // Sem esta linha, o dado mais velho de todos seria o único a perder-se.
121
+ // `p34` migrates its VOCABULARY here and its SHAPE in `loadKB`, which already did that before this module
122
+ // existed. Without this line, the oldest data of all would be the only one to get lost.
122
123
  if (Array.isArray(s.p34))
123
- out.p34 = s.p34.map((m) => migrarEsquema(m));
124
+ out.p34 = s.p34.map((m) => migrateScheme(m));
124
125
  return out;
125
126
  }
@@ -0,0 +1,59 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ import type { Action } from '../core/actions.js';
3
+ /**
4
+ * Every word of one language, by position.
5
+ *
6
+ * ⚠️ WRITTEN AS THE RECOGNISER'S VOCABULARY SPELLS THEM — lowercase, with their accents (the Dev's words are «ação» and
7
+ * «Acción»): a grammar word the model does not know is dropped and its position goes mute (measured below). Comparing ignores
8
+ * accents: the reader (`platform/speech-recognition`) matches a heard sentence and a table entry by a key without them.
9
+ */
10
+ export type VoiceWords = {
11
+ readonly [A in Action]?: readonly string[];
12
+ };
13
+ /** The table of the child's language. An unknown tag falls back to Portuguese, the project's base language (ADR-0010 pillar 3). */
14
+ export declare function voiceWordsFor(language: string): VoiceWords;
15
+ /**
16
+ * THE CLOSED GRAMMAR: every word the recogniser is allowed to return, and nothing else (ADR-0189, ADR-0193).
17
+ *
18
+ * 📌 A grammar is what makes 32 MiB answer in a quarter of a second, and it is also what makes it answer WRONG when a child
19
+ * says something that is not a command: the lab measured free speech being pushed to the nearest word («configurações de
20
+ * inclusão» → «quatro»). That is the cost of the closed list and the reason the words are short and unlike each other.
21
+ * ⚠️ `extra` is what the MENU is showing right now (ADR-0194): saying the name of an item activates it, so those words join the
22
+ * grammar while that menu is open and leave with it — AS THE LANGUAGE WRITES THEM (§1, «as shown»), accents kept: stripped,
23
+ * «configurações» is not in the model's vocabulary and the name goes mute, the same defect «acao» was.
24
+ */
25
+ export declare function voiceGrammar(language: string, extra?: readonly string[]): readonly string[];
26
+ /** What a heard phrase asks for: a position of the controller, or the item of the open menu that has this name (ADR-0194). */
27
+ export type VoiceCommand = {
28
+ readonly kind: 'position';
29
+ readonly action: Action;
30
+ }
31
+ /** `name` is the item's name exactly as it was handed to `items` — the name on screen, to find the item by. */
32
+ | {
33
+ readonly kind: 'item';
34
+ readonly name: string;
35
+ };
36
+ export interface VoiceCommands {
37
+ /**
38
+ * The names the open menu is showing, as shown (ADR-0194 §2): from now on, saying one is a command — until the next call.
39
+ * An empty list is a menu closed.
40
+ */
41
+ items(names: readonly string[]): void;
42
+ /**
43
+ * What the recogniser has heard SO FAR in this utterance → the commands it completes that were not answered before.
44
+ * ⚠️ It answers on the PARTIAL and not at the end of the sentence (ADR-0193 erratum, the Dev's choice after running both):
45
+ * 0.25–0.45 s against 1.3–1.4 s, which is the difference between a command and a delay a child gives up on — except for a
46
+ * name another item's name continues, which waits for `final` (ADR-0194 §3).
47
+ */
48
+ partial(text: string): readonly VoiceCommand[];
49
+ /** The utterance ended with this text: what it completes that the partials held back. The next partial starts afresh. */
50
+ final(text: string): readonly VoiceCommand[];
51
+ }
52
+ /**
53
+ * The Dev's words and the open menu's names, READ BY `platform/speech-recognition.createCommandReader` — the one reader of what
54
+ * was heard (ADR-0194 §2–§3); this adds only what a word MEANS here, the position it presses.
55
+ *
56
+ * 🎯 A PARTIAL GROWS, AND ONLY WHAT IS NEW FIRES. «acima» then «acima abaixo» is a child who said two words, and the second
57
+ * press must be `down`, not `up` all over again — the reader counts what it already answered for in each utterance.
58
+ */
59
+ export declare function createVoiceCommands(language: string): VoiceCommands;
@@ -0,0 +1,123 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // input/voice-map — THE WORDS A CHILD SAYS, AND THE POSITION EACH ONE PRESSES (ADR-0204 erratum; issues #184, #190).
3
+ //
4
+ // This is the pure half of playing by voice: a table of words per language, a closed grammar built from it and the open menu's
5
+ // names, and what a heard phrase MEANS — a position, or an item by name (ADR-0194). WHEN a phrase is heard is the reader's
6
+ // (`platform/speech-recognition`). Nothing here opens a microphone or loads a model — the same division that let the gaze cycle
7
+ // be measured without a camera.
8
+ //
9
+ // 🎯 THE WORDS ARE THE DEV'S, checked by the Dev against what games use (racing R2 accelerate / L2 brake, shooters L2 aim / R2 fire)
10
+ // and then run in the lab's bar protocol in the three languages: pt 23/23, es 24/24, en 24/24 detected, all rated «ok».
11
+ // A position answers to ANY of its words, and L1/R1 are the ship's sides — his adaptation.
12
+ //
13
+ // ⚠️ ONE WORD IS KNOWN TO BE MISSING from a model's vocabulary: «boreste» is not in Vosk small pt (measured against its `Gr.fst`
14
+ // symbol table), so under that recogniser R1 answers only to «estibordo». It stays in the table because the browser's own
15
+ // recogniser has no closed vocabulary and does hear it — a word is dropped by the RECOGNISER that cannot hear it, never by this
16
+ // table, which is the Dev's decision written down.
17
+ import { spokenText, createCommandReader } from '../platform/speech-recognition.js';
18
+ const PT = {
19
+ up: ['acima'], down: ['abaixo'], left: ['esquerda'], right: ['direita'],
20
+ start: ['start'], select: ['select'],
21
+ // 🔴 `ação` WITH THE CEDILLA AND THE TILDE, measured in a browser: written `acao`, the pt model answered "Ignoring word
22
+ // missing in vocabulary: 'acao'" and the first position was MUTE — the child said the word and nothing happened. The
23
+ // spelling here is the recogniser's VOCABULARY's; whoever compares no longer looks at accents (the reader's key), so spelling it
24
+ // right costs nothing on the hearing side. ⚠️ `boreste` is still outside the small model's vocabulary (ADR-0204 erratum)
25
+ // and stays on the table on purpose: it is the Dev's word, and a larger model hears it.
26
+ action1: ['ação'], action2: ['confirma', 'pega', 'ativar', 'pulo'], action3: ['voltar', 'solta', 'cancelar', 'especial'],
27
+ action4: ['lista', 'troca'],
28
+ leftShoulder: ['bombordo'], leftTrigger: ['mira', 'freia'],
29
+ rightShoulder: ['estibordo', 'boreste'], rightTrigger: ['gatilho', 'acelera'],
30
+ };
31
+ const ES = {
32
+ up: ['arriba'], down: ['abajo'], left: ['izquierda'], right: ['derecha'],
33
+ start: ['start'], select: ['select'],
34
+ // `acción` with its accent, by the pt line's measurement: the model's vocabulary keeps the word as the language writes it.
35
+ action1: ['acción'], action2: ['confirma', 'recoger', 'activar', 'saltar'], action3: ['volver', 'soltar', 'cancelar', 'especial'],
36
+ action4: ['inventario', 'lista', 'cambiar'],
37
+ leftShoulder: ['babor'], leftTrigger: ['apuntar', 'frenar'],
38
+ rightShoulder: ['estribor'], rightTrigger: ['disparar', 'acelerar'],
39
+ };
40
+ const EN = {
41
+ up: ['up'], down: ['down'], left: ['left'], right: ['right'],
42
+ start: ['start'], select: ['select'],
43
+ action1: ['action'], action2: ['confirm', 'grab', 'pick up', 'activate', 'jump'], action3: ['back', 'drop', 'cancel', 'special'],
44
+ action4: ['inventory', 'swap'],
45
+ leftShoulder: ['port'], leftTrigger: ['aim', 'brake'],
46
+ rightShoulder: ['starboard'], rightTrigger: ['trigger', 'accelerate'],
47
+ };
48
+ const BY_LANGUAGE = { pt: PT, es: ES, en: EN };
49
+ /** The table of the child's language. An unknown tag falls back to Portuguese, the project's base language (ADR-0010 pillar 3). */
50
+ export function voiceWordsFor(language) {
51
+ return BY_LANGUAGE[language.split('-')[0].toLowerCase()] ?? PT;
52
+ }
53
+ /**
54
+ * THE CLOSED GRAMMAR: every word the recogniser is allowed to return, and nothing else (ADR-0189, ADR-0193).
55
+ *
56
+ * 📌 A grammar is what makes 32 MiB answer in a quarter of a second, and it is also what makes it answer WRONG when a child
57
+ * says something that is not a command: the lab measured free speech being pushed to the nearest word («configurações de
58
+ * inclusão» → «quatro»). That is the cost of the closed list and the reason the words are short and unlike each other.
59
+ * ⚠️ `extra` is what the MENU is showing right now (ADR-0194): saying the name of an item activates it, so those words join the
60
+ * grammar while that menu is open and leave with it — AS THE LANGUAGE WRITES THEM (§1, «as shown»), accents kept: stripped,
61
+ * «configurações» is not in the model's vocabulary and the name goes mute, the same defect «acao» was.
62
+ */
63
+ export function voiceGrammar(language, extra = []) {
64
+ const all = new Set();
65
+ for (const words of Object.values(voiceWordsFor(language)))
66
+ for (const p of words)
67
+ all.add(p.toLowerCase());
68
+ for (const e of extra) {
69
+ const clean = spokenText(e);
70
+ if (clean)
71
+ all.add(clean);
72
+ }
73
+ return [...all];
74
+ }
75
+ /**
76
+ * The Dev's words and the open menu's names, READ BY `platform/speech-recognition.createCommandReader` — the one reader of what
77
+ * was heard (ADR-0194 §2–§3); this adds only what a word MEANS here, the position it presses.
78
+ *
79
+ * 🎯 A PARTIAL GROWS, AND ONLY WHAT IS NEW FIRES. «acima» then «acima abaixo» is a child who said two words, and the second
80
+ * press must be `down`, not `up` all over again — the reader counts what it already answered for in each utterance.
81
+ */
82
+ export function createVoiceCommands(language) {
83
+ const actionOf = new Map();
84
+ for (const [action, spoken] of Object.entries(voiceWordsFor(language))) {
85
+ for (const phrase of spoken)
86
+ if (!actionOf.has(spokenText(phrase)))
87
+ actionOf.set(spokenText(phrase), action);
88
+ }
89
+ const reader = createCommandReader([...actionOf.keys()]);
90
+ let names = [];
91
+ let utterance = 0;
92
+ let previous = [];
93
+ const meaning = (heard) => heard.flatMap((h) => {
94
+ if (h.kind === 'palavra') {
95
+ const action = actionOf.get(h.word);
96
+ return action ? [{ kind: 'position', action }] : [];
97
+ }
98
+ const name = names.find((n) => spokenText(n) === h.name);
99
+ return name === undefined ? [] : [{ kind: 'item', name }];
100
+ });
101
+ return {
102
+ items(n) { names = [...n]; reader.items(names); },
103
+ partial(text) {
104
+ const heard = spokenText(text).split(' ').filter(Boolean);
105
+ /*
106
+ * 🔴 A NEW UTTERANCE IS ONE THAT DOES NOT CONTINUE THE LAST, and counting words is not enough to see it: a recogniser
107
+ * starts a new partial without saying so — and does whenever the grammar changes, because a new grammar is a new
108
+ * recogniser — and «abaixo» after «acima abaixo» is shorter than what was already answered. What a partial of the same
109
+ * utterance always is, is the previous one plus more.
110
+ */
111
+ if (!previous.every((p, i) => heard[i] === p))
112
+ utterance += 1;
113
+ previous = heard;
114
+ return meaning(reader.read(utterance, text, false));
115
+ },
116
+ final(text) {
117
+ const done = meaning(reader.read(utterance, text, true));
118
+ utterance += 1;
119
+ previous = [];
120
+ return done;
121
+ },
122
+ };
123
+ }
@@ -1,21 +1,32 @@
1
1
  // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ import type { Role, Spot } from '../core/contract.js';
2
3
  interface Vec2 {
3
4
  x: number;
4
5
  y: number;
5
6
  }
6
7
  export interface AudioAmbientCtx {
8
+ /**
9
+ * WHERE THE WATER IS, ASKED OF THE GAME through the contract's `roleAt` — never read off a tile number. "Water is
10
+ * tile 3" is true of ONE map (the platformer's), not of the engine: a game with another numbering would hear a river
11
+ * in its lava and silence in its lake, with no error and no red test (ADR-0027's no. 1 coupling, the one
12
+ * `render/high-contrast` shed by receiving `roleOf`). `roleAt` and not a bespoke `isWater`, because every cartridge
13
+ * already answers it (`GameDeclaration.roleAt` is required) and 'water' is already a contract `Role`: a second
14
+ * question would be a second table to drift from the one that paints high contrast and routes the sonar. Asked in
15
+ * world units, at the corner of each cell of the neighbourhood (`TILE` per cell).
16
+ * ⚠️ REQUIRED, by ADR-0224's precedent: an optional port would let a game forget it, and forgetting it would silence
17
+ * the water without a word.
18
+ */
19
+ roleAt: (at: Spot) => Role;
7
20
  ensureAC: () => AudioContext | null;
8
21
  getAudioCtx: () => AudioContext | null;
9
22
  catNode: (cat: string) => AudioNode | null;
10
23
  audioOut: () => AudioNode | null;
11
- noiseBuffer: (ac: AudioContext) => AudioBuffer;
12
24
  getSoundOn: () => boolean;
13
25
  getVolume: () => number;
14
26
  getAudioCat: () => Record<string, {
15
27
  on: boolean;
16
28
  }> | null;
17
29
  getPlayers: () => Vec2[];
18
- tileAt: (x: number, y: number) => number;
19
30
  TILE: number;
20
31
  getRainLevel: () => number;
21
32
  }
@@ -1,12 +1,11 @@
1
1
  // SPDX-License-Identifier: AGPL-3.0-or-later
2
- // platform/audio-ambient — trilha de AMBIENTE sintetizada (loops de ruído filtrado) + trovão. Última rodada do Tier 2 de
3
- // áudio (r4). O clima VISUAL (updateWeather/drawWeather/chuva) fica no game.js e migra p/ render depois — aqui só o SOM.
4
- // Ponte com o clima: `_rainLevel` (0..1) é calculado por updateWeather (game.js) e LIDO aqui por getter → o volume da
5
- // chuva "segue o visual". Injeção por closure (padrão Tier 1).
6
- // updateAmbient() — por frame: constrói a trilha 1x (lazy) e ajusta o ganho de ÁGUA (proximidade de tiles de água) e
7
- // de CHUVA (segue _rainLevel). Gated por audioCat.ambient.on.
8
- // thunder(inten) — rumor grave sintetizado (ruído passa-baixas), intensidade variável; chamado por updateWeather.
9
- // Extraído do game.js. Ver docs/5-Refactoring/plano-modularizacao-mapa.md (Tier 2, áudio rodada 4).
2
+ // platform/audio-ambient — a synthesised AMBIENT track (loops of filtered noise) + thunder. Only the SOUND lives here;
3
+ // the visual weather is the game's. The bridge to it: the rain level (0..1) is computed by the game's weather and READ
4
+ // here through a getter, so the rain's volume "follows the picture". Injection by closure.
5
+ // updateAmbient() — per frame: builds the track once (lazily) and sets the WATER gain (nearness of what the game
6
+ // calls 'water') and the RAIN gain (follows the rain level). Gated by audioCat.ambient.on.
7
+ // thunder(inten) — a synthesised low rumble (low-passed noise) of variable intensity; called by the weather.
8
+ import { noiseBuffer } from './audio.js';
10
9
  export function createAudioAmbient(ctx) {
11
10
  let _ambient = null;
12
11
  function buildAmbient(ac) {
@@ -19,7 +18,7 @@ export function createAudioAmbient(ctx) {
19
18
  const w = Math.random() * 2 - 1;
20
19
  last = (last + 0.02 * w) / 1.02;
21
20
  d[i] = last * 3.2;
22
- } // ruído rosa em loop
21
+ } // looping pink noise
23
22
  const mk = (type, freq, q, vol) => {
24
23
  const s = ac.createBufferSource();
25
24
  s.buffer = buf;
@@ -38,7 +37,7 @@ export function createAudioAmbient(ctx) {
38
37
  catch (e) { /* noop */ }
39
38
  return g;
40
39
  };
41
- // trânsito/rumor · folhas/vento · água (proximidade) · chuva (ciclo)
40
+ // traffic/rumble · leaves/wind · water (nearness) · rain (cycle)
42
41
  return { hum: mk('lowpass', 480, 0, 0.055), wind: mk('highpass', 3200, 0, 0.028), water: mk('bandpass', 820, 1.4, 0), rain: mk('highpass', 1700, 0, 0) };
43
42
  }
44
43
  function updateAmbient() {
@@ -54,11 +53,11 @@ export function createAudioAmbient(ctx) {
54
53
  let nearWater = 0;
55
54
  for (let dx = -3; dx <= 3; dx++)
56
55
  for (let dy = -3; dy <= 3; dy++) {
57
- if (ctx.tileAt(px + dx, py + dy) === 3)
56
+ if (ctx.roleAt({ x: (px + dx) * ctx.TILE, y: (py + dy) * ctx.TILE }) === 'water')
58
57
  nearWater = Math.max(nearWater, 1 - Math.hypot(dx, dy) / 4.2);
59
58
  }
60
59
  _ambient.water.gain.setTargetAtTime(0.15 * nearWater, ac.currentTime, 0.3);
61
- _ambient.rain.gain.setTargetAtTime(0.09 * ctx.getRainLevel(), ac.currentTime, 0.5); // chuva segue _rainLevel; 0 = silêncio total
60
+ _ambient.rain.gain.setTargetAtTime(0.09 * ctx.getRainLevel(), ac.currentTime, 0.5); // the rain follows the rain level; 0 = full silence
62
61
  }
63
62
  function thunder(inten) {
64
63
  if (!ctx.getSoundOn() || ctx.getVolume() <= 0)
@@ -68,8 +67,9 @@ export function createAudioAmbient(ctx) {
68
67
  return;
69
68
  try {
70
69
  const vol = ctx.getVolume();
70
+ // the engine's own white noise (ADR-0258): a game could only ever hand back this same function
71
71
  const src = ac.createBufferSource();
72
- src.buffer = ctx.noiseBuffer(ac);
72
+ src.buffer = noiseBuffer(ac);
73
73
  src.loop = true;
74
74
  const bq = ac.createBiquadFilter();
75
75
  bq.type = 'lowpass';
@@ -83,7 +83,7 @@ export function createAudioAmbient(ctx) {
83
83
  src.start(t);
84
84
  src.stop(t + dur + 0.1);
85
85
  }
86
- catch (e) { /* Web Audio indisponível */ }
86
+ catch (e) { /* no Web Audio */ }
87
87
  }
88
88
  return { updateAmbient, thunder };
89
89
  }
@@ -1,39 +1,40 @@
1
1
  // SPDX-License-Identifier: AGPL-3.0-or-later
2
2
  /**
3
- * A definição de UM earcon. A tabela é do JOGO (item 19); esta é a forma que a engine sabe tocar.
3
+ * The definition of ONE earcon. The table is the GAME's; this is the shape the engine knows how to play.
4
4
  *
5
- * ⚠️ EXPORTADA DESDE 2026-09-07 (#124), e a falta era um defeito de interface: o `game-platformer` declarava
6
- * a sua própria cópia deste tipo, palavra por palavra, porque não tinha como o nomear. Uma forma que cada
7
- * consumidor redescobre por cópia é uma forma que diverge — foi assim que cinco cópias de `Gfx` divergiram
8
- * nesta árvore, e está escrito noutro módulo.
5
+ * ⚠️ EXPORTED (#124), and its absence was an interface defect: a game declared its own copy of this type, word for word,
6
+ * because it had no way to name it. A shape each consumer rediscovers by copying is a shape that diverges.
9
7
  */
10
8
  export interface SfxDef {
11
- /** O timbre. */
9
+ /** The timbre. */
12
10
  t: OscillatorType;
13
- /** A frequência inicial, em hertz. */
11
+ /** The starting frequency, in hertz. */
14
12
  f: number;
15
- /** A duração, em segundos. */
13
+ /** The duration, in seconds. */
16
14
  d: number;
17
- /** A CHAVE de i18n da legenda (a11y surdez). Quem exibe resolve. */
15
+ /** The caption's i18n KEY (deaf accessibility). Whoever shows it resolves it. */
18
16
  cap?: string;
19
17
  /**
20
- * A frequência FINAL, em hertz. Ausente = nota parada, que é o que sempre houve.
18
+ * The FINAL frequency, in hertz. Absent = a held note, which is what there always was.
21
19
  *
22
- * ⚠️ ELE EXISTE PORQUE UM EARCON PRECISA DE PODER IR PARA ALGUM LADO (#124). Medido ao construir o
23
- * `game-soccer`: marcar e sofrer golo têm de ser distinguíveis **só de ouvido** — uma criança cega ouve a
24
- * sala reagir e precisa de saber para que lado antes de a narração chegar. O desenho óbvio é uma figura
25
- * que SOBE para o golo dela e DESCE para o do outro, e a tabela não o sabia dizer. O que sobrava era
26
- * agudo-e-longo contra grave-e-curto: distinguível, e menos informação do que o momento carrega.
20
+ * ⚠️ IT EXISTS BECAUSE AN EARCON HAS TO BE ABLE TO GO SOMEWHERE (#124). Measured building a football game: scoring and
21
+ * conceding must be told apart **by ear alone** — a blind child hears the room react and needs to know which way before
22
+ * the narration arrives. The obvious design is a figure that RISES for their goal and FALLS for the other's, and the
23
+ * table could not say it. What was left was high-and-long against low-and-short: distinguishable, and less information
24
+ * than the moment carries.
27
25
  *
28
- * ⚠️ E A CAPACIDADE JÁ ESTAVA NESTE FICHEIRO, sem ser alcançável da tabela: o `doorSound` faz exatamente
29
- * isto, com `frequency.exponentialRampToValueAtTime`. O conserto não é síntese nova — é abrir a porta.
26
+ * ⚠️ AND THE CAPABILITY WAS ALREADY IN THIS FILE, unreachable from the table: `doorSound` does exactly this with
27
+ * `frequency.exponentialRampToValueAtTime`. The fix was not new synthesis — it was opening the door.
30
28
  *
31
- * A rampa é EXPONENCIAL e não linear porque a altura é percebida em razão e não em diferença: uma rampa
32
- * linear de 200 a 800 sobe depressa no início e devagar no fim, e ouve-se torta.
29
+ * The ramp is EXPONENTIAL and not linear because pitch is perceived as a ratio, not a difference: a linear ramp from 200
30
+ * to 800 rises fast at the start and slowly at the end, and sounds crooked.
33
31
  */
34
32
  f2?: number;
35
33
  }
34
+ import type { Translate } from '../core/i18n.js';
36
35
  export interface AudioEarconsCtx {
36
+ /** Translates in the page's language — the root's translator (ADR-0232 D3). REQUIRED: text built from nowhere is a raw key. */
37
+ t: Translate;
37
38
  SFX: Record<string, SfxDef | undefined>;
38
39
  ensureAC: () => AudioContext | null;
39
40
  catNode: (cat: string) => AudioNode | null;