@the-inclusionist/engine 9.0.0 → 11.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (532) hide show
  1. package/README.md +120 -78
  2. package/app/css/style.css +761 -377
  3. package/app/public/vendor/fonts/playwrite-cu.woff2 +0 -0
  4. package/app/public/vendor/fonts/playwrite-es-deco.woff2 +0 -0
  5. package/app/public/vendor/fonts/playwrite-es.woff2 +0 -0
  6. package/app/public/vendor/fonts/playwrite-gb-j.woff2 +0 -0
  7. package/app/public/vendor/fonts/playwrite-gb-s.woff2 +0 -0
  8. package/app/public/vendor/fonts/playwrite-pe.woff2 +0 -0
  9. package/app/public/vendor/fonts/playwrite-pt.woff2 +0 -0
  10. package/app/public/vendor/fonts-licences/NOTICE.txt +166 -0
  11. package/app/public/vendor/fonts-licences/OFL-1.1.txt +86 -0
  12. package/app/public/vendor/fonts.css +36 -84
  13. package/dist-pkg/boot/create-game.d.ts +499 -219
  14. package/dist-pkg/boot/create-game.js +3944 -562
  15. package/dist-pkg/core/a11y-sr.d.ts +25 -3
  16. package/dist-pkg/core/a11y-sr.js +38 -19
  17. package/dist-pkg/core/accessible-label.d.ts +17 -0
  18. package/dist-pkg/core/accessible-label.js +37 -0
  19. package/dist-pkg/core/accommodation-subjects.d.ts +26 -0
  20. package/dist-pkg/core/accommodation-subjects.js +32 -0
  21. package/dist-pkg/core/accommodations.d.ts +125 -0
  22. package/dist-pkg/core/accommodations.js +195 -0
  23. package/dist-pkg/core/actions.d.ts +108 -55
  24. package/dist-pkg/core/actions.js +122 -71
  25. package/dist-pkg/core/calm-mode.d.ts +31 -0
  26. package/dist-pkg/core/calm-mode.js +47 -0
  27. package/dist-pkg/core/camera-cycle.d.ts +15 -0
  28. package/dist-pkg/core/camera-cycle.js +7 -0
  29. package/dist-pkg/core/caption-duration.d.ts +15 -0
  30. package/dist-pkg/core/caption-duration.js +19 -0
  31. package/dist-pkg/core/cartridge-problems.d.ts +34 -0
  32. package/dist-pkg/core/cartridge-problems.js +53 -0
  33. package/dist-pkg/core/constants.d.ts +0 -54
  34. package/dist-pkg/core/constants.js +9 -76
  35. package/dist-pkg/core/contract.d.ts +142 -164
  36. package/dist-pkg/core/contract.js +222 -208
  37. package/dist-pkg/core/dom-query.d.ts +4 -4
  38. package/dist-pkg/core/dom-query.js +12 -20
  39. package/dist-pkg/core/entity.d.ts +62 -77
  40. package/dist-pkg/core/entity.js +18 -32
  41. package/dist-pkg/core/escape-html.d.ts +9 -10
  42. package/dist-pkg/core/escape-html.js +16 -20
  43. package/dist-pkg/core/flash-threshold.d.ts +17 -0
  44. package/dist-pkg/core/flash-threshold.js +113 -0
  45. package/dist-pkg/core/game-speed.d.ts +11 -0
  46. package/dist-pkg/core/game-speed.js +16 -0
  47. package/dist-pkg/core/genres.d.ts +20 -0
  48. package/dist-pkg/core/genres.js +44 -0
  49. package/dist-pkg/core/i18n.d.ts +71 -60
  50. package/dist-pkg/core/i18n.js +186 -200
  51. package/dist-pkg/core/loop.d.ts +17 -6
  52. package/dist-pkg/core/loop.js +26 -26
  53. package/dist-pkg/core/pause-icon-catalogue.d.ts +15 -0
  54. package/dist-pkg/core/pause-icon-catalogue.js +54 -0
  55. package/dist-pkg/core/ring.d.ts +17 -0
  56. package/dist-pkg/core/ring.js +29 -0
  57. package/dist-pkg/core/rng.d.ts +7 -12
  58. package/dist-pkg/core/rng.js +23 -53
  59. package/dist-pkg/core/route.d.ts +26 -26
  60. package/dist-pkg/core/route.js +126 -121
  61. package/dist-pkg/core/scenes.d.ts +37 -40
  62. package/dist-pkg/core/scenes.js +41 -43
  63. package/dist-pkg/core/screens.d.ts +4 -4
  64. package/dist-pkg/core/screens.js +11 -16
  65. package/dist-pkg/core/session-clock.d.ts +23 -0
  66. package/dist-pkg/core/session-clock.js +37 -0
  67. package/dist-pkg/core/setting-defaults.d.ts +61 -0
  68. package/dist-pkg/core/setting-defaults.js +66 -0
  69. package/dist-pkg/core/speech-rate.d.ts +29 -0
  70. package/dist-pkg/core/speech-rate.js +47 -0
  71. package/dist-pkg/core/state.d.ts +118 -118
  72. package/dist-pkg/core/state.js +322 -297
  73. package/dist-pkg/core/visual-cycles.d.ts +30 -0
  74. package/dist-pkg/core/visual-cycles.js +52 -0
  75. package/dist-pkg/core/visual-state.d.ts +13 -0
  76. package/dist-pkg/core/visual-state.js +6 -0
  77. package/dist-pkg/educational/activities-registry.d.ts +1 -1
  78. package/dist-pkg/educational/activities-registry.js +9 -8
  79. package/dist-pkg/educational/adaptive-engine.d.ts +11 -11
  80. package/dist-pkg/educational/adaptive-engine.js +17 -17
  81. package/dist-pkg/educational/segment-bar.d.ts +12 -12
  82. package/dist-pkg/educational/segment-bar.js +9 -9
  83. package/dist-pkg/i18n/en.js +324 -223
  84. package/dist-pkg/i18n/es.js +323 -223
  85. package/dist-pkg/i18n/pt.js +348 -226
  86. package/dist-pkg/input/default-bindings.d.ts +37 -38
  87. package/dist-pkg/input/default-bindings.js +127 -144
  88. package/dist-pkg/input/devices.d.ts +7 -7
  89. package/dist-pkg/input/devices.js +17 -16
  90. package/dist-pkg/input/edges.d.ts +23 -32
  91. package/dist-pkg/input/edges.js +23 -28
  92. package/dist-pkg/input/empathy-filter.d.ts +22 -0
  93. package/dist-pkg/input/empathy-filter.js +34 -0
  94. package/dist-pkg/input/face-map.d.ts +96 -0
  95. package/dist-pkg/input/face-map.js +255 -0
  96. package/dist-pkg/input/face-signals.d.ts +40 -0
  97. package/dist-pkg/input/face-signals.js +48 -0
  98. package/dist-pkg/input/gamepad.d.ts +195 -145
  99. package/dist-pkg/input/gamepad.js +383 -556
  100. package/dist-pkg/input/gaze-cycle.d.ts +64 -0
  101. package/dist-pkg/input/gaze-cycle.js +133 -0
  102. package/dist-pkg/input/gaze-relative.d.ts +68 -0
  103. package/dist-pkg/input/gaze-relative.js +142 -0
  104. package/dist-pkg/input/hand-map.d.ts +28 -0
  105. package/dist-pkg/input/hand-map.js +142 -0
  106. package/dist-pkg/input/input-cooldown.d.ts +17 -0
  107. package/dist-pkg/input/input-cooldown.js +39 -0
  108. package/dist-pkg/input/key-default.d.ts +30 -0
  109. package/dist-pkg/input/key-default.js +60 -0
  110. package/dist-pkg/input/keyboard-runtime.d.ts +15 -21
  111. package/dist-pkg/input/keyboard-runtime.js +23 -27
  112. package/dist-pkg/input/keyboard.d.ts +68 -63
  113. package/dist-pkg/input/keyboard.js +83 -105
  114. package/dist-pkg/input/keydown.d.ts +123 -137
  115. package/dist-pkg/input/keydown.js +284 -289
  116. package/dist-pkg/input/latch-edge.d.ts +33 -21
  117. package/dist-pkg/input/latch-edge.js +34 -35
  118. package/dist-pkg/input/latch-scope.d.ts +51 -50
  119. package/dist-pkg/input/latch-scope.js +61 -82
  120. package/dist-pkg/input/latch-store.d.ts +26 -30
  121. package/dist-pkg/input/latch-store.js +42 -50
  122. package/dist-pkg/input/latch-sync.d.ts +29 -29
  123. package/dist-pkg/input/latch-sync.js +25 -25
  124. package/dist-pkg/input/latch.d.ts +14 -17
  125. package/dist-pkg/input/latch.js +29 -37
  126. package/dist-pkg/input/pad-defaults.d.ts +13 -16
  127. package/dist-pkg/input/pad-defaults.js +21 -28
  128. package/dist-pkg/input/pad-reading.d.ts +76 -0
  129. package/dist-pkg/input/pad-reading.js +169 -0
  130. package/dist-pkg/input/pad-wizard.d.ts +99 -0
  131. package/dist-pkg/input/pad-wizard.js +256 -0
  132. package/dist-pkg/input/pointer-space.d.ts +19 -20
  133. package/dist-pkg/input/pointer-space.js +27 -32
  134. package/dist-pkg/input/pointer.d.ts +39 -40
  135. package/dist-pkg/input/pointer.js +42 -45
  136. package/dist-pkg/input/state.d.ts +101 -89
  137. package/dist-pkg/input/state.js +54 -134
  138. package/dist-pkg/input/switch-scan.d.ts +59 -0
  139. package/dist-pkg/input/switch-scan.js +67 -0
  140. package/dist-pkg/input/synthetic-source.d.ts +42 -0
  141. package/dist-pkg/input/synthetic-source.js +57 -0
  142. package/dist-pkg/input/touch-bindings.d.ts +127 -116
  143. package/dist-pkg/input/touch-bindings.js +158 -173
  144. package/dist-pkg/input/touch.d.ts +147 -72
  145. package/dist-pkg/input/touch.js +362 -126
  146. package/dist-pkg/input/transport-in-use.d.ts +107 -0
  147. package/dist-pkg/input/transport-in-use.js +130 -0
  148. package/dist-pkg/input/transports.d.ts +90 -117
  149. package/dist-pkg/input/transports.js +104 -106
  150. package/dist-pkg/input/virtual-controller.d.ts +82 -0
  151. package/dist-pkg/input/virtual-controller.js +69 -0
  152. package/dist-pkg/input/vocabulary-migration.d.ts +50 -49
  153. package/dist-pkg/input/vocabulary-migration.js +80 -79
  154. package/dist-pkg/input/voice-map.d.ts +59 -0
  155. package/dist-pkg/input/voice-map.js +123 -0
  156. package/dist-pkg/platform/audio-ambient.d.ts +13 -2
  157. package/dist-pkg/platform/audio-ambient.js +14 -14
  158. package/dist-pkg/platform/audio-earcons.d.ts +20 -19
  159. package/dist-pkg/platform/audio-earcons.js +51 -42
  160. package/dist-pkg/platform/audio-jingles.js +9 -10
  161. package/dist-pkg/platform/audio-mixer.d.ts +14 -2
  162. package/dist-pkg/platform/audio-mixer.js +31 -39
  163. package/dist-pkg/platform/audio-sonar.d.ts +49 -152
  164. package/dist-pkg/platform/audio-sonar.js +124 -302
  165. package/dist-pkg/platform/audio.d.ts +62 -19
  166. package/dist-pkg/platform/audio.js +193 -168
  167. package/dist-pkg/platform/choose-by-voice.d.ts +70 -0
  168. package/dist-pkg/platform/choose-by-voice.js +157 -0
  169. package/dist-pkg/platform/flash-sampler.d.ts +19 -0
  170. package/dist-pkg/platform/flash-sampler.js +79 -0
  171. package/dist-pkg/platform/font-library.d.ts +77 -0
  172. package/dist-pkg/platform/font-library.js +98 -0
  173. package/dist-pkg/platform/font-library.json +721 -0
  174. package/dist-pkg/platform/heavy-catalogue.d.ts +77 -0
  175. package/dist-pkg/platform/heavy-catalogue.js +256 -0
  176. package/dist-pkg/platform/heavy-mirror.d.ts +20 -0
  177. package/dist-pkg/platform/heavy-mirror.js +73 -0
  178. package/dist-pkg/platform/heavy.d.ts +136 -0
  179. package/dist-pkg/platform/heavy.js +324 -0
  180. package/dist-pkg/platform/interruptible-speech.d.ts +23 -16
  181. package/dist-pkg/platform/interruptible-speech.js +77 -54
  182. package/dist-pkg/platform/kokoro-port.d.ts +27 -0
  183. package/dist-pkg/platform/kokoro-port.js +50 -0
  184. package/dist-pkg/platform/kokoro-runtime.d.ts +20 -0
  185. package/dist-pkg/platform/kokoro-runtime.js +52 -0
  186. package/dist-pkg/platform/kokoro.d.ts +70 -0
  187. package/dist-pkg/platform/kokoro.js +133 -0
  188. package/dist-pkg/platform/listener-scope.d.ts +12 -0
  189. package/dist-pkg/platform/listener-scope.js +90 -0
  190. package/dist-pkg/platform/locale-host.d.ts +14 -0
  191. package/dist-pkg/platform/locale-host.js +45 -0
  192. package/dist-pkg/platform/microphone.d.ts +51 -0
  193. package/dist-pkg/platform/microphone.js +118 -0
  194. package/dist-pkg/platform/onnx-runtime.d.ts +34 -0
  195. package/dist-pkg/platform/onnx-runtime.js +42 -0
  196. package/dist-pkg/platform/reading-in-worker.d.ts +64 -0
  197. package/dist-pkg/platform/reading-in-worker.js +125 -0
  198. package/dist-pkg/platform/reading-model.d.ts +94 -0
  199. package/dist-pkg/platform/reading-model.js +272 -0
  200. package/dist-pkg/platform/reading-runtime.d.ts +24 -0
  201. package/dist-pkg/platform/reading-runtime.js +164 -0
  202. package/dist-pkg/platform/reading-worker.d.ts +41 -0
  203. package/dist-pkg/platform/reading-worker.js +58 -0
  204. package/dist-pkg/platform/reading.d.ts +80 -0
  205. package/dist-pkg/platform/reading.js +136 -0
  206. package/dist-pkg/platform/speech-recognition.d.ts +60 -0
  207. package/dist-pkg/platform/speech-recognition.js +104 -0
  208. package/dist-pkg/platform/speech.d.ts +23 -1
  209. package/dist-pkg/platform/speech.js +29 -18
  210. package/dist-pkg/platform/storage-keys.d.ts +96 -0
  211. package/dist-pkg/platform/storage-keys.js +106 -0
  212. package/dist-pkg/platform/storage.d.ts +46 -111
  213. package/dist-pkg/platform/storage.js +104 -169
  214. package/dist-pkg/platform/tts.d.ts +85 -39
  215. package/dist-pkg/platform/tts.js +447 -127
  216. package/dist-pkg/platform/vision-loop.d.ts +23 -0
  217. package/dist-pkg/platform/vision-loop.js +56 -0
  218. package/dist-pkg/platform/vision.d.ts +132 -0
  219. package/dist-pkg/platform/vision.js +117 -0
  220. package/dist-pkg/platform/voice-listener.d.ts +40 -0
  221. package/dist-pkg/platform/voice-listener.js +88 -0
  222. package/dist-pkg/platform/voice-plan.d.ts +15 -98
  223. package/dist-pkg/platform/voice-plan.js +56 -143
  224. package/dist-pkg/platform/vosk-runtime.d.ts +96 -0
  225. package/dist-pkg/platform/vosk-runtime.js +151 -0
  226. package/dist-pkg/platform/vosk-vocabulary.d.ts +6 -0
  227. package/dist-pkg/platform/vosk-vocabulary.js +178 -0
  228. package/dist-pkg/render/canvas.d.ts +13 -7
  229. package/dist-pkg/render/canvas.js +13 -13
  230. package/dist-pkg/render/crt.d.ts +48 -12
  231. package/dist-pkg/render/crt.js +78 -80
  232. package/dist-pkg/render/cvd-matrices.d.ts +22 -18
  233. package/dist-pkg/render/cvd-matrices.js +37 -38
  234. package/dist-pkg/render/hc-role-data.d.ts +7 -7
  235. package/dist-pkg/render/hc-role-data.js +14 -14
  236. package/dist-pkg/render/high-contrast.d.ts +67 -56
  237. package/dist-pkg/render/high-contrast.js +226 -217
  238. package/dist-pkg/render/low-vision-drawing.d.ts +5 -0
  239. package/dist-pkg/render/low-vision-drawing.js +46 -0
  240. package/dist-pkg/render/lq-filter.d.ts +32 -20
  241. package/dist-pkg/render/lq-filter.js +59 -65
  242. package/dist-pkg/render/port.d.ts +86 -88
  243. package/dist-pkg/render/port.js +17 -23
  244. package/dist-pkg/render/screen-pipeline.d.ts +42 -42
  245. package/dist-pkg/render/screen-pipeline.js +80 -87
  246. package/dist-pkg/render/sprite-fx.d.ts +2 -1
  247. package/dist-pkg/render/sprite-fx.js +16 -14
  248. package/dist-pkg/render/viewports.d.ts +20 -15
  249. package/dist-pkg/render/viewports.js +83 -114
  250. package/dist-pkg/render/viz-axes-labels.d.ts +47 -0
  251. package/dist-pkg/render/viz-axes-labels.js +88 -0
  252. package/dist-pkg/render/viz-axes.d.ts +89 -86
  253. package/dist-pkg/render/viz-axes.js +128 -116
  254. package/dist-pkg/render/viz-modes.d.ts +28 -29
  255. package/dist-pkg/render/viz-modes.js +40 -45
  256. package/dist-pkg/render/viz-refusal.d.ts +32 -0
  257. package/dist-pkg/render/viz-refusal.js +57 -0
  258. package/dist-pkg/render/viz-setters.d.ts +104 -66
  259. package/dist-pkg/render/viz-setters.js +182 -191
  260. package/dist-pkg/ui/aac-sets.d.ts +45 -0
  261. package/dist-pkg/ui/aac-sets.js +41 -0
  262. package/dist-pkg/ui/audio-choices.d.ts +68 -0
  263. package/dist-pkg/ui/audio-choices.js +98 -0
  264. package/dist-pkg/ui/audio-rows-that-apply.d.ts +12 -0
  265. package/dist-pkg/ui/audio-rows-that-apply.js +43 -0
  266. package/dist-pkg/ui/camera-control.d.ts +53 -0
  267. package/dist-pkg/ui/camera-control.js +48 -0
  268. package/dist-pkg/ui/changed-mark.d.ts +9 -8
  269. package/dist-pkg/ui/changed-mark.js +22 -53
  270. package/dist-pkg/ui/control-choices.d.ts +37 -0
  271. package/dist-pkg/ui/control-choices.js +84 -0
  272. package/dist-pkg/ui/debug-panel.d.ts +59 -43
  273. package/dist-pkg/ui/debug-panel.js +68 -69
  274. package/dist-pkg/ui/declared-words.d.ts +21 -0
  275. package/dist-pkg/ui/declared-words.js +66 -0
  276. package/dist-pkg/ui/dom.d.ts +10 -14
  277. package/dist-pkg/ui/dom.js +10 -47
  278. package/dist-pkg/ui/drawing-problems.d.ts +25 -0
  279. package/dist-pkg/ui/drawing-problems.js +94 -0
  280. package/dist-pkg/ui/eye-control.d.ts +36 -0
  281. package/dist-pkg/ui/eye-control.js +178 -0
  282. package/dist-pkg/ui/face-control.d.ts +25 -0
  283. package/dist-pkg/ui/face-control.js +183 -0
  284. package/dist-pkg/ui/focus-trap.d.ts +39 -49
  285. package/dist-pkg/ui/focus-trap.js +56 -65
  286. package/dist-pkg/ui/fonts.d.ts +157 -68
  287. package/dist-pkg/ui/fonts.js +305 -115
  288. package/dist-pkg/ui/footer-scroll-driver.d.ts +14 -0
  289. package/dist-pkg/ui/footer-scroll-driver.js +201 -0
  290. package/dist-pkg/ui/footer-scroll.d.ts +38 -0
  291. package/dist-pkg/ui/footer-scroll.js +55 -0
  292. package/dist-pkg/ui/game-options.d.ts +47 -0
  293. package/dist-pkg/ui/game-options.js +158 -0
  294. package/dist-pkg/ui/gaze-overlay.d.ts +64 -0
  295. package/dist-pkg/ui/gaze-overlay.js +165 -0
  296. package/dist-pkg/ui/hand-control.d.ts +25 -0
  297. package/dist-pkg/ui/hand-control.js +143 -0
  298. package/dist-pkg/ui/help-panel.d.ts +87 -0
  299. package/dist-pkg/ui/help-panel.js +242 -0
  300. package/dist-pkg/ui/hud-bands.d.ts +62 -0
  301. package/dist-pkg/ui/hud-bands.js +146 -0
  302. package/dist-pkg/ui/hud-row.d.ts +34 -0
  303. package/dist-pkg/ui/hud-row.js +80 -0
  304. package/dist-pkg/ui/hud.d.ts +73 -73
  305. package/dist-pkg/ui/hud.js +94 -98
  306. package/dist-pkg/ui/item-announcement.d.ts +13 -12
  307. package/dist-pkg/ui/item-announcement.js +25 -26
  308. package/dist-pkg/ui/layout.d.ts +103 -28
  309. package/dist-pkg/ui/layout.js +117 -99
  310. package/dist-pkg/ui/libras-avatar-clip.d.ts +30 -0
  311. package/dist-pkg/ui/libras-avatar-clip.js +86 -0
  312. package/dist-pkg/ui/libras-avatar-load.d.ts +52 -0
  313. package/dist-pkg/ui/libras-avatar-load.js +134 -0
  314. package/dist-pkg/ui/libras-avatar-plan.d.ts +102 -0
  315. package/dist-pkg/ui/libras-avatar-plan.js +211 -0
  316. package/dist-pkg/ui/libras-avatar-player.d.ts +28 -0
  317. package/dist-pkg/ui/libras-avatar-player.js +226 -0
  318. package/dist-pkg/ui/libras-avatar-stage.d.ts +37 -0
  319. package/dist-pkg/ui/libras-avatar-stage.js +208 -0
  320. package/dist-pkg/ui/libras-glosses.d.ts +28 -0
  321. package/dist-pkg/ui/libras-glosses.js +161 -0
  322. package/dist-pkg/ui/locale-flags.d.ts +16 -0
  323. package/dist-pkg/ui/locale-flags.js +30 -0
  324. package/dist-pkg/ui/loop-crash.d.ts +17 -17
  325. package/dist-pkg/ui/loop-crash.js +33 -62
  326. package/dist-pkg/ui/menu-intent.d.ts +61 -0
  327. package/dist-pkg/ui/menu-intent.js +88 -0
  328. package/dist-pkg/ui/menu-items.d.ts +20 -0
  329. package/dist-pkg/ui/menu-items.js +51 -0
  330. package/dist-pkg/ui/menu-nav.d.ts +98 -92
  331. package/dist-pkg/ui/menu-nav.js +398 -286
  332. package/dist-pkg/ui/mobility-choices.d.ts +26 -0
  333. package/dist-pkg/ui/mobility-choices.js +41 -0
  334. package/dist-pkg/ui/motion-choices.d.ts +31 -0
  335. package/dist-pkg/ui/motion-choices.js +68 -0
  336. package/dist-pkg/ui/motion-scene.d.ts +24 -19
  337. package/dist-pkg/ui/motion-scene.js +29 -61
  338. package/dist-pkg/ui/mount-panel.d.ts +81 -0
  339. package/dist-pkg/ui/mount-panel.js +61 -0
  340. package/dist-pkg/ui/panel-shell.d.ts +65 -31
  341. package/dist-pkg/ui/panel-shell.js +92 -66
  342. package/dist-pkg/ui/panel-widgets.d.ts +150 -0
  343. package/dist-pkg/ui/panel-widgets.js +294 -0
  344. package/dist-pkg/ui/pause-buttons.d.ts +35 -0
  345. package/dist-pkg/ui/pause-buttons.js +66 -0
  346. package/dist-pkg/ui/pause-icons.d.ts +261 -347
  347. package/dist-pkg/ui/pause-icons.js +671 -601
  348. package/dist-pkg/ui/pause-markup.d.ts +86 -0
  349. package/dist-pkg/ui/pause-markup.js +125 -0
  350. package/dist-pkg/ui/reach-notice.d.ts +22 -23
  351. package/dist-pkg/ui/reach-notice.js +55 -58
  352. package/dist-pkg/ui/scan-overlay.d.ts +23 -0
  353. package/dist-pkg/ui/scan-overlay.js +50 -0
  354. package/dist-pkg/ui/screen-text.d.ts +25 -0
  355. package/dist-pkg/ui/screen-text.js +152 -0
  356. package/dist-pkg/ui/session-clock.d.ts +32 -0
  357. package/dist-pkg/ui/session-clock.js +67 -0
  358. package/dist-pkg/ui/settings-aac.d.ts +70 -0
  359. package/dist-pkg/ui/settings-aac.js +208 -0
  360. package/dist-pkg/ui/settings-audio.d.ts +97 -114
  361. package/dist-pkg/ui/settings-audio.js +493 -425
  362. package/dist-pkg/ui/settings-controls.d.ts +53 -96
  363. package/dist-pkg/ui/settings-controls.js +143 -215
  364. package/dist-pkg/ui/settings-empathy.d.ts +39 -30
  365. package/dist-pkg/ui/settings-empathy.js +53 -60
  366. package/dist-pkg/ui/settings-mobility.d.ts +192 -0
  367. package/dist-pkg/ui/settings-mobility.js +332 -0
  368. package/dist-pkg/ui/settings-motion.d.ts +91 -70
  369. package/dist-pkg/ui/settings-motion.js +287 -198
  370. package/dist-pkg/ui/settings-panel.d.ts +58 -45
  371. package/dist-pkg/ui/settings-panel.js +189 -94
  372. package/dist-pkg/ui/settings-typo.d.ts +29 -78
  373. package/dist-pkg/ui/settings-typo.js +158 -129
  374. package/dist-pkg/ui/settings-visual.d.ts +29 -87
  375. package/dist-pkg/ui/settings-visual.js +240 -193
  376. package/dist-pkg/ui/shell.d.ts +112 -135
  377. package/dist-pkg/ui/shell.js +148 -217
  378. package/dist-pkg/ui/simulation-list.d.ts +27 -0
  379. package/dist-pkg/ui/simulation-list.js +76 -0
  380. package/dist-pkg/ui/simulation-over-the-world.d.ts +23 -0
  381. package/dist-pkg/ui/simulation-over-the-world.js +131 -0
  382. package/dist-pkg/ui/simulation-refusal.d.ts +5 -31
  383. package/dist-pkg/ui/simulation-refusal.js +8 -56
  384. package/dist-pkg/ui/switchable-control.d.ts +11 -0
  385. package/dist-pkg/ui/switchable-control.js +19 -0
  386. package/dist-pkg/ui/title.d.ts +3 -12
  387. package/dist-pkg/ui/title.js +49 -18
  388. package/dist-pkg/ui/top-band.d.ts +26 -0
  389. package/dist-pkg/ui/top-band.js +96 -0
  390. package/dist-pkg/ui/typo-choices.d.ts +71 -0
  391. package/dist-pkg/ui/typo-choices.js +85 -0
  392. package/dist-pkg/ui/visual-axes-panel.d.ts +11 -47
  393. package/dist-pkg/ui/visual-axes-panel.js +12 -94
  394. package/dist-pkg/ui/visual-choices.d.ts +77 -0
  395. package/dist-pkg/ui/visual-choices.js +107 -0
  396. package/dist-pkg/ui/vlibras.d.ts +77 -10
  397. package/dist-pkg/ui/vlibras.js +130 -81
  398. package/dist-pkg/ui/voice-control.d.ts +68 -0
  399. package/dist-pkg/ui/voice-control.js +276 -0
  400. package/dist-pkg/ui/voice-settings.d.ts +95 -0
  401. package/dist-pkg/ui/voice-settings.js +352 -0
  402. package/dist-pkg/ui/where-the-child-is.d.ts +31 -0
  403. package/dist-pkg/ui/where-the-child-is.js +70 -0
  404. package/docs/CREDITS.md +300 -44
  405. package/docs/LICENSES.md +189 -145
  406. package/package.json +51 -27
  407. package/scripts/check-cartridge.mjs +78 -0
  408. package/scripts/game-build.d.mts +25 -0
  409. package/scripts/game-build.mjs +144 -0
  410. package/scripts/heavy-into-the-delivery.mjs +388 -0
  411. package/scripts/libras-avatar.json +680 -0
  412. package/scripts/libras-avatar.mjs +246 -0
  413. package/scripts/libras-glosses/gloss.py +95 -0
  414. package/scripts/libras-glosses/pyproject.toml +32 -0
  415. package/scripts/libras-glosses/uv.lock +781 -0
  416. package/scripts/libras-glosses.mjs +351 -0
  417. package/scripts/licences/Apache-2.0.txt +176 -0
  418. package/scripts/licences/GPL-3.0.txt +674 -0
  419. package/scripts/licences/MIT.txt +17 -0
  420. package/scripts/licences/OFL-1.1.txt +86 -0
  421. package/scripts/licences/UFL-1.0.txt +96 -0
  422. package/scripts/licences/fonts.mjs +389 -0
  423. package/scripts/licences/third-party.mjs +301 -0
  424. package/scripts/licences/vosk-browser.NOTICE.txt +268 -0
  425. package/app/public/vendor/fonts/comicneue-400.woff2 +0 -0
  426. package/app/public/vendor/fonts/comicneue-700.woff2 +0 -0
  427. package/app/public/vendor/fonts/fondamento-400-ext.woff2 +0 -0
  428. package/app/public/vendor/fonts/fondamento-400.woff2 +0 -0
  429. package/app/public/vendor/fonts/inter-400.woff2 +0 -0
  430. package/app/public/vendor/fonts/inter-700.woff2 +0 -0
  431. package/app/public/vendor/fonts/lato-400.woff2 +0 -0
  432. package/app/public/vendor/fonts/lato-700.woff2 +0 -0
  433. package/app/public/vendor/fonts/literata-400.woff2 +0 -0
  434. package/app/public/vendor/fonts/literata-700.woff2 +0 -0
  435. package/app/public/vendor/fonts/newsreader-400.woff2 +0 -0
  436. package/app/public/vendor/fonts/newsreader-700.woff2 +0 -0
  437. package/app/public/vendor/fonts/opendyslexic-400.woff2 +0 -0
  438. package/app/public/vendor/fonts/opensans-400.woff2 +0 -0
  439. package/app/public/vendor/fonts/opensans-700.woff2 +0 -0
  440. package/app/public/vendor/fonts/pinyon-400.woff2 +0 -0
  441. package/app/public/vendor/fonts/pressstart-400.woff2 +0 -0
  442. package/app/public/vendor/fonts/quattro-400.woff2 +0 -0
  443. package/app/public/vendor/fonts/quattro-700.woff2 +0 -0
  444. package/app/public/vendor/fonts/sourcesans-400.woff2 +0 -0
  445. package/app/public/vendor/fonts/sourcesans-700.woff2 +0 -0
  446. package/app/public/vendor/fonts/sourceserif-400.woff2 +0 -0
  447. package/app/public/vendor/fonts/sourceserif-700.woff2 +0 -0
  448. package/app/public/vendor/fonts/ufmag-400.woff2 +0 -0
  449. package/dist-pkg/core/anel.d.ts +0 -17
  450. package/dist-pkg/core/anel.js +0 -31
  451. package/dist-pkg/core/collision.d.ts +0 -21
  452. package/dist-pkg/core/collision.js +0 -62
  453. package/dist-pkg/core/layers.d.ts +0 -63
  454. package/dist-pkg/core/layers.js +0 -95
  455. package/dist-pkg/core/letter-grid.d.ts +0 -55
  456. package/dist-pkg/core/letter-grid.js +0 -116
  457. package/dist-pkg/core/password.d.ts +0 -34
  458. package/dist-pkg/core/password.js +0 -205
  459. package/dist-pkg/core/rotulo-acessivel.d.ts +0 -17
  460. package/dist-pkg/core/rotulo-acessivel.js +0 -41
  461. package/dist-pkg/core/run-state.d.ts +0 -115
  462. package/dist-pkg/core/run-state.js +0 -40
  463. package/dist-pkg/core/tiles.d.ts +0 -12
  464. package/dist-pkg/core/tiles.js +0 -61
  465. package/dist-pkg/core/world.d.ts +0 -4
  466. package/dist-pkg/core/world.js +0 -58
  467. package/dist-pkg/input/origem-sintetica.d.ts +0 -44
  468. package/dist-pkg/input/origem-sintetica.js +0 -60
  469. package/dist-pkg/input/transporte-em-uso.d.ts +0 -101
  470. package/dist-pkg/input/transporte-em-uso.js +0 -130
  471. package/dist-pkg/platform/audio-nav.d.ts +0 -51
  472. package/dist-pkg/platform/audio-nav.js +0 -99
  473. package/dist-pkg/platform/guide-intensity.d.ts +0 -40
  474. package/dist-pkg/platform/guide-intensity.js +0 -73
  475. package/dist-pkg/platform/pesados-catalogo.d.ts +0 -14
  476. package/dist-pkg/platform/pesados-catalogo.js +0 -177
  477. package/dist-pkg/platform/pesados.d.ts +0 -31
  478. package/dist-pkg/platform/pesados.js +0 -75
  479. package/dist-pkg/platform/vozes-prontas.d.ts +0 -28
  480. package/dist-pkg/platform/vozes-prontas.js +0 -61
  481. package/dist-pkg/render/camera.d.ts +0 -58
  482. package/dist-pkg/render/camera.js +0 -105
  483. package/dist-pkg/render/cenario-data.d.ts +0 -127
  484. package/dist-pkg/render/cenario-data.js +0 -145
  485. package/dist-pkg/render/city-tex.d.ts +0 -63
  486. package/dist-pkg/render/city-tex.js +0 -228
  487. package/dist-pkg/render/city-tiles.d.ts +0 -11
  488. package/dist-pkg/render/city-tiles.js +0 -140
  489. package/dist-pkg/render/draw.d.ts +0 -149
  490. package/dist-pkg/render/draw.js +0 -231
  491. package/dist-pkg/render/fx.d.ts +0 -63
  492. package/dist-pkg/render/fx.js +0 -123
  493. package/dist-pkg/render/minimap.d.ts +0 -11
  494. package/dist-pkg/render/minimap.js +0 -94
  495. package/dist-pkg/render/parallax.d.ts +0 -85
  496. package/dist-pkg/render/parallax.js +0 -158
  497. package/dist-pkg/render/player-anim.d.ts +0 -71
  498. package/dist-pkg/render/player-anim.js +0 -152
  499. package/dist-pkg/render/recycling-tex.d.ts +0 -48
  500. package/dist-pkg/render/recycling-tex.js +0 -164
  501. package/dist-pkg/render/scene-city.d.ts +0 -77
  502. package/dist-pkg/render/scene-city.js +0 -181
  503. package/dist-pkg/render/scene-parallax.d.ts +0 -72
  504. package/dist-pkg/render/scene-parallax.js +0 -426
  505. package/dist-pkg/render/scene-sky.d.ts +0 -126
  506. package/dist-pkg/render/scene-sky.js +0 -295
  507. package/dist-pkg/render/set-cenario.d.ts +0 -54
  508. package/dist-pkg/render/set-cenario.js +0 -103
  509. package/dist-pkg/render/textures.d.ts +0 -75
  510. package/dist-pkg/render/textures.js +0 -240
  511. package/dist-pkg/render/title-scene.d.ts +0 -46
  512. package/dist-pkg/render/title-scene.js +0 -75
  513. package/dist-pkg/render/weather.d.ts +0 -96
  514. package/dist-pkg/render/weather.js +0 -171
  515. package/dist-pkg/render/wheelchair-sprites.d.ts +0 -32
  516. package/dist-pkg/render/wheelchair-sprites.js +0 -69
  517. package/dist-pkg/render/world-tex.d.ts +0 -18
  518. package/dist-pkg/render/world-tex.js +0 -87
  519. package/dist-pkg/ui/activities-menu.d.ts +0 -258
  520. package/dist-pkg/ui/activities-menu.js +0 -648
  521. package/dist-pkg/ui/caa-sets.d.ts +0 -39
  522. package/dist-pkg/ui/caa-sets.js +0 -65
  523. package/dist-pkg/ui/latch-refusal.d.ts +0 -32
  524. package/dist-pkg/ui/latch-refusal.js +0 -60
  525. package/dist-pkg/ui/map-hub.d.ts +0 -56
  526. package/dist-pkg/ui/map-hub.js +0 -138
  527. package/dist-pkg/ui/settings-caa.d.ts +0 -45
  528. package/dist-pkg/ui/settings-caa.js +0 -137
  529. package/dist-pkg/ui/settings-motor.d.ts +0 -175
  530. package/dist-pkg/ui/settings-motor.js +0 -342
  531. package/dist-pkg/ui/webcam.d.ts +0 -7
  532. package/dist-pkg/ui/webcam.js +0 -93
@@ -0,0 +1,276 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // ui/voice-control — PLAYING BY VOICE, PUT TOGETHER (ADR-0189, ADR-0193, ADR-0194, ADR-0204 erratum; issues #184, #190).
3
+ //
4
+ // The four pieces that existed separately, joined: the Dev's vocabulary and the firing rule (`input/voice-map`), the recogniser
5
+ // opened from the delivery (`platform/vosk-runtime`), the microphone that stays open (`platform/voice-listener`), and the
6
+ // presses on the virtual controller with the source `fala` (ADR-0111). The 👄 of the quick bar drives it; what cannot start is
7
+ // SAID, written once in `problems`, and puts the icon back to off — a child must never meet a button that does nothing.
8
+ //
9
+ // 📌 THE GRAMMAR FOLLOWS THE MENU (ADR-0194): saying the name of an item activates it, so the words the open menu is showing
10
+ // join the closed grammar while it is open and leave with it.
11
+ //
12
+ // 🎯 A NAME HEARD IS A CURSOR AND A CONFIRM, NOT A CLICK (ADR-0194 §2): the root's menu navigation puts the cursor on the
13
+ // named item (`pointAt`), and the confirm position is pressed on the virtual controller like any other spoken word — so the
14
+ // item is activated by the same path, with the same spoken feedback, as a child confirming it with the cursor on it. WHEN a
15
+ // name fires is the reader's (`input/voice-map` over `platform/speech-recognition`): at once, unless another item's name
16
+ // continues it («voltar» / «voltar ao jogo»), which waits for the end of the utterance (§3). A LOCKED item's name is heard
17
+ // too and takes the same path: the confirm reaches the item's own press, which says its reason and does nothing (§5, ADR-0161).
18
+ // A name with a word the loaded model lacks cannot be heard at all, and is a line of `problems` (§4).
19
+ // ⚠️ EXCEPT WITH ONE BUTTON ONLY ON (ADR-0218 §4): then every word heard, a name included, is the child's switch and nothing more
20
+ // — one press, which the controller takes as «take the one shown», and no cursor put anywhere first.
21
+ //
22
+ // ⚠️ A SPOKEN COMMAND IS A TAP, NOT A HOLD. The word arrives, the position is pressed and let go — one press, one release.
23
+ // Whether that tap keeps a direction going is the latch's, and on speech the latch starts as the GAME answers `holdsKeys()`
24
+ // (ADR-0249, resolved by the root when the press arrives): a platform game keeps walking on «direita», a quiz moves once on
25
+ // «abaixo», and the child may change either for her voice.
26
+ import { voiceGrammar, createVoiceCommands } from '../input/voice-map.js';
27
+ import { loadVoskRuntime } from '../platform/vosk-runtime.js';
28
+ import { startVoiceListening } from '../platform/voice-listener.js';
29
+ import { spokenText } from '../platform/speech-recognition.js';
30
+ import { MENU_CONFIRM } from './menu-intent.js';
31
+ /** How long a spoken position stays pressed. The same pulse the scan uses: long enough for a game to see a press and a release. */
32
+ export const VOICE_PULSE_MS = 400;
33
+ /** The base of a language tag: `en-US` and `en` are the same model. */
34
+ const baseOf = (tag) => tag.split('-')[0].toLowerCase();
35
+ export function createVoiceControl(d) {
36
+ const { t } = d;
37
+ const loadRuntime = d.loadRuntime ?? loadVoskRuntime;
38
+ const listen = d.listen ?? startVoiceListening;
39
+ const said = new Set();
40
+ let on = false, starting = null;
41
+ let listener = null;
42
+ let commands = null;
43
+ let grammarGiven = '';
44
+ /** The words the model now listening knows, once read (`platform/vosk-vocabulary`); `null` while unknown. */
45
+ let vocabulary = null;
46
+ /** Bumped by every stop, so a vocabulary that arrives for a recogniser already replaced is dropped. */
47
+ let generation = 0;
48
+ /**
49
+ * 🔴 THE LANGUAGE THE LISTENING STARTED IN, read ONCE. The model and the grammar must come from the same reading: `start()` used
50
+ * to read the language again after the model had loaded, and a page booting in English (pt-BR until its stored language
51
+ * applies) opened the Portuguese model with the English grammar — 15 «missing in vocabulary», measured on 2026-09-27.
52
+ */
53
+ let listeningLanguage = '';
54
+ /** A choice among the game's options borrowing this ear (ADR-0256): what is heard goes to it, and no command is obeyed. */
55
+ let lent = null;
56
+ /** The open menu's names that can enter the grammar. A one-letter name stays out: in a closed grammar every short noise lands on it. */
57
+ const sayableNames = () => d.menuWords().filter((n) => spokenText(n).length > 1);
58
+ /**
59
+ * 🔴 A NAME THE MODEL CANNOT HEAR IS SAID TO THE ADULT (ADR-0194 §4, ADR-0169): Kaldi drops a grammar word its vocabulary lacks,
60
+ * in silence, and the item goes mute for the child — she can still walk to it with the direction words. One line per item and
61
+ * language, naming the words, the cost and the fix. Nothing is said while the vocabulary is unknown: no line beats a false one.
62
+ */
63
+ const reportUnsayable = (names) => {
64
+ const known = vocabulary;
65
+ if (!known)
66
+ return;
67
+ const language = listeningLanguage;
68
+ for (const name of names) {
69
+ const lacking = [...new Set(spokenText(name).split(' '))].filter((w) => w && !known.has(w));
70
+ const kind = `unsayable:${language}:${name}`;
71
+ if (!lacking.length || said.has(kind))
72
+ continue;
73
+ said.add(kind);
74
+ d.report(`voice control: the menu item "${name}" has ${lacking.length === 1 ? 'a word' : 'words'} the ${language} speech `
75
+ + `model does not know (${lacking.map((w) => `"${w}"`).join(', ')}) — the child cannot choose this item by saying its name, `
76
+ + `only by walking to it with the direction words; reword the item's label in the ${language} dictionary with words the `
77
+ + 'model knows (ADR-0194 §4)');
78
+ }
79
+ };
80
+ /** The open menu's names, handed to the reader and checked against the model, and the grammar with them. */
81
+ const followMenu = () => {
82
+ const names = sayableNames();
83
+ commands?.items(names);
84
+ reportUnsayable(names);
85
+ return voiceGrammar(listeningLanguage, names);
86
+ };
87
+ const command = (action) => {
88
+ d.controller.press(action, 'fala');
89
+ d.after(() => d.controller.release(action, 'fala'), VOICE_PULSE_MS);
90
+ };
91
+ /*
92
+ * 🔴 ONE WORD HEARD IS ONE ACTION (ADR-0111 errata, ADR-0218 §4). With one button only on, the controller takes every press as
93
+ * «take the one shown», so a word may do nothing BESIDES its press: asked here, first, for every word — a position, a name, any
94
+ * kind a later reader adds. 📏 Before, a name was still pointed at: with the chip on «próximo» the cursor jumped to the named
95
+ * item and then stepped on from it; on «cancelar» the press meant nothing and the cursor had moved anyway.
96
+ */
97
+ const obey = (heard) => {
98
+ for (const c of heard ?? []) {
99
+ if (d.oneButtonOnly())
100
+ command(c.kind === 'position' ? c.action : MENU_CONFIRM);
101
+ else if (c.kind === 'position')
102
+ command(c.action);
103
+ else if (d.pointAt(c.name))
104
+ command(MENU_CONFIRM);
105
+ }
106
+ };
107
+ const stop = () => {
108
+ const going = listener;
109
+ listener = null;
110
+ commands = null;
111
+ lent = null;
112
+ vocabulary = null;
113
+ generation += 1;
114
+ void going?.stop();
115
+ };
116
+ /**
117
+ * Nothing started: the child hears why, the adult reads it once, and the icon goes back to off. A failure that arrives after
118
+ * the 👄 was turned off (or the root disposed, ADR-0220) has nobody waiting for it and says and writes nothing.
119
+ */
120
+ const failed = (kind, line, spoken) => {
121
+ if (!on)
122
+ return;
123
+ if (!said.has(kind)) {
124
+ said.add(kind);
125
+ d.report(line);
126
+ }
127
+ d.alert(spoken);
128
+ d.turnOff();
129
+ };
130
+ const start = async () => {
131
+ /*
132
+ * 🔴 THE LOAD IS INSIDE A `try` AND THIS WAS MEASURED, not foreseen: on 2026-09-21 the delivery's bundle turned out to be an
133
+ * ES module and the loader threw. Nothing here caught it, so the rejection died as an unhandled promise and the 👄 STAYED
134
+ * LIT over a microphone that had never opened — the exact defect this module exists to prevent, one layer above where it
135
+ * was being prevented. A failure that has no name is still a failure the child has to be told about.
136
+ */
137
+ let load;
138
+ const language = d.language();
139
+ try {
140
+ load = await loadRuntime({
141
+ base: d.base, language, hasFile: d.hasFile, loadBundle: d.loadBundle, ...(d.fetch ? { fetch: d.fetch } : {}),
142
+ });
143
+ }
144
+ catch (e) {
145
+ failed('runtime', `voice control: the recogniser did not open (${e instanceof Error ? e.message : String(e)}) — the child `
146
+ + 'cannot play by speaking; check that the delivery carries the command files', t('sr.voice.failed'));
147
+ return;
148
+ }
149
+ if (!load.ok) {
150
+ /*
151
+ * 📌 ONE LINE PER LANGUAGE, NAMING THAT LANGUAGE'S FIX (ADR-0225 and its erratum, ADR-0169). The delivery carries every
152
+ * language unless its `--commands` list narrowed it, and the install fetches every one the delivery carries — so a model
153
+ * missing here was left out of the delivery or has not come down yet, and «open it online» alone sends the adult after a
154
+ * download that cannot happen in the first case. The reading's line names its two fixes the same way.
155
+ */
156
+ const lang = language.split('-')[0].toLowerCase();
157
+ failed(`files:${lang}`, `voice control: ${load.missing.join(', ')} not on this device for ${language} — the child cannot play `
158
+ + `by speaking in this language; build the delivery with \`npx inclusionist-heavy\` without \`--commands\` (it carries every `
159
+ + `language) or with \`--commands ${lang}\` in its list, and open the game once online so the install fetches them`, t('sr.voice.needsInternet'));
160
+ return;
161
+ }
162
+ // the language changed while the model loaded (a page booting before its stored language applies): start again in it
163
+ if (baseOf(d.language()) !== baseOf(language)) {
164
+ await start();
165
+ return;
166
+ }
167
+ listeningLanguage = language;
168
+ commands = createVoiceCommands(language);
169
+ const grammar = followMenu();
170
+ grammarGiven = grammar.join('\n');
171
+ try {
172
+ listener = await listen({
173
+ model: load.model,
174
+ grammar,
175
+ getUserMedia: d.getUserMedia,
176
+ createContext: d.createContext,
177
+ onPartial: (text) => { if (lent)
178
+ lent(text, false);
179
+ else
180
+ obey(commands?.partial(text)); },
181
+ onFinal: (text) => { if (lent)
182
+ lent(text, true);
183
+ else
184
+ obey(commands?.final(text)); },
185
+ });
186
+ }
187
+ catch {
188
+ failed('microphone', 'voice control: the microphone did not open — the child cannot play by speaking; allow the microphone '
189
+ + 'for this page, or plug one in', t('sr.voice.noMicrophone'));
190
+ return;
191
+ }
192
+ // ⚠️ TURNED OFF WHILE IT WAS STARTING: the microphone opened after the child let go of the icon, and a listener nobody asked
193
+ // for would go on hearing her. The check is here and not only in `apply`, because starting is not instantaneous.
194
+ if (!on) {
195
+ stop();
196
+ return;
197
+ }
198
+ // a menu that opened or closed while the microphone was opening changed the names after the grammar above was built
199
+ api.refreshGrammar();
200
+ d.say(t('sr.voice.ready'));
201
+ // 📌 THE VOCABULARY IS READ AFTER THE CHILD CAN SPEAK, never before: it is a report for the adult, and reading the model's
202
+ // archive a second time must not delay the first command. It checks the names showing when it arrives and every later menu.
203
+ const mine = generation;
204
+ void load.vocabulary?.().then((words) => {
205
+ if (mine !== generation || !listener)
206
+ return;
207
+ vocabulary = words;
208
+ reportUnsayable(sayableNames());
209
+ });
210
+ };
211
+ const api = {
212
+ async apply(next) {
213
+ on = next;
214
+ if (!next) {
215
+ stop();
216
+ return;
217
+ }
218
+ if (listener || starting)
219
+ return;
220
+ starting = start().finally(() => { starting = null; });
221
+ await starting;
222
+ },
223
+ refreshGrammar() {
224
+ // lent to a choice: the options are the grammar until it gives the ear back
225
+ if (!listener || lent)
226
+ return;
227
+ // 📌 A new grammar is a new recogniser (`platform/voice-listener`), and the root asks on every menu change it sees —
228
+ // most of which change no name. Only a grammar that differs is handed on.
229
+ const grammar = followMenu();
230
+ const given = grammar.join('\n');
231
+ if (given === grammarGiven)
232
+ return;
233
+ grammarGiven = given;
234
+ listener.setGrammar(grammar);
235
+ },
236
+ async languageChanged() {
237
+ /*
238
+ * 🔴 THE RECOGNISER STOPS CHOOSING ITS LANGUAGE ONCE (ADR-0225). `start()` reads `d.language()` for the MODEL and for the
239
+ * vocabulary, and it used to read it only at the moment the 👄 was switched on — so a child who changed language while
240
+ * listening went on being heard in the old one.
241
+ * ⚠️ AND THAT IS WORSE THAN STOPPING, which is why this is not cosmetic: the grammar DOES follow, because it is rebuilt
242
+ * whenever a menu opens (ADR-0194). So after a change the new language's words were fed to the old language's model, and
243
+ * a closed grammar answers with the nearest candidate — measured in the lab on 2026-09-14, where «configurações de
244
+ * inclusão» came back as «quatro». A microphone that acts on a word the child did not say is the defect here.
245
+ * 📌 OFF IS ALREADY RIGHT: the next start reads the language then. This only has work to do while it is listening.
246
+ */
247
+ if (!on)
248
+ return;
249
+ // ⚠️ A START IN FLIGHT IS IN THE OLD LANGUAGE. Letting it finish and replacing it costs one opening; skipping it would
250
+ // leave `starting` to install the old model on top of the new one, which is the race the `if (!on)` in `start` exists for.
251
+ await starting;
252
+ // a start that noticed the change itself is already listening in the new language: nothing to redo
253
+ if (listener && baseOf(listeningLanguage) === baseOf(d.language()))
254
+ return;
255
+ stop();
256
+ await api.apply(true);
257
+ },
258
+ borrow(language, grammar, onHeard) {
259
+ if (!listener || lent || baseOf(language) !== baseOf(listeningLanguage))
260
+ return null;
261
+ const held = listener;
262
+ lent = onHeard;
263
+ held.setGrammar(grammar);
264
+ grammarGiven = '';
265
+ return {
266
+ async stop() {
267
+ if (lent !== onHeard)
268
+ return;
269
+ lent = null;
270
+ api.refreshGrammar(); // the menu's grammar back
271
+ },
272
+ };
273
+ },
274
+ };
275
+ return api;
276
+ }
@@ -0,0 +1,95 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ import type { Translator } from '../core/i18n.js';
3
+ import type { DomQuery } from '../core/dom-query.js';
4
+ import { type AudioCatState } from './audio-choices.js';
5
+ export interface TtsPanelEngine {
6
+ id: string;
7
+ speak: (text: string) => void;
8
+ }
9
+ /** Minimal shape of the injected `tts` (platform/tts.ts's createTts() instance) this section drives. */
10
+ export interface TtsPanel {
11
+ getEngineSel: () => string;
12
+ setEngineSel: (v: string) => void;
13
+ getEngine: () => TtsPanelEngine | null;
14
+ getVoiceObj: () => SpeechSynthesisVoice | null;
15
+ setVoiceObj: (v: SpeechSynthesisVoice | null) => void;
16
+ loadTTS: () => void;
17
+ narrate: (text: string) => void;
18
+ /**
19
+ * Does this assembly have a neural engine (the game's Kokoro port, ADR-0198)? Optional, absent reads `true`: a panel test
20
+ * fake written before this field has no opinion on neural engines.
21
+ */
22
+ neuralAvailable?: boolean;
23
+ /** The voices of the language (ADR-0185). Optional: a panel driven without them offers no «Voz» list and locks nothing. */
24
+ voices?: () => readonly PanelVoice[];
25
+ currentVoice?: () => PanelVoice | null;
26
+ setVoice?: (id: string) => boolean;
27
+ }
28
+ /** A voice as this panel lists it: `webspeech:<name>` for the browser's, `xx_name` for Kokoro's. */
29
+ export interface PanelVoice {
30
+ readonly voice: string;
31
+ readonly engine?: string;
32
+ readonly recommended?: boolean;
33
+ }
34
+ /**
35
+ * The browser, as four functions.
36
+ *
37
+ * 🔴 A module that reaches `document` or `window` undoes a decision (ADR-0178), and the health ratchet refuses a NEW one that
38
+ * does. These four are what the voice section actually needs from the browser, and naming them is what makes the section
39
+ * testable: hand it a fake `doc`, a list of voices and two no-ops, and every branch below runs without a speech engine.
40
+ */
41
+ export interface VoicePorts {
42
+ /** Makes one <option> — the only element this section builds. A FUNCTION and not the document: a port read at wiring
43
+ * time would make a panel unmountable where there is no document yet, and the node suite mounts exactly there. */
44
+ readonly newOption: () => HTMLOptionElement;
45
+ /** The system voices, or an empty list where the browser has none or throws. */
46
+ readonly systemVoices: () => readonly SpeechSynthesisVoice[];
47
+ /** Speak the sample through the BROWSER's own synthesiser — the neural engine speaks through `tts` instead. */
48
+ readonly speakSample: (text: string, chosen: SpeechSynthesisVoice | null) => void;
49
+ /** Called again whenever the browser finishes loading its voice list: they arrive late, and often empty first.
50
+ * ⚠️ The parameter is `again` and not `run`, which is a GAME ACTION word this layer does not say (the boundary of 2026-09-06). */
51
+ readonly whenVoicesChange: (again: () => void) => void;
52
+ }
53
+ /**
54
+ * What this section needs from the panel's ctx.
55
+ *
56
+ * ⚠️ The store shape is written here and NOT imported from `ui/settings-audio`, which declares the same two methods: that
57
+ * module imports this one, and importing it back would be the cycle ADR-0173 forbids — for a type as much as for a value.
58
+ */
59
+ /**
60
+ * THE SETTINGS THIS SECTION READS AND WRITES — the page's settings store, built by the root (ADR-0232 D2c, issue #207): the
61
+ * spoken index (ADR-0044 item 3) and the speech rate (ADR-0183). The names are `core/state`'s, so a root passes the store
62
+ * itself; the reads are LIVE, because the quick bar and a second screen change them too.
63
+ */
64
+ export interface VoiceSettingsStore {
65
+ readonly menuIndexOn: boolean;
66
+ setMenuIndexOnValue(on: boolean): void;
67
+ readonly speechPpm: number;
68
+ setSpeechPpmValue(ppm: number): void;
69
+ }
70
+ export interface VoiceSettingsCtx {
71
+ /**
72
+ * The root's translator (ADR-0232 D3): its `t`, and the page's language as a BCP-47 tag, which picks the system voices the
73
+ * list offers. A Pick and not a bare `t`, because the voice list reads the language.
74
+ */
75
+ translator: Pick<Translator, 't' | 'bcp47'>;
76
+ /** The page's settings store (see `VoiceSettingsStore`). REQUIRED: a section reading from nowhere would show the defaults. */
77
+ settings: VoiceSettingsStore;
78
+ $: DomQuery;
79
+ srSay: (msg: string) => void;
80
+ toggleBtn: (btn: HTMLElement, on: boolean) => void;
81
+ store: {
82
+ get(key: string, fallback?: string | null): string | null;
83
+ set(key: string, value: string | number | boolean): boolean;
84
+ };
85
+ tts: TtsPanel;
86
+ getAudioCat: () => Record<string, AudioCatState> | null;
87
+ setCatGain: (k: string) => void;
88
+ }
89
+ export interface VoiceSettings {
90
+ /** Everything the section shows, refreshed from the stored state. Called by the panel's own render. */
91
+ render: () => void;
92
+ /** Just the narration switch, its volume and the chosen engine. Exported because the root reflects it from outside. */
93
+ reflectTts: () => void;
94
+ }
95
+ export declare function createVoiceSettings(ctx: VoiceSettingsCtx, ports: VoicePorts): VoiceSettings;