@the-inclusionist/engine 10.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 (528) hide show
  1. package/README.md +32 -1
  2. package/app/css/style.css +216 -42
  3. package/app/public/vendor/fonts-licences/NOTICE.txt +166 -0
  4. package/app/public/vendor/fonts-licences/OFL-1.1.txt +86 -0
  5. package/app/public/vendor/fonts.css +11 -1175
  6. package/dist-pkg/boot/create-game.d.ts +84 -33
  7. package/dist-pkg/boot/create-game.js +708 -307
  8. package/dist-pkg/core/a11y-sr.d.ts +1 -1
  9. package/dist-pkg/core/a11y-sr.js +3 -5
  10. package/dist-pkg/core/accommodations.d.ts +26 -8
  11. package/dist-pkg/core/accommodations.js +27 -10
  12. package/dist-pkg/core/actions.d.ts +39 -10
  13. package/dist-pkg/core/actions.js +28 -6
  14. package/dist-pkg/core/contract.d.ts +0 -2
  15. package/dist-pkg/core/contract.js +0 -2
  16. package/dist-pkg/core/i18n.d.ts +36 -71
  17. package/dist-pkg/core/i18n.js +131 -198
  18. package/dist-pkg/core/session-clock.d.ts +23 -0
  19. package/dist-pkg/core/session-clock.js +37 -0
  20. package/dist-pkg/i18n/en.js +44 -49
  21. package/dist-pkg/i18n/es.js +44 -49
  22. package/dist-pkg/i18n/pt.js +49 -54
  23. package/dist-pkg/input/gamepad.d.ts +42 -11
  24. package/dist-pkg/input/gamepad.js +50 -15
  25. package/dist-pkg/input/key-default.d.ts +30 -0
  26. package/dist-pkg/input/key-default.js +60 -0
  27. package/dist-pkg/input/keyboard-runtime.d.ts +0 -4
  28. package/dist-pkg/input/keyboard.d.ts +0 -1
  29. package/dist-pkg/input/keydown.d.ts +5 -11
  30. package/dist-pkg/input/keydown.js +3 -3
  31. package/dist-pkg/input/latch-edge.d.ts +10 -2
  32. package/dist-pkg/input/latch-edge.js +4 -3
  33. package/dist-pkg/input/latch-scope.d.ts +27 -22
  34. package/dist-pkg/input/latch-scope.js +25 -37
  35. package/dist-pkg/input/latch-store.d.ts +9 -12
  36. package/dist-pkg/input/latch-store.js +13 -18
  37. package/dist-pkg/input/latch-sync.d.ts +5 -4
  38. package/dist-pkg/input/latch-sync.js +5 -5
  39. package/dist-pkg/input/pad-reading.js +7 -2
  40. package/dist-pkg/input/pad-wizard.d.ts +4 -1
  41. package/dist-pkg/input/pad-wizard.js +60 -9
  42. package/dist-pkg/input/switch-scan.d.ts +29 -12
  43. package/dist-pkg/input/switch-scan.js +15 -2
  44. package/dist-pkg/input/touch-bindings.d.ts +3 -6
  45. package/dist-pkg/input/touch.d.ts +17 -2
  46. package/dist-pkg/input/touch.js +55 -17
  47. package/dist-pkg/input/transport-in-use.d.ts +5 -5
  48. package/dist-pkg/input/transport-in-use.js +5 -5
  49. package/dist-pkg/input/transports.d.ts +0 -9
  50. package/dist-pkg/input/transports.js +1 -1
  51. package/dist-pkg/input/virtual-controller.d.ts +39 -3
  52. package/dist-pkg/input/virtual-controller.js +32 -4
  53. package/dist-pkg/input/voice-map.d.ts +27 -11
  54. package/dist-pkg/input/voice-map.js +41 -43
  55. package/dist-pkg/platform/audio-ambient.d.ts +0 -1
  56. package/dist-pkg/platform/audio-ambient.js +3 -1
  57. package/dist-pkg/platform/audio-mixer.js +4 -9
  58. package/dist-pkg/platform/audio-sonar.d.ts +8 -94
  59. package/dist-pkg/platform/audio-sonar.js +22 -207
  60. package/dist-pkg/platform/audio.d.ts +10 -1
  61. package/dist-pkg/platform/audio.js +35 -2
  62. package/dist-pkg/platform/choose-by-voice.d.ts +70 -0
  63. package/dist-pkg/platform/choose-by-voice.js +157 -0
  64. package/dist-pkg/platform/font-library.d.ts +77 -0
  65. package/dist-pkg/platform/font-library.js +98 -0
  66. package/dist-pkg/platform/font-library.json +721 -0
  67. package/dist-pkg/platform/heavy-catalogue.d.ts +46 -0
  68. package/dist-pkg/platform/heavy-catalogue.js +47 -14
  69. package/dist-pkg/platform/heavy-mirror.d.ts +3 -9
  70. package/dist-pkg/platform/heavy-mirror.js +14 -17
  71. package/dist-pkg/platform/heavy.d.ts +40 -8
  72. package/dist-pkg/platform/heavy.js +177 -22
  73. package/dist-pkg/platform/interruptible-speech.d.ts +15 -8
  74. package/dist-pkg/platform/interruptible-speech.js +37 -13
  75. package/dist-pkg/platform/listener-scope.js +22 -13
  76. package/dist-pkg/platform/locale-host.d.ts +7 -3
  77. package/dist-pkg/platform/locale-host.js +13 -2
  78. package/dist-pkg/platform/reading-in-worker.d.ts +24 -0
  79. package/dist-pkg/platform/reading-in-worker.js +38 -0
  80. package/dist-pkg/platform/reading-model.d.ts +21 -0
  81. package/dist-pkg/platform/reading-model.js +25 -0
  82. package/dist-pkg/platform/reading-runtime.js +13 -8
  83. package/dist-pkg/platform/reading.d.ts +12 -1
  84. package/dist-pkg/platform/reading.js +9 -1
  85. package/dist-pkg/platform/speech-recognition.d.ts +10 -1
  86. package/dist-pkg/platform/speech-recognition.js +29 -9
  87. package/dist-pkg/platform/speech.d.ts +10 -1
  88. package/dist-pkg/platform/speech.js +24 -13
  89. package/dist-pkg/platform/tts.d.ts +37 -4
  90. package/dist-pkg/platform/tts.js +279 -57
  91. package/dist-pkg/platform/voice-listener.d.ts +6 -2
  92. package/dist-pkg/platform/voice-listener.js +1 -1
  93. package/dist-pkg/platform/voice-plan.d.ts +13 -0
  94. package/dist-pkg/platform/voice-plan.js +57 -2
  95. package/dist-pkg/platform/vosk-runtime.d.ts +25 -0
  96. package/dist-pkg/platform/vosk-runtime.js +59 -1
  97. package/dist-pkg/platform/vosk-vocabulary.d.ts +6 -0
  98. package/dist-pkg/platform/vosk-vocabulary.js +178 -0
  99. package/dist-pkg/render/crt.d.ts +11 -9
  100. package/dist-pkg/render/crt.js +6 -10
  101. package/dist-pkg/render/cvd-matrices.d.ts +5 -1
  102. package/dist-pkg/render/cvd-matrices.js +5 -1
  103. package/dist-pkg/render/high-contrast.d.ts +1 -2
  104. package/dist-pkg/render/high-contrast.js +5 -1
  105. package/dist-pkg/render/viz-axes.d.ts +9 -0
  106. package/dist-pkg/render/viz-axes.js +15 -2
  107. package/dist-pkg/ui/aac-sets.d.ts +29 -24
  108. package/dist-pkg/ui/aac-sets.js +22 -30
  109. package/dist-pkg/ui/audio-choices.d.ts +10 -5
  110. package/dist-pkg/ui/audio-choices.js +14 -10
  111. package/dist-pkg/ui/camera-control.d.ts +47 -0
  112. package/dist-pkg/ui/camera-control.js +35 -1
  113. package/dist-pkg/ui/declared-words.d.ts +21 -0
  114. package/dist-pkg/ui/declared-words.js +66 -0
  115. package/dist-pkg/ui/drawing-problems.js +4 -3
  116. package/dist-pkg/ui/eye-control.js +17 -17
  117. package/dist-pkg/ui/face-control.js +15 -15
  118. package/dist-pkg/ui/fonts.d.ts +42 -8
  119. package/dist-pkg/ui/fonts.js +58 -24
  120. package/dist-pkg/ui/footer-scroll-driver.d.ts +14 -0
  121. package/dist-pkg/ui/footer-scroll-driver.js +201 -0
  122. package/dist-pkg/ui/footer-scroll.d.ts +38 -0
  123. package/dist-pkg/ui/footer-scroll.js +55 -0
  124. package/dist-pkg/ui/game-options.d.ts +14 -9
  125. package/dist-pkg/ui/game-options.js +41 -21
  126. package/dist-pkg/ui/hand-control.js +15 -15
  127. package/dist-pkg/ui/help-panel.d.ts +19 -8
  128. package/dist-pkg/ui/help-panel.js +15 -5
  129. package/dist-pkg/ui/hud-bands.d.ts +29 -12
  130. package/dist-pkg/ui/hud-bands.js +63 -25
  131. package/dist-pkg/ui/hud-row.d.ts +34 -0
  132. package/dist-pkg/ui/hud-row.js +80 -0
  133. package/dist-pkg/ui/hud.d.ts +0 -3
  134. package/dist-pkg/ui/layout.js +5 -2
  135. package/dist-pkg/ui/libras-avatar-clip.d.ts +30 -0
  136. package/dist-pkg/ui/libras-avatar-clip.js +86 -0
  137. package/dist-pkg/ui/libras-avatar-load.d.ts +52 -0
  138. package/dist-pkg/ui/libras-avatar-load.js +134 -0
  139. package/dist-pkg/ui/libras-avatar-plan.d.ts +102 -0
  140. package/dist-pkg/ui/libras-avatar-plan.js +211 -0
  141. package/dist-pkg/ui/libras-avatar-player.d.ts +28 -0
  142. package/dist-pkg/ui/libras-avatar-player.js +226 -0
  143. package/dist-pkg/ui/libras-avatar-stage.d.ts +37 -0
  144. package/dist-pkg/ui/libras-avatar-stage.js +208 -0
  145. package/dist-pkg/ui/libras-glosses.d.ts +28 -0
  146. package/dist-pkg/ui/libras-glosses.js +161 -0
  147. package/dist-pkg/ui/menu-intent.d.ts +29 -0
  148. package/dist-pkg/ui/menu-intent.js +35 -1
  149. package/dist-pkg/ui/menu-items.d.ts +10 -0
  150. package/dist-pkg/ui/menu-items.js +29 -0
  151. package/dist-pkg/ui/menu-nav.d.ts +40 -4
  152. package/dist-pkg/ui/menu-nav.js +132 -47
  153. package/dist-pkg/ui/panel-widgets.d.ts +34 -0
  154. package/dist-pkg/ui/panel-widgets.js +86 -0
  155. package/dist-pkg/ui/pause-icons.d.ts +30 -14
  156. package/dist-pkg/ui/pause-icons.js +106 -45
  157. package/dist-pkg/ui/scan-overlay.d.ts +3 -3
  158. package/dist-pkg/ui/scan-overlay.js +11 -3
  159. package/dist-pkg/ui/screen-text.d.ts +25 -0
  160. package/dist-pkg/ui/screen-text.js +152 -0
  161. package/dist-pkg/ui/session-clock.d.ts +32 -0
  162. package/dist-pkg/ui/session-clock.js +67 -0
  163. package/dist-pkg/ui/settings-aac.d.ts +3 -1
  164. package/dist-pkg/ui/settings-aac.js +32 -13
  165. package/dist-pkg/ui/settings-audio.d.ts +10 -5
  166. package/dist-pkg/ui/settings-audio.js +101 -35
  167. package/dist-pkg/ui/settings-controls.d.ts +0 -4
  168. package/dist-pkg/ui/settings-mobility.d.ts +3 -6
  169. package/dist-pkg/ui/settings-mobility.js +4 -46
  170. package/dist-pkg/ui/settings-motion.js +2 -2
  171. package/dist-pkg/ui/settings-panel.d.ts +10 -0
  172. package/dist-pkg/ui/settings-panel.js +25 -1
  173. package/dist-pkg/ui/settings-typo.d.ts +5 -6
  174. package/dist-pkg/ui/settings-typo.js +20 -11
  175. package/dist-pkg/ui/settings-visual.js +2 -2
  176. package/dist-pkg/ui/shell.d.ts +1 -3
  177. package/dist-pkg/ui/shell.js +8 -4
  178. package/dist-pkg/ui/title.d.ts +2 -11
  179. package/dist-pkg/ui/title.js +39 -6
  180. package/dist-pkg/ui/top-band.d.ts +5 -3
  181. package/dist-pkg/ui/top-band.js +21 -25
  182. package/dist-pkg/ui/typo-choices.d.ts +3 -3
  183. package/dist-pkg/ui/typo-choices.js +9 -8
  184. package/dist-pkg/ui/vlibras.d.ts +71 -27
  185. package/dist-pkg/ui/vlibras.js +116 -44
  186. package/dist-pkg/ui/voice-control.d.ts +23 -1
  187. package/dist-pkg/ui/voice-control.js +149 -16
  188. package/dist-pkg/ui/voice-settings.d.ts +2 -3
  189. package/dist-pkg/ui/voice-settings.js +53 -3
  190. package/dist-pkg/ui/where-the-child-is.js +2 -2
  191. package/docs/CREDITS.md +122 -10
  192. package/docs/LICENSES.md +36 -7
  193. package/package.json +34 -9
  194. package/scripts/check-cartridge.mjs +78 -0
  195. package/scripts/game-build.d.mts +25 -0
  196. package/scripts/game-build.mjs +144 -0
  197. package/scripts/heavy-into-the-delivery.mjs +284 -40
  198. package/scripts/libras-avatar.json +680 -0
  199. package/scripts/libras-avatar.mjs +246 -0
  200. package/scripts/libras-glosses/gloss.py +95 -0
  201. package/scripts/libras-glosses/pyproject.toml +32 -0
  202. package/scripts/libras-glosses/uv.lock +781 -0
  203. package/scripts/libras-glosses.mjs +351 -0
  204. package/scripts/licences/OFL-1.1.txt +86 -0
  205. package/scripts/licences/UFL-1.0.txt +96 -0
  206. package/scripts/licences/fonts.mjs +389 -0
  207. package/scripts/licences/third-party.mjs +35 -21
  208. package/app/public/vendor/fonts/bitter-var-ext.woff2 +0 -0
  209. package/app/public/vendor/fonts/bitter-var.woff2 +0 -0
  210. package/app/public/vendor/fonts/bodonimoda-var-ext.woff2 +0 -0
  211. package/app/public/vendor/fonts/bodonimoda-var.woff2 +0 -0
  212. package/app/public/vendor/fonts/cat-abril_fatface-400-ext.woff2 +0 -0
  213. package/app/public/vendor/fonts/cat-abril_fatface-400.woff2 +0 -0
  214. package/app/public/vendor/fonts/cat-adlam_display-400-ext.woff2 +0 -0
  215. package/app/public/vendor/fonts/cat-adlam_display-400.woff2 +0 -0
  216. package/app/public/vendor/fonts/cat-agu_display-400-ext.woff2 +0 -0
  217. package/app/public/vendor/fonts/cat-agu_display-400.woff2 +0 -0
  218. package/app/public/vendor/fonts/cat-alfa_slab_one-400-ext.woff2 +0 -0
  219. package/app/public/vendor/fonts/cat-alfa_slab_one-400.woff2 +0 -0
  220. package/app/public/vendor/fonts/cat-are_you_serious-400-ext.woff2 +0 -0
  221. package/app/public/vendor/fonts/cat-are_you_serious-400.woff2 +0 -0
  222. package/app/public/vendor/fonts/cat-asimovian-400-ext.woff2 +0 -0
  223. package/app/public/vendor/fonts/cat-asimovian-400.woff2 +0 -0
  224. package/app/public/vendor/fonts/cat-atkinson_hyperlegible_next-400-ext.woff2 +0 -0
  225. package/app/public/vendor/fonts/cat-atkinson_hyperlegible_next-400.woff2 +0 -0
  226. package/app/public/vendor/fonts/cat-bakbak_one-400-ext.woff2 +0 -0
  227. package/app/public/vendor/fonts/cat-bakbak_one-400.woff2 +0 -0
  228. package/app/public/vendor/fonts/cat-bangers-400-ext.woff2 +0 -0
  229. package/app/public/vendor/fonts/cat-bangers-400.woff2 +0 -0
  230. package/app/public/vendor/fonts/cat-barriecito-400-ext.woff2 +0 -0
  231. package/app/public/vendor/fonts/cat-barriecito-400.woff2 +0 -0
  232. package/app/public/vendor/fonts/cat-berkshire_swash-400-ext.woff2 +0 -0
  233. package/app/public/vendor/fonts/cat-berkshire_swash-400.woff2 +0 -0
  234. package/app/public/vendor/fonts/cat-bevan-400-ext.woff2 +0 -0
  235. package/app/public/vendor/fonts/cat-bevan-400.woff2 +0 -0
  236. package/app/public/vendor/fonts/cat-bitcount-400-ext.woff2 +0 -0
  237. package/app/public/vendor/fonts/cat-bitcount-400.woff2 +0 -0
  238. package/app/public/vendor/fonts/cat-bowlby_one_sc-400-ext.woff2 +0 -0
  239. package/app/public/vendor/fonts/cat-bowlby_one_sc-400.woff2 +0 -0
  240. package/app/public/vendor/fonts/cat-bungee_shade-400-ext.woff2 +0 -0
  241. package/app/public/vendor/fonts/cat-bungee_shade-400.woff2 +0 -0
  242. package/app/public/vendor/fonts/cat-charm-400-ext.woff2 +0 -0
  243. package/app/public/vendor/fonts/cat-charm-400.woff2 +0 -0
  244. package/app/public/vendor/fonts/cat-charm-700-ext.woff2 +0 -0
  245. package/app/public/vendor/fonts/cat-charm-700.woff2 +0 -0
  246. package/app/public/vendor/fonts/cat-cherry_bomb_one-400-ext.woff2 +0 -0
  247. package/app/public/vendor/fonts/cat-cherry_bomb_one-400.woff2 +0 -0
  248. package/app/public/vendor/fonts/cat-chokokutai-400-ext.woff2 +0 -0
  249. package/app/public/vendor/fonts/cat-chokokutai-400.woff2 +0 -0
  250. package/app/public/vendor/fonts/cat-chonburi-400-ext.woff2 +0 -0
  251. package/app/public/vendor/fonts/cat-chonburi-400.woff2 +0 -0
  252. package/app/public/vendor/fonts/cat-climate_crisis-400-ext.woff2 +0 -0
  253. package/app/public/vendor/fonts/cat-climate_crisis-400.woff2 +0 -0
  254. package/app/public/vendor/fonts/cat-cookie-400.woff2 +0 -0
  255. package/app/public/vendor/fonts/cat-dela_gothic_one-400-ext.woff2 +0 -0
  256. package/app/public/vendor/fonts/cat-dela_gothic_one-400.woff2 +0 -0
  257. package/app/public/vendor/fonts/cat-fascinate_inline-400-ext.woff2 +0 -0
  258. package/app/public/vendor/fonts/cat-fascinate_inline-400.woff2 +0 -0
  259. package/app/public/vendor/fonts/cat-ga_maamli-400-ext.woff2 +0 -0
  260. package/app/public/vendor/fonts/cat-ga_maamli-400.woff2 +0 -0
  261. package/app/public/vendor/fonts/cat-itim-400-ext.woff2 +0 -0
  262. package/app/public/vendor/fonts/cat-itim-400.woff2 +0 -0
  263. package/app/public/vendor/fonts/cat-jacquard_24-400-ext.woff2 +0 -0
  264. package/app/public/vendor/fonts/cat-jacquard_24-400.woff2 +0 -0
  265. package/app/public/vendor/fonts/cat-kablammo-400-ext.woff2 +0 -0
  266. package/app/public/vendor/fonts/cat-kablammo-400.woff2 +0 -0
  267. package/app/public/vendor/fonts/cat-kalam-400-ext.woff2 +0 -0
  268. package/app/public/vendor/fonts/cat-kalam-400.woff2 +0 -0
  269. package/app/public/vendor/fonts/cat-kalam-700-ext.woff2 +0 -0
  270. package/app/public/vendor/fonts/cat-kalam-700.woff2 +0 -0
  271. package/app/public/vendor/fonts/cat-katibeh-400-ext.woff2 +0 -0
  272. package/app/public/vendor/fonts/cat-katibeh-400.woff2 +0 -0
  273. package/app/public/vendor/fonts/cat-lemonada-400-ext.woff2 +0 -0
  274. package/app/public/vendor/fonts/cat-lemonada-400.woff2 +0 -0
  275. package/app/public/vendor/fonts/cat-lilita_one-400-ext.woff2 +0 -0
  276. package/app/public/vendor/fonts/cat-lilita_one-400.woff2 +0 -0
  277. package/app/public/vendor/fonts/cat-lobster-400-ext.woff2 +0 -0
  278. package/app/public/vendor/fonts/cat-lobster-400.woff2 +0 -0
  279. package/app/public/vendor/fonts/cat-luckiest_guy-400-ext.woff2 +0 -0
  280. package/app/public/vendor/fonts/cat-luckiest_guy-400.woff2 +0 -0
  281. package/app/public/vendor/fonts/cat-marhey-400-ext.woff2 +0 -0
  282. package/app/public/vendor/fonts/cat-marhey-400.woff2 +0 -0
  283. package/app/public/vendor/fonts/cat-modak-400-ext.woff2 +0 -0
  284. package/app/public/vendor/fonts/cat-modak-400.woff2 +0 -0
  285. package/app/public/vendor/fonts/cat-momo_signature-400-ext.woff2 +0 -0
  286. package/app/public/vendor/fonts/cat-momo_signature-400.woff2 +0 -0
  287. package/app/public/vendor/fonts/cat-monofett-400-ext.woff2 +0 -0
  288. package/app/public/vendor/fonts/cat-monofett-400.woff2 +0 -0
  289. package/app/public/vendor/fonts/cat-monomakh-400-ext.woff2 +0 -0
  290. package/app/public/vendor/fonts/cat-monomakh-400.woff2 +0 -0
  291. package/app/public/vendor/fonts/cat-monoton-400-ext.woff2 +0 -0
  292. package/app/public/vendor/fonts/cat-monoton-400.woff2 +0 -0
  293. package/app/public/vendor/fonts/cat-nerko_one-400-ext.woff2 +0 -0
  294. package/app/public/vendor/fonts/cat-nerko_one-400.woff2 +0 -0
  295. package/app/public/vendor/fonts/cat-noto_sans_adlam-400-ext.woff2 +0 -0
  296. package/app/public/vendor/fonts/cat-noto_sans_adlam-400.woff2 +0 -0
  297. package/app/public/vendor/fonts/cat-noto_sans_arabic-400-ext.woff2 +0 -0
  298. package/app/public/vendor/fonts/cat-noto_sans_arabic-400.woff2 +0 -0
  299. package/app/public/vendor/fonts/cat-noto_sans_devanagari-400-ext.woff2 +0 -0
  300. package/app/public/vendor/fonts/cat-noto_sans_devanagari-400.woff2 +0 -0
  301. package/app/public/vendor/fonts/cat-noto_sans_hebrew-400-ext.woff2 +0 -0
  302. package/app/public/vendor/fonts/cat-noto_sans_hebrew-400.woff2 +0 -0
  303. package/app/public/vendor/fonts/cat-noto_sans_jp-400-ext.woff2 +0 -0
  304. package/app/public/vendor/fonts/cat-noto_sans_jp-400.woff2 +0 -0
  305. package/app/public/vendor/fonts/cat-noto_sans_khmer-400-ext.woff2 +0 -0
  306. package/app/public/vendor/fonts/cat-noto_sans_khmer-400.woff2 +0 -0
  307. package/app/public/vendor/fonts/cat-noto_sans_kr-400-ext.woff2 +0 -0
  308. package/app/public/vendor/fonts/cat-noto_sans_kr-400.woff2 +0 -0
  309. package/app/public/vendor/fonts/cat-noto_sans_sc-400-ext.woff2 +0 -0
  310. package/app/public/vendor/fonts/cat-noto_sans_sc-400.woff2 +0 -0
  311. package/app/public/vendor/fonts/cat-noto_sans_thai-400-ext.woff2 +0 -0
  312. package/app/public/vendor/fonts/cat-noto_sans_thai-400.woff2 +0 -0
  313. package/app/public/vendor/fonts/cat-ole-400-ext.woff2 +0 -0
  314. package/app/public/vendor/fonts/cat-ole-400.woff2 +0 -0
  315. package/app/public/vendor/fonts/cat-pacifico-400-ext.woff2 +0 -0
  316. package/app/public/vendor/fonts/cat-pacifico-400.woff2 +0 -0
  317. package/app/public/vendor/fonts/cat-patrick_hand-400-ext.woff2 +0 -0
  318. package/app/public/vendor/fonts/cat-patrick_hand-400.woff2 +0 -0
  319. package/app/public/vendor/fonts/cat-petit_formal_script-400-ext.woff2 +0 -0
  320. package/app/public/vendor/fonts/cat-petit_formal_script-400.woff2 +0 -0
  321. package/app/public/vendor/fonts/cat-pixelify_sans-400-ext.woff2 +0 -0
  322. package/app/public/vendor/fonts/cat-pixelify_sans-400.woff2 +0 -0
  323. package/app/public/vendor/fonts/cat-plaster-400-ext.woff2 +0 -0
  324. package/app/public/vendor/fonts/cat-plaster-400.woff2 +0 -0
  325. package/app/public/vendor/fonts/cat-playwrite_ar_guides-400-0.woff2 +0 -0
  326. package/app/public/vendor/fonts/cat-playwrite_at-400-0.woff2 +0 -0
  327. package/app/public/vendor/fonts/cat-playwrite_at_guides-400-0.woff2 +0 -0
  328. package/app/public/vendor/fonts/cat-playwrite_au_nsw-400-0.woff2 +0 -0
  329. package/app/public/vendor/fonts/cat-playwrite_au_nsw_guides-400-0.woff2 +0 -0
  330. package/app/public/vendor/fonts/cat-playwrite_au_qld-400-0.woff2 +0 -0
  331. package/app/public/vendor/fonts/cat-playwrite_au_qld_guides-400-0.woff2 +0 -0
  332. package/app/public/vendor/fonts/cat-playwrite_au_sa-400-0.woff2 +0 -0
  333. package/app/public/vendor/fonts/cat-playwrite_au_sa_guides-400-0.woff2 +0 -0
  334. package/app/public/vendor/fonts/cat-playwrite_au_tas-400-0.woff2 +0 -0
  335. package/app/public/vendor/fonts/cat-playwrite_au_tas_guides-400-0.woff2 +0 -0
  336. package/app/public/vendor/fonts/cat-playwrite_au_vic-400-0.woff2 +0 -0
  337. package/app/public/vendor/fonts/cat-playwrite_au_vic_guides-400-0.woff2 +0 -0
  338. package/app/public/vendor/fonts/cat-playwrite_be_vlg-400-0.woff2 +0 -0
  339. package/app/public/vendor/fonts/cat-playwrite_be_vlg_guides-400-0.woff2 +0 -0
  340. package/app/public/vendor/fonts/cat-playwrite_be_wal-400-0.woff2 +0 -0
  341. package/app/public/vendor/fonts/cat-playwrite_be_wal_guides-400-0.woff2 +0 -0
  342. package/app/public/vendor/fonts/cat-playwrite_br_guides-400-0.woff2 +0 -0
  343. package/app/public/vendor/fonts/cat-playwrite_ca_guides-400-0.woff2 +0 -0
  344. package/app/public/vendor/fonts/cat-playwrite_cl_guides-400-0.woff2 +0 -0
  345. package/app/public/vendor/fonts/cat-playwrite_co_guides-400-0.woff2 +0 -0
  346. package/app/public/vendor/fonts/cat-playwrite_cu_guides-400-0.woff2 +0 -0
  347. package/app/public/vendor/fonts/cat-playwrite_cz-400-0.woff2 +0 -0
  348. package/app/public/vendor/fonts/cat-playwrite_cz_guides-400-0.woff2 +0 -0
  349. package/app/public/vendor/fonts/cat-playwrite_de_grund-400-0.woff2 +0 -0
  350. package/app/public/vendor/fonts/cat-playwrite_de_grund_guides-400-0.woff2 +0 -0
  351. package/app/public/vendor/fonts/cat-playwrite_de_la-400-0.woff2 +0 -0
  352. package/app/public/vendor/fonts/cat-playwrite_de_la_guides-400-0.woff2 +0 -0
  353. package/app/public/vendor/fonts/cat-playwrite_de_sas-400-0.woff2 +0 -0
  354. package/app/public/vendor/fonts/cat-playwrite_de_sas_guides-400-0.woff2 +0 -0
  355. package/app/public/vendor/fonts/cat-playwrite_de_va-400-0.woff2 +0 -0
  356. package/app/public/vendor/fonts/cat-playwrite_de_va_guides-400-0.woff2 +0 -0
  357. package/app/public/vendor/fonts/cat-playwrite_dk_loopet-400-0.woff2 +0 -0
  358. package/app/public/vendor/fonts/cat-playwrite_dk_loopet_guides-400-0.woff2 +0 -0
  359. package/app/public/vendor/fonts/cat-playwrite_dk_uloopet-400-0.woff2 +0 -0
  360. package/app/public/vendor/fonts/cat-playwrite_dk_uloopet_guides-400-0.woff2 +0 -0
  361. package/app/public/vendor/fonts/cat-playwrite_es_deco_guides-400-0.woff2 +0 -0
  362. package/app/public/vendor/fonts/cat-playwrite_es_guides-400-0.woff2 +0 -0
  363. package/app/public/vendor/fonts/cat-playwrite_fr_moderne-400-0.woff2 +0 -0
  364. package/app/public/vendor/fonts/cat-playwrite_fr_moderne_guides-400-0.woff2 +0 -0
  365. package/app/public/vendor/fonts/cat-playwrite_fr_trad-400-0.woff2 +0 -0
  366. package/app/public/vendor/fonts/cat-playwrite_fr_trad_guides-400-0.woff2 +0 -0
  367. package/app/public/vendor/fonts/cat-playwrite_gb_j_guides-400-0.woff2 +0 -0
  368. package/app/public/vendor/fonts/cat-playwrite_gb_s_guides-400-0.woff2 +0 -0
  369. package/app/public/vendor/fonts/cat-playwrite_hr-400-0.woff2 +0 -0
  370. package/app/public/vendor/fonts/cat-playwrite_hr_guides-400-0.woff2 +0 -0
  371. package/app/public/vendor/fonts/cat-playwrite_hr_lijeva-400-0.woff2 +0 -0
  372. package/app/public/vendor/fonts/cat-playwrite_hr_lijeva_guides-400-0.woff2 +0 -0
  373. package/app/public/vendor/fonts/cat-playwrite_hu-400-0.woff2 +0 -0
  374. package/app/public/vendor/fonts/cat-playwrite_hu_guides-400-0.woff2 +0 -0
  375. package/app/public/vendor/fonts/cat-playwrite_id-400-0.woff2 +0 -0
  376. package/app/public/vendor/fonts/cat-playwrite_id_guides-400-0.woff2 +0 -0
  377. package/app/public/vendor/fonts/cat-playwrite_ie-400-0.woff2 +0 -0
  378. package/app/public/vendor/fonts/cat-playwrite_ie_guides-400-0.woff2 +0 -0
  379. package/app/public/vendor/fonts/cat-playwrite_in-400-0.woff2 +0 -0
  380. package/app/public/vendor/fonts/cat-playwrite_in_guides-400-0.woff2 +0 -0
  381. package/app/public/vendor/fonts/cat-playwrite_is-400-0.woff2 +0 -0
  382. package/app/public/vendor/fonts/cat-playwrite_is_guides-400-0.woff2 +0 -0
  383. package/app/public/vendor/fonts/cat-playwrite_it_moderna-400-0.woff2 +0 -0
  384. package/app/public/vendor/fonts/cat-playwrite_it_moderna_guides-400-0.woff2 +0 -0
  385. package/app/public/vendor/fonts/cat-playwrite_it_trad-400-0.woff2 +0 -0
  386. package/app/public/vendor/fonts/cat-playwrite_it_trad_guides-400-0.woff2 +0 -0
  387. package/app/public/vendor/fonts/cat-playwrite_mx_guides-400-0.woff2 +0 -0
  388. package/app/public/vendor/fonts/cat-playwrite_ng_modern-400-0.woff2 +0 -0
  389. package/app/public/vendor/fonts/cat-playwrite_ng_modern_guides-400-0.woff2 +0 -0
  390. package/app/public/vendor/fonts/cat-playwrite_nl-400-0.woff2 +0 -0
  391. package/app/public/vendor/fonts/cat-playwrite_nl_guides-400-0.woff2 +0 -0
  392. package/app/public/vendor/fonts/cat-playwrite_no-400-0.woff2 +0 -0
  393. package/app/public/vendor/fonts/cat-playwrite_no_guides-400-0.woff2 +0 -0
  394. package/app/public/vendor/fonts/cat-playwrite_nz-400-0.woff2 +0 -0
  395. package/app/public/vendor/fonts/cat-playwrite_nz_basic-400-0.woff2 +0 -0
  396. package/app/public/vendor/fonts/cat-playwrite_nz_basic_guides-400-0.woff2 +0 -0
  397. package/app/public/vendor/fonts/cat-playwrite_nz_guides-400-0.woff2 +0 -0
  398. package/app/public/vendor/fonts/cat-playwrite_pe_guides-400-0.woff2 +0 -0
  399. package/app/public/vendor/fonts/cat-playwrite_pl-400-0.woff2 +0 -0
  400. package/app/public/vendor/fonts/cat-playwrite_pl_guides-400-0.woff2 +0 -0
  401. package/app/public/vendor/fonts/cat-playwrite_pt_guides-400-0.woff2 +0 -0
  402. package/app/public/vendor/fonts/cat-playwrite_ro-400-0.woff2 +0 -0
  403. package/app/public/vendor/fonts/cat-playwrite_ro_guides-400-0.woff2 +0 -0
  404. package/app/public/vendor/fonts/cat-playwrite_sk-400-0.woff2 +0 -0
  405. package/app/public/vendor/fonts/cat-playwrite_sk_guides-400-0.woff2 +0 -0
  406. package/app/public/vendor/fonts/cat-playwrite_tz-400-0.woff2 +0 -0
  407. package/app/public/vendor/fonts/cat-playwrite_tz_guides-400-0.woff2 +0 -0
  408. package/app/public/vendor/fonts/cat-playwrite_us_modern_guides-400-0.woff2 +0 -0
  409. package/app/public/vendor/fonts/cat-playwrite_us_trad_guides-400-0.woff2 +0 -0
  410. package/app/public/vendor/fonts/cat-playwrite_vn-400-0.woff2 +0 -0
  411. package/app/public/vendor/fonts/cat-playwrite_vn_guides-400-0.woff2 +0 -0
  412. package/app/public/vendor/fonts/cat-playwrite_za-400-0.woff2 +0 -0
  413. package/app/public/vendor/fonts/cat-playwrite_za_guides-400-0.woff2 +0 -0
  414. package/app/public/vendor/fonts/cat-potta_one-400-ext.woff2 +0 -0
  415. package/app/public/vendor/fonts/cat-potta_one-400.woff2 +0 -0
  416. package/app/public/vendor/fonts/cat-ranchers-400-ext.woff2 +0 -0
  417. package/app/public/vendor/fonts/cat-ranchers-400.woff2 +0 -0
  418. package/app/public/vendor/fonts/cat-ranga-400-ext.woff2 +0 -0
  419. package/app/public/vendor/fonts/cat-ranga-400.woff2 +0 -0
  420. package/app/public/vendor/fonts/cat-ranga-700-ext.woff2 +0 -0
  421. package/app/public/vendor/fonts/cat-ranga-700.woff2 +0 -0
  422. package/app/public/vendor/fonts/cat-risque-400-ext.woff2 +0 -0
  423. package/app/public/vendor/fonts/cat-risque-400.woff2 +0 -0
  424. package/app/public/vendor/fonts/cat-rowdies-400-ext.woff2 +0 -0
  425. package/app/public/vendor/fonts/cat-rowdies-400.woff2 +0 -0
  426. package/app/public/vendor/fonts/cat-rowdies-700-ext.woff2 +0 -0
  427. package/app/public/vendor/fonts/cat-rowdies-700.woff2 +0 -0
  428. package/app/public/vendor/fonts/cat-ruge_boogie-400-ext.woff2 +0 -0
  429. package/app/public/vendor/fonts/cat-ruge_boogie-400.woff2 +0 -0
  430. package/app/public/vendor/fonts/cat-ruslan_display-400-ext.woff2 +0 -0
  431. package/app/public/vendor/fonts/cat-ruslan_display-400.woff2 +0 -0
  432. package/app/public/vendor/fonts/cat-sacramento-400-ext.woff2 +0 -0
  433. package/app/public/vendor/fonts/cat-sacramento-400.woff2 +0 -0
  434. package/app/public/vendor/fonts/cat-sancreek-400-ext.woff2 +0 -0
  435. package/app/public/vendor/fonts/cat-sancreek-400.woff2 +0 -0
  436. package/app/public/vendor/fonts/cat-sankofa_display-400-ext.woff2 +0 -0
  437. package/app/public/vendor/fonts/cat-sankofa_display-400.woff2 +0 -0
  438. package/app/public/vendor/fonts/cat-sedgwick_ave_display-400-ext.woff2 +0 -0
  439. package/app/public/vendor/fonts/cat-sedgwick_ave_display-400.woff2 +0 -0
  440. package/app/public/vendor/fonts/cat-shantell_sans-400-ext.woff2 +0 -0
  441. package/app/public/vendor/fonts/cat-shantell_sans-400.woff2 +0 -0
  442. package/app/public/vendor/fonts/cat-smokum-400-ext.woff2 +0 -0
  443. package/app/public/vendor/fonts/cat-smokum-400.woff2 +0 -0
  444. package/app/public/vendor/fonts/cat-solitreo-400-ext.woff2 +0 -0
  445. package/app/public/vendor/fonts/cat-solitreo-400.woff2 +0 -0
  446. package/app/public/vendor/fonts/cat-spicy_rice-400-ext.woff2 +0 -0
  447. package/app/public/vendor/fonts/cat-spicy_rice-400.woff2 +0 -0
  448. package/app/public/vendor/fonts/cat-sriracha-400-ext.woff2 +0 -0
  449. package/app/public/vendor/fonts/cat-sriracha-400.woff2 +0 -0
  450. package/app/public/vendor/fonts/cat-srisakdi-400-ext.woff2 +0 -0
  451. package/app/public/vendor/fonts/cat-srisakdi-400.woff2 +0 -0
  452. package/app/public/vendor/fonts/cat-srisakdi-700-ext.woff2 +0 -0
  453. package/app/public/vendor/fonts/cat-srisakdi-700.woff2 +0 -0
  454. package/app/public/vendor/fonts/cat-stix_two_math-400-0.woff2 +0 -0
  455. package/app/public/vendor/fonts/cat-tillana-400-ext.woff2 +0 -0
  456. package/app/public/vendor/fonts/cat-tillana-400.woff2 +0 -0
  457. package/app/public/vendor/fonts/cat-tillana-700-ext.woff2 +0 -0
  458. package/app/public/vendor/fonts/cat-tillana-700.woff2 +0 -0
  459. package/app/public/vendor/fonts/cat-titan_one-400-ext.woff2 +0 -0
  460. package/app/public/vendor/fonts/cat-titan_one-400.woff2 +0 -0
  461. package/app/public/vendor/fonts/cat-yatra_one-400-ext.woff2 +0 -0
  462. package/app/public/vendor/fonts/cat-yatra_one-400.woff2 +0 -0
  463. package/app/public/vendor/fonts/cat-yomogi-400-ext.woff2 +0 -0
  464. package/app/public/vendor/fonts/cat-yomogi-400.woff2 +0 -0
  465. package/app/public/vendor/fonts/dmserifdisplay-400-ext.woff2 +0 -0
  466. package/app/public/vendor/fonts/dmserifdisplay-400.woff2 +0 -0
  467. package/app/public/vendor/fonts/domine-var-ext.woff2 +0 -0
  468. package/app/public/vendor/fonts/domine-var.woff2 +0 -0
  469. package/app/public/vendor/fonts/fondamento-400-ext.woff2 +0 -0
  470. package/app/public/vendor/fonts/fondamento-400.woff2 +0 -0
  471. package/app/public/vendor/fonts/fraunces-var-ext.woff2 +0 -0
  472. package/app/public/vendor/fonts/fraunces-var.woff2 +0 -0
  473. package/app/public/vendor/fonts/fredoka-var-ext.woff2 +0 -0
  474. package/app/public/vendor/fonts/fredoka-var.woff2 +0 -0
  475. package/app/public/vendor/fonts/inter-400.woff2 +0 -0
  476. package/app/public/vendor/fonts/inter-700.woff2 +0 -0
  477. package/app/public/vendor/fonts/jakarta-var-ext.woff2 +0 -0
  478. package/app/public/vendor/fonts/jakarta-var.woff2 +0 -0
  479. package/app/public/vendor/fonts/lato-400.woff2 +0 -0
  480. package/app/public/vendor/fonts/lato-700.woff2 +0 -0
  481. package/app/public/vendor/fonts/literata-400.woff2 +0 -0
  482. package/app/public/vendor/fonts/literata-700.woff2 +0 -0
  483. package/app/public/vendor/fonts/lora-var-ext.woff2 +0 -0
  484. package/app/public/vendor/fonts/lora-var.woff2 +0 -0
  485. package/app/public/vendor/fonts/merriweather-var-ext.woff2 +0 -0
  486. package/app/public/vendor/fonts/merriweather-var.woff2 +0 -0
  487. package/app/public/vendor/fonts/newsreader-400.woff2 +0 -0
  488. package/app/public/vendor/fonts/newsreader-700.woff2 +0 -0
  489. package/app/public/vendor/fonts/notosans-var-ext.woff2 +0 -0
  490. package/app/public/vendor/fonts/notosans-var.woff2 +0 -0
  491. package/app/public/vendor/fonts/nunito-var-ext.woff2 +0 -0
  492. package/app/public/vendor/fonts/nunito-var.woff2 +0 -0
  493. package/app/public/vendor/fonts/opendyslexic-400.woff2 +0 -0
  494. package/app/public/vendor/fonts/opensans-400.woff2 +0 -0
  495. package/app/public/vendor/fonts/opensans-700.woff2 +0 -0
  496. package/app/public/vendor/fonts/pinyon-400.woff2 +0 -0
  497. package/app/public/vendor/fonts/playfair-var-ext.woff2 +0 -0
  498. package/app/public/vendor/fonts/playfair-var.woff2 +0 -0
  499. package/app/public/vendor/fonts/pressstart-400.woff2 +0 -0
  500. package/app/public/vendor/fonts/quattro-400.woff2 +0 -0
  501. package/app/public/vendor/fonts/quattro-700.woff2 +0 -0
  502. package/app/public/vendor/fonts/quicksand-var-ext.woff2 +0 -0
  503. package/app/public/vendor/fonts/quicksand-var.woff2 +0 -0
  504. package/app/public/vendor/fonts/robotoflex-var-ext.woff2 +0 -0
  505. package/app/public/vendor/fonts/robotoflex-var.woff2 +0 -0
  506. package/app/public/vendor/fonts/sora-var-ext.woff2 +0 -0
  507. package/app/public/vendor/fonts/sora-var.woff2 +0 -0
  508. package/app/public/vendor/fonts/sourcesans-400.woff2 +0 -0
  509. package/app/public/vendor/fonts/sourcesans-700.woff2 +0 -0
  510. package/app/public/vendor/fonts/sourceserif-400.woff2 +0 -0
  511. package/app/public/vendor/fonts/sourceserif-700.woff2 +0 -0
  512. package/app/public/vendor/fonts/spacegrotesk-var-ext.woff2 +0 -0
  513. package/app/public/vendor/fonts/spacegrotesk-var.woff2 +0 -0
  514. package/app/public/vendor/fonts/spectral-400-ext.woff2 +0 -0
  515. package/app/public/vendor/fonts/spectral-400.woff2 +0 -0
  516. package/app/public/vendor/fonts/spectral-700-ext.woff2 +0 -0
  517. package/app/public/vendor/fonts/spectral-700.woff2 +0 -0
  518. package/app/public/vendor/fonts/teachers-var-ext.woff2 +0 -0
  519. package/app/public/vendor/fonts/teachers-var.woff2 +0 -0
  520. package/app/public/vendor/fonts/ubuntu-400-ext.woff2 +0 -0
  521. package/app/public/vendor/fonts/ubuntu-400.woff2 +0 -0
  522. package/app/public/vendor/fonts/ubuntu-700-ext.woff2 +0 -0
  523. package/app/public/vendor/fonts/ubuntu-700.woff2 +0 -0
  524. package/app/public/vendor/fonts/ufmag-400.woff2 +0 -0
  525. package/dist-pkg/platform/guide-intensity.d.ts +0 -39
  526. package/dist-pkg/platform/guide-intensity.js +0 -71
  527. package/dist-pkg/ui/latch-refusal.d.ts +0 -33
  528. package/dist-pkg/ui/latch-refusal.js +0 -60
@@ -22,6 +22,7 @@
22
22
  // the contract you think you have; the one that exists was three lines of the served file away.
23
23
  import { HEAVY_FILES, deliveryPath } from './heavy.js';
24
24
  import { commandsLanguageOf } from './heavy-catalogue.js';
25
+ import { readModelVocabulary } from './vosk-vocabulary.js';
25
26
  /** The three files of the runtime. The model is chosen by language, below. */
26
27
  export const VOICE_RUNTIME_FILES = ['commands:runtime', 'commands:runtime:worker', 'commands:runtime:wasm'];
27
28
  /** The catalogue id of the model that hears THIS language, or `null` where the project has none. */
@@ -52,6 +53,48 @@ export function createBundleLoader(load) {
52
53
  return pending;
53
54
  };
54
55
  }
56
+ /**
57
+ * ONE MODEL PER LANGUAGE, LOADED ONCE AND KEPT (ADR-0256): the 👄 and a choice by voice ask the same `load`, and a listener that
58
+ * stops no longer ends the model — the one handed out has a `terminate` that does nothing; the real one is ended when it is
59
+ * pushed out (more than `keep` languages, the oldest first) or by `closeAll()`, the root's end.
60
+ * 📏 Why: every start used to spawn a worker and unpack ~40 MiB of model, and every choice started two — the Dev's machine froze
61
+ * on 2026-09-27. A load that failed is forgotten, so a model that comes down later is found.
62
+ */
63
+ export function keepVoskModels(load, keep = 2) {
64
+ /** Per language: the load as it came (whose model is the one to end) and the one handed out. */
65
+ const kept = new Map();
66
+ const end = (entry) => {
67
+ void entry.real.then((l) => { if (l.ok)
68
+ l.model.terminate?.(); }, () => { });
69
+ };
70
+ return {
71
+ load(d) {
72
+ const key = d.language.split('-')[0].toLowerCase();
73
+ const found = kept.get(key);
74
+ if (found) {
75
+ kept.delete(key);
76
+ kept.set(key, found);
77
+ return found.lent;
78
+ } // the most recent last
79
+ const real = load(d);
80
+ const lent = real.then((l) => (l.ok ? { ...l, model: { get KaldiRecognizer() { return l.model.KaldiRecognizer; }, terminate: () => { } } } : l));
81
+ const entry = { real, lent };
82
+ kept.set(key, entry);
83
+ const forget = () => { if (kept.get(key) === entry)
84
+ kept.delete(key); };
85
+ real.then((l) => { if (!l.ok)
86
+ forget(); }, forget);
87
+ while (kept.size > keep) {
88
+ const [oldest, q] = kept.entries().next().value;
89
+ kept.delete(oldest);
90
+ end(q);
91
+ }
92
+ return lent;
93
+ },
94
+ closeAll() { for (const entry of kept.values())
95
+ end(entry); kept.clear(); },
96
+ };
97
+ }
55
98
  /**
56
99
  * Opens the recogniser for the child's language, or says what is missing.
57
100
  *
@@ -89,5 +132,20 @@ export async function loadVoskRuntime(d) {
89
132
  throw new Error(`vosk-runtime: the bundle asked for a file this delivery does not carry: ${logicalPath}`);
90
133
  };
91
134
  // 📌 `-1` is the bundle's «say nothing»: a recogniser that logs every frame fills a school machine's console with noise.
92
- return { ok: true, model: await api.createModel(at(modelId), beside, -1) };
135
+ const model = await api.createModel(at(modelId), beside, -1);
136
+ const { fetch } = d;
137
+ if (!fetch)
138
+ return { ok: true, model };
139
+ // the SAME address the worker was given: the service worker answers both from the checked cache, and the words read are the
140
+ // words of the model that was loaded, never another language's
141
+ const vocabulary = async () => {
142
+ try {
143
+ const r = await fetch(at(modelId));
144
+ return r.ok && r.body ? await readModelVocabulary(r.body) : null;
145
+ }
146
+ catch {
147
+ return null;
148
+ }
149
+ };
150
+ return { ok: true, model, vocabulary };
93
151
  }
@@ -0,0 +1,6 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ /**
3
+ * The words the model in this `.tar.gz` knows — the output symbol table of its `Gr.fst` — or `null` when that cannot be read
4
+ * (no word graph, a header that is not OpenFst's, a stream that ends early). Stops reading the archive once the table is read.
5
+ */
6
+ export declare function readModelVocabulary(archive: ReadableStream<BufferSource>): Promise<ReadonlySet<string> | null>;
@@ -0,0 +1,178 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // platform/vosk-vocabulary — THE WORDS A VOSK MODEL KNOWS, READ FROM ITS OWN ARCHIVE (ADR-0194 §4, ADR-0169).
3
+ //
4
+ // A menu item whose name has a word the model lacks cannot be said: Kaldi drops that word from the grammar and the item goes
5
+ // mute. The recogniser cannot tell the page so, and this was MEASURED in the delivery's runtime (`vosk-browser-dynamic-
6
+ // execution-0`), not assumed: the drop is a Kaldi warning («Ignoring word missing in vocabulary: 'acao'») written by the wasm to
7
+ // the WORKER's console, never posted to the page, and the bundle's API has no call that lists the vocabulary. So the page reads
8
+ // the list where the model keeps it — the output symbol table of its word graph `Gr.fst`, in the header, before any state — from
9
+ // the same archive the worker opens, and stops reading as soon as the table ends.
10
+ //
11
+ // 📏 In `vosk-model-small-pt-0.3.tar.gz` (the delivery's archive, sorted by `scripts/models/repack-vosk-models.py`) `Gr.fst` is
12
+ // the first entry and its table (99 105 words) ends 4 MB into the uncompressed tar; «configurações», «ação» and «estibordo» are
13
+ // in it, «configuracoes», «acao» and «boreste» are not — the same answers the earlier lab and browser measurements gave. The es
14
+ // and en archives were NOT on the machine that measured this; their `Gr.fst` should sit under `graph/`, after other entries,
15
+ // which the walk below passes over.
16
+ //
17
+ // ⚠️ A model with no `Gr.fst` answers `null` («cannot know»), not an empty set: such a model has no runtime grammar at all, and
18
+ // an empty set would report every word of every name as missing.
19
+ /** The magic numbers of an OpenFst binary file and of a symbol table written inside one (`fst/fst.h`, `fst/symbol-table.h`). */
20
+ const FST_MAGIC = 2125659606;
21
+ const SYMBOL_TABLE_MAGIC = 2125658996;
22
+ /** The header flags that say an input / output symbol table follows the header. */
23
+ const HAS_INPUT_SYMBOLS = 1;
24
+ const HAS_OUTPUT_SYMBOLS = 2;
25
+ const TAR_BLOCK = 512;
26
+ /** Guards against reading a corrupt length as a request for gigabytes. */
27
+ const MAX_STRING = 1 << 16;
28
+ const MAX_SYMBOLS = 5_000_000;
29
+ const UTF8 = new TextDecoder('utf-8');
30
+ const ASCII = new TextDecoder('latin1');
31
+ /** The archive ended, or a length in it cannot be true: whatever was being read cannot be known. */
32
+ class Unreadable extends Error {
33
+ }
34
+ /**
35
+ * A pull reader over a byte stream: takes exactly what it is asked for, across chunk boundaries, and THROWS `Unreadable` when the
36
+ * stream ends first — so a reader of a format states the format, and one `catch` answers every truncation.
37
+ */
38
+ class Bytes {
39
+ chunks = [];
40
+ head = 0;
41
+ have = 0;
42
+ source;
43
+ constructor(source) { this.source = source; }
44
+ async fill(n) {
45
+ while (this.have < n) {
46
+ const { done, value } = await this.source.read();
47
+ if (done)
48
+ throw new Unreadable('the archive ended');
49
+ if (value.length) {
50
+ this.chunks.push(value);
51
+ this.have += value.length;
52
+ }
53
+ }
54
+ }
55
+ drop(k) {
56
+ this.head += k;
57
+ this.have -= k;
58
+ if (this.head === this.chunks[0].length) {
59
+ this.chunks.shift();
60
+ this.head = 0;
61
+ }
62
+ }
63
+ /** The next `n` bytes. */
64
+ async take(n) {
65
+ await this.fill(n);
66
+ const out = new Uint8Array(n);
67
+ for (let o = 0; o < n;) {
68
+ const k = Math.min(n - o, this.chunks[0].length - this.head);
69
+ out.set(this.chunks[0].subarray(this.head, this.head + k), o);
70
+ o += k;
71
+ this.drop(k);
72
+ }
73
+ return out;
74
+ }
75
+ /** Passes `n` bytes without keeping them. */
76
+ async skip(n) {
77
+ for (let left = n; left > 0;) {
78
+ await this.fill(1);
79
+ const k = Math.min(left, this.chunks[0].length - this.head);
80
+ left -= k;
81
+ this.drop(k);
82
+ }
83
+ }
84
+ async int32() { return new DataView((await this.take(4)).buffer).getInt32(0, true); }
85
+ async int64() { return Number(new DataView((await this.take(8)).buffer).getBigInt64(0, true)); }
86
+ /** An OpenFst string: a 32-bit length, then that many bytes of UTF-8. */
87
+ async text() { return UTF8.decode(await this.take(bounded(await this.int32(), MAX_STRING))); }
88
+ }
89
+ /** A length read from the archive, refused when it cannot be one. */
90
+ function bounded(n, max) {
91
+ if (!Number.isFinite(n) || n < 0 || n > max)
92
+ throw new Unreadable(`a length of ${n}`);
93
+ return n;
94
+ }
95
+ /** A NUL-padded tar field as text. */
96
+ const field = (block, from, length) => ASCII.decode(block.subarray(from, from + length)).replace(/\0.*$/s, '');
97
+ /** The value of `path` in a pax extended header, if it sets one. */
98
+ const paxPath = (data) => /(?:^|\n)\d+ path=([^\n]*)\n/.exec(UTF8.decode(data))?.[1] ?? null;
99
+ /** The path a ustar header names: its prefix and its name. */
100
+ const headerPath = (header) => {
101
+ const prefix = field(header, 345, 155);
102
+ return prefix ? `${prefix}/${field(header, 0, 100)}` : field(header, 0, 100);
103
+ };
104
+ const isWordGraph = (type, path) => (type === '0' || type === '\0') && /(?:^|\/)Gr\.fst$/.test(path);
105
+ /**
106
+ * Walks the tar entries until one whose path ends in `/Gr.fst` and leaves the reader at its first byte. `false` when the archive
107
+ * ends without one. Pax (`x`) and GNU long-name (`L`) headers name the entry that follows them; a global pax header is skipped.
108
+ */
109
+ async function toWordGraph(bytes) {
110
+ let named = null;
111
+ for (;;) {
112
+ const header = await bytes.take(TAR_BLOCK);
113
+ if (header.every((b) => b === 0))
114
+ return false;
115
+ const size = bounded(parseInt(field(header, 124, 12).trim() || '0', 8), Number.MAX_SAFE_INTEGER);
116
+ const padded = Math.ceil(size / TAR_BLOCK) * TAR_BLOCK;
117
+ const type = String.fromCharCode(header[156]);
118
+ if (type === 'x' || type === 'L') {
119
+ const data = (await bytes.take(padded)).subarray(0, size);
120
+ named = type === 'x' ? paxPath(data) : field(data, 0, size);
121
+ continue;
122
+ }
123
+ if (isWordGraph(type, named ?? headerPath(header)))
124
+ return true;
125
+ named = null;
126
+ await bytes.skip(padded);
127
+ }
128
+ }
129
+ /** One symbol table, as `fst::SymbolTable::Write` lays it out: magic, name, available key, size, then (symbol, key) pairs. */
130
+ async function symbolTable(bytes) {
131
+ if ((await bytes.int32()) !== SYMBOL_TABLE_MAGIC)
132
+ throw new Unreadable('not a symbol table');
133
+ await bytes.text();
134
+ await bytes.int64();
135
+ const size = bounded(await bytes.int64(), MAX_SYMBOLS);
136
+ const words = new Set();
137
+ for (let i = 0; i < size; i++) {
138
+ words.add(await bytes.text());
139
+ await bytes.int64();
140
+ }
141
+ return words;
142
+ }
143
+ /**
144
+ * The OpenFst header up to its flags — magic, fst type, arc type, version, flags, properties, start, states, arcs — and the flags,
145
+ * leaving the reader at the first symbol table.
146
+ */
147
+ async function fstFlags(bytes) {
148
+ if ((await bytes.int32()) !== FST_MAGIC)
149
+ throw new Unreadable('not an OpenFst file');
150
+ await bytes.text();
151
+ await bytes.text();
152
+ await bytes.int32();
153
+ const flags = await bytes.int32();
154
+ await bytes.skip(4 * 8);
155
+ return flags;
156
+ }
157
+ /**
158
+ * The words the model in this `.tar.gz` knows — the output symbol table of its `Gr.fst` — or `null` when that cannot be read
159
+ * (no word graph, a header that is not OpenFst's, a stream that ends early). Stops reading the archive once the table is read.
160
+ */
161
+ export async function readModelVocabulary(archive) {
162
+ const reader = archive.pipeThrough(new DecompressionStream('gzip')).getReader();
163
+ const bytes = new Bytes(reader);
164
+ try {
165
+ if (!(await toWordGraph(bytes)))
166
+ return null;
167
+ const flags = await fstFlags(bytes);
168
+ const input = flags & HAS_INPUT_SYMBOLS ? await symbolTable(bytes) : null;
169
+ // the words a grammar is matched against are the OUTPUT side; an acceptor written with one table has it on both
170
+ return flags & HAS_OUTPUT_SYMBOLS ? await symbolTable(bytes) : input;
171
+ }
172
+ catch {
173
+ return null; // truncated, not OpenFst, not gzip: each is one more «cannot know»
174
+ }
175
+ finally {
176
+ reader.cancel().catch(() => { });
177
+ }
178
+ }
@@ -22,17 +22,19 @@ export interface CrtCtx {
22
22
  */
23
23
  numPlayers: () => number;
24
24
  /**
25
- * Is ANY player in a visual accessibility mode? (anything other than `normal`.)
25
+ * Does the SCANLINE yield now? The ROOT answers, because the root is where the visual modes are known (ADR-0232): under
26
+ * every visual accessibility mode but a colour-vision one on its own (ADR-0241). A correction or a simulation of colour
27
+ * blindness changes hue; a stripe costs a low-vision child half the vertical resolution of a glyph (ADR-0047).
26
28
  *
27
- * ADR-0020 decides that accessibility modes SUPPRESS the CRT and decorative effects — accessibility takes precedence
28
- * over aesthetics. Without it, `crt-vig-1` survived in `hc-direto`, `fix-deuter`, `lv-blur` and `blind` with the
29
- * vignette on: a vignette darkening the edges works against the mode that exists to RAISE contrast.
30
- *
31
- * `ANY` and not "player 1": the CRT is GLOBAL decoration, one for the whole screen. There is no darkening the edges of
32
- * half a screen. If the decoration and any child's accessibility contradict each other, the decoration yields — which
33
- * is literally what accessibility-over-aesthetics means.
29
+ * A question for the whole screen and not for "player 1": the CRT is GLOBAL decoration, and if it contradicts any
30
+ * child's accessibility, the decoration yields.
31
+ */
32
+ scanlineYields: () => boolean;
33
+ /**
34
+ * Does the VIGNETTE yield now? Under EVERY visual accessibility mode, the colour-vision ones included (ADR-0047,
35
+ * kept by ADR-0241): darkened edges work against a mode that exists to raise contrast, whatever it does to hue.
34
36
  */
35
- a11yVisualOn: () => boolean;
37
+ vignetteYields: () => boolean;
36
38
  /** Where the CRT is read from at build and kept at every `apply()`. Required: a look read from nowhere resets each visit. */
37
39
  store: CrtStore;
38
40
  }
@@ -3,8 +3,8 @@
3
3
  // region). `createCrt` builds one per root (ADR-0232 D4, issue #207): its `cfg` is the config {scan,vig,round} (0=off,1,2;
4
4
  // scan/vig are on/off) read from the injected store at build, migrating the old boolean format. `scanVars` anchors the
5
5
  // scanline to REAL PIXELS (recomputed from the region's real height + dpr → 1 line per art pixel, regular spacing).
6
- // Everything it touches arrives by injection: the region, the window's pixel ratio, the player count, the accessibility
7
- // question and the store.
6
+ // Everything it touches arrives by injection: the region, the window's pixel ratio, the player count, the two
7
+ // accessibility answers (does the scanline yield? does the vignette?) and the store.
8
8
  import { screenGrid } from '../core/screens.js';
9
9
  import { KEYS } from '../platform/storage-keys.js';
10
10
  // scanlines ON by default (the Dev's decision). Migrates incl_crt (boolean) → incl_crt2 (levels 0..2).
@@ -70,21 +70,17 @@ export function createCrt(ctx) {
70
70
  if (!g)
71
71
  return;
72
72
  ['crt-scan-1', 'crt-vig-1', 'crt-round-0', 'crt-round-2'].forEach((c) => g.classList.remove(c));
73
- // BOTH EFFECTS YIELD TO ACCESSIBILITY, WITH NO EXCEPTION (ADR-0020 + ADR-0047).
74
- //
75
- // A version with a per-effect escape switch existed, at the Dev's request. They removed it after SEEING the result
76
- // on screen: «Ceder fez muito bem ao jogo nos modos de acessibilidade». It is noted because the switch's absence is a
77
- // decision, not an oversight — and because pillar 2 is back to having no exception at all.
73
+ // EACH EFFECT YIELDS TO ITS OWN ANSWER (ADR-0047, ADR-0241): the vignette to every visual mode, the scanline to every
74
+ // one but a colour-vision mode on its own. There is no escape switch for the child, by the Dev's decision (ADR-0047).
78
75
  //
79
76
  // The stored value is NOT changed: the preference stays and counts again by itself on leaving the accessibility
80
77
  // mode. Suppressing is not turning off — it is the distinction that keeps the child from losing what they chose every
81
78
  // time they turn high contrast on.
82
- const a11y = ctx.a11yVisualOn();
83
- if (cfg.scan && !a11y) {
79
+ if (cfg.scan && !ctx.scanlineYields()) {
84
80
  g.classList.add('crt-scan-' + cfg.scan);
85
81
  scanVars();
86
82
  }
87
- if (cfg.vig && !a11y)
83
+ if (cfg.vig && !ctx.vignetteYields())
88
84
  g.classList.add('crt-vig-' + cfg.vig);
89
85
  if (cfg.round !== 1)
90
86
  g.classList.add('crt-round-' + cfg.round); // 1 = the default look (8px), no class
@@ -1,7 +1,11 @@
1
1
  // SPDX-License-Identifier: AGPL-3.0-or-later
2
2
  /** The six colour-blindness mode keys — the SAME keys as `VIZ_MODES` in render/viz-modes. */
3
3
  export type CvdKey = 'sim-protan' | 'sim-deuter' | 'sim-tritan' | 'fix-protan' | 'fix-deuter' | 'fix-tritan';
4
- /** Canonical order: the three simulations (what the person sees) before the three corrections (what helps them see). */
4
+ /**
5
+ * Canonical order: the three simulations (what the person sees) before the three corrections (what helps them see).
6
+ * It is also THE list of colour-vision modes the rest of the engine asks about — `onlyColourVision` (render/viz-axes)
7
+ * reads it for the scanline that stays under them (ADR-0241).
8
+ */
5
9
  export declare const CVD_KEYS: readonly CvdKey[];
6
10
  /**
7
11
  * The matrices, 4×5 in row order. Row A = `0 0 0 1 0` in all of them: none of the six modes touches alpha.
@@ -17,7 +17,11 @@
17
17
  // `feColorMatrix type="matrix"` and `PIXI.ColorMatrixFilter#matrix` — so the array serves both with no conversion.
18
18
  // Applied in sRGB on both paths (the web's standard approximation; see the research's decision 2). Primary source and
19
19
  // the value-by-value check: docs/research/RESEARCH-DALTONIZATION.md.
20
- /** Canonical order: the three simulations (what the person sees) before the three corrections (what helps them see). */
20
+ /**
21
+ * Canonical order: the three simulations (what the person sees) before the three corrections (what helps them see).
22
+ * It is also THE list of colour-vision modes the rest of the engine asks about — `onlyColourVision` (render/viz-axes)
23
+ * reads it for the scanline that stays under them (ADR-0241).
24
+ */
21
25
  export const CVD_KEYS = ['sim-protan', 'sim-deuter', 'sim-tritan', 'fix-protan', 'fix-deuter', 'fix-tritan'];
22
26
  /**
23
27
  * The matrices, 4×5 in row order. Row A = `0 0 0 1 0` in all of them: none of the six modes touches alpha.
@@ -2,8 +2,7 @@
2
2
  import { type CanvasDoc } from './canvas.js';
3
3
  import { type HcRoleKey, type PaintableRole } from './hc-role-data.js';
4
4
  import type { Store } from '../platform/storage.js';
5
- export type { PaintableRole, HcRoleKey } from './hc-role-data.js';
6
- export { HC_ROLE_KEYS, HC_ROLE_DEF } from './hc-role-data.js';
5
+ export { HC_ROLE_DEF } from './hc-role-data.js';
7
6
  /** A role palette: one RGB triple per role. */
8
7
  export type RolePalette = Record<HcRoleKey, [number, number, number]>;
9
8
  export interface DirectCfg {
@@ -14,7 +14,11 @@ import { outlineCanvas } from './sprite-fx.js';
14
14
  import { TILE } from '../core/constants.js';
15
15
  import { HC_ROLE_DEF } from './hc-role-data.js';
16
16
  import { KEYS } from '../platform/storage-keys.js';
17
- export { HC_ROLE_KEYS, HC_ROLE_DEF } from './hc-role-data.js';
17
+ /* ===================== role → colour (color-blocking) ===================== */
18
+ // The roles and their default colours live in render/hc-role-data (a leaf, no dependencies), because the visual
19
+ // accessibility panel needs the SAME list to offer a colour picker per role. `HC_ROLE_DEF` is re-exported here because the
20
+ // engine imports it from here; the rest is read from the leaf (issue #204).
21
+ export { HC_ROLE_DEF } from './hc-role-data.js';
18
22
  /** The defaults with the stored colours laid over them, each clamped to 0–255; a malformed entry keeps its default. */
19
23
  function storedPalette(store) {
20
24
  const palette = JSON.parse(JSON.stringify(HC_ROLE_DEF));
@@ -54,6 +54,15 @@ export declare function isSimulation(v: VisualState): boolean;
54
54
  export declare function isBlind(v: VisualState): boolean;
55
55
  /** Is it one of the five LOW-VISION simulations? They need the overlay as a texture, not only a filter. */
56
56
  export declare function isLowVision(v: VisualState): boolean;
57
+ /**
58
+ * Is a COLOUR-VISION mode the only visual mode in this state — the 🚥 correction, or the simulation of the same colour
59
+ * blindness, with the theme at its default? The decorative scanline stays under these (ADR-0241): they change hue, not
60
+ * contrast or sharpness.
61
+ *
62
+ * 📌 The list is `CVD_KEYS`, where those six modes are defined with their matrices: a colour-vision mode added there
63
+ * counts here, with no second list of names to keep true.
64
+ */
65
+ export declare function onlyColourVision(v: VisualState): boolean;
57
66
  /** Is the theme off its default? It replaces a `/^hc-direto/` test on the old string. */
58
67
  export declare function hasHighContrast(v: VisualState): boolean;
59
68
  /** The next THEME in the quick bar icon's cycle. It moves only on its own axis and does not touch the correction. */
@@ -1,6 +1,6 @@
1
1
  // SPDX-License-Identifier: AGPL-3.0-or-later
2
- // render/viz-axes — THE TWO AXES, and the simulation that is NOT one of them (ADR-0076). Only dependency: the shapes,
3
- // from `core/visual-state`.
2
+ // render/viz-axes — THE TWO AXES, and the simulation that is NOT one of them (ADR-0076). Dependencies: the shapes, from
3
+ // `core/visual-state`, and the list of colour-vision modes, from the leaf `render/cvd-matrices` that defines them.
4
4
  //
5
5
  // ========================= THE DEFECT THIS FIXES =========================
6
6
  // With ONE radio and `p.viz` keeping ONE string, choosing `fix-deuter` turned 7:1 contrast off, and choosing a contrast
@@ -19,6 +19,7 @@
19
19
  // Over an already corrected screen, a simulation shows neither the disability nor the correction; over a high-contrast
20
20
  // theme, it shows what the theme does and not what deuteranopia does. A demonstration running over an adaptation is not
21
21
  // a weaker demonstration — **it teaches something false**.
22
+ import { CVD_KEYS } from './cvd-matrices.js';
22
23
  export const DEFAULT_VISUAL = Object.freeze({ tema: 'padrao', correcao: 'tricro', simulacao: null });
23
24
  /**
24
25
  * Are both axes at their default?
@@ -91,6 +92,18 @@ export function isBlind(v) {
91
92
  export function isLowVision(v) {
92
93
  return v.simulacao !== null && v.simulacao.startsWith('lv-');
93
94
  }
95
+ /**
96
+ * Is a COLOUR-VISION mode the only visual mode in this state — the 🚥 correction, or the simulation of the same colour
97
+ * blindness, with the theme at its default? The decorative scanline stays under these (ADR-0241): they change hue, not
98
+ * contrast or sharpness.
99
+ *
100
+ * 📌 The list is `CVD_KEYS`, where those six modes are defined with their matrices: a colour-vision mode added there
101
+ * counts here, with no second list of names to keep true.
102
+ */
103
+ export function onlyColourVision(v) {
104
+ const key = filterKey(v);
105
+ return !hasHighContrast(v) && key !== null && CVD_KEYS.includes(key);
106
+ }
94
107
  /** Is the theme off its default? It replaces a `/^hc-direto/` test on the old string. */
95
108
  export function hasHighContrast(v) {
96
109
  return v.tema !== 'padrao';
@@ -1,40 +1,45 @@
1
1
  // SPDX-License-Identifier: AGPL-3.0-or-later
2
2
  import type { Translate } from '../core/i18n.js';
3
3
  export type AacTier =
4
- /** CC BY-SA or ours: it may travel inside the game and works with no network at all. */
5
- 'bundled'
6
- /** The licence allows use, not redistribution: the person downloads it, once, and it stays cached. */
7
- | 'fetched'
8
- /** Permission is missing. It appears in the menu and is not selectable. */
9
- | 'negotiating';
10
- export interface AacSet {
4
+ /** A licence is RECORDED in `license`: the set may be chosen. */
5
+ 'licensed'
6
+ /** No licence: the set appears in the menu, locked, with the reason. */
7
+ | 'unlicensed';
8
+ interface AacSetBase {
11
9
  readonly key: string;
12
10
  readonly name: string;
13
- readonly tier: AacTier;
14
- /** The licence as it was VERIFIED, or null when there is none yet. Short text, to fit on screen. */
15
- readonly license: string | null;
16
- /** Does it work IN THIS build? The pictograms are files that have not arrived yet. */
17
- readonly available: boolean;
18
11
  /** Pictograms only: one line about the origin, when it changes what the choice means. */
19
12
  readonly note?: string;
20
13
  }
14
+ /**
15
+ * A pictogram set. The tier and the licence travel together BY TYPE: a licensed set names its licence (short text, to
16
+ * fit on screen), and an unlicensed one has none to name.
17
+ */
18
+ export type AacSet = (AacSetBase & {
19
+ readonly tier: 'licensed';
20
+ readonly license: string;
21
+ }) | (AacSetBase & {
22
+ readonly tier: 'unlicensed';
23
+ readonly license: null;
24
+ });
21
25
  export declare const AAC_SETS: readonly AacSet[];
22
26
  export declare const AAC_BY_KEY: Readonly<Record<string, AacSet>>;
23
- /** Which SETS the child can choose today: none. The menu does not pretend otherwise — the pictograms are thousands
24
- * of files that have not entered the repository, and four of the sets depend on negotiation. */
27
+ /** Can the child choose this set? Only when it is licensed — the tier is the one fact the menu reads. */
28
+ export declare function aacSelectable(s: AacSet): boolean;
29
+ /** Which SETS the child can choose today: none, because no licence has been obtained (ADR-0233). */
25
30
  export declare function aacAvailable(): AacSet[];
26
31
  /**
27
- * Why this set is not available — an i18n key, never finished text.
28
- *
29
- * The two answers tell the reader different things, which is why there are two: "in preparation" means the licence
30
- * is settled and the work is OURS; "awaiting negotiation" means the permission belongs to SOMEONE ELSE and no effort
31
- * of ours brings it forward.
32
- *
33
- * And that is why the unavailable ones are shown instead of hidden: they are exactly the sets a Brazilian school is
34
- * most likely to already use in another product. The educator who looks for PCS and does not find it concludes the
35
- * game cannot do it — when the obstacle is a licence, not a capability. Hiding would answer the wrong question, and
36
- * answer it wrongly.
32
+ * Is the AAC menu's DOOR disabled? Yes while no set is licensed (ADR-0233 erratum): the menu exists for pictogram
33
+ * exercises and a pictogram keyboard, and with no set it has nothing to offer. The door then says only that the menu is
34
+ * disabled (`pause.motivo.caa`) — never the licence reason. Letter case does not wait for it: it lives in the quick bar's
35
+ * communication cycle (ADR-0151).
36
+ */
37
+ export declare function aacMenuLocked(): boolean;
38
+ /**
39
+ * Why this set cannot be chosen — an i18n key, never finished text. One answer, because there is one reason: no
40
+ * licence. A selectable set has nothing to explain.
37
41
  */
38
42
  export declare function aacReason(s: AacSet): string | null;
39
43
  /** A label ready for the menu row: the name + the reason, when there is one. */
40
44
  export declare function aacLabel(t: Translate, s: AacSet): string;
45
+ export {};
@@ -3,44 +3,36 @@
3
3
  // off, with off meaning both cases), not two entries. A binary choice presented as two options makes the child compare
4
4
  // two rows to discover they are the same question; a switch asks once.
5
5
  //
6
- // What remains here is genuinely a LIST: the pictogram sets, of which one is chosen.
6
+ // What remains here is genuinely a LIST: the pictogram sets, of which one is chosen. «PCS» is the Picture Communication
7
+ // Symbols set (ADR-0233 reads the Dev's «PECS» as PCS: PECS is a teaching method, not a symbol set).
7
8
  export const AAC_SETS = [
8
- // --- PICTOGRAMS that MAY travel with the game (CC BY-SA, verified by the Dev). ---
9
- { key: 'mulberry', name: 'Mulberry Symbols', tier: 'bundled', license: 'CC BY-SA', available: false },
10
- { key: 'blissymbolics', name: 'Blissymbolics', tier: 'bundled', license: 'CC BY-SA 4.0', available: false,
11
- note: 'Escrita simbólica própria: os símbolos se combinam para formar sentidos novos.' },
12
- { key: 'tawasol', name: 'Tawasol', tier: 'bundled', license: 'CC BY-SA 4.0', available: false,
13
- note: 'Desenhado na e para a cultura árabe. Um pictograma não é neutro — a criança reconhece mais depressa os objetos, as roupas e os rostos do próprio mundo.' },
14
- // --- PICTOGRAMS that must be DOWNLOADED. ---
15
- { key: 'arasaac', name: 'ARASAAC', tier: 'fetched', license: 'baixado à parte', available: false },
16
- // --- AWAITING NEGOTIATION. Visible on purpose: see `aacReason`. ---
17
- { key: 'sclera', name: 'Sclera', tier: 'negotiating', license: null, available: false },
18
- { key: 'pcs', name: 'PCS', tier: 'negotiating', license: null, available: false },
19
- { key: 'symbolstix', name: 'SymbolStix', tier: 'negotiating', license: null, available: false },
20
- { key: 'widgit', name: 'Widgit Symbols', tier: 'negotiating', license: null, available: false },
9
+ { key: 'arasaac', name: 'ARASAAC', tier: 'unlicensed', license: null },
10
+ { key: 'pcs', name: 'PCS', tier: 'unlicensed', license: null },
21
11
  ];
22
12
  export const AAC_BY_KEY = Object.fromEntries(AAC_SETS.map((s) => [s.key, s]));
23
- /** Which SETS the child can choose today: none. The menu does not pretend otherwise — the pictograms are thousands
24
- * of files that have not entered the repository, and four of the sets depend on negotiation. */
13
+ /** Can the child choose this set? Only when it is licensed — the tier is the one fact the menu reads. */
14
+ export function aacSelectable(s) {
15
+ return s.tier === 'licensed';
16
+ }
17
+ /** Which SETS the child can choose today: none, because no licence has been obtained (ADR-0233). */
25
18
  export function aacAvailable() {
26
- return AAC_SETS.filter((s) => s.available);
19
+ return AAC_SETS.filter(aacSelectable);
20
+ }
21
+ /**
22
+ * Is the AAC menu's DOOR disabled? Yes while no set is licensed (ADR-0233 erratum): the menu exists for pictogram
23
+ * exercises and a pictogram keyboard, and with no set it has nothing to offer. The door then says only that the menu is
24
+ * disabled (`pause.motivo.caa`) — never the licence reason. Letter case does not wait for it: it lives in the quick bar's
25
+ * communication cycle (ADR-0151).
26
+ */
27
+ export function aacMenuLocked() {
28
+ return aacAvailable().length === 0;
27
29
  }
28
30
  /**
29
- * Why this set is not available — an i18n key, never finished text.
30
- *
31
- * The two answers tell the reader different things, which is why there are two: "in preparation" means the licence
32
- * is settled and the work is OURS; "awaiting negotiation" means the permission belongs to SOMEONE ELSE and no effort
33
- * of ours brings it forward.
34
- *
35
- * And that is why the unavailable ones are shown instead of hidden: they are exactly the sets a Brazilian school is
36
- * most likely to already use in another product. The educator who looks for PCS and does not find it concludes the
37
- * game cannot do it — when the obstacle is a licence, not a capability. Hiding would answer the wrong question, and
38
- * answer it wrongly.
31
+ * Why this set cannot be chosen — an i18n key, never finished text. One answer, because there is one reason: no
32
+ * licence. A selectable set has nothing to explain.
39
33
  */
40
34
  export function aacReason(s) {
41
- if (s.available)
42
- return null;
43
- return s.tier === 'negotiating' ? 'aac.aguardandoNegociacao' : 'aac.emPreparo';
35
+ return aacSelectable(s) ? null : 'aac.noLicence';
44
36
  }
45
37
  /** A label ready for the menu row: the name + the reason, when there is one. */
46
38
  export function aacLabel(t, s) {
@@ -9,8 +9,11 @@ export interface AudioCatState {
9
9
  on: boolean;
10
10
  vol: number;
11
11
  }
12
- /** The SOUND-NAVIGATION categories (cane/sonar/guard/guide) — their master volume is separate from the game's sound. */
13
- export declare const NAV_CATS: readonly ["sonar", "guard", "guide"];
12
+ /**
13
+ * The SOUND-NAVIGATION categories — their master volume is separate from the game's sound. Only the sonar since `guard` and
14
+ * `guide` left (ADR-0258): the engine plays nothing in them since the guide moved to the platformer.
15
+ */
16
+ export declare const NAV_CATS: readonly ["sonar"];
14
17
  /** The game's GENERAL categories (TTS lives in the Voice section, outside this list). */
15
18
  export declare const GEN_CATS: readonly ["music", "ambient", "interact", "earcons"];
16
19
  /** 0..1 -> 0..100 rounded (slider display value). */
@@ -36,14 +39,16 @@ export interface VoiceLike {
36
39
  lang: string;
37
40
  }
38
41
  /**
39
- * The system voices in the language asked for; with none of them, the whole list.
42
+ * The system voices in the language asked for, and ONLY those (ADR-0185): with none of them, none — the list then says there is
43
+ * no voice, and the speech rows lock.
40
44
  *
41
45
  * It was called `pickPtVoices` and filtered a hard-coded `/^pt/i`, so a game in English offered a child a list of PORTUGUESE
42
46
  * voices to read English text. The name told the truth about what it did and lied about what it should do.
43
47
  *
44
48
  * The comparison is by language PREFIX and not by the whole tag: a child playing in pt-BR should also be able to choose a
45
- * pt-PT voice if that is the only one installed, and a school's browser rarely has the exact variant. The fallback to the
46
- * whole list stays: an empty list would be worse than a list in the wrong language, which at least she can hear and reject.
49
+ * pt-PT voice if that is the only one installed, and a school's browser rarely has the exact variant. 🔴 There is no fallback
50
+ * to the whole list: a voice of another language reads the text with the wrong phonetics, and ADR-0243 §3 decided that a wrong
51
+ * voice is worse than none — silence is noticed, a wrong accent is learned.
47
52
  */
48
53
  export declare function pickVoicesFor<T extends VoiceLike>(voices: readonly T[], lang: string): readonly T[];
49
54
  /** "<name> (<lang>)" option label. */