@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,353 +1,633 @@
1
1
  // SPDX-License-Identifier: AGPL-3.0-or-later
2
- import { type Alcance, type Disponibilidade } from '../input/transports.js';
2
+ import { type Translate } from '../core/i18n.js';
3
+ import { type LiveInput } from '../input/state.js';
4
+ import { type FlashMeasurement } from '../platform/flash-sampler.js';
5
+ import { type Reach, type Availability } from '../input/transports.js';
6
+ import { type AccommodationAnswers } from '../core/accommodations.js';
3
7
  import { type ActionPreset } from '../core/actions.js';
4
8
  import type { KeyScheme } from '../core/entity.js';
9
+ import { type HowToPlaySlide } from '../ui/help-panel.js';
10
+ import { type SettingsStore } from '../core/state.js';
11
+ import { type Interpreter } from '../ui/vlibras.js';
5
12
  import { type GameDeclaration } from '../core/contract.js';
6
13
  import { type SceneStack } from '../core/scenes.js';
7
- import { createTts, type CarregarVozNeural } from '../platform/tts.js';
8
- import { type AudioSonar, type SonarPlayer } from '../platform/audio-sonar.js';
9
- import { type Tema, type Correcao } from '../render/viz-axes.js';
10
- import type { AlcanceDoFiltro } from '../render/port.js';
14
+ import { createTts } from '../platform/tts.js';
15
+ import { type Reading } from '../platform/reading.js';
16
+ import { type Audio } from '../platform/audio.js';
17
+ import { type AudioSonar } from '../platform/audio-sonar.js';
18
+ import { type Theme, type Correction } from '../render/viz-axes.js';
19
+ import type { FilterReach } from '../render/port.js';
11
20
  import { type SettingsPanelApi } from '../ui/settings-panel.js';
21
+ import { type VirtualCommand, type VirtualController } from '../input/virtual-controller.js';
22
+ export type { VirtualCommand } from '../input/virtual-controller.js';
23
+ import { type LqFilter } from '../render/lq-filter.js';
24
+ import { type Crt } from '../render/crt.js';
25
+ import { type HudNumber } from '../ui/hud-bands.js';
26
+ import { type GameOption } from '../ui/game-options.js';
27
+ import { type StorageLike } from '../platform/storage.js';
12
28
  import { type MenuNavApi } from '../ui/menu-nav.js';
13
29
  import type { NavKeys } from '../input/edges.js';
30
+ import { type GamepadGameHooks } from '../input/gamepad.js';
14
31
  import { type KeyboardRuntime } from '../input/keyboard-runtime.js';
15
- import { type RelatorioPesado } from '../platform/pesados.js';
16
- /** O que o jogo empresta do documento. Tudo opcional menos `doc`/`win`: o que faltar vira `problems`. */
32
+ import { type KeyboardConfigApi } from '../input/keyboard.js';
33
+ import { type HeavyReport } from '../platform/heavy.js';
34
+ /** What the game lends from the document. Everything optional but `doc`/`win`: what is missing becomes `problems`. */
17
35
  export interface EngineHost {
18
36
  readonly doc: Document;
19
37
  readonly win: Window;
20
- /** Um `<svg>` vazio onde os seis filtros de daltonismo são montados em tempo de execução. */
38
+ /** An empty `<svg>` where the six colour-vision filters are mounted at run time. */
21
39
  readonly cvdHost?: SVGElement | Element | null;
22
40
  /**
23
- * ONDE A BARRA DE ACESSIBILIDADE ENTRA, na PRIMEIRA tela do jogo.
41
+ * WHERE THE ACCESSIBILITY BAR GOES, on the game's FIRST screen.
24
42
  *
25
- * ⚠️ PEDIDO DO DEV, 2026-09-07: «o menu de pausa, o design do menu de pausa e os ícones de acessibilidade
43
+ * ⚠️ THE DEV'S REQUEST, 2026-09-07: «o menu de pausa, o design do menu de pausa e os ícones de acessibilidade
26
44
  * que aparecem no jogo desde a primeira tela devem ser oferecidos pela ENGINE e não pela programação do
27
45
  * jogo. Todo jogo da engine inclusionist deve ter o mesmo menu de pausa e ícones de acessibilidade desde a
28
46
  * primeira.»
29
47
  *
30
- * ⚠️ E A MEDIÇÃO DE 2026-09-08 MOSTROU QUE É UM ACHADO, e não uma arrumação: dos seis jogos do catálogo
31
- * local, CINCO não têm barra de acessibilidade nenhuma — nem menu de pausa. `pixi-15-puzzle`, `game-chess`,
32
- * `game-soccer`, `2048` e `whackwhack` não chamam `initPauseIcons` nem montam HUD, e o `createGame` nunca
33
- * os montou por eles. O comentário do `ui/pause-icons` diz porquê sem o notar: «`initPauseIcons` é chamado
34
- * pela raiz de composição de CADA jogo» — ou seja, cada jogo tinha de se lembrar, e cinco não se lembraram.
35
- * A criança que depende do modo cego, do TTS ou do alto contraste abre esses cinco jogos e não tem por onde.
48
+ * Left to each game, the bar was missing from most of the catalogue: each game had to remember to call
49
+ * `initPauseIcons`, and a child who depends on blind mode, narration or high contrast opened those games with no way
50
+ * in. So the engine mounts it (ADR-0106 §4, step 2).
36
51
  *
37
- * Ausente, a engine procura `#title-icons` — o id que o jogo de plataforma usa desde sempre — e, não o
38
- * achando, diz-o em `problems`.
39
- *
40
- * ✅ E DESDE 2026-09-08 ELA TAMBÉM **MONTA** (etapa 2 do ADR-0106 §4). Este parágrafo dizia «dizer não é
41
- * montar, e a montagem é o passo seguinte»; o passo seguinte aconteceu. O que a destravou foram as etapas
42
- * 1 e 3: nenhuma delas era sobre montar, e sem as duas o `PauseIconsCtx` exigia sete coisas que esta raiz
43
- * não sabe responder por um jogo que não conhece.
44
- *
45
- * ⚠️ Achando o hospedeiro, a barra é escrita e fiada aqui. Não achando, continua a ser `problems` — porque
46
- * a engine pode oferecer os ícones, mas não pode adivinhar ONDE eles cabem no desenho de um jogo alheio.
52
+ * Absent, the engine looks for `#title-icons` — the id the platformer has always used. Found, the bar is written and
53
+ * wired here; not found, it is a line of `problems`: the engine can offer the icons, but it cannot guess WHERE they
54
+ * fit in the layout of a game it does not know.
47
55
  */
48
56
  readonly a11yBarHost?: Element | null;
49
57
  /**
50
- * ONDE O CARTÃO DE PAUSA da primeira tela é pendurado. Ausente, a engine usa `#game-region`.
58
+ * WHERE THE PAUSE CARD of the first screen is hung. Absent, the engine uses `#game-region`.
51
59
  *
52
- * ⚠️ Existe pela mesma razão do `a11yBarHost`: a engine pode OFERECER a pausa, mas não sabe onde ela cabe
53
- * no desenho de um jogo alheio. 📌 E desde o ADR-0120 ONDE já não é SE: a pausa deixou de ser declinável, e
54
- * o que sobra deste campo é o lugar. Um hospedeiro que não aceite filhos vira linha de `problems`, que é a
55
- * diferença entre a engine não saber e a engine calar-se.
60
+ * ⚠️ For the same reason as `a11yBarHost`: the engine OFFERS the pause, but does not know where it fits in the
61
+ * layout of a game it does not know. 📌 And since ADR-0120/ADR-0122 WHERE is not WHETHER: the pause is not
62
+ * declinable, and what this field keeps is the place. A host that takes no children becomes a line of `problems` —
63
+ * the difference between the engine not knowing and the engine keeping quiet.
56
64
  */
57
65
  readonly pauseHost?: Element | null;
66
+ /**
67
+ * WHERE THE VIRTUAL CONTROLLER IS HUNG (ADR-0143). Absent, the engine uses `#game-region`.
68
+ *
69
+ * ⚠️ For the same reason as `pauseHost`: the engine draws the pad from the `preset`, but does not know where it fits
70
+ * in the layout of a game it does not know. And the stylesheet puts `.touch` at `position:absolute` at the bottom of
71
+ * the host — so the host is the game's rectangle, not the page.
72
+ */
73
+ readonly touchHost?: Element | null;
74
+ /**
75
+ * WHERE THE CHILD'S SETTINGS ARE KEPT (ADR-0232, issue #207). The root builds the page's one store from it and hands that
76
+ * store to every module that persists.
77
+ *
78
+ * 📌 OPTIONAL, against ADR-0224/ADR-0227's preference for required ports, because here absence has a SAFE answer rather
79
+ * than a silent one: the host window's `localStorage`, which is what «this browser remembers the child's choices» means.
80
+ * A host passes one to keep a root's settings apart from everything else on the origin — a test file its own
81
+ * `memoryBackend()`, so no file inherits or races another's keys; a page with two roots one each (ADR-0142).
82
+ */
83
+ readonly storage?: StorageLike;
84
+ /**
85
+ * WHO SIGNS IN DEAF MODE (ADR-0234): the Libras player behind the `Interpreter` port, handed the text the sonar reads. Absent:
86
+ * the free player the delivery shipped (`ui/libras-avatar-player`, `inclusionist-heavy --libras`); and where it shipped none,
87
+ * the answer `NO_INTERPRETER` gives — «signing unavailable», a line of `problems` and a notice to the child, with the captions
88
+ * and the text intact. A host's own interpreter always wins; a test hands its double here.
89
+ */
90
+ readonly interpreter?: Interpreter;
58
91
  }
59
92
  /**
60
- * O que este jogo NÃO tem. Declarado, e não deduzido de um getter que devolve null.
93
+ * What this game does NOT have. Declared, not deduced from a getter that returns null.
61
94
  *
62
- * O achado 10 do segundo consumidor é a razão de isto existir como tipo: o quiz precisava se declarar
63
- * "pausado" para navegar os próprios menus, porque a engine não tinha por onde ouvir "eu não tenho fases".
95
+ * Finding 10 of the second consumer is why this exists as a type: the quiz had to declare itself paused to navigate its
96
+ * own menus, because the engine had no way to hear that a game has no phases.
64
97
  */
65
98
  export interface Declinios {
66
- /** Sem assistente de mapeamento de controle. */
67
- readonly semAssistenteDePad?: boolean;
68
- /** Sem "ator da pausa" — quem apertou o botão que abriu o menu. */
69
- readonly semAtorDePausa?: boolean;
99
+ /** No pause actor — who pressed the button that opened the menu. */
100
+ readonly noPauseActor?: boolean;
70
101
  /**
71
- * Sem voz neural — este jogo não abre a porta do ADR-0094.
72
- *
73
- * ⚠️ EXISTE PORQUE A AUSÊNCIA ESTAVA A SER SILENCIOSA, e a medição de 2026-09-08 diz quanto: dos SEIS jogos
74
- * do catálogo local, TRÊS declaram `carregarVozNeural` (platformer, 15-puzzle, 2048) e TRÊS não
75
- * (`game-soccer`, `whackwhack`, `game-chess`). Nos três últimos não há voz neural nenhuma, e nada o dizia.
102
+ * No neural voice — this game does not declare `uses: { neuralVoice: true }` (ADR-0216 §3).
76
103
  *
77
- * ⚠️ E ISSO CONTRADIZ UMA PROMESSA ESCRITA. O ADR-0065 §3 diz que as vozes «fazem parte da engine, e não do
78
- * jogo em si» e que um cartucho «não tem de saber que existe»; o ADR-0094 — com razão, e por 135 MB de WASM
79
- * — passou a exigir UMA LINHA do jogo. As duas coisas podem ser verdade ao mesmo tempo (a engine é dona das
80
- * VOZES, o jogo nomeia o FORNECEDOR), mas só se quem esquece a linha for avisado. Declinar é escolha; não
81
- * declarar era omissão.
104
+ * Exists because the absence is otherwise silent: a game that does not ask for one has only the browser's voice, which a school
105
+ * Chromebook may not have for the child's language. Declining is a choice; not declaring is an omission, and `problems` says so.
82
106
  */
83
- readonly semVozNeural?: boolean;
107
+ readonly noNeuralVoice?: boolean;
84
108
  }
85
109
  export interface CreateGameOptions {
86
- /** Os SETE CAMPOS (core/contract). É o que a pilha de acessibilidade lê, e a única coisa que ela lê. */
110
+ /** The SEVEN FIELDS (core/contract). It is what the accessibility stack reads, and the only thing it reads. */
87
111
  readonly declaration: GameDeclaration;
88
112
  readonly host: EngineHost;
89
113
  readonly declines?: Declinios;
90
114
  /**
91
- * É AGORA hora de navegar menu? A plataforma responde `phase === 'paused'`; um quiz responde `true`.
92
- * Ausente = `true`, que é o caso do jogo sem fases — o mais simples, e o que não obriga a inventar uma.
115
+ * Is it menu-navigation time NOW? The platformer answers `phase === 'paused'`; a quiz answers `true`.
116
+ * Absent = `true`, the case of a game with no phases — the simplest one, and the one that does not force one to be invented.
93
117
  */
94
118
  readonly isNavigable?: () => boolean;
119
+ /** Is the position index (item N of M) on? Absent = yes. See `withIndex` in ui/menu-nav. */
120
+ readonly withIndex?: () => boolean;
95
121
  /**
96
- * O MODO `accessibility` do ADR-0044 (item 7): o direcional dirige a barra rápida em vez do personagem.
122
+ * ADR-0044's `accessibility` MODE (item 7): the directional drives the quick bar instead of the character.
97
123
  *
98
- * Opcionais porque um hospedeiro pode não ter barra nenhuma — sem eles a resposta é "ninguém está nela" e
99
- * nada é chamado. O jogo de plataforma os fornece; um quiz sem HUD de a11y, não.
124
+ * Optional: absent, the engine answers with the bar it mounted itself (`ui/pause-icons`). A game that draws its own
125
+ * bar answers for it.
100
126
  */
101
- /** O índice "N de M" está ligado? Ausente = sim. Ver `comIndice` em ui/menu-nav. */
102
- readonly comIndice?: () => boolean;
103
- readonly naBarraDe?: (i: number) => boolean;
127
+ readonly onBar?: (i: number) => boolean;
104
128
  readonly navBar?: (i: number, k: NavKeys) => void;
105
- /** Jogadores para o teclado remapeável. `Pick<ControlledPlayer,'ctrl'>` — esquema de teclas e nada mais.
106
- * ⚠️ `KeyScheme` e não `Record<string, string[]>` desde a #118: era uma CÓPIA ESTRUTURAL do tipo, e uma
107
- * cópia que ninguém obriga a concordar diverge — é a lição que o próprio `core/entity` abre a dizer, com
108
- * o `DomQuery` (dezasseis cópias) e o `KeyScheme` (seis) como as contas já pagas. */
129
+ /**
130
+ * Players for the remappable keyboard. `Pick<ControlledPlayer,'ctrl'>` — a key scheme and nothing more.
131
+ * ⚠️ `KeyScheme` and not `Record<string, string[]>` (#118): a structural COPY of a type that nothing forces to agree
132
+ * drifts — the lesson `core/entity` opens with.
133
+ *
134
+ * ⚠️ `audioSink` is ADDITIVE AND OPTIONAL: no consumer needs to write it.
135
+ *
136
+ * 🎯 It is here because the seat has a second reader inside the engine. The hearing panel, which the engine mounts,
137
+ * writes into it the audio output the child chose — and `ui/pause-icons` reads it to answer a question that changes
138
+ * what the bar offers: does this child have an output of HER OWN? Without it, changing sound, narration or blind
139
+ * mode on a shared headset would change everyone's audio.
140
+ *
141
+ * 📌 AND ONLY THIS FIELD. The MOTOR fields (`easy`, `toggleMove`, `toggleRun`) are left out: they are required in
142
+ * `MobilityPlayer` — the panel READS them to draw its state — and making them required here would force every game
143
+ * to carry them. That is a contract decision still to be taken, and it is not taken in passing by a cast that would
144
+ * silence the compiler.
145
+ */
109
146
  readonly players?: {
110
147
  ctrl: KeyScheme;
148
+ audioSink?: string | null;
111
149
  }[];
112
- /** Troca de fase, para quem tem fases. Ausente = não faz nada (o jogo sem fases não perde nada). */
150
+ /** Phase change, for a game that has phases. Absent = does nothing (a game without phases loses nothing). */
113
151
  readonly setPhase?: (p: 'title' | 'playing' | 'paused') => void;
152
+ /** Blind mode on? Absent = the engine's own stored value (`core/state.blindMode`). Applies to every player. */
153
+ readonly isBlindMode?: () => boolean;
114
154
  /**
115
- * Os jogadores como a NAVEGAÇÃO SONORA os vê. Ausente, `createGame` DERIVA um do campo 4 do contrato: o
116
- * foco diz onde o jogador está, que é tudo o que o sonar precisa saber sobre posição.
155
+ * THIS GAME'S POSITIONS AND THE KEYS OF THEIR WORDS (`core/actions`). Without them the engine does not know HOW MANY
156
+ * actions to ask of a transport, and ADR-0079 §3's guarantee cannot be measured (issue #112).
157
+ *
158
+ * 🔴 KEYS of `dictionaries`, never words (ADR-0232 D3, erratum of 2026-09-25): the root's translator resolves them each
159
+ * time the engine draws or speaks them, so a language change reaches the help, the remap screen, the pad and the scan.
160
+ * A key the dictionaries lack leaves its position unnamed and is a line of `problems`.
117
161
  *
118
- * Um jogo com vários jogadores, ou com dispositivo de áudio por jogador, fornece a sua lista. Um jogo de
119
- * uma criança só não fornece nada — e ganha sonar assim mesmo, que é o ponto.
162
+ * Optional because a game may not declare a preset yet; without it the reach notice simply does not appear, and the
163
+ * help and the scan have no words to show.
120
164
  */
121
- readonly sonarPlayers?: () => SonarPlayer[];
122
- /** Modo cego ligado? Ausente = não. Vale para todos os jogadores, como no jogo de plataforma. */
123
- readonly isBlindMode?: () => boolean;
165
+ readonly preset?: ActionPreset;
166
+ /**
167
+ * THE ON-SCREEN PAD, only when the cartridge asks for it (ADR-0166): «Controle de tela é só para jogo que não funciona tão
168
+ * bem como via mouse e/ou touch (o cartucho decide), nunca para menu.» Absent = no pad — a game played by touching its
169
+ * own elements, and every menu, need none on top of them. It leaves the pause card and the panels; it stays on the quick
170
+ * pause.
171
+ */
172
+ readonly onScreenPad?: boolean;
173
+ /**
174
+ * THE NUMBERS THIS GAME SHOWS, each in the band of what it is about (ADR-0168, ADR-0175; issue #162). The engine mounts
175
+ * the HUD and places them: `identity` top left and `mission` under it, `power` top right (under the clock), `learning` bars (one to three, as
176
+ * `educational/segment-bar.barOf` returns them) centred in the footer, under the explanation; the room the game leaves free at the
177
+ * top (`--barra-a11y-h`) grows by what they take. Absent = no HUD mounted, and the game keeps drawing its own.
178
+ * 📏 Measured on 2026-09-13: six sibling games, six HUDs of their own, none in the bands.
179
+ * A malformed list is refused at boot and at `mount`, like the declaration.
180
+ */
181
+ readonly hud?: readonly HudNumber[];
124
182
  /**
125
- * AS PALAVRAS DESTE JOGO (`core/actions`). Sem elas a engine não sabe QUANTAS ações pedir a um transporte,
126
- * e a garantia do ADR-0079 §3 não tem como ser medida — foi a lacuna que a issue #112 encontrou: a
127
- * aritmética existia, testada, e o `createGame` tinha ZERO ocorrências de qualquer coisa sobre ações.
183
+ * THE OPTIONS OF THIS GAME, as rows the engine draws (ADR-0182; issue #178): the keys of a label and hint in the game's
184
+ * dictionary (resolved at every drawing, ADR-0232 D3), a kind (steps, list or switch), how to read the value and how to write it. «Opções do jogo» opens them in a panel of the
185
+ * engine's own; absent or empty, the door stays on the card locked with its reason (ADR-0161). A cartridge draws its own
186
+ * options only where rows cannot express what it needs. A malformed list is refused at boot and at `mount`.
187
+ */
188
+ readonly gameOptions?: readonly GameOption[];
189
+ /**
190
+ * HOW TO PLAY THIS GAME, as slides the help shows before the buttons (ADR-0195; issue #188): «O "Como jogar" é justamente algo a
191
+ * ser feito pelo cartucho.» Each slide's text is a KEY of `dictionaries`, resolved at every showing in the page's language
192
+ * (ADR-0232 D3); its figure, when given, is drawn
193
+ * by the cartridge on a surface the engine gives, with the time for an animation (still under reduced motion). Absent = the help
194
+ * shows the buttons alone. A malformed list is refused at boot and at `mount`.
195
+ */
196
+ readonly howToPlay?: readonly HowToPlaySlide[];
197
+ /**
198
+ * THIS GAME'S DICTIONARIES, one per language (`pt`, `en`, `es`), registered into THIS ROOT's translator before anything is
199
+ * translated (ADR-0232 D3 erratum): a key resolves for this game and for no other root on the page. A string with markup is
200
+ * refused and named in the console; a key given in one language and not another is a line of `problems`.
201
+ *
202
+ * 🔴 THE ONE PLACE A GAME'S WORDS LIVE (ADR-0232 D3, erratum of 2026-09-25): every word the game DECLARES — `preset`,
203
+ * `accommodations`, `gameOptions`, `howToPlay`, `hud` — is a key of these, and a declared key they lack is a line of
204
+ * `problems` and is never shown. A cartridge `mount()` swaps in may bring its own; they are added to these.
205
+ * Decision (mechanical, ADR-0232 D3): an option and not a method on the handle, because the markup is translated at boot.
206
+ */
207
+ readonly dictionaries?: Readonly<Record<string, Readonly<Record<string, string>>>>;
208
+ /**
209
+ * THE VIRTUAL CONTROLLER'S COMMANDS, CARRIED TO THE GAME (ADR-0111 and its erratum; issue #197): «a engine lida com o hardware e passa
210
+ * para o jogo o nome virtual do botão». Each press and release of a position the child's hardware reached — the keyboard by the
211
+ * child's scheme, the eyes — with its source and seat. What it executes is the game's, named by its `preset`. Not called while a menu
212
+ * has the directional. Absent = the game hears commands only through what it reads itself.
213
+ */
214
+ readonly onCommand?: (command: VirtualCommand) => void;
215
+ /**
216
+ /**
217
+ * WHAT ONLY THIS GAME KNOWS ABOUT THE GAMEPAD (ADR-0224). The engine mounts the transport; these are the few answers
218
+ * nothing in it can know — the title screen, the attract demo, the modal, joining and respawning a seat, the waiting
219
+ * badge, the wizard's art, and whether the world is running.
128
220
  *
129
- * Opcional porque um jogo pode não declarar preset ainda; sem ele o aviso de alcance simplesmente não
130
- * aparece, que é o comportamento de hoje e não uma regressão.
221
+ * 🎯 **Absent altogether is an answer**, not an oversight: the gamepad works, and what depends on the cartridge's
222
+ * world simply does not happen. Each absence is written in `GamepadGameHooks`.
131
223
  */
132
- readonly preset?: ActionPreset;
224
+ readonly gamepad?: GamepadGameHooks;
133
225
  /**
134
- * COMO SE CARREGA A VOZ NEURAL — uma linha do lado do jogo (ADR-0094):
226
+ * THE ACCOMMODATIONS THAT HAVE A SUBJECT IN THIS GAME — the cartridge's answer, REQUIRED (ADR-0153).
135
227
  *
136
- * carregarVozNeural: () => import('@mintplex-labs/piper-tts-web')
228
+ * 🔴 For each of the sixteen only the game can answer (`GAME_KEYED` in `core/accommodations`): the KEYS of the game's
229
+ * word in `dictionaries` (ADR-0232 D3), if it has a subject here, or `false`. In the Dev's words: «Gênero não precisa responder todas as acomodações, mas
230
+ * sim o cartucho, obrigatoriamente.»
137
231
  *
138
- * ⚠️ AUSENTE POR OMISSÃO, E ISSO É A DECISÃO E NÃO UM DESCUIDO. A engine não pode nomear o fornecedor:
139
- * ele traz `onnxruntime-web` como peer NÃO-opcional, que o npm instala sozinho — **135,4 MB** no
140
- * `node_modules` de todo consumidor, incluindo um jogo que nunca fale por voz neural. E declará-lo em
141
- * `devDependencies`, que era o estado até 06/09, publicou uma engine que NÃO COMPILAVA para ninguém
142
- * (ADR-0093). A porta é a única forma que resolve as duas coisas ao mesmo tempo.
232
+ * ⚠️ REQUIRED, by the `holdsAtOnce` rubric: there is no safe default — yes mounts the wheelchair in chess, no
233
+ * hides it from the platformer — and forgetting fails INVISIBLY to whoever writes the game. A missing or incomplete
234
+ * answer is a malformed declaration, and the boot REFUSES.
143
235
  *
144
- * Sem ela a narração cai na voz do navegador (Web Speech), que fala o idioma certo e não pesa nada — e o
145
- * painel de áudio deixa de OFERECER o motor neural, em vez de o oferecer e nunca o carregar.
236
+ * 📌 The general ones always mount and the contract's are derived; none of them is answered here.
237
+ */
238
+ readonly accommodations: AccommodationAnswers;
239
+ /**
240
+ * The game's genre, OPTIONAL (ADR-0153), from the engine's list (`core/genres`, ADR-0156): what the game plays like.
241
+ * The cartridge chooses it and nobody assigns it. Casino game and a name outside the list refuse the boot; Horror game
242
+ * boots and `problems` carries its «avoid» mark.
146
243
  */
147
- readonly carregarVozNeural?: CarregarVozNeural;
244
+ readonly genre?: string;
148
245
  /**
149
- * BAIXAR AS COISAS PESADAS NO PRIMEIRO CARREGAMENTO? Padrão **sim** (ADR-0110 (b), ADR-0116, ADR-0119).
246
+ * WHAT THIS GAME USES OF THE VOICE (ADR-0216 §3) — never how. Two answers, and each one is a sentence about the child, not
247
+ * about a library:
248
+ *
249
+ * · `neuralVoice: true` — a child who cannot read is read TO by this game, so it wants a voice even where the device has
250
+ * none of its own. The engine loads Kokoro from the delivery at the first such utterance (ADR-0216 §1); the game names no
251
+ * phonemizer, runtime or model. Absent, no Kokoro voice is listed, the audio panel does not offer the neural engine, and
252
+ * the 371 MiB of model, voices and runtime never enter the delivery.
253
+ * · `reading: true` — a child reads aloud TO this game and it wants the text; the engine decides who hears her, and a
254
+ * delivery carries the reading models of pt, en and es because of this answer (each device keeps the three, her
255
+ * language's first: she can switch at any moment). Absent, `motor.reading.listen()` refuses and
256
+ * says which line is missing: a game that asks for a microphone it never declared would also be a delivery without the
257
+ * model, which is a silence in a school nobody can debug.
258
+ * · `fonts: ['Lato', 'Press Start 2P']` — the font LIBRARY's families this game draws with (ADR-0255). The engine packages only
259
+ * its own faces (the typography button's and the mathematics face); every other family is delivered in `heavy/` when the
260
+ * delivery is built with `inclusionist-heavy <folder> --fonts …`. The engine writes their `@font-face` rules and keeps each
261
+ * file in the checked cache for the days without a network; a family the library does not hold, or the delivery did not
262
+ * carry, is a line of `problems`. The engine's own faces need no declaration.
263
+ */
264
+ readonly uses?: {
265
+ readonly reading?: boolean;
266
+ readonly neuralVoice?: boolean;
267
+ readonly fonts?: readonly string[];
268
+ };
269
+ /**
270
+ * FETCH THE HEAVY FILES ON THE FIRST LOAD? Default **yes** (ADR-0110 (b), ADR-0116, ADR-0119).
150
271
  *
151
- * As quatro vozes neurais são ~241 MB e descem em SEGUNDO PLANO, uma de cada vez, sem bloquear o jogo: a
152
- * criança joga enquanto elas chegam, e o que não pode acontecer é ela voltar no segundo dia, sem rede, e
153
- * descobrir que a voz nunca foi buscada. O pilar 8 é «primeiro dia ONLINE, depois offline-first», e o
154
- * ADR-0116 tirou a contradição que travava isto — instalar já é um acto de rede.
272
+ * The vision runtime and models, and with `uses.neuralVoice` Kokoro's model, voices and runtime (371 MiB), come down in the
273
+ * BACKGROUND, one at a time, without blocking the game: the child plays while they arrive, and what must not happen is a child
274
+ * back on the second day, offline, finding they were never fetched. Pillar 8 is «first day ONLINE, then offline-first», and
275
+ * ADR-0116 removed the contradiction that blocked this — installing is already a network act.
155
276
  *
156
- * ⚠️ PÔR `false` É PARA QUEM TEM RAZÃO PARA O FAZER, e a razão que já existe é um TESTE: um caso que monte
157
- * o arranque num navegador de verdade não pode disparar 241 MB contra o Hugging Face. Um jogo em produção
158
- * que o desligue está a decidir que a criança dele fica sem voz neural offline.
277
+ * ⚠️ `false` IS FOR WHOEVER HAS A REASON, and the reason that exists is a TEST: a case that mounts the start in a real browser
278
+ * cannot fire hundreds of MB at the network. A game in production that turns it off decides its child has no neural voice offline.
159
279
  *
160
- * 📌 E o ADR-0117 diz que quem devia pagar isto uma vez é a PLATAFORMA, não cada cartucho — a Cache Storage
161
- * é particionada por origem, e num site só os 241 MB descem uma vez para todos os jogos. Enquanto a
162
- * plataforma não os pede, é o jogo que os pede: melhor descer duas vezes do que nunca.
280
+ * 📌 ADR-0117 says the one who should pay this once is the PLATFORM, not each cartridge — the Cache Storage is partitioned by
281
+ * origin, and on one site the heavy files come down once for every game. Until the platform asks for them, the game does: better
282
+ * twice than never.
163
283
  */
164
- readonly baixarPesados?: boolean;
284
+ readonly downloadHeavy?: boolean;
165
285
  /**
166
- * O QUE ACONTECEU COM CADA COISA PESADA, à medida que acontece. Ausente = ninguém está a ver.
286
+ * WHAT HAPPENED TO EACH HEAVY FILE, as it happens. Absent = nobody is watching.
167
287
  *
168
- * ⚠️ É AQUI E NÃO EM `problems` porque a descarga é de FUNDO: `problems` é devolvido sincronamente pelo
169
- * `createGame`, e uma linha que chegue depois disso entra num vector que o leitor já leu. O ADR-0110 pede
170
- * que uma busca falhada seja REPORTADA — reportar é ter um canal que existe quando a notícia chega, e não
171
- * empurrar para uma lista que já foi entregue.
288
+ * ⚠️ HERE AND NOT IN `problems`, because the download runs in the BACKGROUND: `createGame` returns `problems` synchronously, and a
289
+ * line arriving later lands in an array its reader already read. ADR-0110 asks that a failed fetch be REPORTED — reporting is having
290
+ * a channel that exists when the news arrives, not pushing into a list already delivered.
172
291
  *
173
- * 📌 A engine não inventa superfície nenhuma com isto: quem sabe onde cabe «faltam 241 MB» na tela de um
174
- * jogo é o jogo. `pesoPorBaixar(relatorio)` dá o número para a frase.
292
+ * 📌 The engine invents no surface for this: where «N MB left» fits on a game's screen is the game's to know.
293
+ * `bytesLeftToDownload(relatorio)` gives the number for the sentence.
175
294
  */
176
- readonly aoProgredirPesados?: (r: RelatorioPesado) => void;
295
+ readonly onHeavyProgress?: (r: HeavyReport) => void;
177
296
  /**
178
- * Como se descobre que cada transporte está aqui. Ausente = a engine pergunta ao aparelho.
297
+ * How each transport is found to be here. Absent = the engine asks the device.
179
298
  *
180
- * Injetável porque «há um controle ligado?» e «isto é uma tela de toque?» são perguntas ao navegador, e um
181
- * teste que não as possa responder não consegue exercitar a tela que depende delas.
299
+ * Injectable because whether a gamepad is connected and whether this is a touch screen are questions to the browser, and a
300
+ * test that cannot answer them cannot exercise the screen that depends on them.
182
301
  */
183
- readonly disponibilidade?: Disponibilidade;
302
+ readonly availability?: Availability;
184
303
  /**
185
- * O QUE CADA ITEM DO CARTÃO DE PAUSA FAZ NESTE JOGO — «continuar», «sair», «ajuda», o que o jogo ligar.
304
+ * WHAT EACH ITEM OF THE PAUSE CARD DOES IN THIS GAME — «continuar», «sair», «ajuda», whatever the game wires.
186
305
  *
187
- * 🔴 ESTE CAMPO FALTAVA, E A FALTA ALCANÇAVA TODOS OS CONSUMIDORES DE UMA VEZ. O `initPauseIcons`
188
- * aceita `getPauseActs` desde que existe; esta raiz não o passava e não tinha campo para ele, logo
189
- * **nenhum jogo montado por `createGame`** conseguia ligar um item. O `refrescarItensDaPausa` esconde o
190
- * que não acciona — o §5 do ADR-0106, que proíbe botão morto — e o resultado era um cartão com os TRÊS
191
- * itens que a engine acciona sozinha (`ITENS_DA_ENGINE`) e nada mais, em todo o catálogo.
306
+ * 🔴 The engine hides every item nothing acts on (ADR-0106 §5, no dead buttons — `refreshPauseItems` in
307
+ * `ui/pause-icons`), so without this field a game could wire no item beyond the ones the engine acts on itself
308
+ * (`ENGINE_ITEMS` and the engine's own actions below). The game's table is spread over the engine's, so the game wins.
192
309
  *
193
- * ⚠️ E O CUSTO MAIOR NÃO ERA O CARTÃO, ERA A BARRA. O `entrarNaBarra` chama `acts.resume?.()` para sair
194
- * do cartão antes de entregar as direcções à barra de acessibilidade; com a tabela vazia esse `resume` era
195
- * `undefined`, o cartão ficava por cima do jogo, e o item 7 do ADR-0044 — o direccional a conduzir a barra
196
- * — era **inalcançável a partir de qualquer jogo**.
197
- *
198
- * 📌 FUNÇÃO e não valor, pela razão que o próprio `ui/pause-icons` regista: a tabela de um jogo muda
199
- * durante a partida (um «sair» que só liga depois da primeira fase), e congelá-la no arranque já partiu
200
- * um caso lá dentro. Ausente = tabela vazia, que é o comportamento de sempre.
310
+ * 📌 A FUNCTION and not a value, for the reason `ui/pause-icons` records: a game's table changes during a match (a
311
+ * «sair» that is wired only after the first level), and freezing it at boot broke a case there. Absent = only what
312
+ * the engine acts on.
201
313
  */
202
314
  readonly getPauseActs?: () => Record<string, (() => void) | undefined>;
203
315
  /**
204
- * QUEM ABRIU A PAUSA, quando há mais de um assento — o painel de controle edita o assento DESTE índice.
316
+ * WHO OPENED THE PAUSE, when there is more than one seat — the keyboard panel edits the seat of THIS index.
205
317
  *
206
- * ⚠️ A RAIZ JÁ SE DENUNCIAVA POR NÃO TER ISTO: o bloco 4d empurra uma linha de `problems` quando um jogo
207
- * declara mais de um jogador, porque sem ator da pausa a criança do SEGUNDO assento não tem como remapear.
208
- * O que faltava para a linha ser accionável era este campo — até agora ela dizia «conserte» sem haver por
209
- * onde, e a única saída era declarar `semAtorDePausa`, que é aceitar a perda em vez de a corrigir.
318
+ * ⚠️ Without it the child in the SECOND seat cannot remap, and `problems` says so for a game that declares more than
319
+ * one player and does not answer this (`core/cartridge-problems`).
210
320
  */
211
- readonly setPauseActor?: (i: number, ...resto: unknown[]) => void;
321
+ readonly setPauseActor?: (i: number, ...rest: unknown[]) => void;
212
322
  /**
213
- * COMO ESTE JOGO REPINTA PARA ALTO CONTRASTE, e como corrige daltonismo — os dois eixos do ADR-0104.
323
+ * HOW THIS GAME REPAINTS FOR HIGH CONTRAST, and how it corrects colour vision — the two axes of ADR-0104.
214
324
  *
215
- * 🔴 SEM ELES OS ÍCONES ⚫ E 🚥 NÃO SÃO MONTÁVEIS POR NENHUM JOGO. O `iconesQueAccionam` só os monta
216
- * para quem entrega quem os escreve, e essa regra está certa — um ícone que não acciona é pior que um
217
- * ícone a menos. O que estava errado era não haver PORTA: o consumidor externo que mediu isto leu a
218
- * ausência como «este jogo tem os seus próprios controles», o que é verdade sobre o resultado e falso
219
- * sobre a causa. Uma lacuna que o consumidor lê como escolha é a pior forma de lacuna.
325
+ * 🔴 THE ⚫ ICON IS OFFERED ONLY TO A GAME THAT HANDS IN A WRITER. `iconsThatAct` mounts an icon only for whoever
326
+ * hands in its writer, and that rule is right — an icon that acts on nothing is worse than one icon fewer. What
327
+ * these fields add is the DOOR: without it, an outside consumer read the absence as this game having its own
328
+ * controls, true about the result and false about the cause. A gap the consumer reads as a choice is the worst kind.
329
+ * The 🚥 has an engine default (a filter over the world, below); a game that corrects colour in its own render wins.
220
330
  *
221
- * ⚠️ SÃO DOIS CAMPOS E NÃO UM, porque são duas perguntas: um jogo pode saber repintar texturas e não ter
222
- * como corrigir cor, ou o contrário. O `game-pinball` é o segundo caso — a imagem dele é um framebuffer
223
- * de 320x180 sem textura para repintar, e o filtro de cor ele aplica há semanas.
331
+ * ⚠️ TWO FIELDS AND NOT ONE, because they are two questions: a game may know how to repaint textures and have no way
332
+ * to correct colour, or the other way round — `game-pinball`'s image is a 320×180 framebuffer with no texture to
333
+ * repaint, and it applies the colour filter itself.
224
334
  */
225
- readonly setTemaDoJogador?: (i: number, tema: Tema) => void;
226
- readonly setCorrecaoDoJogador?: (i: number, correcao: Correcao) => void;
335
+ readonly setPlayerTheme?: (i: number, theme: Theme) => void;
336
+ readonly setPlayerCorrection?: (i: number, correction: Correction) => void;
227
337
  }
228
338
  export interface Engine {
229
339
  readonly declaration: GameDeclaration;
230
340
  /**
231
- * A PAUSA QUE ESTA RAIZ MONTOU — mostrar e esconder, sem o consumidor caçar id nenhum.
232
- *
233
- * ⚠️ EXISTE PORQUE MONTAR NÃO É MOSTRAR, e a etapa 2 do ADR-0106 tinha ficado a meio sem que nada o
234
- * dissesse. O cartão nasce `hidden` (é assim que o `buildScreenPause` o entrega, e tem de ser: a pausa
235
- * abre-se, não está aberta) e QUEM O REVELA é o `ui/shell`, por fase — que esta raiz **não monta**, de
236
- * propósito: «não substitui o boot do main.js, que tem catorze anos de ordem própria».
341
+ * THE PAUSE THIS ROOT MOUNTED — show and hide, without the consumer hunting for any id.
237
342
  *
238
- * ⚠️ Sem estas duas, um jogo montado por `createGame` ficava com um cartão de pausa que NADA mostrava. Não
239
- * é «o consumidor esqueceu-se»: não havia por onde, a não ser procurar `#vp-pause-0` no documento — que é
240
- * exactamente o tipo de conhecimento que este ficheiro existe para não exigir.
343
+ * ⚠️ IT EXISTS BECAUSE MOUNTING IS NOT SHOWING. The card is born `hidden` (that is how `buildScreenPause` delivers
344
+ * it, and it must be: a pause is opened, not open). The engine opens it on SELECT, the ☰ and the quick pause's
345
+ * `action4`; these two let a game open and close it too, without looking for `#vp-pause-0` in the document — exactly
346
+ * the kind of knowledge this file exists not to demand.
241
347
  *
242
- * 📌 A engine OFERECE o mecanismo e não toma a fase. Quando abrir a pausa continua a ser do jogo, porque só
243
- * ele sabe o que é estar a jogar; o que deixa de ser dele é saber COMO.
348
+ * 📌 The engine OFFERS the mechanism and does not take the phase. When to open the pause stays the game's, because
349
+ * only it knows what playing is; what stops being its business is HOW.
244
350
  */
245
- readonly pausa: {
246
- /** Revela o cartão da tela `i` e refaz os itens — o §5 avaliado no instante em que ela abre. */
247
- readonly mostrar: (i: number) => void;
248
- /** Esconde-o outra vez. */
249
- readonly esconder: (i: number) => void;
351
+ readonly pause: {
352
+ /** Reveals screen `i`'s card and rebuilds its items — ADR-0106 §5 evaluated the moment it opens. */
353
+ readonly show: (i: number) => void;
354
+ /** Hides it again. */
355
+ readonly hide: (i: number) => void;
250
356
  };
251
357
  readonly tts: ReturnType<typeof createTts>;
358
+ /**
359
+ * THE CHILD READS ALOUD AND THIS ANSWERS WITH TEXT (ADR-0216, issue #200; the Dev, 2026-09-21: «o jogo… apenas deve pedir
360
+ * para ouvir e receber o texto»). `listen()` ends when she goes quiet or the ceiling falls; `stop()` gives the microphone
361
+ * back; `ready()` says whether this device can hear her language at all. Which recogniser hears her is the engine's
362
+ * business — and never one that would send her voice to a server. A game that wants it declares `uses: { reading: true }`.
363
+ */
364
+ readonly reading: Reading;
365
+ /**
366
+ * THE SOUND CAPTION, hosted by the engine (study item D3; ADR-0014; ADR-0164 rules 4–5): a line for eyes that cannot
367
+ * hear, in the screen footer above the explanation, at most two lines, gone after a moment. Written only while the
368
+ * child has captions on. Pass it as `createAudioEarcons`'s `showCaption`.
369
+ * 📏 Before it, each game wrote its own `#caption` with its own timer (platformer 1300 ms, soccer 2600 ms).
370
+ */
371
+ readonly captionSound: (text: string) => void;
372
+ /**
373
+ * THE GAME EXPLAINS THE ITEM UNDER ITS OWN CURSOR IN THE ENGINE'S FOOTER (ADR-0244): `text`, already in the child's language as
374
+ * `say` takes it, becomes the footer's RESTING text — the engine's own items (a quick-bar icon, a pause-card item) take the
375
+ * footer while pointed and give it back to this text when they leave. `null` clears it; call it again when the cursor moves
376
+ * or the language changes, and with `null` when the screen it explains goes. At most two lines (ADR-0164): a longer text is
377
+ * clamped there, so say it whole through narration too. Releasing the cartridge clears it.
378
+ */
379
+ readonly explain: (text: string | null) => void;
380
+ /**
381
+ * The game speed the child chose on the quick bar (ADR-0180): 1 is 100%, down to 0.5. Pass it as `startLoop`'s REQUIRED
382
+ * `speed` (ADR-0232 D2c), which multiplies the frame time by it; a game that runs its own frames multiplies by this.
383
+ */
384
+ readonly gameSpeed: () => number;
385
+ /**
386
+ * Does the child want the «N de M» said after an item (ADR-0044 item 3)? Read at each announcement: the hearing panel
387
+ * turns it off and on. A game that announces its own items asks here instead of reading the settings store by import
388
+ * (ADR-0232 D2c), as it asks `gameSpeed` — the demo quiz does.
389
+ */
390
+ readonly menuIndexOn: () => boolean;
391
+ /**
392
+ * TRANSLATES IN THIS ROOT'S LANGUAGE (ADR-0232 D3): the root's own `t`, the one every engine module receives. A game that
393
+ * writes its own words asks here instead of importing `core/i18n` — the demo quiz does — as it asks `gameSpeed`.
394
+ */
395
+ readonly t: Translate;
396
+ /**
397
+ * Resolves when the language chosen at boot has loaded — at once for pt (ADR-0232 D3). A game that draws its first screen
398
+ * waits on it, or the screen is born in the fallback language.
399
+ */
400
+ readonly localeReady: () => Promise<void>;
401
+ /**
402
+ * THE PAGE'S LANGUAGE (`pt`, `en`, `es`) as this root speaks it, and the door to switch it — the one the 🌐 on the bar uses.
403
+ * Since `core/i18n` holds no state (ADR-0232 D3, erratum of 2026-09-25) a game asks here instead of `getLocale`/`setLocale`
404
+ * by import; a switch is kept, told to the page, and followed by every root on it.
405
+ */
406
+ readonly locale: () => string;
407
+ readonly setLocale: (code: string) => Promise<void>;
408
+ /**
409
+ * THIS ROOT'S SCREEN-READER ANNOUNCEMENTS (ADR-0232 D4): «polite» (`#sr-status`, does not interrupt) — the announcer every
410
+ * engine module receives. A game announces HERE instead of importing `core/a11y-sr`, which is a factory now: a second
411
+ * announcer would write the same regions but carry none of this root's Libras mirror.
412
+ */
413
+ readonly say: (text: string) => void;
414
+ /** The «assertive» announcement (`#sr-alert`): interrupts and speaks now — errors, a checkmate, a crash. See `say`. */
415
+ readonly alert: (text: string) => void;
416
+ /**
417
+ * Every `say`/`alert` — the engine's and the game's — also goes to `sink`, until the returned release. The root connects
418
+ * nothing. ⚠️ Deaf mode does NOT sign announcements: the interpreter signs what the sonar finds, when the child asks
419
+ * (ADR-0234) — no queue of messages.
420
+ */
421
+ readonly mirrorAnnouncements: (sink: (text: string) => void) => () => void;
422
+ /**
423
+ * THIS ROOT'S DEAF MODE (ADR-0234): the one the bar's 🦻 toggles. With it on, every sound is captioned and the sonar has
424
+ * the interpreter sign what it found; `isOn` is the child's choice, `toggle` flips it from a game's own control.
425
+ * `captionsOn` is whether a sound gets its caption now — the captions setting OR deaf mode: pass it as
426
+ * `createAudioEarcons`'s `getCaptionsOn`, beside `captionSound` as its `showCaption`.
427
+ */
428
+ readonly deafMode: {
429
+ readonly isOn: () => boolean;
430
+ readonly toggle: () => void;
431
+ readonly captionsOn: () => boolean;
432
+ };
433
+ /**
434
+ * THIS ROOT'S SETTINGS STORE (ADR-0232 D4): the child's settings, read through live getters (`settings.blindMode`) and written
435
+ * by the setters, plus the bus a game subscribes and emits on. A game reads HERE instead of importing `core/state`, which is
436
+ * a factory now: a second store would read the same storage but hear none of this root's changes.
437
+ */
438
+ readonly settings: SettingsStore;
439
+ /**
440
+ * WHERE A GAME MOUNTS ITS MAP (ADR-0239 point 4): the bottom-right cell of the HUD row. The engine draws nothing in it — an
441
+ * empty slot takes no room — and a game with a map appends its own element here. `null` where the page has no
442
+ * `#game-region` to hold the row.
443
+ */
444
+ readonly mapSlot: HTMLElement | null;
445
+ /**
446
+ * THIS ROOT'S INPUT STATE (ADR-0232 D4): the held keys and who pressed them, the transport in use per player, the pads'
447
+ * frames, and `held(player, action)`. A game reads and writes HERE instead of importing `input/state`, which is a factory
448
+ * now: a second instance would hold keys this root's transports never press.
449
+ */
450
+ readonly input: LiveInput;
451
+ /**
452
+ * THIS ROOT'S KEYBOARD MAP and its doors (ADR-0232 D4): `kb()` the live map, `set`, `save`, `reset` (back to the GAME's
453
+ * default, ADR-0115), `factoryWithGame()` and `load()`. For a game with a remapping screen of its own — `game-2048`'s —
454
+ * instead of importing `input/keyboard`'s module map, which no longer exists.
455
+ */
456
+ readonly keyboardConfig: KeyboardConfigApi;
457
+ /**
458
+ * THIS ROOT'S SOUND (ADR-0232 D4): the context (made at the first sound, from the host's window), the master, the mixer and
459
+ * the syntheses. Read through live getters (`audio.soundOn`, `audio.audioCat`) and moved by its methods (`ensureAC()`,
460
+ * `tone(…)`). A game asks HERE instead of importing `platform/audio`, which is a factory now: a second one would make a
461
+ * second context and a second mixer, deaf to this root's volume and categories.
462
+ */
463
+ readonly audio: Audio;
464
+ /**
465
+ * THIS ROOT'S CRT (ADR-0232 D4): its live config, `apply()` (classes on `#game-region`, kept in the store) and
466
+ * `scanVars()`, which re-anchors the scanlines to real pixels. A game that scales its own stage with `ui/layout`'s
467
+ * `createLayout` passes `engine.crt.scanVars` as `afterScale`; a game with a CRT menu of its own reads and writes `cfg`
468
+ * and calls `apply()` here instead of importing `render/crt`, as it asks `gameSpeed`.
469
+ */
470
+ readonly crt: Crt;
471
+ /**
472
+ * THIS ROOT'S L→Q CONTRAST ENHANCEMENT (ADR-0232 D4): `filter()` for composing a CSS filter, `t()` the amount, `set(t)`
473
+ * to change it (kept in the store; the root recomposes the world's filter). A game asks here instead of importing
474
+ * `render/lq-filter`.
475
+ */
476
+ readonly lq: LqFilter;
477
+ /**
478
+ * MEASURES WHAT THE WORLD'S CANVAS FLASHES for `ms`, against the WCAG 2.3.1 general flash threshold (study item B2;
479
+ * `core/flash-threshold`). Only when called — reading pixels every frame costs a school machine (pillar 1), so play never
480
+ * pays for it. A failure is also a line of `problems`. `lido: false` says why nothing was measured (no canvas, a canvas
481
+ * the page may not read, or one that reads transparent, as a WebGL canvas without `preserveDrawingBuffer` does) — never a
482
+ * pass by silence. The red flash is not measured.
483
+ */
484
+ readonly measureFlashes: (ms: number) => Promise<FlashMeasurement>;
252
485
  readonly overlays: SettingsPanelApi;
253
486
  readonly nav: MenuNavApi;
254
487
  readonly keyboard: KeyboardRuntime;
255
488
  /**
256
- * APLICA O FILTRO DE VISÃO NO MUNDO QUE ESTE JOGO DECLAROU (ADR-0087).
489
+ * THE CONTROLLER OBJECT, offered to the cartridge (ADR-0216, in the Dev's words: «Assim como a engine oferece o objeto de
490
+ * controle, ela deve oferecer objetos de leitura e TTS»).
491
+ *
492
+ * 📌 Every transport the engine knows — keyboard, gamepad (ADR-0224), touch, eyes, face, hands, voice, scan — is mounted
493
+ * here and presses this controller by itself. This is for a transport a game mounts ITSELF: pressing a position by hand
494
+ * is telling the engine that the child's device produced that position — with the source, which ADR-0109 demands.
495
+ *
496
+ * ⚠️ What it is NOT: a second way for the game to receive input. The game receives through `onCommand`.
497
+ */
498
+ readonly controller: VirtualController;
499
+ /**
500
+ * APPLIES THE VISION FILTER TO THE WORLD THIS GAME DECLARED (ADR-0087).
257
501
  *
258
- * ⚠️ Existe porque, sem ela, cada consumidor escrevia a sua — e o `game-15puzzle` escreveu, com o
259
- * raciocínio certo e sozinho. O que ela acrescenta é a regra dos MENUS, que um consumidor não tem como
260
- * saber: eles vivem por cima da simulação e são o instrumento de sair dela, então se herdaram o filtro por
261
- * estarem DENTRO do mundo, ele é desfeito neles. Uma cegueira que apagasse o menu de pausa trancaria a
262
- * criança dentro da simulação (#82).
502
+ * ⚠️ It exists because, without it, each consumer wrote its own — and `game-15puzzle` did, with the right reasoning
503
+ * and on its own. What it adds is the rule of MENUS, which a consumer has no way to know: they live over the
504
+ * simulation and are the way out of it, so if they inherited the filter by being INSIDE the world, it is undone on
505
+ * them. A blindness that blacked out the pause menu would lock the child inside the simulation (#82).
263
506
  */
264
- readonly aplicarFiltroDeVisao: (css: string, alcance: AlcanceDoFiltro) => void;
507
+ readonly applyVisionFilter: (css: string, reach: FilterReach) => void;
265
508
  /**
266
- * A NAVEGAÇÃO SONORA, pronta e ligada à declaração deste jogo (item 19).
509
+ * NAVIGATION SOUND, ready and wired to this game's declaration (item 19).
267
510
  *
268
- * Ela vem de graça porque o sonar deixou de precisar de tiles: pergunta topologia (campo 1), alvos (campo
269
- * 5) e nome (campo 3), e o jogo já declarou os três para existir. Era o achado 9 do segundo consumidor —
270
- * "ligá-lo exigiria MENTIR para a engine" —, e a mentira era exigida pela FORMA da pergunta, não pelo som.
511
+ * It comes for free because the sonar no longer needs tiles: it asks for topology (field 1), targets (field 5) and
512
+ * names (field 3), and the game already declared all three to exist. It was finding 9 of the second consumer: wiring
513
+ * the sonar meant LYING to the engine, and the lie was demanded by the SHAPE of the question, not by the sound.
271
514
  */
272
515
  readonly sonar: AudioSonar;
273
516
  /**
274
- * A PILHA DE CENAS (item 22, C3 do ADR-0030), vazia e pronta.
517
+ * THE SCENE STACK (item 22, C3 of ADR-0030), empty and ready.
275
518
  *
276
- * Vem de `createGame` e não de cada jogo pelo mesmo motivo do sonar: é infraestrutura, e um jogo que a
277
- * montasse sozinho montaria a décima quinta versão de push/pop. O que ela substitui é o
278
- * `phase: 'title' | 'playing' | 'paused'` — um enum DESTE jogo que doze módulos leem, e que um jogo com
279
- * mapa de fases ou tela de resultados não teria como estender sem pedir constante nova à engine (o ADR-0030
280
- * registra alargar a união como NÃO-opção, e é essa a razão).
519
+ * It comes from `createGame` and not from each game for the same reason as the sonar: it is infrastructure. What it
520
+ * replaces is `phase: 'title' | 'playing' | 'paused'` — ONE game's enum that engine modules read, and that a game
521
+ * with a level map or a results screen could not extend without asking the engine for a new constant (ADR-0030
522
+ * records widening the union as a NON-option, and that is why).
281
523
  *
282
- * Nasce VAZIA: quem empilha é o jogo, porque quais são as cenas é a única parte disto que é dele.
524
+ * It is born EMPTY: the game pushes, because which scenes there are is the only part of this that is its own.
283
525
  */
284
- readonly cenas: SceneStack;
285
- /** Quantos filtros de daltonismo foram montados. `0` = não havia host, e o menu visual perde metade. */
526
+ readonly scenes: SceneStack;
527
+ /**
528
+ * TELLS THE CARTRIDGE THE LANGUAGE CHANGED — the only thing it needs to know about it (ADR-0225).
529
+ *
530
+ * 📌 The engine already redraws everything that IS its own: the bar, the caption, the card, the pad, an open panel, the
531
+ * voice that speaks and the model that listens. What it cannot redraw is the ACTIVITY — and without this door the
532
+ * cartridge would have to subscribe to `i18n:change` on the window, knowing the event's name and reaching a global to
533
+ * do it. It is the rule the Dev wrote for reading and speech: «o jogo não deve precisar saber como isso funciona»
534
+ * (ADR-0216).
535
+ *
536
+ * ⚠️ Called AFTER the engine has redrawn itself, so the cartridge never sees a half-translated screen.
537
+ */
538
+ readonly onLocaleChange: (fn: () => void) => void;
539
+ /** How many colour-vision filters were mounted. `0` = there was no host, and the visual menu loses half. */
286
540
  readonly cvdFilters: number;
287
- /** O que FALTOU no documento do consumidor. Vazia = o hospedeiro cumpriu o contrato de marcação. */
541
+ /** What the host page lacks and what the cartridge left undone, each line naming its fix. Empty = nothing to fix. */
288
542
  readonly problems: readonly string[];
289
- /** O que este jogo declarou não ter. Devolvido para poder ser auditado — declinar fica no registro. */
543
+ /** What this game declared it does not have. Returned so it can be audited — declining stays on the record. */
290
544
  readonly declines: Declinios;
291
545
  /**
292
- * O ANÚNCIO DE QUE O LAÇO PAROU, pronto para entrar em `startLoop(ticker, quadro, maxDt, { aoFalhar })`.
546
+ * THE ANNOUNCEMENT THAT THE LOOP STOPPED, ready to go into `startLoop(ticker, frame, maxDt, { onFailure })`.
293
547
  *
294
- * ⚠️ O ADR-0054 diz por escrito que fica *"só metade verdadeiro"* enquanto isto não existir, e a metade que
295
- * faltava é a que importa: **criança cega não vê tela congelada.** Sem anúncio, o modo cego não distingue
296
- * «travou» de «está pensando», e o silêncio é a mesma coisa nos dois casos.
548
+ * ⚠️ ADR-0054 says in writing that it is only half true while this does not exist, and the missing half is the one
549
+ * that matters: **a blind child does not see a frozen screen.** Without an announcement, blind mode cannot tell
550
+ * a crash from a pause to think, and the silence is the same in both cases.
297
551
  *
298
- * ⚠️ VEM DA ENGINE E NÃO DE CADA JOGO porque a mensagem é a mesma em todos e o canal (leitor de tela +
299
- * narração + o que se VÊ) é infraestrutura. Mas quem chama `startLoop` é o JOGO — ele é o dono do ticker —,
300
- * então isto é entregue e não instalado: um jogo que monte o laço sem passar isto continua a PARAR, porque
301
- * parar não é opcional; o que ele perde é dizer que parou.
552
+ * ⚠️ IT COMES FROM THE ENGINE AND NOT FROM EACH GAME because the message is the same in all of them and the channel
553
+ * (screen reader + narration + what is SEEN) is infrastructure. The game owns the ticker and calls `startLoop`, whose
554
+ * `onFailure` is REQUIRED (ADR-0232 D4): the game passes this, and no registration in `core/loop` stands in for it.
302
555
  */
303
- readonly aoFalhar: (erro: unknown) => void;
556
+ readonly onFailure: (failure: unknown) => void;
304
557
  /**
305
- * O ALCANCE MEDIDO NO ARRANQUE — a garantia do ADR-0079 §3 como dado, para quem quiser lê-la.
558
+ * THE REACH MEASURED FOR THE MOUNTED CARTRIDGE — ADR-0079 §3's guarantee as data, for whoever wants to read it.
306
559
  *
307
- * A engine já mostrou o aviso se havia o que dizer; isto fica devolvido porque um jogo pode querer decidir
308
- * mais (esconder uma fase que exige doze ações, por exemplo), e porque `ok: false` é o tipo de facto que
309
- * tem de poder ser auditado em vez de ficar só numa tela que já fechou.
560
+ * The engine has already shown the notice if there was something to say; this is returned because a game may want to
561
+ * decide more (hide a level that demands twelve actions, for example), and because `ok: false` is the kind of fact
562
+ * that must be auditable instead of living only on a screen that has closed.
310
563
  */
311
- readonly alcance: Alcance;
564
+ readonly reach: Reach;
312
565
  /**
313
- * TORNA ESTE CARTUCHO O CORRENTE (ADR-0142). Uma raiz de composição, vários jogos.
566
+ * MAKES THIS CARTRIDGE THE CURRENT ONE (ADR-0142). One composition root, several games.
314
567
  *
315
- * ⚠️ **LANÇA** numa declaração malformada, e não a põe em `problems`: o contrato é PRÉ-CONDIÇÃO e não
316
- * diagnóstico, tal como no arranque. Um cartucho mau não chega a ser montado.
568
+ * ⚠️ **THROWS** on a malformed declaration, and does not put it in `problems`: the contract is a PRECONDITION and not
569
+ * a diagnosis, as at boot. A bad cartridge is never mounted.
317
570
  *
318
- * 📌 O que ele refaz é só o que não se conserta lendo de novo: os dois registos de mapeamento, que são
319
- * efeito global, e o alcance com o seu aviso, que escreve DOM. `problems` e `alcance` passam a descrever
320
- * o cartucho montado porque são derivados, não porque `mount` os copie.
571
+ * 📌 What it redoes is only what reading again cannot fix: effects written elsewhere — the two mapping registers, the
572
+ * HUD, the game-options rows, the bar, the reach notice and the pad. `problems` and `reach` describe the mounted
573
+ * cartridge because they are derived when read, not because `mount` copies them.
321
574
  */
322
- mount(declaration: GameDeclaration, ganchos?: GanchosDoCartucho): void;
575
+ mount(declaration: GameDeclaration, hooks?: CartridgeHooks): void;
323
576
  /**
324
- * SOLTA O CORRENTE: mapeamentos a `null`, aviso de alcance retirado, pilha de cenas esvaziada.
577
+ * RELEASES THE CURRENT ONE: mappings set to `null`, reach notice and HUD removed, reading thread
578
+ * closed, scene stack emptied.
325
579
  *
326
- * ⚠️ A pilha esvazia-se com `pop()` e não com um `clear()`, e a diferença é a decisão: 📏 medido nos seis
327
- * jogos, os quatro `exit()` que existem são LIMPEZA DE DOM, logo dispará-los é o teardown que se quer.
328
- * Um `clear()` que os saltasse seria o conserto errado.
580
+ * ⚠️ The stack is emptied with `pop()` and not with a `clear()`, and the difference is the decision: a scene's `exit()`
581
+ * is its DOM cleanup, so running it is the teardown wanted. A `clear()` that skipped them would be the wrong fix.
329
582
  */
330
583
  unmount(): void;
584
+ /**
585
+ * ENDS THIS ROOT: it releases the current cartridge, like `unmount()`, AND STOPS LISTENING TO THE WINDOW.
586
+ *
587
+ * 🔴 The two are separate on purpose, and the separation is the whole point. `unmount()` releases the CARTRIDGE (ADR-0142) and
588
+ * a `mount()` after it must find a root that still hears the keyboard — so `unmount()` may not take the listeners off. But a
589
+ * page that is finished with a root had, until this method existed, no way to say so: the root kept its ~30 window listeners
590
+ * for the lifetime of the document, and since every query it makes is document-wide, it went on driving the pause card of
591
+ * whatever root came after it. 📏 Measured: one ArrowDown moved the cursor one item with one root, two with a second root
592
+ * alive, three with a third.
593
+ *
594
+ * ⚠️ A disposed root is not to be used again: it no longer hears anything. Call it when the page drops the root, not between
595
+ * cartridges — that is what `unmount()` is for.
596
+ */
597
+ dispose(): void;
331
598
  }
332
- /** A metade do jogo SEM a declaração — o que `mount` recebe ao lado dela. */
333
- export type GanchosDoCartucho = Omit<MetadeDoJogo, 'declaration'>;
599
+ /** The game's half WITHOUT the declaration — what `mount` receives beside it. */
600
+ export type CartridgeHooks = Omit<GameHalf, 'declaration'>;
601
+ /**
602
+ * EVERY LINE `createGame` AND `mount()` REFUSE A CARTRIDGE WITH — one list, read by the boot, by `mount()` and by the
603
+ * cartridge checker the engine ships (`inclusionist-check-cartridge`, ADR-0253). EMPTY means the cartridge holds.
604
+ *
605
+ * 🎯 ONE LIST AND NOT A CHECKER'S COPY OF IT: a checker that re-listed these rules would drift from the boot the first time
606
+ * a rule was added here, and it would then accept a cartridge the platform refuses — the drift ADR-0253 exists to end.
607
+ *
608
+ * ⚠️ EACH ROW THROWS AT BOOT, by the rubric of ADR-0169: every one is a PROGRAM defect, never a gap of the host, and
609
+ * `problems` is for what still lets the child play.
610
+ * · the declaration's shape (`core/contract`, ADR-0030);
611
+ * · «start» and «select» belong to the pause, and a cartridge does not take them (ADR-0144 §4, ADR-0155) — the sentences
612
+ * live in `core/actions`, where the validity of a preset lives;
613
+ * · the accommodations are ANSWERED, all of them (ADR-0153): the engine would not know which rows to mount;
614
+ * · a genre outside the engine's list, or Casino game (ADR-0156 §2, §4); an absent genre is conformant;
615
+ * · a malformed `hud`, game options or `howToPlay` (ADR-0169): the engine would not know what to place, draw or show.
616
+ */
617
+ export declare function cartridgeRefusals(declaration: Partial<GameDeclaration> | null | undefined, hooks: Partial<CartridgeHooks> | null | undefined): string[];
334
618
  /**
335
- * Liga a engine para um jogo declarado.
619
+ * THE GAME'S HALF of `CreateGameOptions` — the fields ADR-0139 §1 says a cartridge SUPPLIES, apart from the ones that
620
+ * describe the page and the device.
336
621
  *
337
- * ⚠️ LANÇA se a declaração for malformada, e NÃO lança se faltar marcação. A diferença não é gosto: uma
338
- * declaração errada é defeito de PROGRAMA, e um jogo que roda meio declarado é pior do que um que não abre;
339
- * um id ausente é lacuna do HOSPEDEIRO, e o quiz provou que ligar só a parte que serve é legítimo — foi
340
- * assim que ele recusou o pad e o sonar sem mentir. Por isso um vira exceção e o outro vira `problems`.
622
+ * The test that record gives: could a PAGE answer this without knowing which game runs? If not, the field is here.
341
623
  */
624
+ type GameHalf = Pick<CreateGameOptions, 'declaration' | 'isNavigable' | 'withIndex' | 'onBar' | 'navBar' | 'players' | 'setPhase' | 'isBlindMode' | 'preset' | 'declines' | 'getPauseActs' | 'setPauseActor' | 'setPlayerTheme' | 'setPlayerCorrection' | 'accommodations' | 'genre' | 'onScreenPad' | 'hud' | 'gameOptions' | 'howToPlay' | 'onCommand' | 'gamepad' | 'dictionaries'>;
342
625
  /**
343
- * A METADE DO JOGO de `CreateGameOptions` — os quinze campos que o ADR-0139 §1 diz que um cartucho
344
- * FORNECE, separados dos cinco que descrevem a página e o aparelho.
626
+ * Switches the engine on for a declared game.
345
627
  *
346
- * ⚠️ São quinze e não dez: a primeira versão daquele registo contou quinze campos num total de vinte e
347
- * deixou `declines`, `getPauseActs`, `setPauseActor`, `setTemaDoJogador` e `setCorrecaoDoJogador` de fora.
348
- * O teste que ele próprio dá — «uma PÁGINA conseguiria responder isto sem saber que jogo corre?» — põe os
349
- * cinco deste lado, e a errata de 2026-09-11 corrigiu a lista.
628
+ * ⚠️ THROWS if the declaration is malformed, and does NOT throw if markup is missing. The difference is not taste: a
629
+ * wrong declaration is a PROGRAM defect, and a game that runs half-declared is worse than one that does not open; a
630
+ * missing id is a gap of the HOST, and the quiz proved that switching on only the part that serves is legitimate —
631
+ * that is how it declined the pad and the sonar without lying. So one becomes an exception and the other `problems`.
350
632
  */
351
- type MetadeDoJogo = Pick<CreateGameOptions, 'declaration' | 'isNavigable' | 'comIndice' | 'naBarraDe' | 'navBar' | 'players' | 'setPhase' | 'sonarPlayers' | 'isBlindMode' | 'preset' | 'declines' | 'getPauseActs' | 'setPauseActor' | 'setTemaDoJogador' | 'setCorrecaoDoJogador'>;
352
633
  export declare function createGame(o: CreateGameOptions): Engine;
353
- export {};