@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,352 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // ui/voice-settings — THE VOICE SECTION of the hearing panel: the narration switch, the engine, the voice, the rate, the
3
+ // spoken index and the sample button (ADR-0221; issue #203).
4
+ //
5
+ // 🔴 WHY IT LEFT `ui/settings-audio`: that module was the third largest in the engine, and half of it was about SPEECH while
6
+ // the other half was about sound categories, the cane, blind mode and the audio outputs. The two halves never needed each
7
+ // other. What binds these seven pieces is one question — «what does the child hear when the engine talks, and what does she
8
+ // change to hear it differently».
9
+ //
10
+ // ⚠️ AND IT RECEIVES THE BROWSER INSTEAD OF REACHING IT, which the ratchet of ADR-0221 step 7d made compulsory rather than
11
+ // tasteful: a module born today that reaches a global is refused, because there the ceiling is ZERO and not a p90. So
12
+ // `document.createElement`, `speechSynthesis.getVoices`, the sample utterance and the `onvoiceschanged` subscription arrive
13
+ // as four ports. What that buys beyond the gate: the whole section can be exercised without a browser's speech engine.
14
+ //
15
+ // 📌 THE SPOKEN INDEX («6 of 10») LIVES HERE and not in a visual panel, by the decision of ADR-0044 item 3: it changes the
16
+ // NARRATION, and whoever turns it off is shortening what she hears at every step. She comes here when what she hears bothers
17
+ // her.
18
+ import { toggleLabel } from './dom.js';
19
+ import { SPEECH_RATES } from '../core/speech-rate.js';
20
+ import { volPercent, voiceEngineOptions, pickVoicesFor, voiceLabel, } from './audio-choices.js';
21
+ /** The name a child sees for a voice: the browser's name, or the name inside a Kokoro identifier — `pf_dora` is «Dora». */
22
+ function voiceName(v) {
23
+ // a browser voice is named by the browser (ADR-0200); Kokoro ids read `xx_name` (`pf_dora`)
24
+ // («Microsoft Maria - Portuguese (Brazil)» is «Microsoft Maria»: the language is already the list's, and no parentheses, ADR-0158)
25
+ if (v.voice.startsWith('webspeech:'))
26
+ return v.voice.slice('webspeech:'.length).replace(/\s*\([^)]*\)/g, '').split(' - ')[0].trim();
27
+ const name = v.voice.split('_')[1] ?? v.voice;
28
+ return name.charAt(0).toUpperCase() + name.slice(1);
29
+ }
30
+ /**
31
+ * What the list SHOWS: Kokoro's two good voices, Heart and Bella, carry a heart (ADR-0198 §3). Only SHOWN: what is said is
32
+ * the name alone (ADR-0159 rule 12, no glyph inside a spoken name).
33
+ */
34
+ function panelVoiceLabel(v) {
35
+ return (v.recommended ? '❤️ ' : '') + voiceName(v);
36
+ }
37
+ /**
38
+ * The rows a missing voice locks (ADR-0185 §4): narration, its volume and rate, the spoken index, the voice — and the «test voice»
39
+ * button a page may carry, which with no voice of the language would ask the browser for one of another (ADR-0243 §3).
40
+ */
41
+ const SPEECH_ROWS = ['#opt-tts', '#tts-vol', '#tts-ppm', '#opt-menuindex', '#tts-voz', '#opt-tts-test'];
42
+ export function createVoiceSettings(ctx, ports) {
43
+ const { t, bcp47 } = ctx.translator;
44
+ function reflectTts() {
45
+ const b = ctx.$('#opt-tts');
46
+ const cat = ctx.getAudioCat();
47
+ const on = !!(cat && cat.tts && cat.tts.on);
48
+ if (b) {
49
+ ctx.toggleBtn(b, on);
50
+ b.textContent = toggleLabel(t, on);
51
+ }
52
+ // and its volume: the slider was never drawn from the mixer, so it opened at the range's middle whatever was stored
53
+ const vol = ctx.$('#tts-vol');
54
+ if (vol && cat?.tts)
55
+ vol.value = String(volPercent(cat.tts.vol));
56
+ const e = ctx.$('#tts-engine');
57
+ if (e)
58
+ e.value = ctx.tts.getEngineSel();
59
+ }
60
+ function reflectMenuIndex() {
61
+ const b = ctx.$('#opt-menuindex');
62
+ if (b) {
63
+ ctx.toggleBtn(b, ctx.settings.menuIndexOn);
64
+ b.textContent = toggleLabel(t, ctx.settings.menuIndexOn);
65
+ }
66
+ }
67
+ function populateTtsEngines() {
68
+ const sel = ctx.$('#tts-engine');
69
+ if (!sel || sel.dataset.filled)
70
+ return;
71
+ sel.dataset.filled = '1';
72
+ voiceEngineOptions(ctx.tts.neuralAvailable !== false).forEach(([v, l]) => {
73
+ const o = ports.newOption();
74
+ o.value = v;
75
+ o.textContent = t(l);
76
+ sel.appendChild(o);
77
+ });
78
+ sel.value = ctx.tts.getEngineSel();
79
+ }
80
+ function populateTtsVoices() {
81
+ const sel = ctx.$('#tts-voice');
82
+ if (!sel)
83
+ return;
84
+ const list = pickVoicesFor(ports.systemVoices(), bcp47());
85
+ sel.innerHTML = '';
86
+ if (!list.length) {
87
+ const o = ports.newOption();
88
+ o.textContent = t('audio.noSystemVoices');
89
+ sel.appendChild(o);
90
+ ctx.tts.setVoiceObj(null);
91
+ return;
92
+ }
93
+ list.forEach((v) => {
94
+ const o = ports.newOption();
95
+ o.value = v.name;
96
+ o.textContent = voiceLabel(v);
97
+ sel.appendChild(o);
98
+ });
99
+ const saved = ctx.store.get('incl_tts_voice', null);
100
+ const pick = list.find((v) => v.name === saved) || list[0];
101
+ sel.value = pick.name;
102
+ ctx.tts.setVoiceObj(pick);
103
+ }
104
+ const noVoice = () => !!ctx.tts.voices && ctx.tts.voices().length === 0;
105
+ /** The lock last drawn on the speech rows; `null` before it is first drawn. Compared when the voices change. */
106
+ let lockDrawn = null;
107
+ /** The speech rate list: each step «N PPM», the stored one selected (ADR-0183 §1, ADR-0196). */
108
+ function renderRate() {
109
+ const sel = ctx.$('#tts-ppm');
110
+ if (!sel)
111
+ return;
112
+ while (sel.firstChild)
113
+ sel.removeChild(sel.firstChild);
114
+ for (const ppm of SPEECH_RATES) {
115
+ const o = ports.newOption();
116
+ o.value = String(ppm);
117
+ o.textContent = t('visual.legenda.ppm', { n: ppm });
118
+ sel.appendChild(o);
119
+ }
120
+ sel.value = String(ctx.settings.speechPpm);
121
+ }
122
+ /** The voices of the language as ids, one per line: what the «Voz» list was last drawn from. */
123
+ const voiceIds = () => (ctx.tts.voices?.() ?? []).map((v) => v.voice).join('\n');
124
+ let listDrawn = null;
125
+ function renderVoiceList() {
126
+ const sel = ctx.$('#tts-voz');
127
+ if (!sel || !ctx.tts.voices)
128
+ return;
129
+ const list = ctx.tts.voices();
130
+ listDrawn = voiceIds();
131
+ sel.replaceChildren(); // options built node by node: no markup sink
132
+ if (!list.length) {
133
+ const o = ports.newOption();
134
+ o.textContent = t('audio.voz.nenhuma');
135
+ sel.appendChild(o);
136
+ return;
137
+ }
138
+ for (const v of list) {
139
+ const o = ports.newOption();
140
+ o.value = v.voice;
141
+ o.textContent = panelVoiceLabel(v);
142
+ sel.appendChild(o);
143
+ }
144
+ sel.value = ctx.tts.currentVoice?.()?.voice ?? list[0].voice;
145
+ }
146
+ /**
147
+ * No voice speaks the language (ADR-0185 §4): the five speech rows are LOCKED with the reason, never hidden (ADR-0161).
148
+ * `aria-disabled` and not `disabled`, so the keyboard still reaches the row and hears why.
149
+ */
150
+ function lockSpeechRows() {
151
+ const lock = noVoice();
152
+ lockDrawn = lock;
153
+ for (const id of SPEECH_ROWS) {
154
+ const el = ctx.$(id);
155
+ if (!el)
156
+ continue;
157
+ if (lock) {
158
+ el.setAttribute('aria-disabled', 'true');
159
+ el.dataset.motivo = t('audio.semVoz');
160
+ }
161
+ else {
162
+ el.removeAttribute('aria-disabled');
163
+ delete el.dataset.motivo;
164
+ }
165
+ }
166
+ }
167
+ /*
168
+ * A LOCKED SPEECH ROW DOES NOTHING AND SAYS WHY (ADR-0185 §4). In the CAPTURE phase on the control itself, so it runs
169
+ * before the row's own listener and stops it; a moved range goes back to the stored volume, a changed list to the voice in
170
+ * use. The footer shows the reason when the row takes focus: the card hears `focusin` after the row's own explanation.
171
+ */
172
+ /**
173
+ * What a refused control puts back, by row: the moved volume to the stored one, the picked voice and rate to the ones in use.
174
+ * A row not here has nothing to undo (a switch was never flipped). A table, as the other «which row does what» answers here.
175
+ */
176
+ const UNDO_ON_REFUSAL = {
177
+ '#tts-vol': (el) => { const cat = ctx.getAudioCat(); if (cat?.tts)
178
+ el.value = String(volPercent(cat.tts.vol)); },
179
+ '#tts-voz': () => renderVoiceList(),
180
+ '#tts-ppm': () => renderRate(),
181
+ };
182
+ function refuseWhileLocked() {
183
+ for (const id of SPEECH_ROWS) {
184
+ const el = ctx.$(id);
185
+ if (!el)
186
+ continue;
187
+ const refuse = (e) => {
188
+ if (el.getAttribute('aria-disabled') !== 'true')
189
+ return;
190
+ e.stopImmediatePropagation();
191
+ e.preventDefault();
192
+ UNDO_ON_REFUSAL[id]?.(el);
193
+ ctx.srSay(el.dataset.motivo ?? t('audio.semVoz'));
194
+ };
195
+ for (const kind of ['click', 'input', 'change'])
196
+ el.addEventListener(kind, refuse, true);
197
+ el.closest('.overlay__card')?.addEventListener('focusin', (e) => {
198
+ if (e.target !== el || el.getAttribute('aria-disabled') !== 'true')
199
+ return;
200
+ const footer = el.closest('.overlay__card')?.querySelector('.opt-explain');
201
+ if (footer)
202
+ footer.textContent = el.dataset.motivo ?? '';
203
+ });
204
+ }
205
+ }
206
+ refuseWhileLocked();
207
+ /**
208
+ * THE SEVEN CONTROLS, wired where the page carries them: the voice list, the rate, the narration switch, the spoken
209
+ * index, and the three the engine never builds but a page may (the engine selector, the system voice, the sample).
210
+ * A page that lacks one simply has that row missing.
211
+ */
212
+ function wireControls() {
213
+ const voiceSel = ctx.$('#tts-voz');
214
+ if (voiceSel)
215
+ voiceSel.addEventListener('change', () => {
216
+ if (!ctx.tts.setVoice?.(voiceSel.value)) {
217
+ renderVoiceList();
218
+ return;
219
+ }
220
+ const v = ctx.tts.currentVoice?.();
221
+ if (v)
222
+ ctx.srSay(t('sr.audio.voz', { nome: voiceName(v) }));
223
+ });
224
+ const rateSel = ctx.$('#tts-ppm');
225
+ if (rateSel)
226
+ rateSel.addEventListener('change', () => {
227
+ ctx.settings.setSpeechPpmValue(Number(rateSel.value));
228
+ renderRate();
229
+ ctx.srSay(`${t('audio.ttsPpm')}: ${t('visual.legenda.ppm', { n: ctx.settings.speechPpm })}`);
230
+ });
231
+ const ttsBtn = ctx.$('#opt-tts');
232
+ if (ttsBtn)
233
+ ttsBtn.addEventListener('click', () => {
234
+ const cat = ctx.getAudioCat();
235
+ if (!cat)
236
+ return;
237
+ cat.tts.on = !cat.tts.on;
238
+ ctx.setCatGain('tts');
239
+ reflectTts();
240
+ ctx.srSay(t(cat.tts.on ? 'sr.audio.ttsOn' : 'sr.audio.ttsOff'));
241
+ if (cat.tts.on)
242
+ ctx.tts.narrate(t('sr.audio.ttsOnSpoken'));
243
+ });
244
+ const idxBtn = ctx.$('#opt-menuindex');
245
+ if (idxBtn)
246
+ idxBtn.addEventListener('click', () => {
247
+ ctx.settings.setMenuIndexOnValue(!ctx.settings.menuIndexOn);
248
+ reflectMenuIndex();
249
+ // The announcement of the change carries NO index: it is not an item of any list, and a «1 of 1» here would be noise at
250
+ // exactly the moment the child is judging whether the noise bothers her.
251
+ ctx.srSay(t(ctx.settings.menuIndexOn ? 'sr.menu.indexOn' : 'sr.menu.indexOff'));
252
+ });
253
+ const ttsEngSel = ctx.$('#tts-engine');
254
+ if (ttsEngSel)
255
+ ttsEngSel.addEventListener('change', () => {
256
+ ctx.tts.setEngineSel(ttsEngSel.value);
257
+ ctx.store.set('incl_tts_engine', ttsEngSel.value);
258
+ if (ttsEngSel.value !== 'webspeech')
259
+ ctx.tts.loadTTS();
260
+ const opt = ttsEngSel.options[ttsEngSel.selectedIndex];
261
+ ctx.tts.narrate(t('sr.audio.engineSet', { motor: opt ? opt.text : '' }));
262
+ });
263
+ const ttsVoiceSel = ctx.$('#tts-voice');
264
+ if (ttsVoiceSel)
265
+ ttsVoiceSel.addEventListener('change', () => {
266
+ ctx.tts.setVoiceObj(ports.systemVoices().find((v) => v.name === ttsVoiceSel.value) || null);
267
+ ctx.store.set('incl_tts_voice', ttsVoiceSel.value);
268
+ ctx.tts.narrate(t('sr.audio.voicePicked'));
269
+ });
270
+ const ttsTestBtn = ctx.$('#opt-tts-test');
271
+ if (ttsTestBtn)
272
+ ttsTestBtn.addEventListener('click', () => {
273
+ const sample = t('audio.voiceSample');
274
+ const engine = ctx.tts.getEngine();
275
+ if (ctx.tts.getEngineSel() !== 'webspeech' && engine && engine.speak) {
276
+ try {
277
+ engine.speak(sample);
278
+ }
279
+ catch (e) { /* noop */ } // the neural engine is already loaded
280
+ }
281
+ else {
282
+ ports.speakSample(sample, ctx.tts.getVoiceObj()); // audible fallback (volume 1) + starts the neural download
283
+ if (ctx.tts.getEngineSel() !== 'webspeech')
284
+ ctx.tts.loadTTS();
285
+ }
286
+ ctx.srSay(t('sr.audio.testingVoice'));
287
+ });
288
+ const ttsVolEl = ctx.$('#tts-vol');
289
+ if (ttsVolEl)
290
+ ttsVolEl.addEventListener('input', () => {
291
+ const cat = ctx.getAudioCat();
292
+ if (!cat)
293
+ return;
294
+ cat.tts.vol = (+ttsVolEl.value) / 100;
295
+ cat.tts.on = true;
296
+ ctx.setCatGain('tts');
297
+ reflectTts();
298
+ });
299
+ }
300
+ wireControls();
301
+ /**
302
+ * A LOCK THAT FLIPS UNDER THE CURSOR IS TOLD, and only there (ADR-0185 erratum 2026-09-26). The footer is written with what
303
+ * focusing the row would show NOW — the reason, or the row's own explanation — because it would otherwise go on saying «no
304
+ * voice» under a row that works. Locking is heard once, through the footer, which is `aria-live`, as when a locked row is
305
+ * focused. Unlocking gets one sentence more: the footer then gives the row's explanation, which does not say the state
306
+ * changed, and whether a removed `aria-disabled` on the focused control is spoken is left to each screen reader.
307
+ */
308
+ function tellUnderCursor() {
309
+ const el = SPEECH_ROWS.map((id) => ctx.$(id)).find((e) => !!e && e.ownerDocument?.activeElement === e);
310
+ if (!el)
311
+ return;
312
+ const footer = el.closest('.overlay__card')?.querySelector('.opt-explain');
313
+ if (lockDrawn) {
314
+ if (footer)
315
+ footer.textContent = el.dataset.motivo ?? '';
316
+ return;
317
+ }
318
+ if (footer)
319
+ footer.textContent = el.closest('.ctrl-row')?.dataset.explain || (footer.dataset.idle ?? '');
320
+ ctx.srSay(t('audio.comVoz'));
321
+ }
322
+ /**
323
+ * 🔴 THE DEVICE'S VOICES CHANGED (ADR-0185 §4, erratum 2026-09-26): a device may list them a moment after load — empty first,
324
+ * then full — and may lose them. The lock is a fact about those voices, so it is evaluated again here, both ways, IN PLACE:
325
+ * attributes on the same nodes, never a redraw, so the focus stays where the child left it. Only a change is drawn or told:
326
+ * the voice list when its voices differ, the rows and the cursor's row when the lock flips.
327
+ */
328
+ function voicesChanged() {
329
+ populateTtsVoices();
330
+ if (voiceIds() !== listDrawn)
331
+ renderVoiceList(); // the same voices keep the same options, which the child may be reading
332
+ const before = lockDrawn;
333
+ lockSpeechRows();
334
+ if (lockDrawn !== before)
335
+ tellUnderCursor();
336
+ }
337
+ ports.whenVoicesChange(voicesChanged);
338
+ return {
339
+ render: () => {
340
+ reflectTts();
341
+ // 🔴 THIS ONE WAS MISSING, and only the case for the engine-mounted panel saw it (2026-09-12): the index is born ON, the
342
+ // button was born with no state, and the panel opened saying «off» — the control lying to the screen reader again.
343
+ reflectMenuIndex();
344
+ populateTtsEngines();
345
+ populateTtsVoices();
346
+ renderVoiceList();
347
+ renderRate();
348
+ lockSpeechRows();
349
+ },
350
+ reflectTts,
351
+ };
352
+ }
@@ -0,0 +1,31 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ import type { Translate } from '../core/i18n.js';
3
+ export interface WhereTheChildIsCtx {
4
+ /** Translates in the page's language — the root's translator (ADR-0232 D3). REQUIRED: text built from nowhere is a raw key. */
5
+ t: Translate;
6
+ /** The overlay on top, if any — the same one the focus trap and the menu navigation treat as «the open menu». */
7
+ readonly topVisibleOverlay: () => HTMLElement | null;
8
+ /** The pause card, when it is showing. */
9
+ readonly pauseCard: () => HTMLElement | null;
10
+ /** Who has the focus right now: it decides WHICH item of a panel is named. */
11
+ readonly focused: () => Element | null;
12
+ /**
13
+ * Does this cartridge want «N of M» spoken (ADR-0167)? It is only ever SPOKEN — never drawn — and a cartridge may
14
+ * silence it.
15
+ */
16
+ readonly withIndex: () => boolean;
17
+ }
18
+ /** Where the child is: a key to compare, and the sentence to say when it changed. */
19
+ export interface Place {
20
+ /** What «did she move?» is decided on. It names the panel or the card's open list, never just «a panel». */
21
+ readonly key: string;
22
+ /** What to say on arriving here — `null` in play, which is announced by the caller in its own words. */
23
+ readonly sentence: string | null;
24
+ }
25
+ /**
26
+ * The place, in the order the screen reads: a panel sits over the card, the card sits over the game.
27
+ *
28
+ * 📌 PLAY HAS NO SENTENCE HERE and that is deliberate: «back to the game» is the caller's word for its own world, and
29
+ * this module has no business naming a game it does not know.
30
+ */
31
+ export declare function whereTheChildIs(ctx: WhereTheChildIsCtx): Place;
@@ -0,0 +1,70 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // ui/where-the-child-is — WHERE THE CHILD IS NOW, AND WHAT THAT PLACE SAYS (ADR-0159 rule 3; ADR-0221 step 7c).
3
+ //
4
+ // «Opening a menu or panel speaks its title; closing it speaks where the child is back to; entering play is announced.»
5
+ // 📏 Measured in the dist on 2026-09-12, before that rule was built: SELECT opened the card in silence, and a panel
6
+ // opened, closed and went back to the root without a word. A child who plays by ear was moved without being told.
7
+ //
8
+ // WHAT THIS ANSWERS, AND WHY IT IS ONE QUESTION
9
+ // Two things at once, and they belong together: a KEY, which is what «did she move?» is decided on, and the SENTENCE
10
+ // to say when she did. Splitting them would let a caller compare one and speak the other, which is the defect the key
11
+ // exists to prevent — an arrow inside a menu changes no `hidden`, so it must not read as a change of place.
12
+ //
13
+ // WHY IT IS NOT IN THE COMPOSITION ROOT
14
+ // It used to be, and it was the densest function in `boot/create-game` after the root's own body: 19 branches in 24
15
+ // lines. A root holds WIRING (ADR-0221's erratum: its debt is paid when the BRANCHES tend to zero), and «is this a
16
+ // panel, a list of the pause card, or play, and what is its name» is a rule with a reason, not a wire.
17
+ //
18
+ // ⚠️ THE DOM ARRIVES BY QUESTION AND NOT BY GLOBAL: the overlay on top, the open card and the focused element are
19
+ // asked of whoever owns the document. That is what lets a case drive it without a root, and what keeps the module out
20
+ // of the `globalReach` ratchet (ADR-0221 step 7d).
21
+ import { navigableItems } from './menu-items.js';
22
+ import { announceItem } from './item-announcement.js';
23
+ import { controlParts } from './menu-nav.js';
24
+ import { PM_VISIBLE_ITEMS, LIST_DOOR } from './pause-icons.js';
25
+ import { accessibleLabel } from '../core/accessible-label.js';
26
+ /** Title and item joined the way a screen reader reads them: the place first, then where the cursor is in it. */
27
+ const say = (title, item) => [title, item].filter(Boolean).join('. ');
28
+ /** A PANEL: its title, and the item the focus is on — not the first, because the child may have walked into it. */
29
+ function panelPlace(panel, ctx) {
30
+ const title = panel.querySelector('h2')?.textContent?.trim() ?? '';
31
+ const items = navigableItems(panel.querySelector('.overlay__card') ?? panel);
32
+ const focused = items.indexOf(ctx.focused());
33
+ const n = focused >= 0 ? focused : 0;
34
+ const item = items[n] ? announceItem(ctx.t, { ...controlParts(ctx.t, items[n]), position: n + 1, total: items.length }, ctx.withIndex()) : '';
35
+ // ⚠️ THE KEY NAMES WHICH PANEL. Two panels reading as the same place is a child moving from the sound panel to the
36
+ // visual one in silence — and going from one panel straight to another is a real path: «map the keyboard» opens the
37
+ // controls panel on top of the motor one.
38
+ return { key: `painel:${panel.id}`, sentence: say(title, item) };
39
+ }
40
+ /** THE PAUSE CARD: which list of it is showing, named by the item that opens it, and the item under the cursor. */
41
+ function cardPlace(card, ctx) {
42
+ const sub = card.querySelector('.pause-menu:not([hidden])')?.dataset.sub ?? 'raiz';
43
+ // a submenu is named by the item that opens it; the root by the card's title
44
+ const door = LIST_DOOR[sub] ?? null;
45
+ const button = door ? card.querySelector(`.pm-btn[data-act="${door}"]`) : null;
46
+ const title = button ? accessibleLabel(button) : (card.querySelector('h2')?.textContent?.trim() ?? '');
47
+ const items = [...card.querySelectorAll(PM_VISIBLE_ITEMS)];
48
+ // 📌 THE MARKED ITEM AND NOT THE FIRST: coming back from a panel, the cursor is on the row that opened it. Naming
49
+ // the first row would send a child who cannot see the mark looking where she is not.
50
+ const marked = card.querySelector('.pm-sel') ?? items[0];
51
+ const item = marked ? announceItem(ctx.t, { label: accessibleLabel(marked), position: items.indexOf(marked) + 1, total: items.length }, ctx.withIndex()) : '';
52
+ // ⚠️ AND THE KEY CARRIES THE LIST: the root and a submenu are the same card with different rows, so a key that
53
+ // stopped at the card's id would make entering the settings a non-event.
54
+ return { key: `cartao:${card.id}:${sub}`, sentence: say(title, item) };
55
+ }
56
+ /**
57
+ * The place, in the order the screen reads: a panel sits over the card, the card sits over the game.
58
+ *
59
+ * 📌 PLAY HAS NO SENTENCE HERE and that is deliberate: «back to the game» is the caller's word for its own world, and
60
+ * this module has no business naming a game it does not know.
61
+ */
62
+ export function whereTheChildIs(ctx) {
63
+ const panel = ctx.topVisibleOverlay();
64
+ if (panel)
65
+ return panelPlace(panel, ctx);
66
+ const card = ctx.pauseCard();
67
+ if (card)
68
+ return cardPlace(card, ctx);
69
+ return { key: 'jogo', sentence: null };
70
+ }