@the-inclusionist/engine 9.0.0 → 10.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 (756) hide show
  1. package/README.md +89 -78
  2. package/app/css/style.css +584 -374
  3. package/app/public/vendor/fonts/bitter-var-ext.woff2 +0 -0
  4. package/app/public/vendor/fonts/bitter-var.woff2 +0 -0
  5. package/app/public/vendor/fonts/bodonimoda-var-ext.woff2 +0 -0
  6. package/app/public/vendor/fonts/bodonimoda-var.woff2 +0 -0
  7. package/app/public/vendor/fonts/cat-abril_fatface-400-ext.woff2 +0 -0
  8. package/app/public/vendor/fonts/cat-abril_fatface-400.woff2 +0 -0
  9. package/app/public/vendor/fonts/cat-adlam_display-400-ext.woff2 +0 -0
  10. package/app/public/vendor/fonts/cat-adlam_display-400.woff2 +0 -0
  11. package/app/public/vendor/fonts/cat-agu_display-400-ext.woff2 +0 -0
  12. package/app/public/vendor/fonts/cat-agu_display-400.woff2 +0 -0
  13. package/app/public/vendor/fonts/cat-alfa_slab_one-400-ext.woff2 +0 -0
  14. package/app/public/vendor/fonts/cat-alfa_slab_one-400.woff2 +0 -0
  15. package/app/public/vendor/fonts/cat-are_you_serious-400-ext.woff2 +0 -0
  16. package/app/public/vendor/fonts/cat-are_you_serious-400.woff2 +0 -0
  17. package/app/public/vendor/fonts/cat-asimovian-400-ext.woff2 +0 -0
  18. package/app/public/vendor/fonts/cat-asimovian-400.woff2 +0 -0
  19. package/app/public/vendor/fonts/cat-atkinson_hyperlegible_next-400-ext.woff2 +0 -0
  20. package/app/public/vendor/fonts/cat-atkinson_hyperlegible_next-400.woff2 +0 -0
  21. package/app/public/vendor/fonts/cat-bakbak_one-400-ext.woff2 +0 -0
  22. package/app/public/vendor/fonts/cat-bakbak_one-400.woff2 +0 -0
  23. package/app/public/vendor/fonts/cat-bangers-400-ext.woff2 +0 -0
  24. package/app/public/vendor/fonts/cat-bangers-400.woff2 +0 -0
  25. package/app/public/vendor/fonts/cat-barriecito-400-ext.woff2 +0 -0
  26. package/app/public/vendor/fonts/cat-barriecito-400.woff2 +0 -0
  27. package/app/public/vendor/fonts/cat-berkshire_swash-400-ext.woff2 +0 -0
  28. package/app/public/vendor/fonts/cat-berkshire_swash-400.woff2 +0 -0
  29. package/app/public/vendor/fonts/cat-bevan-400-ext.woff2 +0 -0
  30. package/app/public/vendor/fonts/cat-bevan-400.woff2 +0 -0
  31. package/app/public/vendor/fonts/cat-bitcount-400-ext.woff2 +0 -0
  32. package/app/public/vendor/fonts/cat-bitcount-400.woff2 +0 -0
  33. package/app/public/vendor/fonts/cat-bowlby_one_sc-400-ext.woff2 +0 -0
  34. package/app/public/vendor/fonts/cat-bowlby_one_sc-400.woff2 +0 -0
  35. package/app/public/vendor/fonts/cat-bungee_shade-400-ext.woff2 +0 -0
  36. package/app/public/vendor/fonts/cat-bungee_shade-400.woff2 +0 -0
  37. package/app/public/vendor/fonts/cat-charm-400-ext.woff2 +0 -0
  38. package/app/public/vendor/fonts/cat-charm-400.woff2 +0 -0
  39. package/app/public/vendor/fonts/cat-charm-700-ext.woff2 +0 -0
  40. package/app/public/vendor/fonts/cat-charm-700.woff2 +0 -0
  41. package/app/public/vendor/fonts/cat-cherry_bomb_one-400-ext.woff2 +0 -0
  42. package/app/public/vendor/fonts/cat-cherry_bomb_one-400.woff2 +0 -0
  43. package/app/public/vendor/fonts/cat-chokokutai-400-ext.woff2 +0 -0
  44. package/app/public/vendor/fonts/cat-chokokutai-400.woff2 +0 -0
  45. package/app/public/vendor/fonts/cat-chonburi-400-ext.woff2 +0 -0
  46. package/app/public/vendor/fonts/cat-chonburi-400.woff2 +0 -0
  47. package/app/public/vendor/fonts/cat-climate_crisis-400-ext.woff2 +0 -0
  48. package/app/public/vendor/fonts/cat-climate_crisis-400.woff2 +0 -0
  49. package/app/public/vendor/fonts/cat-cookie-400.woff2 +0 -0
  50. package/app/public/vendor/fonts/cat-dela_gothic_one-400-ext.woff2 +0 -0
  51. package/app/public/vendor/fonts/cat-dela_gothic_one-400.woff2 +0 -0
  52. package/app/public/vendor/fonts/cat-fascinate_inline-400-ext.woff2 +0 -0
  53. package/app/public/vendor/fonts/cat-fascinate_inline-400.woff2 +0 -0
  54. package/app/public/vendor/fonts/cat-ga_maamli-400-ext.woff2 +0 -0
  55. package/app/public/vendor/fonts/cat-ga_maamli-400.woff2 +0 -0
  56. package/app/public/vendor/fonts/cat-itim-400-ext.woff2 +0 -0
  57. package/app/public/vendor/fonts/cat-itim-400.woff2 +0 -0
  58. package/app/public/vendor/fonts/cat-jacquard_24-400-ext.woff2 +0 -0
  59. package/app/public/vendor/fonts/cat-jacquard_24-400.woff2 +0 -0
  60. package/app/public/vendor/fonts/cat-kablammo-400-ext.woff2 +0 -0
  61. package/app/public/vendor/fonts/cat-kablammo-400.woff2 +0 -0
  62. package/app/public/vendor/fonts/cat-kalam-400-ext.woff2 +0 -0
  63. package/app/public/vendor/fonts/cat-kalam-400.woff2 +0 -0
  64. package/app/public/vendor/fonts/cat-kalam-700-ext.woff2 +0 -0
  65. package/app/public/vendor/fonts/cat-kalam-700.woff2 +0 -0
  66. package/app/public/vendor/fonts/cat-katibeh-400-ext.woff2 +0 -0
  67. package/app/public/vendor/fonts/cat-katibeh-400.woff2 +0 -0
  68. package/app/public/vendor/fonts/cat-lemonada-400-ext.woff2 +0 -0
  69. package/app/public/vendor/fonts/cat-lemonada-400.woff2 +0 -0
  70. package/app/public/vendor/fonts/cat-lilita_one-400-ext.woff2 +0 -0
  71. package/app/public/vendor/fonts/cat-lilita_one-400.woff2 +0 -0
  72. package/app/public/vendor/fonts/cat-lobster-400-ext.woff2 +0 -0
  73. package/app/public/vendor/fonts/cat-lobster-400.woff2 +0 -0
  74. package/app/public/vendor/fonts/cat-luckiest_guy-400-ext.woff2 +0 -0
  75. package/app/public/vendor/fonts/cat-luckiest_guy-400.woff2 +0 -0
  76. package/app/public/vendor/fonts/cat-marhey-400-ext.woff2 +0 -0
  77. package/app/public/vendor/fonts/cat-marhey-400.woff2 +0 -0
  78. package/app/public/vendor/fonts/cat-modak-400-ext.woff2 +0 -0
  79. package/app/public/vendor/fonts/cat-modak-400.woff2 +0 -0
  80. package/app/public/vendor/fonts/cat-momo_signature-400-ext.woff2 +0 -0
  81. package/app/public/vendor/fonts/cat-momo_signature-400.woff2 +0 -0
  82. package/app/public/vendor/fonts/cat-monofett-400-ext.woff2 +0 -0
  83. package/app/public/vendor/fonts/cat-monofett-400.woff2 +0 -0
  84. package/app/public/vendor/fonts/cat-monomakh-400-ext.woff2 +0 -0
  85. package/app/public/vendor/fonts/cat-monomakh-400.woff2 +0 -0
  86. package/app/public/vendor/fonts/cat-monoton-400-ext.woff2 +0 -0
  87. package/app/public/vendor/fonts/cat-monoton-400.woff2 +0 -0
  88. package/app/public/vendor/fonts/cat-nerko_one-400-ext.woff2 +0 -0
  89. package/app/public/vendor/fonts/cat-nerko_one-400.woff2 +0 -0
  90. package/app/public/vendor/fonts/cat-noto_sans_adlam-400-ext.woff2 +0 -0
  91. package/app/public/vendor/fonts/cat-noto_sans_adlam-400.woff2 +0 -0
  92. package/app/public/vendor/fonts/cat-noto_sans_arabic-400-ext.woff2 +0 -0
  93. package/app/public/vendor/fonts/cat-noto_sans_arabic-400.woff2 +0 -0
  94. package/app/public/vendor/fonts/cat-noto_sans_devanagari-400-ext.woff2 +0 -0
  95. package/app/public/vendor/fonts/cat-noto_sans_devanagari-400.woff2 +0 -0
  96. package/app/public/vendor/fonts/cat-noto_sans_hebrew-400-ext.woff2 +0 -0
  97. package/app/public/vendor/fonts/cat-noto_sans_hebrew-400.woff2 +0 -0
  98. package/app/public/vendor/fonts/cat-noto_sans_jp-400-ext.woff2 +0 -0
  99. package/app/public/vendor/fonts/cat-noto_sans_jp-400.woff2 +0 -0
  100. package/app/public/vendor/fonts/cat-noto_sans_khmer-400-ext.woff2 +0 -0
  101. package/app/public/vendor/fonts/cat-noto_sans_khmer-400.woff2 +0 -0
  102. package/app/public/vendor/fonts/cat-noto_sans_kr-400-ext.woff2 +0 -0
  103. package/app/public/vendor/fonts/cat-noto_sans_kr-400.woff2 +0 -0
  104. package/app/public/vendor/fonts/cat-noto_sans_sc-400-ext.woff2 +0 -0
  105. package/app/public/vendor/fonts/cat-noto_sans_sc-400.woff2 +0 -0
  106. package/app/public/vendor/fonts/cat-noto_sans_thai-400-ext.woff2 +0 -0
  107. package/app/public/vendor/fonts/cat-noto_sans_thai-400.woff2 +0 -0
  108. package/app/public/vendor/fonts/cat-ole-400-ext.woff2 +0 -0
  109. package/app/public/vendor/fonts/cat-ole-400.woff2 +0 -0
  110. package/app/public/vendor/fonts/cat-pacifico-400-ext.woff2 +0 -0
  111. package/app/public/vendor/fonts/cat-pacifico-400.woff2 +0 -0
  112. package/app/public/vendor/fonts/cat-patrick_hand-400-ext.woff2 +0 -0
  113. package/app/public/vendor/fonts/cat-patrick_hand-400.woff2 +0 -0
  114. package/app/public/vendor/fonts/cat-petit_formal_script-400-ext.woff2 +0 -0
  115. package/app/public/vendor/fonts/cat-petit_formal_script-400.woff2 +0 -0
  116. package/app/public/vendor/fonts/cat-pixelify_sans-400-ext.woff2 +0 -0
  117. package/app/public/vendor/fonts/cat-pixelify_sans-400.woff2 +0 -0
  118. package/app/public/vendor/fonts/cat-plaster-400-ext.woff2 +0 -0
  119. package/app/public/vendor/fonts/cat-plaster-400.woff2 +0 -0
  120. package/app/public/vendor/fonts/cat-playwrite_ar_guides-400-0.woff2 +0 -0
  121. package/app/public/vendor/fonts/cat-playwrite_at-400-0.woff2 +0 -0
  122. package/app/public/vendor/fonts/cat-playwrite_at_guides-400-0.woff2 +0 -0
  123. package/app/public/vendor/fonts/cat-playwrite_au_nsw-400-0.woff2 +0 -0
  124. package/app/public/vendor/fonts/cat-playwrite_au_nsw_guides-400-0.woff2 +0 -0
  125. package/app/public/vendor/fonts/cat-playwrite_au_qld-400-0.woff2 +0 -0
  126. package/app/public/vendor/fonts/cat-playwrite_au_qld_guides-400-0.woff2 +0 -0
  127. package/app/public/vendor/fonts/cat-playwrite_au_sa-400-0.woff2 +0 -0
  128. package/app/public/vendor/fonts/cat-playwrite_au_sa_guides-400-0.woff2 +0 -0
  129. package/app/public/vendor/fonts/cat-playwrite_au_tas-400-0.woff2 +0 -0
  130. package/app/public/vendor/fonts/cat-playwrite_au_tas_guides-400-0.woff2 +0 -0
  131. package/app/public/vendor/fonts/cat-playwrite_au_vic-400-0.woff2 +0 -0
  132. package/app/public/vendor/fonts/cat-playwrite_au_vic_guides-400-0.woff2 +0 -0
  133. package/app/public/vendor/fonts/cat-playwrite_be_vlg-400-0.woff2 +0 -0
  134. package/app/public/vendor/fonts/cat-playwrite_be_vlg_guides-400-0.woff2 +0 -0
  135. package/app/public/vendor/fonts/cat-playwrite_be_wal-400-0.woff2 +0 -0
  136. package/app/public/vendor/fonts/cat-playwrite_be_wal_guides-400-0.woff2 +0 -0
  137. package/app/public/vendor/fonts/cat-playwrite_br_guides-400-0.woff2 +0 -0
  138. package/app/public/vendor/fonts/cat-playwrite_ca_guides-400-0.woff2 +0 -0
  139. package/app/public/vendor/fonts/cat-playwrite_cl_guides-400-0.woff2 +0 -0
  140. package/app/public/vendor/fonts/cat-playwrite_co_guides-400-0.woff2 +0 -0
  141. package/app/public/vendor/fonts/cat-playwrite_cu_guides-400-0.woff2 +0 -0
  142. package/app/public/vendor/fonts/cat-playwrite_cz-400-0.woff2 +0 -0
  143. package/app/public/vendor/fonts/cat-playwrite_cz_guides-400-0.woff2 +0 -0
  144. package/app/public/vendor/fonts/cat-playwrite_de_grund-400-0.woff2 +0 -0
  145. package/app/public/vendor/fonts/cat-playwrite_de_grund_guides-400-0.woff2 +0 -0
  146. package/app/public/vendor/fonts/cat-playwrite_de_la-400-0.woff2 +0 -0
  147. package/app/public/vendor/fonts/cat-playwrite_de_la_guides-400-0.woff2 +0 -0
  148. package/app/public/vendor/fonts/cat-playwrite_de_sas-400-0.woff2 +0 -0
  149. package/app/public/vendor/fonts/cat-playwrite_de_sas_guides-400-0.woff2 +0 -0
  150. package/app/public/vendor/fonts/cat-playwrite_de_va-400-0.woff2 +0 -0
  151. package/app/public/vendor/fonts/cat-playwrite_de_va_guides-400-0.woff2 +0 -0
  152. package/app/public/vendor/fonts/cat-playwrite_dk_loopet-400-0.woff2 +0 -0
  153. package/app/public/vendor/fonts/cat-playwrite_dk_loopet_guides-400-0.woff2 +0 -0
  154. package/app/public/vendor/fonts/cat-playwrite_dk_uloopet-400-0.woff2 +0 -0
  155. package/app/public/vendor/fonts/cat-playwrite_dk_uloopet_guides-400-0.woff2 +0 -0
  156. package/app/public/vendor/fonts/cat-playwrite_es_deco_guides-400-0.woff2 +0 -0
  157. package/app/public/vendor/fonts/cat-playwrite_es_guides-400-0.woff2 +0 -0
  158. package/app/public/vendor/fonts/cat-playwrite_fr_moderne-400-0.woff2 +0 -0
  159. package/app/public/vendor/fonts/cat-playwrite_fr_moderne_guides-400-0.woff2 +0 -0
  160. package/app/public/vendor/fonts/cat-playwrite_fr_trad-400-0.woff2 +0 -0
  161. package/app/public/vendor/fonts/cat-playwrite_fr_trad_guides-400-0.woff2 +0 -0
  162. package/app/public/vendor/fonts/cat-playwrite_gb_j_guides-400-0.woff2 +0 -0
  163. package/app/public/vendor/fonts/cat-playwrite_gb_s_guides-400-0.woff2 +0 -0
  164. package/app/public/vendor/fonts/cat-playwrite_hr-400-0.woff2 +0 -0
  165. package/app/public/vendor/fonts/cat-playwrite_hr_guides-400-0.woff2 +0 -0
  166. package/app/public/vendor/fonts/cat-playwrite_hr_lijeva-400-0.woff2 +0 -0
  167. package/app/public/vendor/fonts/cat-playwrite_hr_lijeva_guides-400-0.woff2 +0 -0
  168. package/app/public/vendor/fonts/cat-playwrite_hu-400-0.woff2 +0 -0
  169. package/app/public/vendor/fonts/cat-playwrite_hu_guides-400-0.woff2 +0 -0
  170. package/app/public/vendor/fonts/cat-playwrite_id-400-0.woff2 +0 -0
  171. package/app/public/vendor/fonts/cat-playwrite_id_guides-400-0.woff2 +0 -0
  172. package/app/public/vendor/fonts/cat-playwrite_ie-400-0.woff2 +0 -0
  173. package/app/public/vendor/fonts/cat-playwrite_ie_guides-400-0.woff2 +0 -0
  174. package/app/public/vendor/fonts/cat-playwrite_in-400-0.woff2 +0 -0
  175. package/app/public/vendor/fonts/cat-playwrite_in_guides-400-0.woff2 +0 -0
  176. package/app/public/vendor/fonts/cat-playwrite_is-400-0.woff2 +0 -0
  177. package/app/public/vendor/fonts/cat-playwrite_is_guides-400-0.woff2 +0 -0
  178. package/app/public/vendor/fonts/cat-playwrite_it_moderna-400-0.woff2 +0 -0
  179. package/app/public/vendor/fonts/cat-playwrite_it_moderna_guides-400-0.woff2 +0 -0
  180. package/app/public/vendor/fonts/cat-playwrite_it_trad-400-0.woff2 +0 -0
  181. package/app/public/vendor/fonts/cat-playwrite_it_trad_guides-400-0.woff2 +0 -0
  182. package/app/public/vendor/fonts/cat-playwrite_mx_guides-400-0.woff2 +0 -0
  183. package/app/public/vendor/fonts/cat-playwrite_ng_modern-400-0.woff2 +0 -0
  184. package/app/public/vendor/fonts/cat-playwrite_ng_modern_guides-400-0.woff2 +0 -0
  185. package/app/public/vendor/fonts/cat-playwrite_nl-400-0.woff2 +0 -0
  186. package/app/public/vendor/fonts/cat-playwrite_nl_guides-400-0.woff2 +0 -0
  187. package/app/public/vendor/fonts/cat-playwrite_no-400-0.woff2 +0 -0
  188. package/app/public/vendor/fonts/cat-playwrite_no_guides-400-0.woff2 +0 -0
  189. package/app/public/vendor/fonts/cat-playwrite_nz-400-0.woff2 +0 -0
  190. package/app/public/vendor/fonts/cat-playwrite_nz_basic-400-0.woff2 +0 -0
  191. package/app/public/vendor/fonts/cat-playwrite_nz_basic_guides-400-0.woff2 +0 -0
  192. package/app/public/vendor/fonts/cat-playwrite_nz_guides-400-0.woff2 +0 -0
  193. package/app/public/vendor/fonts/cat-playwrite_pe_guides-400-0.woff2 +0 -0
  194. package/app/public/vendor/fonts/cat-playwrite_pl-400-0.woff2 +0 -0
  195. package/app/public/vendor/fonts/cat-playwrite_pl_guides-400-0.woff2 +0 -0
  196. package/app/public/vendor/fonts/cat-playwrite_pt_guides-400-0.woff2 +0 -0
  197. package/app/public/vendor/fonts/cat-playwrite_ro-400-0.woff2 +0 -0
  198. package/app/public/vendor/fonts/cat-playwrite_ro_guides-400-0.woff2 +0 -0
  199. package/app/public/vendor/fonts/cat-playwrite_sk-400-0.woff2 +0 -0
  200. package/app/public/vendor/fonts/cat-playwrite_sk_guides-400-0.woff2 +0 -0
  201. package/app/public/vendor/fonts/cat-playwrite_tz-400-0.woff2 +0 -0
  202. package/app/public/vendor/fonts/cat-playwrite_tz_guides-400-0.woff2 +0 -0
  203. package/app/public/vendor/fonts/cat-playwrite_us_modern_guides-400-0.woff2 +0 -0
  204. package/app/public/vendor/fonts/cat-playwrite_us_trad_guides-400-0.woff2 +0 -0
  205. package/app/public/vendor/fonts/cat-playwrite_vn-400-0.woff2 +0 -0
  206. package/app/public/vendor/fonts/cat-playwrite_vn_guides-400-0.woff2 +0 -0
  207. package/app/public/vendor/fonts/cat-playwrite_za-400-0.woff2 +0 -0
  208. package/app/public/vendor/fonts/cat-playwrite_za_guides-400-0.woff2 +0 -0
  209. package/app/public/vendor/fonts/cat-potta_one-400-ext.woff2 +0 -0
  210. package/app/public/vendor/fonts/cat-potta_one-400.woff2 +0 -0
  211. package/app/public/vendor/fonts/cat-ranchers-400-ext.woff2 +0 -0
  212. package/app/public/vendor/fonts/cat-ranchers-400.woff2 +0 -0
  213. package/app/public/vendor/fonts/cat-ranga-400-ext.woff2 +0 -0
  214. package/app/public/vendor/fonts/cat-ranga-400.woff2 +0 -0
  215. package/app/public/vendor/fonts/cat-ranga-700-ext.woff2 +0 -0
  216. package/app/public/vendor/fonts/cat-ranga-700.woff2 +0 -0
  217. package/app/public/vendor/fonts/cat-risque-400-ext.woff2 +0 -0
  218. package/app/public/vendor/fonts/cat-risque-400.woff2 +0 -0
  219. package/app/public/vendor/fonts/cat-rowdies-400-ext.woff2 +0 -0
  220. package/app/public/vendor/fonts/cat-rowdies-400.woff2 +0 -0
  221. package/app/public/vendor/fonts/cat-rowdies-700-ext.woff2 +0 -0
  222. package/app/public/vendor/fonts/cat-rowdies-700.woff2 +0 -0
  223. package/app/public/vendor/fonts/cat-ruge_boogie-400-ext.woff2 +0 -0
  224. package/app/public/vendor/fonts/cat-ruge_boogie-400.woff2 +0 -0
  225. package/app/public/vendor/fonts/cat-ruslan_display-400-ext.woff2 +0 -0
  226. package/app/public/vendor/fonts/cat-ruslan_display-400.woff2 +0 -0
  227. package/app/public/vendor/fonts/cat-sacramento-400-ext.woff2 +0 -0
  228. package/app/public/vendor/fonts/cat-sacramento-400.woff2 +0 -0
  229. package/app/public/vendor/fonts/cat-sancreek-400-ext.woff2 +0 -0
  230. package/app/public/vendor/fonts/cat-sancreek-400.woff2 +0 -0
  231. package/app/public/vendor/fonts/cat-sankofa_display-400-ext.woff2 +0 -0
  232. package/app/public/vendor/fonts/cat-sankofa_display-400.woff2 +0 -0
  233. package/app/public/vendor/fonts/cat-sedgwick_ave_display-400-ext.woff2 +0 -0
  234. package/app/public/vendor/fonts/cat-sedgwick_ave_display-400.woff2 +0 -0
  235. package/app/public/vendor/fonts/cat-shantell_sans-400-ext.woff2 +0 -0
  236. package/app/public/vendor/fonts/cat-shantell_sans-400.woff2 +0 -0
  237. package/app/public/vendor/fonts/cat-smokum-400-ext.woff2 +0 -0
  238. package/app/public/vendor/fonts/cat-smokum-400.woff2 +0 -0
  239. package/app/public/vendor/fonts/cat-solitreo-400-ext.woff2 +0 -0
  240. package/app/public/vendor/fonts/cat-solitreo-400.woff2 +0 -0
  241. package/app/public/vendor/fonts/cat-spicy_rice-400-ext.woff2 +0 -0
  242. package/app/public/vendor/fonts/cat-spicy_rice-400.woff2 +0 -0
  243. package/app/public/vendor/fonts/cat-sriracha-400-ext.woff2 +0 -0
  244. package/app/public/vendor/fonts/cat-sriracha-400.woff2 +0 -0
  245. package/app/public/vendor/fonts/cat-srisakdi-400-ext.woff2 +0 -0
  246. package/app/public/vendor/fonts/cat-srisakdi-400.woff2 +0 -0
  247. package/app/public/vendor/fonts/cat-srisakdi-700-ext.woff2 +0 -0
  248. package/app/public/vendor/fonts/cat-srisakdi-700.woff2 +0 -0
  249. package/app/public/vendor/fonts/cat-stix_two_math-400-0.woff2 +0 -0
  250. package/app/public/vendor/fonts/cat-tillana-400-ext.woff2 +0 -0
  251. package/app/public/vendor/fonts/cat-tillana-400.woff2 +0 -0
  252. package/app/public/vendor/fonts/cat-tillana-700-ext.woff2 +0 -0
  253. package/app/public/vendor/fonts/cat-tillana-700.woff2 +0 -0
  254. package/app/public/vendor/fonts/cat-titan_one-400-ext.woff2 +0 -0
  255. package/app/public/vendor/fonts/cat-titan_one-400.woff2 +0 -0
  256. package/app/public/vendor/fonts/cat-yatra_one-400-ext.woff2 +0 -0
  257. package/app/public/vendor/fonts/cat-yatra_one-400.woff2 +0 -0
  258. package/app/public/vendor/fonts/cat-yomogi-400-ext.woff2 +0 -0
  259. package/app/public/vendor/fonts/cat-yomogi-400.woff2 +0 -0
  260. package/app/public/vendor/fonts/dmserifdisplay-400-ext.woff2 +0 -0
  261. package/app/public/vendor/fonts/dmserifdisplay-400.woff2 +0 -0
  262. package/app/public/vendor/fonts/domine-var-ext.woff2 +0 -0
  263. package/app/public/vendor/fonts/domine-var.woff2 +0 -0
  264. package/app/public/vendor/fonts/fraunces-var-ext.woff2 +0 -0
  265. package/app/public/vendor/fonts/fraunces-var.woff2 +0 -0
  266. package/app/public/vendor/fonts/fredoka-var-ext.woff2 +0 -0
  267. package/app/public/vendor/fonts/fredoka-var.woff2 +0 -0
  268. package/app/public/vendor/fonts/jakarta-var-ext.woff2 +0 -0
  269. package/app/public/vendor/fonts/jakarta-var.woff2 +0 -0
  270. package/app/public/vendor/fonts/lora-var-ext.woff2 +0 -0
  271. package/app/public/vendor/fonts/lora-var.woff2 +0 -0
  272. package/app/public/vendor/fonts/merriweather-var-ext.woff2 +0 -0
  273. package/app/public/vendor/fonts/merriweather-var.woff2 +0 -0
  274. package/app/public/vendor/fonts/notosans-var-ext.woff2 +0 -0
  275. package/app/public/vendor/fonts/notosans-var.woff2 +0 -0
  276. package/app/public/vendor/fonts/nunito-var-ext.woff2 +0 -0
  277. package/app/public/vendor/fonts/nunito-var.woff2 +0 -0
  278. package/app/public/vendor/fonts/playfair-var-ext.woff2 +0 -0
  279. package/app/public/vendor/fonts/playfair-var.woff2 +0 -0
  280. package/app/public/vendor/fonts/playwrite-cu.woff2 +0 -0
  281. package/app/public/vendor/fonts/playwrite-es-deco.woff2 +0 -0
  282. package/app/public/vendor/fonts/playwrite-es.woff2 +0 -0
  283. package/app/public/vendor/fonts/playwrite-gb-j.woff2 +0 -0
  284. package/app/public/vendor/fonts/playwrite-gb-s.woff2 +0 -0
  285. package/app/public/vendor/fonts/playwrite-pe.woff2 +0 -0
  286. package/app/public/vendor/fonts/playwrite-pt.woff2 +0 -0
  287. package/app/public/vendor/fonts/quicksand-var-ext.woff2 +0 -0
  288. package/app/public/vendor/fonts/quicksand-var.woff2 +0 -0
  289. package/app/public/vendor/fonts/robotoflex-var-ext.woff2 +0 -0
  290. package/app/public/vendor/fonts/robotoflex-var.woff2 +0 -0
  291. package/app/public/vendor/fonts/sora-var-ext.woff2 +0 -0
  292. package/app/public/vendor/fonts/sora-var.woff2 +0 -0
  293. package/app/public/vendor/fonts/spacegrotesk-var-ext.woff2 +0 -0
  294. package/app/public/vendor/fonts/spacegrotesk-var.woff2 +0 -0
  295. package/app/public/vendor/fonts/spectral-400-ext.woff2 +0 -0
  296. package/app/public/vendor/fonts/spectral-400.woff2 +0 -0
  297. package/app/public/vendor/fonts/spectral-700-ext.woff2 +0 -0
  298. package/app/public/vendor/fonts/spectral-700.woff2 +0 -0
  299. package/app/public/vendor/fonts/teachers-var-ext.woff2 +0 -0
  300. package/app/public/vendor/fonts/teachers-var.woff2 +0 -0
  301. package/app/public/vendor/fonts/ubuntu-400-ext.woff2 +0 -0
  302. package/app/public/vendor/fonts/ubuntu-400.woff2 +0 -0
  303. package/app/public/vendor/fonts/ubuntu-700-ext.woff2 +0 -0
  304. package/app/public/vendor/fonts/ubuntu-700.woff2 +0 -0
  305. package/app/public/vendor/fonts.css +1167 -51
  306. package/dist-pkg/boot/create-game.d.ts +444 -215
  307. package/dist-pkg/boot/create-game.js +3530 -549
  308. package/dist-pkg/core/a11y-sr.d.ts +25 -3
  309. package/dist-pkg/core/a11y-sr.js +40 -19
  310. package/dist-pkg/core/accessible-label.d.ts +17 -0
  311. package/dist-pkg/core/accessible-label.js +37 -0
  312. package/dist-pkg/core/accommodation-subjects.d.ts +26 -0
  313. package/dist-pkg/core/accommodation-subjects.js +32 -0
  314. package/dist-pkg/core/accommodations.d.ts +107 -0
  315. package/dist-pkg/core/accommodations.js +178 -0
  316. package/dist-pkg/core/actions.d.ts +75 -51
  317. package/dist-pkg/core/actions.js +98 -69
  318. package/dist-pkg/core/calm-mode.d.ts +31 -0
  319. package/dist-pkg/core/calm-mode.js +47 -0
  320. package/dist-pkg/core/camera-cycle.d.ts +15 -0
  321. package/dist-pkg/core/camera-cycle.js +7 -0
  322. package/dist-pkg/core/caption-duration.d.ts +15 -0
  323. package/dist-pkg/core/caption-duration.js +19 -0
  324. package/dist-pkg/core/cartridge-problems.d.ts +34 -0
  325. package/dist-pkg/core/cartridge-problems.js +53 -0
  326. package/dist-pkg/core/constants.d.ts +0 -54
  327. package/dist-pkg/core/constants.js +9 -76
  328. package/dist-pkg/core/contract.d.ts +143 -163
  329. package/dist-pkg/core/contract.js +223 -207
  330. package/dist-pkg/core/dom-query.d.ts +4 -4
  331. package/dist-pkg/core/dom-query.js +12 -20
  332. package/dist-pkg/core/entity.d.ts +62 -77
  333. package/dist-pkg/core/entity.js +18 -32
  334. package/dist-pkg/core/escape-html.d.ts +9 -10
  335. package/dist-pkg/core/escape-html.js +16 -20
  336. package/dist-pkg/core/flash-threshold.d.ts +17 -0
  337. package/dist-pkg/core/flash-threshold.js +113 -0
  338. package/dist-pkg/core/game-speed.d.ts +11 -0
  339. package/dist-pkg/core/game-speed.js +16 -0
  340. package/dist-pkg/core/genres.d.ts +20 -0
  341. package/dist-pkg/core/genres.js +44 -0
  342. package/dist-pkg/core/i18n.d.ts +87 -41
  343. package/dist-pkg/core/i18n.js +206 -153
  344. package/dist-pkg/core/loop.d.ts +17 -6
  345. package/dist-pkg/core/loop.js +26 -26
  346. package/dist-pkg/core/pause-icon-catalogue.d.ts +15 -0
  347. package/dist-pkg/core/pause-icon-catalogue.js +54 -0
  348. package/dist-pkg/core/ring.d.ts +17 -0
  349. package/dist-pkg/core/ring.js +29 -0
  350. package/dist-pkg/core/rng.d.ts +7 -12
  351. package/dist-pkg/core/rng.js +23 -53
  352. package/dist-pkg/core/route.d.ts +26 -26
  353. package/dist-pkg/core/route.js +126 -121
  354. package/dist-pkg/core/scenes.d.ts +37 -40
  355. package/dist-pkg/core/scenes.js +41 -43
  356. package/dist-pkg/core/screens.d.ts +4 -4
  357. package/dist-pkg/core/screens.js +11 -16
  358. package/dist-pkg/core/setting-defaults.d.ts +61 -0
  359. package/dist-pkg/core/setting-defaults.js +66 -0
  360. package/dist-pkg/core/speech-rate.d.ts +29 -0
  361. package/dist-pkg/core/speech-rate.js +47 -0
  362. package/dist-pkg/core/state.d.ts +118 -118
  363. package/dist-pkg/core/state.js +322 -297
  364. package/dist-pkg/core/visual-cycles.d.ts +30 -0
  365. package/dist-pkg/core/visual-cycles.js +52 -0
  366. package/dist-pkg/core/visual-state.d.ts +13 -0
  367. package/dist-pkg/core/visual-state.js +6 -0
  368. package/dist-pkg/educational/activities-registry.d.ts +1 -1
  369. package/dist-pkg/educational/activities-registry.js +9 -8
  370. package/dist-pkg/educational/adaptive-engine.d.ts +11 -11
  371. package/dist-pkg/educational/adaptive-engine.js +17 -17
  372. package/dist-pkg/educational/segment-bar.d.ts +12 -12
  373. package/dist-pkg/educational/segment-bar.js +9 -9
  374. package/dist-pkg/i18n/en.js +321 -215
  375. package/dist-pkg/i18n/es.js +320 -215
  376. package/dist-pkg/i18n/pt.js +342 -215
  377. package/dist-pkg/input/default-bindings.d.ts +37 -38
  378. package/dist-pkg/input/default-bindings.js +127 -144
  379. package/dist-pkg/input/devices.d.ts +7 -7
  380. package/dist-pkg/input/devices.js +17 -16
  381. package/dist-pkg/input/edges.d.ts +23 -32
  382. package/dist-pkg/input/edges.js +23 -28
  383. package/dist-pkg/input/empathy-filter.d.ts +22 -0
  384. package/dist-pkg/input/empathy-filter.js +34 -0
  385. package/dist-pkg/input/face-map.d.ts +96 -0
  386. package/dist-pkg/input/face-map.js +255 -0
  387. package/dist-pkg/input/face-signals.d.ts +40 -0
  388. package/dist-pkg/input/face-signals.js +48 -0
  389. package/dist-pkg/input/gamepad.d.ts +164 -145
  390. package/dist-pkg/input/gamepad.js +351 -559
  391. package/dist-pkg/input/gaze-cycle.d.ts +64 -0
  392. package/dist-pkg/input/gaze-cycle.js +133 -0
  393. package/dist-pkg/input/gaze-relative.d.ts +68 -0
  394. package/dist-pkg/input/gaze-relative.js +142 -0
  395. package/dist-pkg/input/hand-map.d.ts +28 -0
  396. package/dist-pkg/input/hand-map.js +142 -0
  397. package/dist-pkg/input/input-cooldown.d.ts +17 -0
  398. package/dist-pkg/input/input-cooldown.js +39 -0
  399. package/dist-pkg/input/keyboard-runtime.d.ts +18 -20
  400. package/dist-pkg/input/keyboard-runtime.js +23 -27
  401. package/dist-pkg/input/keyboard.d.ts +68 -62
  402. package/dist-pkg/input/keyboard.js +83 -105
  403. package/dist-pkg/input/keydown.d.ts +124 -132
  404. package/dist-pkg/input/keydown.js +283 -288
  405. package/dist-pkg/input/latch-edge.d.ts +25 -21
  406. package/dist-pkg/input/latch-edge.js +33 -35
  407. package/dist-pkg/input/latch-scope.d.ts +43 -47
  408. package/dist-pkg/input/latch-scope.js +66 -75
  409. package/dist-pkg/input/latch-store.d.ts +28 -29
  410. package/dist-pkg/input/latch-store.js +44 -47
  411. package/dist-pkg/input/latch-sync.d.ts +28 -29
  412. package/dist-pkg/input/latch-sync.js +25 -25
  413. package/dist-pkg/input/latch.d.ts +14 -17
  414. package/dist-pkg/input/latch.js +29 -37
  415. package/dist-pkg/input/pad-defaults.d.ts +13 -16
  416. package/dist-pkg/input/pad-defaults.js +21 -28
  417. package/dist-pkg/input/pad-reading.d.ts +76 -0
  418. package/dist-pkg/input/pad-reading.js +164 -0
  419. package/dist-pkg/input/pad-wizard.d.ts +96 -0
  420. package/dist-pkg/input/pad-wizard.js +205 -0
  421. package/dist-pkg/input/pointer-space.d.ts +19 -20
  422. package/dist-pkg/input/pointer-space.js +27 -32
  423. package/dist-pkg/input/pointer.d.ts +39 -40
  424. package/dist-pkg/input/pointer.js +42 -45
  425. package/dist-pkg/input/state.d.ts +101 -89
  426. package/dist-pkg/input/state.js +54 -134
  427. package/dist-pkg/input/switch-scan.d.ts +42 -0
  428. package/dist-pkg/input/switch-scan.js +54 -0
  429. package/dist-pkg/input/synthetic-source.d.ts +42 -0
  430. package/dist-pkg/input/synthetic-source.js +57 -0
  431. package/dist-pkg/input/touch-bindings.d.ts +127 -113
  432. package/dist-pkg/input/touch-bindings.js +158 -173
  433. package/dist-pkg/input/touch.d.ts +130 -70
  434. package/dist-pkg/input/touch.js +321 -123
  435. package/dist-pkg/input/transport-in-use.d.ts +107 -0
  436. package/dist-pkg/input/transport-in-use.js +130 -0
  437. package/dist-pkg/input/transports.d.ts +96 -114
  438. package/dist-pkg/input/transports.js +104 -106
  439. package/dist-pkg/input/virtual-controller.d.ts +46 -0
  440. package/dist-pkg/input/virtual-controller.js +41 -0
  441. package/dist-pkg/input/vocabulary-migration.d.ts +50 -49
  442. package/dist-pkg/input/vocabulary-migration.js +80 -79
  443. package/dist-pkg/input/voice-map.d.ts +43 -0
  444. package/dist-pkg/input/voice-map.js +125 -0
  445. package/dist-pkg/platform/audio-ambient.d.ts +13 -1
  446. package/dist-pkg/platform/audio-ambient.js +11 -13
  447. package/dist-pkg/platform/audio-earcons.d.ts +20 -19
  448. package/dist-pkg/platform/audio-earcons.js +51 -42
  449. package/dist-pkg/platform/audio-jingles.js +9 -10
  450. package/dist-pkg/platform/audio-mixer.d.ts +14 -2
  451. package/dist-pkg/platform/audio-mixer.js +36 -39
  452. package/dist-pkg/platform/audio-sonar.d.ts +96 -113
  453. package/dist-pkg/platform/audio-sonar.js +245 -238
  454. package/dist-pkg/platform/audio.d.ts +53 -19
  455. package/dist-pkg/platform/audio.js +161 -169
  456. package/dist-pkg/platform/flash-sampler.d.ts +19 -0
  457. package/dist-pkg/platform/flash-sampler.js +79 -0
  458. package/dist-pkg/platform/guide-intensity.d.ts +26 -27
  459. package/dist-pkg/platform/guide-intensity.js +50 -52
  460. package/dist-pkg/platform/heavy-catalogue.d.ts +31 -0
  461. package/dist-pkg/platform/heavy-catalogue.js +223 -0
  462. package/dist-pkg/platform/heavy-mirror.d.ts +26 -0
  463. package/dist-pkg/platform/heavy-mirror.js +76 -0
  464. package/dist-pkg/platform/heavy.d.ts +104 -0
  465. package/dist-pkg/platform/heavy.js +169 -0
  466. package/dist-pkg/platform/interruptible-speech.d.ts +16 -16
  467. package/dist-pkg/platform/interruptible-speech.js +50 -51
  468. package/dist-pkg/platform/kokoro-port.d.ts +27 -0
  469. package/dist-pkg/platform/kokoro-port.js +50 -0
  470. package/dist-pkg/platform/kokoro-runtime.d.ts +20 -0
  471. package/dist-pkg/platform/kokoro-runtime.js +52 -0
  472. package/dist-pkg/platform/kokoro.d.ts +70 -0
  473. package/dist-pkg/platform/kokoro.js +133 -0
  474. package/dist-pkg/platform/listener-scope.d.ts +12 -0
  475. package/dist-pkg/platform/listener-scope.js +81 -0
  476. package/dist-pkg/platform/locale-host.d.ts +10 -0
  477. package/dist-pkg/platform/locale-host.js +34 -0
  478. package/dist-pkg/platform/microphone.d.ts +51 -0
  479. package/dist-pkg/platform/microphone.js +118 -0
  480. package/dist-pkg/platform/onnx-runtime.d.ts +34 -0
  481. package/dist-pkg/platform/onnx-runtime.js +42 -0
  482. package/dist-pkg/platform/reading-in-worker.d.ts +40 -0
  483. package/dist-pkg/platform/reading-in-worker.js +87 -0
  484. package/dist-pkg/platform/reading-model.d.ts +73 -0
  485. package/dist-pkg/platform/reading-model.js +247 -0
  486. package/dist-pkg/platform/reading-runtime.d.ts +24 -0
  487. package/dist-pkg/platform/reading-runtime.js +159 -0
  488. package/dist-pkg/platform/reading-worker.d.ts +41 -0
  489. package/dist-pkg/platform/reading-worker.js +58 -0
  490. package/dist-pkg/platform/reading.d.ts +69 -0
  491. package/dist-pkg/platform/reading.js +128 -0
  492. package/dist-pkg/platform/speech-recognition.d.ts +51 -0
  493. package/dist-pkg/platform/speech-recognition.js +84 -0
  494. package/dist-pkg/platform/speech.d.ts +14 -1
  495. package/dist-pkg/platform/speech.js +13 -13
  496. package/dist-pkg/platform/storage-keys.d.ts +96 -0
  497. package/dist-pkg/platform/storage-keys.js +106 -0
  498. package/dist-pkg/platform/storage.d.ts +46 -111
  499. package/dist-pkg/platform/storage.js +104 -169
  500. package/dist-pkg/platform/tts.d.ts +51 -38
  501. package/dist-pkg/platform/tts.js +217 -119
  502. package/dist-pkg/platform/vision-loop.d.ts +23 -0
  503. package/dist-pkg/platform/vision-loop.js +56 -0
  504. package/dist-pkg/platform/vision.d.ts +132 -0
  505. package/dist-pkg/platform/vision.js +117 -0
  506. package/dist-pkg/platform/voice-listener.d.ts +36 -0
  507. package/dist-pkg/platform/voice-listener.js +88 -0
  508. package/dist-pkg/platform/voice-plan.d.ts +6 -102
  509. package/dist-pkg/platform/voice-plan.js +9 -151
  510. package/dist-pkg/platform/vosk-runtime.d.ts +71 -0
  511. package/dist-pkg/platform/vosk-runtime.js +93 -0
  512. package/dist-pkg/render/canvas.d.ts +13 -7
  513. package/dist-pkg/render/canvas.js +13 -13
  514. package/dist-pkg/render/crt.d.ts +46 -12
  515. package/dist-pkg/render/crt.js +82 -80
  516. package/dist-pkg/render/cvd-matrices.d.ts +18 -18
  517. package/dist-pkg/render/cvd-matrices.js +33 -38
  518. package/dist-pkg/render/hc-role-data.d.ts +7 -7
  519. package/dist-pkg/render/hc-role-data.js +14 -14
  520. package/dist-pkg/render/high-contrast.d.ts +66 -54
  521. package/dist-pkg/render/high-contrast.js +221 -216
  522. package/dist-pkg/render/low-vision-drawing.d.ts +5 -0
  523. package/dist-pkg/render/low-vision-drawing.js +46 -0
  524. package/dist-pkg/render/lq-filter.d.ts +32 -20
  525. package/dist-pkg/render/lq-filter.js +59 -65
  526. package/dist-pkg/render/port.d.ts +86 -88
  527. package/dist-pkg/render/port.js +17 -23
  528. package/dist-pkg/render/screen-pipeline.d.ts +42 -42
  529. package/dist-pkg/render/screen-pipeline.js +80 -87
  530. package/dist-pkg/render/sprite-fx.d.ts +2 -1
  531. package/dist-pkg/render/sprite-fx.js +16 -14
  532. package/dist-pkg/render/viewports.d.ts +20 -15
  533. package/dist-pkg/render/viewports.js +83 -114
  534. package/dist-pkg/render/viz-axes-labels.d.ts +47 -0
  535. package/dist-pkg/render/viz-axes-labels.js +88 -0
  536. package/dist-pkg/render/viz-axes.d.ts +80 -86
  537. package/dist-pkg/render/viz-axes.js +115 -116
  538. package/dist-pkg/render/viz-modes.d.ts +28 -29
  539. package/dist-pkg/render/viz-modes.js +40 -45
  540. package/dist-pkg/render/viz-refusal.d.ts +32 -0
  541. package/dist-pkg/render/viz-refusal.js +57 -0
  542. package/dist-pkg/render/viz-setters.d.ts +104 -66
  543. package/dist-pkg/render/viz-setters.js +182 -191
  544. package/dist-pkg/ui/aac-sets.d.ts +40 -0
  545. package/dist-pkg/ui/aac-sets.js +49 -0
  546. package/dist-pkg/ui/audio-choices.d.ts +63 -0
  547. package/dist-pkg/ui/audio-choices.js +94 -0
  548. package/dist-pkg/ui/audio-rows-that-apply.d.ts +12 -0
  549. package/dist-pkg/ui/audio-rows-that-apply.js +43 -0
  550. package/dist-pkg/ui/camera-control.d.ts +6 -0
  551. package/dist-pkg/ui/camera-control.js +14 -0
  552. package/dist-pkg/ui/changed-mark.d.ts +9 -8
  553. package/dist-pkg/ui/changed-mark.js +22 -53
  554. package/dist-pkg/ui/control-choices.d.ts +37 -0
  555. package/dist-pkg/ui/control-choices.js +84 -0
  556. package/dist-pkg/ui/debug-panel.d.ts +59 -43
  557. package/dist-pkg/ui/debug-panel.js +68 -69
  558. package/dist-pkg/ui/dom.d.ts +10 -14
  559. package/dist-pkg/ui/dom.js +10 -47
  560. package/dist-pkg/ui/drawing-problems.d.ts +25 -0
  561. package/dist-pkg/ui/drawing-problems.js +93 -0
  562. package/dist-pkg/ui/eye-control.d.ts +36 -0
  563. package/dist-pkg/ui/eye-control.js +178 -0
  564. package/dist-pkg/ui/face-control.d.ts +25 -0
  565. package/dist-pkg/ui/face-control.js +183 -0
  566. package/dist-pkg/ui/focus-trap.d.ts +39 -49
  567. package/dist-pkg/ui/focus-trap.js +56 -65
  568. package/dist-pkg/ui/fonts.d.ts +118 -63
  569. package/dist-pkg/ui/fonts.js +267 -111
  570. package/dist-pkg/ui/game-options.d.ts +42 -0
  571. package/dist-pkg/ui/game-options.js +138 -0
  572. package/dist-pkg/ui/gaze-overlay.d.ts +64 -0
  573. package/dist-pkg/ui/gaze-overlay.js +165 -0
  574. package/dist-pkg/ui/hand-control.d.ts +25 -0
  575. package/dist-pkg/ui/hand-control.js +143 -0
  576. package/dist-pkg/ui/help-panel.d.ts +76 -0
  577. package/dist-pkg/ui/help-panel.js +232 -0
  578. package/dist-pkg/ui/hud-bands.d.ts +45 -0
  579. package/dist-pkg/ui/hud-bands.js +108 -0
  580. package/dist-pkg/ui/hud.d.ts +73 -70
  581. package/dist-pkg/ui/hud.js +94 -98
  582. package/dist-pkg/ui/item-announcement.d.ts +13 -12
  583. package/dist-pkg/ui/item-announcement.js +25 -26
  584. package/dist-pkg/ui/latch-refusal.d.ts +22 -21
  585. package/dist-pkg/ui/latch-refusal.js +37 -37
  586. package/dist-pkg/ui/layout.d.ts +103 -28
  587. package/dist-pkg/ui/layout.js +114 -99
  588. package/dist-pkg/ui/locale-flags.d.ts +16 -0
  589. package/dist-pkg/ui/locale-flags.js +30 -0
  590. package/dist-pkg/ui/loop-crash.d.ts +17 -17
  591. package/dist-pkg/ui/loop-crash.js +33 -62
  592. package/dist-pkg/ui/menu-intent.d.ts +32 -0
  593. package/dist-pkg/ui/menu-intent.js +54 -0
  594. package/dist-pkg/ui/menu-items.d.ts +10 -0
  595. package/dist-pkg/ui/menu-items.js +22 -0
  596. package/dist-pkg/ui/menu-nav.d.ts +60 -90
  597. package/dist-pkg/ui/menu-nav.js +301 -274
  598. package/dist-pkg/ui/mobility-choices.d.ts +26 -0
  599. package/dist-pkg/ui/mobility-choices.js +41 -0
  600. package/dist-pkg/ui/motion-choices.d.ts +31 -0
  601. package/dist-pkg/ui/motion-choices.js +68 -0
  602. package/dist-pkg/ui/motion-scene.d.ts +24 -19
  603. package/dist-pkg/ui/motion-scene.js +29 -61
  604. package/dist-pkg/ui/mount-panel.d.ts +81 -0
  605. package/dist-pkg/ui/mount-panel.js +61 -0
  606. package/dist-pkg/ui/panel-shell.d.ts +65 -31
  607. package/dist-pkg/ui/panel-shell.js +92 -66
  608. package/dist-pkg/ui/panel-widgets.d.ts +116 -0
  609. package/dist-pkg/ui/panel-widgets.js +208 -0
  610. package/dist-pkg/ui/pause-buttons.d.ts +35 -0
  611. package/dist-pkg/ui/pause-buttons.js +66 -0
  612. package/dist-pkg/ui/pause-icons.d.ts +249 -351
  613. package/dist-pkg/ui/pause-icons.js +614 -605
  614. package/dist-pkg/ui/pause-markup.d.ts +86 -0
  615. package/dist-pkg/ui/pause-markup.js +125 -0
  616. package/dist-pkg/ui/reach-notice.d.ts +22 -23
  617. package/dist-pkg/ui/reach-notice.js +55 -58
  618. package/dist-pkg/ui/scan-overlay.d.ts +23 -0
  619. package/dist-pkg/ui/scan-overlay.js +42 -0
  620. package/dist-pkg/ui/settings-aac.d.ts +68 -0
  621. package/dist-pkg/ui/settings-aac.js +189 -0
  622. package/dist-pkg/ui/settings-audio.d.ts +91 -113
  623. package/dist-pkg/ui/settings-audio.js +407 -405
  624. package/dist-pkg/ui/settings-controls.d.ts +53 -92
  625. package/dist-pkg/ui/settings-controls.js +143 -215
  626. package/dist-pkg/ui/settings-empathy.d.ts +39 -30
  627. package/dist-pkg/ui/settings-empathy.js +53 -60
  628. package/dist-pkg/ui/settings-mobility.d.ts +195 -0
  629. package/dist-pkg/ui/settings-mobility.js +374 -0
  630. package/dist-pkg/ui/settings-motion.d.ts +91 -70
  631. package/dist-pkg/ui/settings-motion.js +287 -198
  632. package/dist-pkg/ui/settings-panel.d.ts +48 -45
  633. package/dist-pkg/ui/settings-panel.js +164 -93
  634. package/dist-pkg/ui/settings-typo.d.ts +28 -76
  635. package/dist-pkg/ui/settings-typo.js +145 -125
  636. package/dist-pkg/ui/settings-visual.d.ts +29 -87
  637. package/dist-pkg/ui/settings-visual.js +240 -193
  638. package/dist-pkg/ui/shell.d.ts +113 -134
  639. package/dist-pkg/ui/shell.js +143 -216
  640. package/dist-pkg/ui/simulation-list.d.ts +27 -0
  641. package/dist-pkg/ui/simulation-list.js +76 -0
  642. package/dist-pkg/ui/simulation-over-the-world.d.ts +23 -0
  643. package/dist-pkg/ui/simulation-over-the-world.js +131 -0
  644. package/dist-pkg/ui/simulation-refusal.d.ts +5 -31
  645. package/dist-pkg/ui/simulation-refusal.js +8 -56
  646. package/dist-pkg/ui/switchable-control.d.ts +11 -0
  647. package/dist-pkg/ui/switchable-control.js +19 -0
  648. package/dist-pkg/ui/title.d.ts +4 -4
  649. package/dist-pkg/ui/title.js +11 -13
  650. package/dist-pkg/ui/top-band.d.ts +24 -0
  651. package/dist-pkg/ui/top-band.js +100 -0
  652. package/dist-pkg/ui/typo-choices.d.ts +71 -0
  653. package/dist-pkg/ui/typo-choices.js +84 -0
  654. package/dist-pkg/ui/visual-axes-panel.d.ts +11 -47
  655. package/dist-pkg/ui/visual-axes-panel.js +12 -94
  656. package/dist-pkg/ui/visual-choices.d.ts +77 -0
  657. package/dist-pkg/ui/visual-choices.js +107 -0
  658. package/dist-pkg/ui/vlibras.d.ts +33 -10
  659. package/dist-pkg/ui/vlibras.js +58 -81
  660. package/dist-pkg/ui/voice-control.d.ts +46 -0
  661. package/dist-pkg/ui/voice-control.js +143 -0
  662. package/dist-pkg/ui/voice-settings.d.ts +96 -0
  663. package/dist-pkg/ui/voice-settings.js +302 -0
  664. package/dist-pkg/ui/where-the-child-is.d.ts +31 -0
  665. package/dist-pkg/ui/where-the-child-is.js +70 -0
  666. package/docs/CREDITS.md +198 -54
  667. package/docs/LICENSES.md +160 -145
  668. package/package.json +24 -25
  669. package/scripts/heavy-into-the-delivery.mjs +144 -0
  670. package/scripts/licences/Apache-2.0.txt +176 -0
  671. package/scripts/licences/GPL-3.0.txt +674 -0
  672. package/scripts/licences/MIT.txt +17 -0
  673. package/scripts/licences/third-party.mjs +287 -0
  674. package/scripts/licences/vosk-browser.NOTICE.txt +268 -0
  675. package/app/public/vendor/fonts/comicneue-400.woff2 +0 -0
  676. package/app/public/vendor/fonts/comicneue-700.woff2 +0 -0
  677. package/dist-pkg/core/anel.d.ts +0 -17
  678. package/dist-pkg/core/anel.js +0 -31
  679. package/dist-pkg/core/collision.d.ts +0 -21
  680. package/dist-pkg/core/collision.js +0 -62
  681. package/dist-pkg/core/layers.d.ts +0 -63
  682. package/dist-pkg/core/layers.js +0 -95
  683. package/dist-pkg/core/letter-grid.d.ts +0 -55
  684. package/dist-pkg/core/letter-grid.js +0 -116
  685. package/dist-pkg/core/password.d.ts +0 -34
  686. package/dist-pkg/core/password.js +0 -205
  687. package/dist-pkg/core/rotulo-acessivel.d.ts +0 -17
  688. package/dist-pkg/core/rotulo-acessivel.js +0 -41
  689. package/dist-pkg/core/run-state.d.ts +0 -115
  690. package/dist-pkg/core/run-state.js +0 -40
  691. package/dist-pkg/core/tiles.d.ts +0 -12
  692. package/dist-pkg/core/tiles.js +0 -61
  693. package/dist-pkg/core/world.d.ts +0 -4
  694. package/dist-pkg/core/world.js +0 -58
  695. package/dist-pkg/input/origem-sintetica.d.ts +0 -44
  696. package/dist-pkg/input/origem-sintetica.js +0 -60
  697. package/dist-pkg/input/transporte-em-uso.d.ts +0 -101
  698. package/dist-pkg/input/transporte-em-uso.js +0 -130
  699. package/dist-pkg/platform/audio-nav.d.ts +0 -51
  700. package/dist-pkg/platform/audio-nav.js +0 -99
  701. package/dist-pkg/platform/pesados-catalogo.d.ts +0 -14
  702. package/dist-pkg/platform/pesados-catalogo.js +0 -177
  703. package/dist-pkg/platform/pesados.d.ts +0 -31
  704. package/dist-pkg/platform/pesados.js +0 -75
  705. package/dist-pkg/platform/vozes-prontas.d.ts +0 -28
  706. package/dist-pkg/platform/vozes-prontas.js +0 -61
  707. package/dist-pkg/render/camera.d.ts +0 -58
  708. package/dist-pkg/render/camera.js +0 -105
  709. package/dist-pkg/render/cenario-data.d.ts +0 -127
  710. package/dist-pkg/render/cenario-data.js +0 -145
  711. package/dist-pkg/render/city-tex.d.ts +0 -63
  712. package/dist-pkg/render/city-tex.js +0 -228
  713. package/dist-pkg/render/city-tiles.d.ts +0 -11
  714. package/dist-pkg/render/city-tiles.js +0 -140
  715. package/dist-pkg/render/draw.d.ts +0 -149
  716. package/dist-pkg/render/draw.js +0 -231
  717. package/dist-pkg/render/fx.d.ts +0 -63
  718. package/dist-pkg/render/fx.js +0 -123
  719. package/dist-pkg/render/minimap.d.ts +0 -11
  720. package/dist-pkg/render/minimap.js +0 -94
  721. package/dist-pkg/render/parallax.d.ts +0 -85
  722. package/dist-pkg/render/parallax.js +0 -158
  723. package/dist-pkg/render/player-anim.d.ts +0 -71
  724. package/dist-pkg/render/player-anim.js +0 -152
  725. package/dist-pkg/render/recycling-tex.d.ts +0 -48
  726. package/dist-pkg/render/recycling-tex.js +0 -164
  727. package/dist-pkg/render/scene-city.d.ts +0 -77
  728. package/dist-pkg/render/scene-city.js +0 -181
  729. package/dist-pkg/render/scene-parallax.d.ts +0 -72
  730. package/dist-pkg/render/scene-parallax.js +0 -426
  731. package/dist-pkg/render/scene-sky.d.ts +0 -126
  732. package/dist-pkg/render/scene-sky.js +0 -295
  733. package/dist-pkg/render/set-cenario.d.ts +0 -54
  734. package/dist-pkg/render/set-cenario.js +0 -103
  735. package/dist-pkg/render/textures.d.ts +0 -75
  736. package/dist-pkg/render/textures.js +0 -240
  737. package/dist-pkg/render/title-scene.d.ts +0 -46
  738. package/dist-pkg/render/title-scene.js +0 -75
  739. package/dist-pkg/render/weather.d.ts +0 -96
  740. package/dist-pkg/render/weather.js +0 -171
  741. package/dist-pkg/render/wheelchair-sprites.d.ts +0 -32
  742. package/dist-pkg/render/wheelchair-sprites.js +0 -69
  743. package/dist-pkg/render/world-tex.d.ts +0 -18
  744. package/dist-pkg/render/world-tex.js +0 -87
  745. package/dist-pkg/ui/activities-menu.d.ts +0 -258
  746. package/dist-pkg/ui/activities-menu.js +0 -648
  747. package/dist-pkg/ui/caa-sets.d.ts +0 -39
  748. package/dist-pkg/ui/caa-sets.js +0 -65
  749. package/dist-pkg/ui/map-hub.d.ts +0 -56
  750. package/dist-pkg/ui/map-hub.js +0 -138
  751. package/dist-pkg/ui/settings-caa.d.ts +0 -45
  752. package/dist-pkg/ui/settings-caa.js +0 -137
  753. package/dist-pkg/ui/settings-motor.d.ts +0 -175
  754. package/dist-pkg/ui/settings-motor.js +0 -342
  755. package/dist-pkg/ui/webcam.d.ts +0 -7
  756. package/dist-pkg/ui/webcam.js +0 -93
@@ -0,0 +1,133 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ const voiceEntry = (id, locale, espeak, isRecommended = false) => Object.freeze({ locale, engine: 'kokoro', voice: id, espeak, recommended: isRecommended });
3
+ /**
4
+ * Kokoro-82M v1.0's voices for Portuguese, Spanish and English, as the model repository lists them (read 2026-09-14). The model's
5
+ * first letter is the language — `p` Brazilian Portuguese, `e` Spanish, `a` American and `b` British English — and the second the
6
+ * voice's gender. The others (French, Hindi, Italian, Japanese, Chinese) are not the engine's languages.
7
+ */
8
+ export const KOKORO_VOICES = Object.freeze([
9
+ voiceEntry('pf_dora', 'pt-BR', 'pt-br'), voiceEntry('pm_alex', 'pt-BR', 'pt-br'), voiceEntry('pm_santa', 'pt-BR', 'pt-br'),
10
+ voiceEntry('ef_dora', 'es', 'es-419'), voiceEntry('em_alex', 'es', 'es-419'), voiceEntry('em_santa', 'es', 'es-419'),
11
+ voiceEntry('af_heart', 'en-US', 'en-us', true), voiceEntry('af_bella', 'en-US', 'en-us', true),
12
+ ...['af_alloy', 'af_aoede', 'af_jessica', 'af_kore', 'af_nicole', 'af_nova', 'af_river', 'af_sarah', 'af_sky',
13
+ 'am_adam', 'am_echo', 'am_eric', 'am_fenrir', 'am_liam', 'am_michael', 'am_onyx', 'am_puck', 'am_santa'].map((id) => voiceEntry(id, 'en-US', 'en-us')),
14
+ ...['bf_alice', 'bf_emma', 'bf_isabella', 'bf_lily', 'bm_daniel', 'bm_fable', 'bm_george', 'bm_lewis'].map((id) => voiceEntry(id, 'en-GB', 'en-gb')),
15
+ ]);
16
+ /**
17
+ * WHERE KOKORO COMES FROM, in one place (ADR-0114's rule for hosts): the model repository the catalogue fetches at build time; the
18
+ * delivery serves the same paths from its own origin (ADR-0177), and a game's port asks for them there.
19
+ */
20
+ const KOKORO_REPOSITORY = 'https://huggingface.co/onnx-community/Kokoro-82M-v1.0-ONNX/resolve/main';
21
+ export const KOKORO_MODEL_URL = `${KOKORO_REPOSITORY}/onnx/model.onnx`;
22
+ export const URL_DO_TOKENIZADOR_KOKORO = `${KOKORO_REPOSITORY}/tokenizer.json`;
23
+ export const kokoroVoiceUrl = (id) => `${KOKORO_REPOSITORY}/voices/${id}.bin`;
24
+ /**
25
+ * Each voice file's SHA-256 and size, read from the repository's LFS metadata on 2026-09-14 (all 522 240 bytes); `pf_dora` also
26
+ * measured on the downloaded file. The model: 325 532 232 bytes, measured on the file. Checked before anything is kept (#168).
27
+ */
28
+ export const KOKORO_VOICES_SHA256 = Object.freeze({
29
+ pf_dora: '3da7b5b2d91847ebf5646f57631af6ececae3c29a89cd300f06edf9aa6cfe9ee',
30
+ pm_alex: '0175c753f59c54e7fd5a995bedef0c5ff2fb67e0043dd3dcb2ae74ec2acbeb2a',
31
+ pm_santa: '8b012db3185778afe2e45a62cbad69db73021774fe68dda634bcc748a982eede',
32
+ ef_dora: 'f66ec66bd295acb18372e37008533a9a3228483ccd294e7538d5d9294ac9a532',
33
+ em_alex: '27809e9eafdcbcfff90a3016c697568676531de2a2c39cee29c96c7bd6b83e95',
34
+ em_santa: 'ad43b774e1ca24d05c6161297d8aeb770ac3d29bb95daf516727af5f7d543683',
35
+ af_heart: 'd583ccff3cdca2f7fae535cb998ac07e9fcb90f09737b9a41fa2734ec44a8f0b',
36
+ af_bella: 'f69d836209b78eb8c66e75e3cda491e26ea838a3674257e9d4e5703cbaf55c8b',
37
+ af_alloy: 'c4a6b876047fd7fb472edf4ebd63cfac7c3b958a7cae7c106e8f038ca6308c45',
38
+ af_aoede: '4a004c33430762e2461eedb2013fad808ef4ab3121f5300f554476caf58d8361',
39
+ af_jessica: 'a240a5e3c15b43563d6e923bdca8ef5613a23471d9b77653694012435df23bd8',
40
+ af_kore: '9be5221b6a941c04b561959b8ff0b06e809444dcc4ab7e75a7b23606f691819e',
41
+ af_nicole: 'cd2191ab31b914ed7b318416b0e4440fdf392ddad9106a060819aa600a64f59a',
42
+ af_nova: '18778272caa0d0eebaea251c35fd635f038434f9eee5e691d02a174bd328414f',
43
+ af_river: '00a2bcf82b1d86e8f19902ede58c65ccf6c0e43b44b7d74fad54e5d8933c9c30',
44
+ af_sarah: '4409fbc125afabacc615d94db5398d847006a737b0247d6892b7a9a0007a2f0a',
45
+ af_sky: '4435255c9744f3f31659e0d714ab7689bf65d9e77ec1cce060f083912614f0b9',
46
+ am_adam: '162b035ed91cfc48b6046982184c645f72edcdd1b82843347f605d7bf7b15716',
47
+ am_echo: '3968b92c3c4cd1c4416dbded36c13eaa388a90d5788d02a13e4d781f5f8cf3c3',
48
+ am_eric: 'e8b5be17edd1e3636901ce7598baafe2dc8dd8ff707a0c23bf9e461add7e2832',
49
+ am_fenrir: 'c27989f741f7ee34d273a39d8a595cc0837d35f5ced9a29b7cc162614616df43',
50
+ am_liam: '52403be32fd047c6a44517cb0bcd6b134f2a18baa73e70ef41651e0eab921ade',
51
+ am_michael: '1d1f21dd8da39c30705cd4c75d039d265e9bc4a2a93ed09bc9e1b1225eb95ba1',
52
+ am_onyx: 'da5d135b424164916d75a68ffb4c2abce3d7d5ccc82dd1ee6cf447ce286145e6',
53
+ am_puck: 'fcf73c989033e9233e0b98713eca600c8c74dcc1614b37009d5450ff4a2274a0',
54
+ am_santa: '61150cf726ab6c5ed7a99f90a304f91f5a72c00c592e89ec94e5df11c319227a',
55
+ bf_alice: '08afa6ba24da61ea5e8efa139e5aadc938d83f0a6da5a900adaf763ac1da5573',
56
+ bf_emma: '669fe0647f9dd04fcab92f1439a40eeb4c8b4ab1f82e4996fe3d918ce4a63b73',
57
+ bf_isabella: '3754352c4aaa46d17f27654ab7518d65b62ad6163a0f55a5f4330c2da2c4e94f',
58
+ bf_lily: '5e0ee32ebe64a467124976b14e69590746f1c4ce41a12b587a50c862edfea335',
59
+ bm_daniel: '6b3194bbceffb746733cbc22c8f593dd44e401a71d53895a2dca891bc595a1e8',
60
+ bm_fable: 'f889083196807b4adb15e9204252165f503b8d33d3982e681c52443c49d798f1',
61
+ bm_george: 'c4b235a4c1f2cd3b939fed08b899ce9385638b763f7b73a59616c4fc9bd6c9bc',
62
+ bm_lewis: 'b8f671cef828c30e66fdf0b0756a76bba58f6bb3398cbbf27058642acbcedb97',
63
+ });
64
+ export const KOKORO_VOICE_BYTES = 522_240;
65
+ export const KOKORO_MODEL_SHA256 = '8fbea51ea711f2af382e88c833d9e288c6dc82ce5e98421ea61c058ce21a34cb';
66
+ export const KOKORO_MODEL_BYTES = 325_532_232;
67
+ export const SHA256_DO_TOKENIZADOR_KOKORO = '77a02c8e164413299b4b4c403b14f8e0e1c1b727db4d46a09d6327b861060a34';
68
+ export const BYTES_DO_TOKENIZADOR_KOKORO = 3_497;
69
+ /** The model's context: ids between the two pad tokens. A longer sentence is cut here (the caller splits text by sentence). */
70
+ export const MAX_KOKORO_TOKENS = 510;
71
+ /** A voice file is a table of style rows, one per token count, 256 numbers each. */
72
+ export const STYLE_DIMENSION = 256;
73
+ const KOKORO_SAMPLE_RATE = 24_000;
74
+ /** The token ids of a phoneme string: each symbol the vocabulary knows, in order, between two pad tokens (id 0). */
75
+ export function tokenize(phonemes, symbolIds) {
76
+ const ids = [];
77
+ for (const symbol of phonemes) {
78
+ const id = symbolIds[symbol];
79
+ if (id !== undefined)
80
+ ids.push(id);
81
+ if (ids.length === MAX_KOKORO_TOKENS)
82
+ break;
83
+ }
84
+ return [0, ...ids, 0];
85
+ }
86
+ /** The style row a sentence of `tokens` ids (pads excluded) takes from a voice table. */
87
+ export function sentenceStyle(styleTable, tokens) {
88
+ const rowCount = Math.floor(styleTable.length / STYLE_DIMENSION);
89
+ const row = Math.max(0, Math.min(rowCount - 1, tokens));
90
+ return styleTable.slice(row * STYLE_DIMENSION, (row + 1) * STYLE_DIMENSION);
91
+ }
92
+ /**
93
+ * Is a synthesis speech? Measured on 2026-09-14: WebGPU on one AMD GPU gave a waveform whose samples ran to 2×10⁷ — noise a child
94
+ * would hear as a burst. A speech waveform stays within ±1 (a sample or two past it is clipping, not noise) and is not silent.
95
+ */
96
+ export function eFala(waveform) {
97
+ if (waveform.length === 0)
98
+ return false;
99
+ let outside = 0, energy = 0;
100
+ for (let i = 0; i < waveform.length; i++) {
101
+ const s = waveform[i];
102
+ if (!Number.isFinite(s))
103
+ return false;
104
+ if (Math.abs(s) > 1.5)
105
+ outside++;
106
+ energy += s * s;
107
+ }
108
+ return outside === 0 && Math.sqrt(energy / waveform.length) > 1e-3;
109
+ }
110
+ /** A 16-bit mono PCM WAV of a waveform, clipped to ±1 — what the narration's media element plays. */
111
+ export function wavDe(waveform, sampleRate = KOKORO_SAMPLE_RATE) {
112
+ const n = waveform.length;
113
+ const buf = new ArrayBuffer(44 + n * 2);
114
+ const v = new DataView(buf);
115
+ const text = (o, s) => { for (let i = 0; i < s.length; i++)
116
+ v.setUint8(o + i, s.charCodeAt(i)); };
117
+ text(0, 'RIFF');
118
+ v.setUint32(4, 36 + n * 2, true);
119
+ text(8, 'WAVE');
120
+ text(12, 'fmt ');
121
+ v.setUint32(16, 16, true);
122
+ v.setUint16(20, 1, true);
123
+ v.setUint16(22, 1, true);
124
+ v.setUint32(24, sampleRate, true);
125
+ v.setUint32(28, sampleRate * 2, true);
126
+ v.setUint16(32, 2, true);
127
+ v.setUint16(34, 16, true);
128
+ text(36, 'data');
129
+ v.setUint32(40, n * 2, true);
130
+ for (let i = 0; i < n; i++)
131
+ v.setInt16(44 + i * 2, Math.round(Math.max(-1, Math.min(1, waveform[i])) * 32767), true);
132
+ return buf;
133
+ }
@@ -0,0 +1,12 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ /** A window that remembers what was hung on it, so it can be taken down. */
3
+ export interface ListenerScope {
4
+ /** The window to hand around. Identical to the real one, except that its listeners are remembered. */
5
+ readonly win: Window;
6
+ /**
7
+ * Takes off every listener still installed through `win`. Idempotent: what was already removed by hand, or removed itself
8
+ * (`once: true`), is simply no longer there — removing a listener that is not installed is a no-op in the DOM.
9
+ */
10
+ releaseAll(): void;
11
+ }
12
+ export declare function createListenerScope(real: Window): ListenerScope;
@@ -0,0 +1,81 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // platform/listener-scope — A WINDOW THAT CAN BE LET GO.
3
+ //
4
+ // 🔴 WHY THIS EXISTS, AND IT IS A DEFECT THAT WAS MEASURED, NOT A TIDINESS. A root (`boot/create-game`) installs about thirty
5
+ // listeners on the window — the scan interception, START, SELECT, `action4`, the resize, the language change, and the five that
6
+ // `ui/menu-nav.attach()` puts there — AND THERE WAS NO WAY TO TAKE THEM OFF. `unmount()` is not that way and must not become it:
7
+ // it releases the CURRENT CARTRIDGE (ADR-0142), and a root that went deaf on `unmount()` would come back from the next `mount()`
8
+ // with no keyboard at all.
9
+ //
10
+ // 📏 What that cost, measured in the browser with the real engine: a root whose host element was removed from the document KEEPS
11
+ // LISTENING, and every query it makes is document-wide (`getPauseMenu` is `doc.querySelector('#vp-pause-0')`,
12
+ // `topVisibleOverlay` is `doc.querySelectorAll('#game-region .overlay')`) — so the dead root finds THE LIVE ROOT'S pause card and
13
+ // navigates it too. One ArrowDown moved the cursor one item with one root, TWO with a second root, THREE with a third. Nobody
14
+ // gets an error: the child presses down once and the cursor jumps two.
15
+ //
16
+ // ⚠️ AND `stopPropagation()` SAVES NOBODY HERE. Every one of these listeners sits on the SAME node in the SAME phase, and
17
+ // stopping propagation does not stop the siblings on that node — only `stopImmediatePropagation()` would, and using it would mean
18
+ // the first root to be built silences every other one, which is a worse rule than the bug.
19
+ //
20
+ // 🎯 THE SHAPE: one seam instead of a `detach()` in each of the seven modules that listen. The root wraps the window ONCE and
21
+ // hands the WRAPPER down; every listener installed through it — by the root or by any module it builds — is remembered, and
22
+ // `releaseAll()` takes all of them off at once. A module that learns to listen tomorrow is covered without being edited, which a
23
+ // list of `detach()` calls could never promise: the one that is forgotten is the one that leaks.
24
+ //
25
+ // 📌 Everything else about the window is UNCHANGED. `getComputedStyle`, `requestAnimationFrame`, `matchMedia`, `innerWidth` and
26
+ // the rest answer exactly as the real window does — this is a window with a memory, not a window with a smaller API.
27
+ const captureOf = (o) => (typeof o === 'boolean' ? o : !!o?.capture);
28
+ export function createListenerScope(real) {
29
+ const installed = [];
30
+ // A released scope is ended: a listener armed before the end and added after it (a timer) must not reach the window.
31
+ let released = false;
32
+ const listen = (type, listener, options) => {
33
+ if (released)
34
+ return;
35
+ real.addEventListener(type, listener, options);
36
+ installed.push({ type, listener, capture: captureOf(options), options });
37
+ };
38
+ const forget = (type, listener, options) => {
39
+ real.removeEventListener(type, listener, options);
40
+ const capture = captureOf(options);
41
+ const i = installed.findIndex((e) => e.type === type && e.listener === listener && e.capture === capture);
42
+ if (i >= 0)
43
+ installed.splice(i, 1);
44
+ };
45
+ /*
46
+ * ⚠️ WHAT LEAVES THIS TRAP IS ANSWERED BY THE REAL WINDOW, NEVER BY THE PROXY, and it is not style: `innerWidth`,
47
+ * `devicePixelRatio` and `location` are GETTERS on the window, and `getComputedStyle`, `requestAnimationFrame` and
48
+ * `matchMedia` are methods of it — run any of them with the proxy as `this` and the browser answers «Illegal invocation».
49
+ * So the receiver is left at its default (which IS the target) and every function is bound before it goes out.
50
+ *
51
+ * 📌 Both halves are exercised, by mutation: forwarding the proxy as the receiver kills the whole collection at load, and
52
+ * dropping the `bind` kills it at the first `getComputedStyle`.
53
+ *
54
+ * 🔴 BUT A CONSTRUCTOR LEAVES UNBOUND, and this was not foreseen — the suite found it. `bind` returns a function with NO
55
+ * `prototype`, and code that reaches a constructor THROUGH the window reads that prototype: `platform/speech-recognition`
56
+ * asks `'processLocally' in api.prototype` before it will use the browser's recogniser on the device, and bound, that read is
57
+ * `in undefined`. 📏 Measured: `tests/reading-no-createGame.browser.test.js` went red — the child who reads aloud got no
58
+ * microphone at all. A constructor never needs `this` from the property access; a method always does, and `prototype` is the
59
+ * one thing that tells the two apart.
60
+ */
61
+ const win = new Proxy(real, {
62
+ get(target, prop) {
63
+ if (prop === 'addEventListener')
64
+ return listen;
65
+ if (prop === 'removeEventListener')
66
+ return forget;
67
+ const value = Reflect.get(target, prop);
68
+ if (typeof value !== 'function')
69
+ return value;
70
+ return Object.hasOwn(value, 'prototype') ? value : value.bind(target);
71
+ },
72
+ });
73
+ const releaseAll = () => {
74
+ released = true;
75
+ // Drained as it goes: a listener that took another off while being removed would otherwise make this skip one, and a scope
76
+ // released twice must not try to take the same listener off again.
77
+ for (const e of installed.splice(0))
78
+ real.removeEventListener(e.type, e.listener, e.options);
79
+ };
80
+ return { win, releaseAll };
81
+ }
@@ -0,0 +1,10 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ /** The two hooks `core/i18n`'s port takes. A host that has no page passes neither, and the engine runs without one. */
3
+ export interface LocaleHostHooks {
4
+ readonly applied: (locale: string, tag: string) => void;
5
+ readonly preferred: () => string | null;
6
+ }
7
+ /** What a browser can offer `core/i18n`: the page effects of a change, and the language the person's browser is set to. */
8
+ export declare function localeHostHooks(doc: Document, win: Window, applyDom: (root: ParentNode) => void): LocaleHostHooks;
9
+ /** Hangs the i18n object on the window for tests and the preview, which is what `core/i18n` used to do to itself. */
10
+ export declare function exposeI18n(win: Window, i18n: unknown): void;
@@ -0,0 +1,34 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // platform/locale-host — WHAT A PAGE DOES WHEN THE LANGUAGE CHANGES, and what language it would prefer (ADR-0221 step 7g).
3
+ //
4
+ // 🔴 WHY THIS MODULE EXISTS: `core/i18n` used to write `document.documentElement.lang`, dispatch `i18n:change` on `window`,
5
+ // read `navigator.language` and hang itself on `window.__i18n` — four reaches into globals from `core`, which ADR-0173
6
+ // defines as «what the engine IS, without a browser». The health ratchet measured them (step 7d) and the Dev chose to SPLIT
7
+ // rather than to move the module (option B, 2026-09-22): the decisions — which languages exist, which one is kept, what a
8
+ // key resolves to — stay in `core`, and the three things only a page can do live here.
9
+ //
10
+ // 📌 WHY NOT INJECT AND LEAVE IT AT THAT, which was the cheaper option C: the sentence «core is what the engine is, without a
11
+ // browser» would have stayed a rule with an exception nobody reads. 📏 The cost measured before choosing was 8 sites; this
12
+ // module is what those 8 became.
13
+ //
14
+ // ⚠️ It touches `document` and `window` ON PURPOSE and that is why it is `platform/`: this is the layer whose job is the
15
+ // browser. What it must never do is decide anything about language — it receives the locale and the tag already chosen.
16
+ /** What a browser can offer `core/i18n`: the page effects of a change, and the language the person's browser is set to. */
17
+ export function localeHostHooks(doc, win, applyDom) {
18
+ return {
19
+ applied: (locale, tag) => {
20
+ // 📌 THE THREE EFFECTS IN THE ORDER THEY WERE WRITTEN IN `core/i18n`, and the order matters to a screen reader: the
21
+ // document's language is announced before the text it governs changes.
22
+ doc.documentElement.lang = tag;
23
+ applyDom(doc);
24
+ win.dispatchEvent(new CustomEvent('i18n:change', { detail: { locale } }));
25
+ },
26
+ // ⚠️ `navigator` through the window, not the global: a host that hands the engine another window — a second document, an
27
+ // iframe — means that window's language, and reading the global would silently answer for the wrong one.
28
+ preferred: () => win.navigator?.language ?? null,
29
+ };
30
+ }
31
+ /** Hangs the i18n object on the window for tests and the preview, which is what `core/i18n` used to do to itself. */
32
+ export function exposeI18n(win, i18n) {
33
+ win.__i18n = i18n;
34
+ }
@@ -0,0 +1,51 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ /** The part of the Web Audio API this uses — narrow on purpose, so a case can answer it without a browser. */
3
+ export interface AudioNodeLike {
4
+ connect(to: unknown): void;
5
+ disconnect(): void;
6
+ }
7
+ export interface ProcessorNode extends AudioNodeLike {
8
+ onaudioprocess: ((e: {
9
+ inputBuffer: {
10
+ getChannelData(ch: number): Float32Array;
11
+ };
12
+ }) => void) | null;
13
+ }
14
+ export interface AudioContextLike {
15
+ readonly sampleRate: number;
16
+ readonly destination: unknown;
17
+ createMediaStreamSource(stream: unknown): AudioNodeLike;
18
+ createScriptProcessor(bufferSize: number, inputs: number, outputs: number): ProcessorNode;
19
+ close(): Promise<void> | void;
20
+ }
21
+ /** A stream, in the one thing that matters after the reading: letting go of it. */
22
+ export interface StreamLike {
23
+ getTracks(): readonly {
24
+ stop(): void;
25
+ }[];
26
+ }
27
+ /** What the microphone uses of the browser, lent by the root from the host's window (ADR-0232 D4). All REQUIRED. */
28
+ export interface MicrophoneDeps {
29
+ /** The host's `navigator.mediaDevices.getUserMedia`. `undefined` is a device with no microphone: REFUSED and said, not hidden. */
30
+ readonly getUserMedia: ((constraints: {
31
+ audio: boolean;
32
+ }) => Promise<StreamLike>) | undefined;
33
+ /** A new audio context at this rate, from the host's `AudioContext`. */
34
+ readonly createContext: (rate: number) => AudioContextLike;
35
+ /** The host's clock, `performance.now()`: what the silence and the ceiling are measured against. */
36
+ readonly now: () => number;
37
+ }
38
+ export interface RecordOptions {
39
+ /** How long a silence ends the reading. Default 1.5 s: shorter cuts a child who is thinking between words. */
40
+ readonly silenceMs?: number;
41
+ /** The ceiling, so a forgotten microphone does not listen for ever. Default 30 s, the window Whisper reads at once. */
42
+ readonly maxMs?: number;
43
+ }
44
+ export interface Microphone {
45
+ /** Listens, and answers with what was said, at 16 kHz mono. */
46
+ record(options?: RecordOptions): Promise<Float32Array>;
47
+ /** Gives the microphone back now; the recording in hand answers with what it has. */
48
+ stop(): void;
49
+ }
50
+ export declare const READING_RATE = 16000;
51
+ export declare function createMicrophone(d: MicrophoneDeps): Microphone;
@@ -0,0 +1,118 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // platform/microphone — THE CHILD'S VOICE, AND NOTHING ELSE (ADR-0216 §2, ADR-0200 erratum; issues #185, #200).
3
+ //
4
+ // The reading model eats 16 kHz mono samples. This is where they come from, and it is the only place in the engine that opens a
5
+ // microphone for reading: what it captures stays in this page, is handed to the model on this machine, and is dropped when the
6
+ // reading ends (ADR-0200's erratum: on the device or not at all).
7
+ //
8
+ // · IT ENDS WHEN THE CHILD DOES. A reading closes after `silenceMs` of quiet FOLLOWING something that was said, or at the `maxMs`
9
+ // ceiling, whichever comes first. A child who stops mid-sentence is not an error: what was heard so far is the answer.
10
+ // · «QUIET» IS MEASURED, NOT ASSUMED. The first moments are taken as the room she is in, and sound is what stands above it —
11
+ // a fixed threshold would end a whispering child's reading in a noisy classroom, and never end in a quiet one.
12
+ // · ⚠️ IT USES `ScriptProcessorNode`, which is deprecated. The replacement, an AudioWorklet, needs its code fetched as a module,
13
+ // and the engine's CSP has no `blob:` in `script-src` since ADR-0214 — so the modern way would need a new file in every
14
+ // delivery and a decision about serving it. This one works everywhere the project targets today, and it is written here so the
15
+ // next person does not «fix» it without that decision.
16
+ export const READING_RATE = 16_000;
17
+ const SILENCE_MS = 1500;
18
+ const MAX_MS = 30_000;
19
+ const BUFFER = 4096;
20
+ /** How much of the start is taken as «the room»: long enough to be a room, short enough not to swallow a first word. */
21
+ const ROOM_MS = 300;
22
+ /** How far above the room a sound must be to be a voice, and the floor under it for a digitally silent input. */
23
+ const OVER_ROOM = 4;
24
+ const QUIETEST = 0.005;
25
+ const rms = (block) => {
26
+ let sum = 0;
27
+ for (const v of block)
28
+ sum += v * v;
29
+ return Math.sqrt(sum / (block.length || 1));
30
+ };
31
+ /** 16 kHz from whatever the device gave us. Linear, because what follows is a mel filterbank, not a listener. */
32
+ function atReadingRate(samples, from) {
33
+ if (from === READING_RATE)
34
+ return samples;
35
+ const out = new Float32Array(Math.round((samples.length * READING_RATE) / from));
36
+ const step = from / READING_RATE;
37
+ for (let i = 0; i < out.length; i++) {
38
+ const at = i * step;
39
+ const left = Math.floor(at);
40
+ const right = Math.min(left + 1, samples.length - 1);
41
+ out[i] = samples[left] + (samples[right] - samples[left]) * (at - left);
42
+ }
43
+ return out;
44
+ }
45
+ export function createMicrophone(d) {
46
+ const { now } = d;
47
+ let askToStop = null;
48
+ return {
49
+ stop() { askToStop?.(); },
50
+ async record(options = {}) {
51
+ const { getUserMedia } = d;
52
+ if (!getUserMedia)
53
+ throw new Error('microphone: this device offers none, so a reading cannot be heard here');
54
+ const makeContext = d.createContext;
55
+ const silenceMs = options.silenceMs ?? SILENCE_MS;
56
+ const maxMs = options.maxMs ?? MAX_MS;
57
+ const stream = await getUserMedia({ audio: true });
58
+ const context = makeContext(READING_RATE);
59
+ const source = context.createMediaStreamSource(stream);
60
+ const processor = context.createScriptProcessor(BUFFER, 1, 1);
61
+ const blocks = [];
62
+ const startedAt = now();
63
+ let room = 0, roomBlocks = 0, spoke = false, lastSound = startedAt, done = false;
64
+ return await new Promise((resolve) => {
65
+ const finish = () => {
66
+ if (done)
67
+ return;
68
+ done = true;
69
+ askToStop = null;
70
+ processor.onaudioprocess = null;
71
+ processor.disconnect();
72
+ source.disconnect();
73
+ // ⚠️ THE LIGHT GOES OUT. A track left running keeps the microphone open and the browser saying so, which to a child
74
+ // and to whoever is with her means the game is still listening.
75
+ for (const track of stream.getTracks())
76
+ track.stop();
77
+ void context.close();
78
+ let total = 0;
79
+ for (const b of blocks)
80
+ total += b.length;
81
+ const whole = new Float32Array(total);
82
+ let at = 0;
83
+ for (const b of blocks) {
84
+ whole.set(b, at);
85
+ at += b.length;
86
+ }
87
+ resolve(atReadingRate(whole, context.sampleRate));
88
+ };
89
+ askToStop = finish;
90
+ processor.onaudioprocess = (e) => {
91
+ if (done)
92
+ return;
93
+ const block = Float32Array.from(e.inputBuffer.getChannelData(0));
94
+ blocks.push(block);
95
+ const level = rms(block);
96
+ const elapsed = now() - startedAt;
97
+ if (elapsed < ROOM_MS) {
98
+ room = (room * roomBlocks + level) / (roomBlocks + 1);
99
+ roomBlocks++;
100
+ return;
101
+ }
102
+ if (level > Math.max(room * OVER_ROOM, QUIETEST)) {
103
+ spoke = true;
104
+ lastSound = now();
105
+ }
106
+ else if (spoke && now() - lastSound >= silenceMs) {
107
+ finish();
108
+ return;
109
+ }
110
+ if (elapsed >= maxMs)
111
+ finish();
112
+ };
113
+ source.connect(processor);
114
+ processor.connect(context.destination);
115
+ });
116
+ },
117
+ };
118
+ }
@@ -0,0 +1,34 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ export interface OnnxTensor {
3
+ readonly data: unknown;
4
+ readonly dims?: readonly number[];
5
+ }
6
+ /** An open graph: the names it answers with, and one run. */
7
+ export interface OnnxSession {
8
+ readonly inputNames?: readonly string[];
9
+ readonly outputNames: readonly string[];
10
+ run(inputs: {
11
+ readonly [name: string]: OnnxTensor;
12
+ }): Promise<{
13
+ readonly [name: string]: OnnxTensor;
14
+ }>;
15
+ }
16
+ /** The part of onnxruntime the engine uses — small on purpose: what is not named here cannot be depended on. */
17
+ export interface OnnxRuntime {
18
+ readonly Tensor: new (type: 'int64' | 'float32' | 'bool', data: BigInt64Array | Float32Array | Uint8Array, shape: readonly number[]) => OnnxTensor;
19
+ readonly InferenceSession: {
20
+ create(model: Uint8Array, options: {
21
+ executionProviders: readonly string[];
22
+ }): Promise<OnnxSession>;
23
+ };
24
+ }
25
+ /** Where the delivery serves a catalogued file, from the page's own address. */
26
+ export declare const atDelivery: (id: string, base: string) => string;
27
+ export interface OnnxRuntimeDeps {
28
+ /** The page's address, which `heavy/` is resolved against. */
29
+ readonly base: string;
30
+ /** `import()`, injected so a case can hand in a module without a network or a wasm engine. */
31
+ readonly importModule?: (url: string) => Promise<unknown>;
32
+ }
33
+ /** The runtime, from the delivery, with its threads pointed at the delivery too. Refuses by NAME when it is not there. */
34
+ export declare function loadOnnxRuntime(d: OnnxRuntimeDeps): Promise<OnnxRuntime>;
@@ -0,0 +1,42 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // platform/onnx-runtime — THE ONE GRAPH RUNNER, LOADED FROM THE DELIVERY (ADR-0216 and its erratum; issues #185, #200).
3
+ //
4
+ // Two things of the engine run an ONNX graph: the neural voice (`platform/kokoro-runtime`) and the reading
5
+ // (`platform/reading-runtime`). They load the same runtime the same way, and that way has one rule nobody may forget, so it is
6
+ // written here once and not in each of them.
7
+ //
8
+ // ⚠️ THE THREADS LOOK FOR THEIR OWN FILES. Left alone, onnxruntime's workers import their glue and their wasm from the address
9
+ // the library was published at — a CDN — and in a school with no network they find nothing, never answer, and the page waits for
10
+ // a session that never opens. Measured in the quiz demo (#181). So the paths are pointed at `heavy/` on the page's own origin.
11
+ //
12
+ // ⚠️ NOTHING HERE IS IMPORTED FROM npm: `import(url)` of an address on our own origin needs no bundler and puts no megabyte in
13
+ // the chunk of a game that runs no graph at all.
14
+ import { deliveryPath } from './heavy.js';
15
+ import { HEAVY_FILES } from './heavy-catalogue.js';
16
+ const ORT_ESM = 'voz:runtime:onnx';
17
+ const ORT_GLUE = 'voz:runtime:onnx:cola';
18
+ const ORT_WASM = 'voz:runtime:onnx:wasm';
19
+ /**
20
+ * ⚠️ THE ADDRESSES ARE NOT WRITTEN HERE. They live once, in the catalogue, with the sha256 that is checked before anything is
21
+ * written into a delivery (ADR-0114, ADR-0177): a second copy in a module would be a version that drifts from the one the build
22
+ * fetched, and the page would ask for a file the delivery does not have.
23
+ */
24
+ function heavyUrlOf(id) {
25
+ const heavy = HEAVY_FILES.find((p) => p.id === id);
26
+ if (!heavy?.url)
27
+ throw new Error(`the heavy catalogue has no address for ${id}`);
28
+ return heavy.url;
29
+ }
30
+ /** Where the delivery serves a catalogued file, from the page's own address. */
31
+ export const atDelivery = (id, base) => new URL(deliveryPath(heavyUrlOf(id)), base).href;
32
+ /** The runtime, from the delivery, with its threads pointed at the delivery too. Refuses by NAME when it is not there. */
33
+ export async function loadOnnxRuntime(d) {
34
+ const loadModule = d.importModule ?? ((url) => import(/* @vite-ignore */ url));
35
+ const module = await loadModule(atDelivery(ORT_ESM, d.base));
36
+ const ort = (module.default ?? module);
37
+ if (!ort?.InferenceSession)
38
+ throw new Error(`onnxruntime did not load from the delivery (${ORT_ESM})`);
39
+ if (ort.env?.wasm)
40
+ ort.env.wasm.wasmPaths = { mjs: atDelivery(ORT_GLUE, d.base), wasm: atDelivery(ORT_WASM, d.base) };
41
+ return ort;
42
+ }
@@ -0,0 +1,40 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ import type { ReadingAnswer, ReadingRequest } from './reading-worker.js';
3
+ /** The little of a `Worker` this module uses, so a gate can answer the protocol without a thread. */
4
+ export interface ReadingWorkerLike {
5
+ postMessage(message: ReadingRequest, transfer?: readonly Transferable[]): void;
6
+ onmessage: ((event: {
7
+ data: ReadingAnswer;
8
+ }) => void) | null;
9
+ onerror?: ((event: unknown) => void) | null;
10
+ terminate(): void;
11
+ }
12
+ export interface ReadingInWorkerDeps {
13
+ /** The page's address: the worker resolves `heavy/` against it, because IT is who fetches the model. */
14
+ readonly base: string;
15
+ readonly language: string;
16
+ /**
17
+ * Opens the thread: `platform/reading-worker` started as a module worker. REQUIRED (ADR-0232 D4), and the root writes it,
18
+ * because the one form a bundler recognises — `new Worker(new URL('…', import.meta.url), { type: 'module' })`, literally —
19
+ * has to sit where the `Worker` is the host's.
20
+ */
21
+ readonly spawn: () => ReadingWorkerLike;
22
+ /**
23
+ * WHY THE THREAD COULD NOT OPEN, WHEN NOBODY IS THERE TO BE TOLD (ADR-0169).
24
+ *
25
+ * 🔴 The opening is awaited by `transcribe()`, and only by it. A game that declares `uses: { reading: true }` and has
26
+ * not listened yet — or one whose child never will — has nobody awaiting the answer, so a model that does not load
27
+ * used to reject into NOTHING: an unhandled rejection in a browser's console, and silence in `problems`.
28
+ * 📌 THE REASON AND NOT THE SENTENCE: what this hands over is the failure's own words, and the line a human reads —
29
+ * what the child loses, what to do about it — is written by whoever owns the diagnostic channel. A module that is
30
+ * one thread's protocol has no business holding interface prose, and the raw-prose ledger is where that shows.
31
+ * 📌 Optional, and the absence is answered: without it the failure is still owned, it is just not reported.
32
+ */
33
+ readonly report?: (reason: string) => void;
34
+ }
35
+ export interface ReadingInWorker {
36
+ transcribe(samples: Float32Array): Promise<string>;
37
+ /** Lets the thread go. A transcription still running answers with the reason instead of never answering. */
38
+ close(): void;
39
+ }
40
+ export declare function createReadingInWorker(d: ReadingInWorkerDeps): ReadingInWorker;
@@ -0,0 +1,87 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // platform/reading-in-worker — THE ENGINE'S SIDE OF THE READING WORKER (ADR-0216 §2; issue #185).
3
+ //
4
+ // Answers the same shape `platform/reading` asks its `model` port for — something with `transcribe(samples)` — and keeps the
5
+ // work off the thread that draws the game and feeds the microphone (`platform/reading-worker` says what that costs when it is
6
+ // not kept off it).
7
+ //
8
+ // 📌 THE WORKER IS OPENED AT THE FIRST READING AND KEPT, not opened per utterance: opening it means compiling the model again,
9
+ // and the model is up to 378 MiB. It is closed when the engine lets the reading go, so a game that stops listening stops
10
+ // paying for a thread.
11
+ export function createReadingInWorker(d) {
12
+ const worker = d.spawn();
13
+ let nextId = 1;
14
+ const waiting = new Map();
15
+ let opened = null;
16
+ let closed = false;
17
+ const openedPromise = new Promise((resolve, reject) => { opened = { resolve, reject }; });
18
+ /*
19
+ * 🔴 THE OPENING HAS AN OWNER FROM BIRTH, and giving it one is the whole job of this line. Nothing awaits
20
+ * `openedPromise` until the first `transcribe()`, so a thread that cannot open — a model file the delivery never
21
+ * wrote, a wasm that will not compile — rejected into nothing.
22
+ * 📏 Measured 2026-09-22: the browser project ended with every one of its 1140 cases green and an exit code of
23
+ * FAILURE, from this rejection alone. A suite that reports failure while passing teaches everyone to stop reading
24
+ * the exit code, and this house has already paid for that once — the published branch's CI was red for eight days.
25
+ * ⚠️ AND IT DOES NOT SWALLOW THE REASON: `transcribe()` awaits this same promise and still throws it, so whoever
26
+ * asked for a reading is told what went wrong. This line is for when NOBODY asks.
27
+ * 📌 Letting the thread go is not a failure: `close()` rejects the opening to release whoever was waiting, and a
28
+ * child who changed games is not a defect to report.
29
+ */
30
+ openedPromise.catch((e) => {
31
+ if (closed)
32
+ return;
33
+ d.report?.(e.message);
34
+ });
35
+ /** Everything pending gives up with the SAME reason — a promise nobody ever settles is how a game freezes politely. */
36
+ const giveUp = (message) => {
37
+ opened?.reject(new Error(message));
38
+ for (const { reject } of waiting.values())
39
+ reject(new Error(message));
40
+ waiting.clear();
41
+ };
42
+ worker.onmessage = (event) => {
43
+ const msg = event.data;
44
+ if (msg.kind === 'ready') {
45
+ opened?.resolve();
46
+ return;
47
+ }
48
+ if (msg.kind === 'text') {
49
+ waiting.get(msg.id)?.resolve(msg.text);
50
+ waiting.delete(msg.id);
51
+ return;
52
+ }
53
+ if (msg.id === undefined) {
54
+ opened?.reject(new Error(msg.message));
55
+ return;
56
+ }
57
+ waiting.get(msg.id)?.reject(new Error(msg.message));
58
+ waiting.delete(msg.id);
59
+ };
60
+ // a worker that dies — a file that did not load, a wasm that ran out of memory — must not leave the child waiting in silence
61
+ if ('onerror' in worker)
62
+ worker.onerror = () => { giveUp('reading worker: the thread stopped before answering'); };
63
+ worker.postMessage({ kind: 'load', base: d.base, language: d.language });
64
+ return {
65
+ async transcribe(samples) {
66
+ if (closed)
67
+ throw new Error('reading worker: asked to transcribe after the thread was let go');
68
+ await openedPromise;
69
+ const id = nextId++;
70
+ const answer = new Promise((resolve, reject) => { waiting.set(id, { resolve, reject }); });
71
+ /*
72
+ * 📌 THE SAMPLES ARE TRANSFERRED AND NOT COPIED: 30 s at 16 kHz is 1.9 MB, and copying it is a pause on the very thread
73
+ * this module exists to keep free. ⚠️ The buffer is empty on this side afterwards, which is correct — the recording
74
+ * belongs to the transcription now, and `platform/microphone` hands over a fresh one for the next reading.
75
+ */
76
+ worker.postMessage({ kind: 'transcribe', id, samples }, [samples.buffer]);
77
+ return await answer;
78
+ },
79
+ close() {
80
+ if (closed)
81
+ return;
82
+ closed = true;
83
+ giveUp('reading worker: the thread was let go while this reading was still running');
84
+ worker.terminate();
85
+ },
86
+ };
87
+ }