@the-inclusionist/engine 8.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.
- package/README.md +89 -78
- package/app/css/style.css +584 -374
- package/app/public/vendor/fonts/bitter-var-ext.woff2 +0 -0
- package/app/public/vendor/fonts/bitter-var.woff2 +0 -0
- package/app/public/vendor/fonts/bodonimoda-var-ext.woff2 +0 -0
- package/app/public/vendor/fonts/bodonimoda-var.woff2 +0 -0
- package/app/public/vendor/fonts/cat-abril_fatface-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-abril_fatface-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-adlam_display-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-adlam_display-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-agu_display-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-agu_display-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-alfa_slab_one-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-alfa_slab_one-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-are_you_serious-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-are_you_serious-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-asimovian-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-asimovian-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-atkinson_hyperlegible_next-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-atkinson_hyperlegible_next-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-bakbak_one-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-bakbak_one-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-bangers-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-bangers-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-barriecito-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-barriecito-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-berkshire_swash-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-berkshire_swash-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-bevan-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-bevan-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-bitcount-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-bitcount-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-bowlby_one_sc-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-bowlby_one_sc-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-bungee_shade-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-bungee_shade-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-charm-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-charm-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-charm-700-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-charm-700.woff2 +0 -0
- package/app/public/vendor/fonts/cat-cherry_bomb_one-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-cherry_bomb_one-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-chokokutai-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-chokokutai-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-chonburi-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-chonburi-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-climate_crisis-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-climate_crisis-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-cookie-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-dela_gothic_one-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-dela_gothic_one-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-fascinate_inline-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-fascinate_inline-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-ga_maamli-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-ga_maamli-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-itim-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-itim-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-jacquard_24-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-jacquard_24-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-kablammo-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-kablammo-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-kalam-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-kalam-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-kalam-700-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-kalam-700.woff2 +0 -0
- package/app/public/vendor/fonts/cat-katibeh-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-katibeh-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-lemonada-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-lemonada-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-lilita_one-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-lilita_one-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-lobster-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-lobster-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-luckiest_guy-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-luckiest_guy-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-marhey-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-marhey-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-modak-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-modak-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-momo_signature-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-momo_signature-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-monofett-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-monofett-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-monomakh-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-monomakh-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-monoton-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-monoton-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-nerko_one-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-nerko_one-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-noto_sans_adlam-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-noto_sans_adlam-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-noto_sans_arabic-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-noto_sans_arabic-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-noto_sans_devanagari-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-noto_sans_devanagari-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-noto_sans_hebrew-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-noto_sans_hebrew-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-noto_sans_jp-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-noto_sans_jp-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-noto_sans_khmer-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-noto_sans_khmer-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-noto_sans_kr-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-noto_sans_kr-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-noto_sans_sc-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-noto_sans_sc-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-noto_sans_thai-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-noto_sans_thai-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-ole-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-ole-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-pacifico-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-pacifico-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-patrick_hand-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-patrick_hand-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-petit_formal_script-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-petit_formal_script-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-pixelify_sans-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-pixelify_sans-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-plaster-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-plaster-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_ar_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_at-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_at_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_au_nsw-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_au_nsw_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_au_qld-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_au_qld_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_au_sa-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_au_sa_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_au_tas-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_au_tas_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_au_vic-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_au_vic_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_be_vlg-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_be_vlg_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_be_wal-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_be_wal_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_br_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_ca_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_cl_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_co_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_cu_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_cz-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_cz_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_de_grund-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_de_grund_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_de_la-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_de_la_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_de_sas-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_de_sas_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_de_va-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_de_va_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_dk_loopet-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_dk_loopet_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_dk_uloopet-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_dk_uloopet_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_es_deco_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_es_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_fr_moderne-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_fr_moderne_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_fr_trad-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_fr_trad_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_gb_j_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_gb_s_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_hr-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_hr_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_hr_lijeva-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_hr_lijeva_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_hu-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_hu_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_id-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_id_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_ie-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_ie_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_in-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_in_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_is-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_is_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_it_moderna-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_it_moderna_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_it_trad-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_it_trad_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_mx_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_ng_modern-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_ng_modern_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_nl-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_nl_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_no-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_no_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_nz-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_nz_basic-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_nz_basic_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_nz_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_pe_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_pl-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_pl_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_pt_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_ro-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_ro_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_sk-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_sk_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_tz-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_tz_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_us_modern_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_us_trad_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_vn-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_vn_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_za-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-playwrite_za_guides-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-potta_one-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-potta_one-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-ranchers-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-ranchers-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-ranga-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-ranga-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-ranga-700-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-ranga-700.woff2 +0 -0
- package/app/public/vendor/fonts/cat-risque-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-risque-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-rowdies-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-rowdies-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-rowdies-700-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-rowdies-700.woff2 +0 -0
- package/app/public/vendor/fonts/cat-ruge_boogie-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-ruge_boogie-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-ruslan_display-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-ruslan_display-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-sacramento-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-sacramento-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-sancreek-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-sancreek-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-sankofa_display-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-sankofa_display-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-sedgwick_ave_display-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-sedgwick_ave_display-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-shantell_sans-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-shantell_sans-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-smokum-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-smokum-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-solitreo-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-solitreo-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-spicy_rice-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-spicy_rice-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-sriracha-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-sriracha-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-srisakdi-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-srisakdi-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-srisakdi-700-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-srisakdi-700.woff2 +0 -0
- package/app/public/vendor/fonts/cat-stix_two_math-400-0.woff2 +0 -0
- package/app/public/vendor/fonts/cat-tillana-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-tillana-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-tillana-700-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-tillana-700.woff2 +0 -0
- package/app/public/vendor/fonts/cat-titan_one-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-titan_one-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-yatra_one-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-yatra_one-400.woff2 +0 -0
- package/app/public/vendor/fonts/cat-yomogi-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/cat-yomogi-400.woff2 +0 -0
- package/app/public/vendor/fonts/dmserifdisplay-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/dmserifdisplay-400.woff2 +0 -0
- package/app/public/vendor/fonts/domine-var-ext.woff2 +0 -0
- package/app/public/vendor/fonts/domine-var.woff2 +0 -0
- package/app/public/vendor/fonts/fraunces-var-ext.woff2 +0 -0
- package/app/public/vendor/fonts/fraunces-var.woff2 +0 -0
- package/app/public/vendor/fonts/fredoka-var-ext.woff2 +0 -0
- package/app/public/vendor/fonts/fredoka-var.woff2 +0 -0
- package/app/public/vendor/fonts/jakarta-var-ext.woff2 +0 -0
- package/app/public/vendor/fonts/jakarta-var.woff2 +0 -0
- package/app/public/vendor/fonts/lora-var-ext.woff2 +0 -0
- package/app/public/vendor/fonts/lora-var.woff2 +0 -0
- package/app/public/vendor/fonts/merriweather-var-ext.woff2 +0 -0
- package/app/public/vendor/fonts/merriweather-var.woff2 +0 -0
- package/app/public/vendor/fonts/notosans-var-ext.woff2 +0 -0
- package/app/public/vendor/fonts/notosans-var.woff2 +0 -0
- package/app/public/vendor/fonts/nunito-var-ext.woff2 +0 -0
- package/app/public/vendor/fonts/nunito-var.woff2 +0 -0
- package/app/public/vendor/fonts/playfair-var-ext.woff2 +0 -0
- package/app/public/vendor/fonts/playfair-var.woff2 +0 -0
- package/app/public/vendor/fonts/playwrite-cu.woff2 +0 -0
- package/app/public/vendor/fonts/playwrite-es-deco.woff2 +0 -0
- package/app/public/vendor/fonts/playwrite-es.woff2 +0 -0
- package/app/public/vendor/fonts/playwrite-gb-j.woff2 +0 -0
- package/app/public/vendor/fonts/playwrite-gb-s.woff2 +0 -0
- package/app/public/vendor/fonts/playwrite-pe.woff2 +0 -0
- package/app/public/vendor/fonts/playwrite-pt.woff2 +0 -0
- package/app/public/vendor/fonts/quicksand-var-ext.woff2 +0 -0
- package/app/public/vendor/fonts/quicksand-var.woff2 +0 -0
- package/app/public/vendor/fonts/robotoflex-var-ext.woff2 +0 -0
- package/app/public/vendor/fonts/robotoflex-var.woff2 +0 -0
- package/app/public/vendor/fonts/sora-var-ext.woff2 +0 -0
- package/app/public/vendor/fonts/sora-var.woff2 +0 -0
- package/app/public/vendor/fonts/spacegrotesk-var-ext.woff2 +0 -0
- package/app/public/vendor/fonts/spacegrotesk-var.woff2 +0 -0
- package/app/public/vendor/fonts/spectral-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/spectral-400.woff2 +0 -0
- package/app/public/vendor/fonts/spectral-700-ext.woff2 +0 -0
- package/app/public/vendor/fonts/spectral-700.woff2 +0 -0
- package/app/public/vendor/fonts/teachers-var-ext.woff2 +0 -0
- package/app/public/vendor/fonts/teachers-var.woff2 +0 -0
- package/app/public/vendor/fonts/ubuntu-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/ubuntu-400.woff2 +0 -0
- package/app/public/vendor/fonts/ubuntu-700-ext.woff2 +0 -0
- package/app/public/vendor/fonts/ubuntu-700.woff2 +0 -0
- package/app/public/vendor/fonts.css +1167 -51
- package/dist-pkg/boot/create-game.d.ts +467 -162
- package/dist-pkg/boot/create-game.js +3563 -426
- package/dist-pkg/core/a11y-sr.d.ts +25 -3
- package/dist-pkg/core/a11y-sr.js +40 -19
- package/dist-pkg/core/accessible-label.d.ts +17 -0
- package/dist-pkg/core/accessible-label.js +37 -0
- package/dist-pkg/core/accommodation-subjects.d.ts +26 -0
- package/dist-pkg/core/accommodation-subjects.js +32 -0
- package/dist-pkg/core/accommodations.d.ts +107 -0
- package/dist-pkg/core/accommodations.js +178 -0
- package/dist-pkg/core/actions.d.ts +75 -51
- package/dist-pkg/core/actions.js +98 -69
- package/dist-pkg/core/calm-mode.d.ts +31 -0
- package/dist-pkg/core/calm-mode.js +47 -0
- package/dist-pkg/core/camera-cycle.d.ts +15 -0
- package/dist-pkg/core/camera-cycle.js +7 -0
- package/dist-pkg/core/caption-duration.d.ts +15 -0
- package/dist-pkg/core/caption-duration.js +19 -0
- package/dist-pkg/core/cartridge-problems.d.ts +34 -0
- package/dist-pkg/core/cartridge-problems.js +53 -0
- package/dist-pkg/core/constants.d.ts +0 -54
- package/dist-pkg/core/constants.js +9 -76
- package/dist-pkg/core/contract.d.ts +143 -163
- package/dist-pkg/core/contract.js +223 -207
- package/dist-pkg/core/dom-query.d.ts +4 -4
- package/dist-pkg/core/dom-query.js +12 -20
- package/dist-pkg/core/entity.d.ts +62 -77
- package/dist-pkg/core/entity.js +18 -32
- package/dist-pkg/core/escape-html.d.ts +9 -10
- package/dist-pkg/core/escape-html.js +16 -20
- package/dist-pkg/core/flash-threshold.d.ts +17 -0
- package/dist-pkg/core/flash-threshold.js +113 -0
- package/dist-pkg/core/game-speed.d.ts +11 -0
- package/dist-pkg/core/game-speed.js +16 -0
- package/dist-pkg/core/genres.d.ts +20 -0
- package/dist-pkg/core/genres.js +44 -0
- package/dist-pkg/core/i18n.d.ts +87 -41
- package/dist-pkg/core/i18n.js +206 -153
- package/dist-pkg/core/loop.d.ts +17 -6
- package/dist-pkg/core/loop.js +26 -26
- package/dist-pkg/core/pause-icon-catalogue.d.ts +15 -0
- package/dist-pkg/core/pause-icon-catalogue.js +54 -0
- package/dist-pkg/core/ring.d.ts +17 -0
- package/dist-pkg/core/ring.js +29 -0
- package/dist-pkg/core/rng.d.ts +7 -12
- package/dist-pkg/core/rng.js +23 -53
- package/dist-pkg/core/route.d.ts +26 -26
- package/dist-pkg/core/route.js +126 -121
- package/dist-pkg/core/scenes.d.ts +37 -40
- package/dist-pkg/core/scenes.js +41 -43
- package/dist-pkg/core/screens.d.ts +4 -4
- package/dist-pkg/core/screens.js +11 -16
- package/dist-pkg/core/setting-defaults.d.ts +61 -0
- package/dist-pkg/core/setting-defaults.js +66 -0
- package/dist-pkg/core/speech-rate.d.ts +29 -0
- package/dist-pkg/core/speech-rate.js +47 -0
- package/dist-pkg/core/state.d.ts +118 -118
- package/dist-pkg/core/state.js +322 -297
- package/dist-pkg/core/visual-cycles.d.ts +30 -0
- package/dist-pkg/core/visual-cycles.js +52 -0
- package/dist-pkg/core/visual-state.d.ts +13 -0
- package/dist-pkg/core/visual-state.js +6 -0
- package/dist-pkg/educational/activities-registry.d.ts +1 -1
- package/dist-pkg/educational/activities-registry.js +9 -8
- package/dist-pkg/educational/adaptive-engine.d.ts +11 -11
- package/dist-pkg/educational/adaptive-engine.js +17 -17
- package/dist-pkg/educational/segment-bar.d.ts +12 -12
- package/dist-pkg/educational/segment-bar.js +9 -9
- package/dist-pkg/i18n/en.js +321 -215
- package/dist-pkg/i18n/es.js +320 -215
- package/dist-pkg/i18n/pt.js +342 -215
- package/dist-pkg/input/default-bindings.d.ts +37 -38
- package/dist-pkg/input/default-bindings.js +127 -144
- package/dist-pkg/input/devices.d.ts +7 -7
- package/dist-pkg/input/devices.js +17 -16
- package/dist-pkg/input/edges.d.ts +23 -32
- package/dist-pkg/input/edges.js +23 -28
- package/dist-pkg/input/empathy-filter.d.ts +22 -0
- package/dist-pkg/input/empathy-filter.js +34 -0
- package/dist-pkg/input/face-map.d.ts +96 -0
- package/dist-pkg/input/face-map.js +255 -0
- package/dist-pkg/input/face-signals.d.ts +40 -0
- package/dist-pkg/input/face-signals.js +48 -0
- package/dist-pkg/input/gamepad.d.ts +164 -145
- package/dist-pkg/input/gamepad.js +351 -559
- package/dist-pkg/input/gaze-cycle.d.ts +64 -0
- package/dist-pkg/input/gaze-cycle.js +133 -0
- package/dist-pkg/input/gaze-relative.d.ts +68 -0
- package/dist-pkg/input/gaze-relative.js +142 -0
- package/dist-pkg/input/hand-map.d.ts +28 -0
- package/dist-pkg/input/hand-map.js +142 -0
- package/dist-pkg/input/input-cooldown.d.ts +17 -0
- package/dist-pkg/input/input-cooldown.js +39 -0
- package/dist-pkg/input/keyboard-runtime.d.ts +18 -20
- package/dist-pkg/input/keyboard-runtime.js +23 -27
- package/dist-pkg/input/keyboard.d.ts +68 -62
- package/dist-pkg/input/keyboard.js +83 -105
- package/dist-pkg/input/keydown.d.ts +124 -132
- package/dist-pkg/input/keydown.js +283 -288
- package/dist-pkg/input/latch-edge.d.ts +25 -21
- package/dist-pkg/input/latch-edge.js +33 -35
- package/dist-pkg/input/latch-scope.d.ts +43 -47
- package/dist-pkg/input/latch-scope.js +66 -75
- package/dist-pkg/input/latch-store.d.ts +28 -29
- package/dist-pkg/input/latch-store.js +44 -47
- package/dist-pkg/input/latch-sync.d.ts +28 -29
- package/dist-pkg/input/latch-sync.js +25 -25
- package/dist-pkg/input/latch.d.ts +14 -17
- package/dist-pkg/input/latch.js +29 -37
- package/dist-pkg/input/pad-defaults.d.ts +13 -16
- package/dist-pkg/input/pad-defaults.js +21 -28
- package/dist-pkg/input/pad-reading.d.ts +76 -0
- package/dist-pkg/input/pad-reading.js +164 -0
- package/dist-pkg/input/pad-wizard.d.ts +96 -0
- package/dist-pkg/input/pad-wizard.js +205 -0
- package/dist-pkg/input/pointer-space.d.ts +19 -20
- package/dist-pkg/input/pointer-space.js +27 -32
- package/dist-pkg/input/pointer.d.ts +39 -40
- package/dist-pkg/input/pointer.js +42 -45
- package/dist-pkg/input/state.d.ts +101 -89
- package/dist-pkg/input/state.js +54 -134
- package/dist-pkg/input/switch-scan.d.ts +42 -0
- package/dist-pkg/input/switch-scan.js +54 -0
- package/dist-pkg/input/synthetic-source.d.ts +42 -0
- package/dist-pkg/input/synthetic-source.js +57 -0
- package/dist-pkg/input/touch-bindings.d.ts +127 -113
- package/dist-pkg/input/touch-bindings.js +158 -173
- package/dist-pkg/input/touch.d.ts +130 -70
- package/dist-pkg/input/touch.js +321 -123
- package/dist-pkg/input/transport-in-use.d.ts +107 -0
- package/dist-pkg/input/transport-in-use.js +130 -0
- package/dist-pkg/input/transports.d.ts +96 -114
- package/dist-pkg/input/transports.js +104 -106
- package/dist-pkg/input/virtual-controller.d.ts +46 -0
- package/dist-pkg/input/virtual-controller.js +41 -0
- package/dist-pkg/input/vocabulary-migration.d.ts +50 -49
- package/dist-pkg/input/vocabulary-migration.js +80 -79
- package/dist-pkg/input/voice-map.d.ts +43 -0
- package/dist-pkg/input/voice-map.js +125 -0
- package/dist-pkg/platform/audio-ambient.d.ts +13 -1
- package/dist-pkg/platform/audio-ambient.js +11 -13
- package/dist-pkg/platform/audio-earcons.d.ts +20 -19
- package/dist-pkg/platform/audio-earcons.js +51 -42
- package/dist-pkg/platform/audio-jingles.js +9 -10
- package/dist-pkg/platform/audio-mixer.d.ts +14 -2
- package/dist-pkg/platform/audio-mixer.js +36 -39
- package/dist-pkg/platform/audio-sonar.d.ts +96 -113
- package/dist-pkg/platform/audio-sonar.js +245 -238
- package/dist-pkg/platform/audio.d.ts +53 -19
- package/dist-pkg/platform/audio.js +161 -169
- package/dist-pkg/platform/flash-sampler.d.ts +19 -0
- package/dist-pkg/platform/flash-sampler.js +79 -0
- package/dist-pkg/platform/guide-intensity.d.ts +26 -27
- package/dist-pkg/platform/guide-intensity.js +50 -52
- package/dist-pkg/platform/heavy-catalogue.d.ts +31 -0
- package/dist-pkg/platform/heavy-catalogue.js +223 -0
- package/dist-pkg/platform/heavy-mirror.d.ts +26 -0
- package/dist-pkg/platform/heavy-mirror.js +76 -0
- package/dist-pkg/platform/heavy.d.ts +104 -0
- package/dist-pkg/platform/heavy.js +169 -0
- package/dist-pkg/platform/interruptible-speech.d.ts +16 -16
- package/dist-pkg/platform/interruptible-speech.js +50 -51
- package/dist-pkg/platform/kokoro-port.d.ts +27 -0
- package/dist-pkg/platform/kokoro-port.js +50 -0
- package/dist-pkg/platform/kokoro-runtime.d.ts +20 -0
- package/dist-pkg/platform/kokoro-runtime.js +52 -0
- package/dist-pkg/platform/kokoro.d.ts +70 -0
- package/dist-pkg/platform/kokoro.js +133 -0
- package/dist-pkg/platform/listener-scope.d.ts +12 -0
- package/dist-pkg/platform/listener-scope.js +81 -0
- package/dist-pkg/platform/locale-host.d.ts +10 -0
- package/dist-pkg/platform/locale-host.js +34 -0
- package/dist-pkg/platform/microphone.d.ts +51 -0
- package/dist-pkg/platform/microphone.js +118 -0
- package/dist-pkg/platform/onnx-runtime.d.ts +34 -0
- package/dist-pkg/platform/onnx-runtime.js +42 -0
- package/dist-pkg/platform/reading-in-worker.d.ts +40 -0
- package/dist-pkg/platform/reading-in-worker.js +87 -0
- package/dist-pkg/platform/reading-model.d.ts +73 -0
- package/dist-pkg/platform/reading-model.js +247 -0
- package/dist-pkg/platform/reading-runtime.d.ts +24 -0
- package/dist-pkg/platform/reading-runtime.js +159 -0
- package/dist-pkg/platform/reading-worker.d.ts +41 -0
- package/dist-pkg/platform/reading-worker.js +58 -0
- package/dist-pkg/platform/reading.d.ts +69 -0
- package/dist-pkg/platform/reading.js +128 -0
- package/dist-pkg/platform/speech-recognition.d.ts +51 -0
- package/dist-pkg/platform/speech-recognition.js +84 -0
- package/dist-pkg/platform/speech.d.ts +14 -1
- package/dist-pkg/platform/speech.js +13 -13
- package/dist-pkg/platform/storage-keys.d.ts +96 -0
- package/dist-pkg/platform/storage-keys.js +106 -0
- package/dist-pkg/platform/storage.d.ts +46 -111
- package/dist-pkg/platform/storage.js +104 -169
- package/dist-pkg/platform/tts.d.ts +51 -38
- package/dist-pkg/platform/tts.js +217 -119
- package/dist-pkg/platform/vision-loop.d.ts +23 -0
- package/dist-pkg/platform/vision-loop.js +56 -0
- package/dist-pkg/platform/vision.d.ts +132 -0
- package/dist-pkg/platform/vision.js +117 -0
- package/dist-pkg/platform/voice-listener.d.ts +36 -0
- package/dist-pkg/platform/voice-listener.js +88 -0
- package/dist-pkg/platform/voice-plan.d.ts +6 -102
- package/dist-pkg/platform/voice-plan.js +9 -151
- package/dist-pkg/platform/vosk-runtime.d.ts +71 -0
- package/dist-pkg/platform/vosk-runtime.js +93 -0
- package/dist-pkg/render/canvas.d.ts +13 -7
- package/dist-pkg/render/canvas.js +13 -13
- package/dist-pkg/render/crt.d.ts +46 -12
- package/dist-pkg/render/crt.js +82 -80
- package/dist-pkg/render/cvd-matrices.d.ts +18 -18
- package/dist-pkg/render/cvd-matrices.js +33 -38
- package/dist-pkg/render/hc-role-data.d.ts +7 -7
- package/dist-pkg/render/hc-role-data.js +14 -14
- package/dist-pkg/render/high-contrast.d.ts +66 -54
- package/dist-pkg/render/high-contrast.js +221 -216
- package/dist-pkg/render/low-vision-drawing.d.ts +5 -0
- package/dist-pkg/render/low-vision-drawing.js +46 -0
- package/dist-pkg/render/lq-filter.d.ts +32 -20
- package/dist-pkg/render/lq-filter.js +59 -65
- package/dist-pkg/render/port.d.ts +86 -88
- package/dist-pkg/render/port.js +17 -23
- package/dist-pkg/render/screen-pipeline.d.ts +42 -42
- package/dist-pkg/render/screen-pipeline.js +80 -87
- package/dist-pkg/render/sprite-fx.d.ts +2 -1
- package/dist-pkg/render/sprite-fx.js +16 -14
- package/dist-pkg/render/viewports.d.ts +20 -15
- package/dist-pkg/render/viewports.js +83 -114
- package/dist-pkg/render/viz-axes-labels.d.ts +47 -0
- package/dist-pkg/render/viz-axes-labels.js +88 -0
- package/dist-pkg/render/viz-axes.d.ts +80 -86
- package/dist-pkg/render/viz-axes.js +115 -116
- package/dist-pkg/render/viz-modes.d.ts +28 -29
- package/dist-pkg/render/viz-modes.js +40 -45
- package/dist-pkg/render/viz-refusal.d.ts +32 -0
- package/dist-pkg/render/viz-refusal.js +57 -0
- package/dist-pkg/render/viz-setters.d.ts +104 -66
- package/dist-pkg/render/viz-setters.js +182 -191
- package/dist-pkg/ui/aac-sets.d.ts +40 -0
- package/dist-pkg/ui/aac-sets.js +49 -0
- package/dist-pkg/ui/audio-choices.d.ts +63 -0
- package/dist-pkg/ui/audio-choices.js +94 -0
- package/dist-pkg/ui/audio-rows-that-apply.d.ts +12 -0
- package/dist-pkg/ui/audio-rows-that-apply.js +43 -0
- package/dist-pkg/ui/camera-control.d.ts +6 -0
- package/dist-pkg/ui/camera-control.js +14 -0
- package/dist-pkg/ui/changed-mark.d.ts +9 -8
- package/dist-pkg/ui/changed-mark.js +22 -53
- package/dist-pkg/ui/control-choices.d.ts +37 -0
- package/dist-pkg/ui/control-choices.js +84 -0
- package/dist-pkg/ui/debug-panel.d.ts +59 -43
- package/dist-pkg/ui/debug-panel.js +68 -69
- package/dist-pkg/ui/dom.d.ts +10 -14
- package/dist-pkg/ui/dom.js +10 -47
- package/dist-pkg/ui/drawing-problems.d.ts +25 -0
- package/dist-pkg/ui/drawing-problems.js +93 -0
- package/dist-pkg/ui/eye-control.d.ts +36 -0
- package/dist-pkg/ui/eye-control.js +178 -0
- package/dist-pkg/ui/face-control.d.ts +25 -0
- package/dist-pkg/ui/face-control.js +183 -0
- package/dist-pkg/ui/focus-trap.d.ts +39 -49
- package/dist-pkg/ui/focus-trap.js +56 -65
- package/dist-pkg/ui/fonts.d.ts +118 -63
- package/dist-pkg/ui/fonts.js +267 -111
- package/dist-pkg/ui/game-options.d.ts +42 -0
- package/dist-pkg/ui/game-options.js +138 -0
- package/dist-pkg/ui/gaze-overlay.d.ts +64 -0
- package/dist-pkg/ui/gaze-overlay.js +165 -0
- package/dist-pkg/ui/hand-control.d.ts +25 -0
- package/dist-pkg/ui/hand-control.js +143 -0
- package/dist-pkg/ui/help-panel.d.ts +76 -0
- package/dist-pkg/ui/help-panel.js +232 -0
- package/dist-pkg/ui/hud-bands.d.ts +45 -0
- package/dist-pkg/ui/hud-bands.js +108 -0
- package/dist-pkg/ui/hud.d.ts +73 -70
- package/dist-pkg/ui/hud.js +94 -98
- package/dist-pkg/ui/item-announcement.d.ts +13 -12
- package/dist-pkg/ui/item-announcement.js +25 -26
- package/dist-pkg/ui/latch-refusal.d.ts +22 -21
- package/dist-pkg/ui/latch-refusal.js +37 -37
- package/dist-pkg/ui/layout.d.ts +103 -28
- package/dist-pkg/ui/layout.js +114 -99
- package/dist-pkg/ui/locale-flags.d.ts +16 -0
- package/dist-pkg/ui/locale-flags.js +30 -0
- package/dist-pkg/ui/loop-crash.d.ts +17 -17
- package/dist-pkg/ui/loop-crash.js +33 -62
- package/dist-pkg/ui/menu-intent.d.ts +32 -0
- package/dist-pkg/ui/menu-intent.js +54 -0
- package/dist-pkg/ui/menu-items.d.ts +10 -0
- package/dist-pkg/ui/menu-items.js +22 -0
- package/dist-pkg/ui/menu-nav.d.ts +60 -90
- package/dist-pkg/ui/menu-nav.js +301 -274
- package/dist-pkg/ui/mobility-choices.d.ts +26 -0
- package/dist-pkg/ui/mobility-choices.js +41 -0
- package/dist-pkg/ui/motion-choices.d.ts +31 -0
- package/dist-pkg/ui/motion-choices.js +68 -0
- package/dist-pkg/ui/motion-scene.d.ts +24 -19
- package/dist-pkg/ui/motion-scene.js +29 -61
- package/dist-pkg/ui/mount-panel.d.ts +81 -0
- package/dist-pkg/ui/mount-panel.js +61 -0
- package/dist-pkg/ui/panel-shell.d.ts +65 -31
- package/dist-pkg/ui/panel-shell.js +92 -66
- package/dist-pkg/ui/panel-widgets.d.ts +116 -0
- package/dist-pkg/ui/panel-widgets.js +208 -0
- package/dist-pkg/ui/pause-buttons.d.ts +35 -0
- package/dist-pkg/ui/pause-buttons.js +66 -0
- package/dist-pkg/ui/pause-icons.d.ts +250 -343
- package/dist-pkg/ui/pause-icons.js +614 -605
- package/dist-pkg/ui/pause-markup.d.ts +86 -0
- package/dist-pkg/ui/pause-markup.js +125 -0
- package/dist-pkg/ui/reach-notice.d.ts +22 -23
- package/dist-pkg/ui/reach-notice.js +55 -58
- package/dist-pkg/ui/scan-overlay.d.ts +23 -0
- package/dist-pkg/ui/scan-overlay.js +42 -0
- package/dist-pkg/ui/settings-aac.d.ts +68 -0
- package/dist-pkg/ui/settings-aac.js +189 -0
- package/dist-pkg/ui/settings-audio.d.ts +91 -113
- package/dist-pkg/ui/settings-audio.js +407 -405
- package/dist-pkg/ui/settings-controls.d.ts +53 -92
- package/dist-pkg/ui/settings-controls.js +143 -215
- package/dist-pkg/ui/settings-empathy.d.ts +39 -30
- package/dist-pkg/ui/settings-empathy.js +53 -60
- package/dist-pkg/ui/settings-mobility.d.ts +195 -0
- package/dist-pkg/ui/settings-mobility.js +374 -0
- package/dist-pkg/ui/settings-motion.d.ts +91 -70
- package/dist-pkg/ui/settings-motion.js +287 -198
- package/dist-pkg/ui/settings-panel.d.ts +48 -45
- package/dist-pkg/ui/settings-panel.js +164 -93
- package/dist-pkg/ui/settings-typo.d.ts +28 -76
- package/dist-pkg/ui/settings-typo.js +145 -125
- package/dist-pkg/ui/settings-visual.d.ts +29 -87
- package/dist-pkg/ui/settings-visual.js +240 -193
- package/dist-pkg/ui/shell.d.ts +113 -134
- package/dist-pkg/ui/shell.js +143 -216
- package/dist-pkg/ui/simulation-list.d.ts +27 -0
- package/dist-pkg/ui/simulation-list.js +76 -0
- package/dist-pkg/ui/simulation-over-the-world.d.ts +23 -0
- package/dist-pkg/ui/simulation-over-the-world.js +131 -0
- package/dist-pkg/ui/simulation-refusal.d.ts +5 -31
- package/dist-pkg/ui/simulation-refusal.js +8 -56
- package/dist-pkg/ui/switchable-control.d.ts +11 -0
- package/dist-pkg/ui/switchable-control.js +19 -0
- package/dist-pkg/ui/title.d.ts +4 -4
- package/dist-pkg/ui/title.js +11 -13
- package/dist-pkg/ui/top-band.d.ts +24 -0
- package/dist-pkg/ui/top-band.js +100 -0
- package/dist-pkg/ui/typo-choices.d.ts +71 -0
- package/dist-pkg/ui/typo-choices.js +84 -0
- package/dist-pkg/ui/visual-axes-panel.d.ts +11 -47
- package/dist-pkg/ui/visual-axes-panel.js +12 -94
- package/dist-pkg/ui/visual-choices.d.ts +77 -0
- package/dist-pkg/ui/visual-choices.js +107 -0
- package/dist-pkg/ui/vlibras.d.ts +33 -10
- package/dist-pkg/ui/vlibras.js +58 -81
- package/dist-pkg/ui/voice-control.d.ts +46 -0
- package/dist-pkg/ui/voice-control.js +143 -0
- package/dist-pkg/ui/voice-settings.d.ts +96 -0
- package/dist-pkg/ui/voice-settings.js +302 -0
- package/dist-pkg/ui/where-the-child-is.d.ts +31 -0
- package/dist-pkg/ui/where-the-child-is.js +70 -0
- package/docs/CREDITS.md +198 -54
- package/docs/LICENSES.md +160 -145
- package/package.json +24 -25
- package/scripts/heavy-into-the-delivery.mjs +144 -0
- package/scripts/licences/Apache-2.0.txt +176 -0
- package/scripts/licences/GPL-3.0.txt +674 -0
- package/scripts/licences/MIT.txt +17 -0
- package/scripts/licences/third-party.mjs +287 -0
- package/scripts/licences/vosk-browser.NOTICE.txt +268 -0
- package/app/public/vendor/fonts/comicneue-400.woff2 +0 -0
- package/app/public/vendor/fonts/comicneue-700.woff2 +0 -0
- package/dist-pkg/core/anel.d.ts +0 -17
- package/dist-pkg/core/anel.js +0 -31
- package/dist-pkg/core/collision.d.ts +0 -21
- package/dist-pkg/core/collision.js +0 -62
- package/dist-pkg/core/layers.d.ts +0 -63
- package/dist-pkg/core/layers.js +0 -95
- package/dist-pkg/core/letter-grid.d.ts +0 -55
- package/dist-pkg/core/letter-grid.js +0 -116
- package/dist-pkg/core/password.d.ts +0 -34
- package/dist-pkg/core/password.js +0 -205
- package/dist-pkg/core/rotulo-acessivel.d.ts +0 -17
- package/dist-pkg/core/rotulo-acessivel.js +0 -41
- package/dist-pkg/core/run-state.d.ts +0 -115
- package/dist-pkg/core/run-state.js +0 -40
- package/dist-pkg/core/tiles.d.ts +0 -12
- package/dist-pkg/core/tiles.js +0 -61
- package/dist-pkg/core/world.d.ts +0 -4
- package/dist-pkg/core/world.js +0 -58
- package/dist-pkg/input/origem-sintetica.d.ts +0 -44
- package/dist-pkg/input/origem-sintetica.js +0 -60
- package/dist-pkg/input/transporte-em-uso.d.ts +0 -101
- package/dist-pkg/input/transporte-em-uso.js +0 -130
- package/dist-pkg/platform/audio-nav.d.ts +0 -51
- package/dist-pkg/platform/audio-nav.js +0 -99
- package/dist-pkg/platform/pesados-catalogo.d.ts +0 -14
- package/dist-pkg/platform/pesados-catalogo.js +0 -177
- package/dist-pkg/platform/pesados.d.ts +0 -31
- package/dist-pkg/platform/pesados.js +0 -75
- package/dist-pkg/platform/vozes-prontas.d.ts +0 -28
- package/dist-pkg/platform/vozes-prontas.js +0 -61
- package/dist-pkg/render/camera.d.ts +0 -58
- package/dist-pkg/render/camera.js +0 -105
- package/dist-pkg/render/cenario-data.d.ts +0 -127
- package/dist-pkg/render/cenario-data.js +0 -145
- package/dist-pkg/render/city-tex.d.ts +0 -63
- package/dist-pkg/render/city-tex.js +0 -228
- package/dist-pkg/render/city-tiles.d.ts +0 -11
- package/dist-pkg/render/city-tiles.js +0 -140
- package/dist-pkg/render/draw.d.ts +0 -149
- package/dist-pkg/render/draw.js +0 -231
- package/dist-pkg/render/fx.d.ts +0 -63
- package/dist-pkg/render/fx.js +0 -123
- package/dist-pkg/render/minimap.d.ts +0 -11
- package/dist-pkg/render/minimap.js +0 -94
- package/dist-pkg/render/parallax.d.ts +0 -85
- package/dist-pkg/render/parallax.js +0 -158
- package/dist-pkg/render/player-anim.d.ts +0 -71
- package/dist-pkg/render/player-anim.js +0 -152
- package/dist-pkg/render/recycling-tex.d.ts +0 -48
- package/dist-pkg/render/recycling-tex.js +0 -164
- package/dist-pkg/render/scene-city.d.ts +0 -77
- package/dist-pkg/render/scene-city.js +0 -181
- package/dist-pkg/render/scene-parallax.d.ts +0 -72
- package/dist-pkg/render/scene-parallax.js +0 -426
- package/dist-pkg/render/scene-sky.d.ts +0 -126
- package/dist-pkg/render/scene-sky.js +0 -295
- package/dist-pkg/render/set-cenario.d.ts +0 -54
- package/dist-pkg/render/set-cenario.js +0 -103
- package/dist-pkg/render/textures.d.ts +0 -75
- package/dist-pkg/render/textures.js +0 -240
- package/dist-pkg/render/title-scene.d.ts +0 -46
- package/dist-pkg/render/title-scene.js +0 -75
- package/dist-pkg/render/weather.d.ts +0 -96
- package/dist-pkg/render/weather.js +0 -171
- package/dist-pkg/render/wheelchair-sprites.d.ts +0 -32
- package/dist-pkg/render/wheelchair-sprites.js +0 -69
- package/dist-pkg/render/world-tex.d.ts +0 -18
- package/dist-pkg/render/world-tex.js +0 -87
- package/dist-pkg/ui/activities-menu.d.ts +0 -258
- package/dist-pkg/ui/activities-menu.js +0 -648
- package/dist-pkg/ui/caa-sets.d.ts +0 -39
- package/dist-pkg/ui/caa-sets.js +0 -65
- package/dist-pkg/ui/map-hub.d.ts +0 -56
- package/dist-pkg/ui/map-hub.js +0 -138
- package/dist-pkg/ui/settings-caa.d.ts +0 -45
- package/dist-pkg/ui/settings-caa.js +0 -137
- package/dist-pkg/ui/settings-motor.d.ts +0 -175
- package/dist-pkg/ui/settings-motor.js +0 -342
- package/dist-pkg/ui/webcam.d.ts +0 -7
- 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
|
+
}
|