@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,73 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
/** What the engine asks of a language: which catalogue files, and what the encoder eats. */
|
|
3
|
+
export interface ReadingModelPlan {
|
|
4
|
+
readonly language: string;
|
|
5
|
+
/** Whisper eats a log-mel spectrogram; Moonshine eats the samples. */
|
|
6
|
+
readonly input: 'log-mel' | 'waveform';
|
|
7
|
+
readonly encoder: string;
|
|
8
|
+
readonly decoder: string;
|
|
9
|
+
/** The second decoder graph, where the model has one: the first pass builds the cache, this one uses it. */
|
|
10
|
+
readonly decoderPast?: string;
|
|
11
|
+
readonly tokenizer: string;
|
|
12
|
+
readonly generation: string;
|
|
13
|
+
/** How many heads the caches hold and how wide each is — what a merged decoder needs to be handed empty ones. */
|
|
14
|
+
readonly config: string;
|
|
15
|
+
/** Only the mel front end needs one. */
|
|
16
|
+
readonly preprocessor?: string;
|
|
17
|
+
}
|
|
18
|
+
/** 16 kHz mono, for every one of them: it is what all three were trained on, and resampling is the caller's job. */
|
|
19
|
+
export declare const READING_RATE = 16000;
|
|
20
|
+
/** Whisper reads a 30 s window at a time, always: shorter is padded with silence, longer is cut. */
|
|
21
|
+
export declare const WHISPER_SAMPLES: number;
|
|
22
|
+
export declare const WHISPER_FRAMES = 3000;
|
|
23
|
+
export declare const WHISPER_BANDS = 80;
|
|
24
|
+
export declare const READING_MODELS: readonly ReadingModelPlan[];
|
|
25
|
+
/** The model of a language tag (`pt-BR` → pt), or `null` where the project has none — which the caller must SAY, not hide. */
|
|
26
|
+
export declare function readingModelFor(language: string): ReadingModelPlan | null;
|
|
27
|
+
/**
|
|
28
|
+
* WHAT WHISPER'S ENCODER EATS: a log-mel spectrogram of 80 bands by 3000 frames, from a 30 s window at 16 kHz.
|
|
29
|
+
*
|
|
30
|
+
* The steps are `WhisperFeatureExtractor`'s, in its order, and each one matters: the window is a PERIODIC Hann (a symmetric one
|
|
31
|
+
* moves every value); the signal is reflected at both ends by half a window, so frame 0 is centred on sample 0; the power
|
|
32
|
+
* spectrum is the magnitude SQUARED; the last frame is dropped, which is why 480 000 samples give 3000 and not 3001; the floor is
|
|
33
|
+
* eight decades below the LOUDEST point of this window, not a constant; and the scale ends in (x + 4) / 4.
|
|
34
|
+
*/
|
|
35
|
+
export declare function logMel(samples: Float32Array, melFilters: readonly (readonly number[])[]): Float32Array;
|
|
36
|
+
/** A `tokenizer.json`, in the parts a reading reads: the vocabulary, the added tokens, and which decoder undoes them. */
|
|
37
|
+
export interface TokenizerFile {
|
|
38
|
+
readonly model?: {
|
|
39
|
+
readonly vocab?: Readonly<Record<string, number>>;
|
|
40
|
+
};
|
|
41
|
+
readonly added_tokens?: readonly {
|
|
42
|
+
readonly id: number;
|
|
43
|
+
readonly content: string;
|
|
44
|
+
readonly special?: boolean;
|
|
45
|
+
}[];
|
|
46
|
+
readonly decoder?: {
|
|
47
|
+
readonly type?: string;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* WHAT THE CHILD ACTUALLY SAID, from the ids the decoder answered.
|
|
52
|
+
*
|
|
53
|
+
* The two families undo themselves differently and the file says which: Whisper writes every byte as a printable character
|
|
54
|
+
* (`ByteLevel`), Moonshine writes a space as `▁` and a rare byte as `<0x..>` (`Replace` + `ByteFallback` + `Fuse` + `Strip`).
|
|
55
|
+
* Both end in ONE decode of UTF-8, which is what keeps an accent that spans two tokens from becoming two broken characters.
|
|
56
|
+
*/
|
|
57
|
+
export declare function readingTextOf(tokenizer: TokenizerFile, ids: readonly number[]): string;
|
|
58
|
+
/** What a `generation_config.json` says must never be answered — Whisper's list of tokens that are not speech. */
|
|
59
|
+
export interface GenerationConfig {
|
|
60
|
+
readonly suppress_tokens?: readonly number[];
|
|
61
|
+
readonly begin_suppress_tokens?: readonly number[];
|
|
62
|
+
readonly eos_token_id?: number;
|
|
63
|
+
readonly decoder_start_token_id?: number;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The tokens refused at this step. `atStart` is the first answered token, where Whisper also refuses the ones that would make it
|
|
67
|
+
* open with a silence or a punctuation mark — without this it answers «.» to a child who read a whole sentence.
|
|
68
|
+
*/
|
|
69
|
+
export declare function suppressedTokens(config: GenerationConfig, atStart: boolean): ReadonlySet<number>;
|
|
70
|
+
/** The id of a named token (`<|pt|>`, `<|transcribe|>`), or `null` — a model that does not have it must be SAID, not guessed. */
|
|
71
|
+
export declare function tokenIdOf(tokenizer: TokenizerFile, content: string): number | null;
|
|
72
|
+
/** The next token: the biggest logit that is not refused. Greedy, because a child reading a known text needs no imagination. */
|
|
73
|
+
export declare function nextToken(logits: ArrayLike<number>, suppressed?: ReadonlySet<number>): number;
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// platform/reading-model — WHAT A READING MODEL NEEDS DONE TO SOUND AND TO TOKENS, in its pure half (ADR-0216 §2; issues #185, #200).
|
|
3
|
+
//
|
|
4
|
+
// The engine hears a child read aloud with one model per language (ADR-0201 erratum): Whisper small for Portuguese, Moonshine
|
|
5
|
+
// streaming small for English and Spanish. Around the graphs there is arithmetic that has nothing to do with a browser — turning
|
|
6
|
+
// sound into what the encoder eats, and turning the numbers the decoder answers back into words — and it lives here, where it can
|
|
7
|
+
// be measured. `platform/reading-runtime` is the half that opens sessions and fetches files.
|
|
8
|
+
//
|
|
9
|
+
// 🎯 MEASURED AGAINST THE ORIGINALS, not against my idea of them: `tests/fixtures/reading-ground-truth.json` is what
|
|
10
|
+
// `WhisperFeatureExtractor` and the three tokenizers answer for one deterministic signal and three phrases, written by
|
|
11
|
+
// `scratchpad/verdade-da-leitura.py`. A front end that is subtly wrong still runs and still returns text — the wrong text — so
|
|
12
|
+
// «it produced something» is not a check.
|
|
13
|
+
//
|
|
14
|
+
// · THE MEL FILTERS ARE NOT COMPUTED HERE. Whisper's own `preprocessor_config.json` carries them (80 × 201), which is most of its
|
|
15
|
+
// 185 KB, and the catalogue already fetches it: a filterbank rebuilt from a formula is a second opinion about the model's input.
|
|
16
|
+
// · Moonshine takes the WAVEFORM. No spectrogram, no padding to 30 s — it is a different front end, not a variant of this one.
|
|
17
|
+
/** 16 kHz mono, for every one of them: it is what all three were trained on, and resampling is the caller's job. */
|
|
18
|
+
export const READING_RATE = 16_000;
|
|
19
|
+
/** Whisper reads a 30 s window at a time, always: shorter is padded with silence, longer is cut. */
|
|
20
|
+
export const WHISPER_SAMPLES = 30 * READING_RATE;
|
|
21
|
+
export const WHISPER_FRAMES = 3000;
|
|
22
|
+
export const WHISPER_BANDS = 80;
|
|
23
|
+
const N_FFT = 400;
|
|
24
|
+
const HOP = 160;
|
|
25
|
+
const HALF = N_FFT / 2;
|
|
26
|
+
const BINS = HALF + 1;
|
|
27
|
+
/** What a band is when its power is clipped: the original clips at 1e-10 before the log. */
|
|
28
|
+
const CLIPPED = Math.log10(1e-10);
|
|
29
|
+
export const READING_MODELS = Object.freeze([
|
|
30
|
+
{
|
|
31
|
+
language: 'pt', input: 'log-mel',
|
|
32
|
+
encoder: 'reading:pt:encoder', decoder: 'reading:pt:decoder', decoderPast: 'reading:pt:decoder:past',
|
|
33
|
+
tokenizer: 'reading:pt:tokenizer', generation: 'reading:pt:generation', config: 'reading:pt:config',
|
|
34
|
+
preprocessor: 'reading:pt:preprocessor',
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
language: 'en', input: 'waveform',
|
|
38
|
+
encoder: 'reading:en:encoder', decoder: 'reading:en:decoder',
|
|
39
|
+
tokenizer: 'reading:en:tokenizer', generation: 'reading:en:generation', config: 'reading:en:config',
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
language: 'es', input: 'waveform',
|
|
43
|
+
encoder: 'reading:es:encoder', decoder: 'reading:es:decoder', decoderPast: 'reading:es:decoder:past',
|
|
44
|
+
tokenizer: 'reading:es:tokenizer', generation: 'reading:es:generation', config: 'reading:es:config',
|
|
45
|
+
},
|
|
46
|
+
]);
|
|
47
|
+
/** The model of a language tag (`pt-BR` → pt), or `null` where the project has none — which the caller must SAY, not hide. */
|
|
48
|
+
export function readingModelFor(language) {
|
|
49
|
+
const tag = language.split('-')[0].toLowerCase();
|
|
50
|
+
return READING_MODELS.find((m) => m.language === tag) ?? null;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* WHAT WHISPER'S ENCODER EATS: a log-mel spectrogram of 80 bands by 3000 frames, from a 30 s window at 16 kHz.
|
|
54
|
+
*
|
|
55
|
+
* The steps are `WhisperFeatureExtractor`'s, in its order, and each one matters: the window is a PERIODIC Hann (a symmetric one
|
|
56
|
+
* moves every value); the signal is reflected at both ends by half a window, so frame 0 is centred on sample 0; the power
|
|
57
|
+
* spectrum is the magnitude SQUARED; the last frame is dropped, which is why 480 000 samples give 3000 and not 3001; the floor is
|
|
58
|
+
* eight decades below the LOUDEST point of this window, not a constant; and the scale ends in (x + 4) / 4.
|
|
59
|
+
*/
|
|
60
|
+
export function logMel(samples, melFilters) {
|
|
61
|
+
const audio = new Float32Array(WHISPER_SAMPLES);
|
|
62
|
+
audio.set(samples.subarray(0, WHISPER_SAMPLES));
|
|
63
|
+
// the reflected padding, written once: `at(i)` is the padded signal, so no copy of 480 000 samples is made
|
|
64
|
+
const at = (i) => {
|
|
65
|
+
const j = i - HALF;
|
|
66
|
+
if (j < 0)
|
|
67
|
+
return audio[-j];
|
|
68
|
+
if (j >= WHISPER_SAMPLES)
|
|
69
|
+
return audio[2 * WHISPER_SAMPLES - j - 2];
|
|
70
|
+
return audio[j];
|
|
71
|
+
};
|
|
72
|
+
const turns = dftTurns();
|
|
73
|
+
const out = new Float32Array(WHISPER_BANDS * WHISPER_FRAMES);
|
|
74
|
+
const power = new Float64Array(BINS);
|
|
75
|
+
let loudest = -Infinity;
|
|
76
|
+
for (let frame = 0; frame < WHISPER_FRAMES; frame++) {
|
|
77
|
+
const start = frame * HOP;
|
|
78
|
+
// 🎯 A WINDOW OF PURE SILENCE IS ANSWERED, NOT COMPUTED, and it is EXACT: every bin of a window of zeros is zero, so every
|
|
79
|
+
// band is the clipped value. A child reads for a few seconds into a 30 s window, so most frames are this one. It never
|
|
80
|
+
// raises the loudest point: no computed band is ever below the clip, and an all-silent window floors at −∞.
|
|
81
|
+
let quiet = true;
|
|
82
|
+
for (let n = 0; n < N_FFT && quiet; n++)
|
|
83
|
+
quiet = at(start + n) === 0;
|
|
84
|
+
if (quiet) {
|
|
85
|
+
for (let band = 0; band < WHISPER_BANDS; band++)
|
|
86
|
+
out[band * WHISPER_FRAMES + frame] = CLIPPED;
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
powerOf(at, start, turns, power);
|
|
90
|
+
loudest = Math.max(loudest, melOf(power, melFilters, out, frame));
|
|
91
|
+
}
|
|
92
|
+
// the floor is eight decades below the LOUDEST point of this window, not a constant, and the scale ends in (x + 4) / 4
|
|
93
|
+
const floor = loudest - 8;
|
|
94
|
+
for (let i = 0; i < out.length; i++)
|
|
95
|
+
out[i] = (Math.max(out[i], floor) + 4) / 4;
|
|
96
|
+
return out;
|
|
97
|
+
}
|
|
98
|
+
/** A PERIODIC Hann window: a symmetric one moves every value. 400 numbers, computed once. */
|
|
99
|
+
const HANN = Float64Array.from({ length: N_FFT }, (_, i) => 0.5 - 0.5 * Math.cos((2 * Math.PI * i) / N_FFT));
|
|
100
|
+
/**
|
|
101
|
+
* ⚠️ THE TURNS ARE COMPUTED ONCE. A 400-point real DFT for 201 bins is 80 400 sines and cosines per frame, and there are 3000
|
|
102
|
+
* frames: asking `Math.cos` for them each time is a quarter of a billion calls, which on a school machine is not «slower», it is
|
|
103
|
+
* a child waiting half a minute to be told what they read.
|
|
104
|
+
*/
|
|
105
|
+
function dftTurns() {
|
|
106
|
+
const cos = new Float64Array(BINS * N_FFT);
|
|
107
|
+
const sin = new Float64Array(BINS * N_FFT);
|
|
108
|
+
for (let k = 0; k < BINS; k++) {
|
|
109
|
+
for (let n = 0; n < N_FFT; n++) {
|
|
110
|
+
const angle = (-2 * Math.PI * k * n) / N_FFT;
|
|
111
|
+
cos[k * N_FFT + n] = Math.cos(angle);
|
|
112
|
+
sin[k * N_FFT + n] = Math.sin(angle);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return { cos, sin };
|
|
116
|
+
}
|
|
117
|
+
/** One frame's power spectrum: the windowed DFT's magnitude SQUARED, one value per bin. */
|
|
118
|
+
function powerOf(at, start, turns, power) {
|
|
119
|
+
for (let k = 0; k < BINS; k++) {
|
|
120
|
+
let re = 0, im = 0;
|
|
121
|
+
const row = k * N_FFT;
|
|
122
|
+
for (let n = 0; n < N_FFT; n++) {
|
|
123
|
+
const x = at(start + n) * HANN[n];
|
|
124
|
+
re += x * turns.cos[row + n];
|
|
125
|
+
im += x * turns.sin[row + n];
|
|
126
|
+
}
|
|
127
|
+
power[k] = re * re + im * im;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/** One frame's 80 bands, each the power through its mel filter, clipped at 1e-10 BEFORE the log. Answers the frame's loudest. */
|
|
131
|
+
function melOf(power, melFilters, out, frame) {
|
|
132
|
+
let loudest = -Infinity;
|
|
133
|
+
for (let band = 0; band < WHISPER_BANDS; band++) {
|
|
134
|
+
const filter = melFilters[band];
|
|
135
|
+
let sum = 0;
|
|
136
|
+
for (let k = 0; k < BINS; k++)
|
|
137
|
+
sum += power[k] * filter[k];
|
|
138
|
+
const value = Math.log10(Math.max(sum, 1e-10));
|
|
139
|
+
out[band * WHISPER_FRAMES + frame] = value;
|
|
140
|
+
if (value > loudest)
|
|
141
|
+
loudest = value;
|
|
142
|
+
}
|
|
143
|
+
return loudest;
|
|
144
|
+
}
|
|
145
|
+
/** id → the piece of text that token stands for, with the special ones (`<|pt|>`, `</s>`…) marked so they never reach a child. */
|
|
146
|
+
function piecesOf(tokenizer) {
|
|
147
|
+
const text = new Map();
|
|
148
|
+
for (const [piece, id] of Object.entries(tokenizer.model?.vocab ?? {}))
|
|
149
|
+
text.set(id, piece);
|
|
150
|
+
const special = new Set();
|
|
151
|
+
for (const added of tokenizer.added_tokens ?? []) {
|
|
152
|
+
text.set(added.id, added.content);
|
|
153
|
+
// ⚠️ `special` is absent on some files and the mark is what keeps `<|startoftranscript|>` out of a child's sentence, so the
|
|
154
|
+
// shape of the content decides when the flag does not: every one of them is bracketed.
|
|
155
|
+
if (added.special ?? /^<.*>$/.test(added.content))
|
|
156
|
+
special.add(added.id);
|
|
157
|
+
}
|
|
158
|
+
return { text, special };
|
|
159
|
+
}
|
|
160
|
+
/** The bytes GPT-2 writes as themselves: its three printable ranges, inclusive (`bytes_to_unicode`, openai/gpt-2 `encoder.py`). */
|
|
161
|
+
const PRINTABLE_RANGES = [[33, 126], [161, 172], [174, 255]];
|
|
162
|
+
// GPT-2's byte alphabet: the printable character each byte is written as, so a tokenizer file stays text. Whisper's decoder is
|
|
163
|
+
// this one (`ByteLevel`), and reversing it is the whole of it.
|
|
164
|
+
const byteOfChar = (() => {
|
|
165
|
+
const map = new Map();
|
|
166
|
+
const printable = [];
|
|
167
|
+
for (const [lo, hi] of PRINTABLE_RANGES)
|
|
168
|
+
for (let b = lo; b <= hi; b++)
|
|
169
|
+
printable.push(b);
|
|
170
|
+
for (const b of printable)
|
|
171
|
+
map.set(String.fromCharCode(b), b);
|
|
172
|
+
let next = 0;
|
|
173
|
+
for (let b = 0; b < 256; b++) {
|
|
174
|
+
if (printable.includes(b))
|
|
175
|
+
continue;
|
|
176
|
+
map.set(String.fromCharCode(256 + next), b);
|
|
177
|
+
next++;
|
|
178
|
+
}
|
|
179
|
+
return map;
|
|
180
|
+
})();
|
|
181
|
+
const BYTE_FALLBACK = /^<0x([0-9A-Fa-f]{2})>$/;
|
|
182
|
+
/**
|
|
183
|
+
* WHAT THE CHILD ACTUALLY SAID, from the ids the decoder answered.
|
|
184
|
+
*
|
|
185
|
+
* The two families undo themselves differently and the file says which: Whisper writes every byte as a printable character
|
|
186
|
+
* (`ByteLevel`), Moonshine writes a space as `▁` and a rare byte as `<0x..>` (`Replace` + `ByteFallback` + `Fuse` + `Strip`).
|
|
187
|
+
* Both end in ONE decode of UTF-8, which is what keeps an accent that spans two tokens from becoming two broken characters.
|
|
188
|
+
*/
|
|
189
|
+
export function readingTextOf(tokenizer, ids) {
|
|
190
|
+
const { text, special } = piecesOf(tokenizer);
|
|
191
|
+
const byteLevel = tokenizer.decoder?.type === 'ByteLevel';
|
|
192
|
+
const bytes = [];
|
|
193
|
+
for (const id of ids) {
|
|
194
|
+
if (special.has(id))
|
|
195
|
+
continue;
|
|
196
|
+
const piece = text.get(id);
|
|
197
|
+
if (piece === undefined)
|
|
198
|
+
continue; // an id the vocabulary does not have is not a character: it is dropped, never guessed
|
|
199
|
+
if (byteLevel) {
|
|
200
|
+
for (const ch of piece)
|
|
201
|
+
bytes.push(byteOfChar.get(ch) ?? ch.charCodeAt(0));
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
const fallback = BYTE_FALLBACK.exec(piece);
|
|
205
|
+
if (fallback) {
|
|
206
|
+
bytes.push(parseInt(fallback[1], 16));
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
for (const b of new TextEncoder().encode(piece.replaceAll('▁', ' ')))
|
|
210
|
+
bytes.push(b);
|
|
211
|
+
}
|
|
212
|
+
const whole = new TextDecoder().decode(Uint8Array.from(bytes));
|
|
213
|
+
return byteLevel ? whole : whole.replace(/^ /, '');
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* The tokens refused at this step. `atStart` is the first answered token, where Whisper also refuses the ones that would make it
|
|
217
|
+
* open with a silence or a punctuation mark — without this it answers «.» to a child who read a whole sentence.
|
|
218
|
+
*/
|
|
219
|
+
export function suppressedTokens(config, atStart) {
|
|
220
|
+
const out = new Set(config.suppress_tokens ?? []);
|
|
221
|
+
if (atStart)
|
|
222
|
+
for (const id of config.begin_suppress_tokens ?? [])
|
|
223
|
+
out.add(id);
|
|
224
|
+
return out;
|
|
225
|
+
}
|
|
226
|
+
/** The id of a named token (`<|pt|>`, `<|transcribe|>`), or `null` — a model that does not have it must be SAID, not guessed. */
|
|
227
|
+
export function tokenIdOf(tokenizer, content) {
|
|
228
|
+
for (const added of tokenizer.added_tokens ?? [])
|
|
229
|
+
if (added.content === content)
|
|
230
|
+
return added.id;
|
|
231
|
+
const fromVocab = tokenizer.model?.vocab?.[content];
|
|
232
|
+
return fromVocab ?? null;
|
|
233
|
+
}
|
|
234
|
+
/** The next token: the biggest logit that is not refused. Greedy, because a child reading a known text needs no imagination. */
|
|
235
|
+
export function nextToken(logits, suppressed) {
|
|
236
|
+
let best = -1, bestValue = -Infinity;
|
|
237
|
+
for (let id = 0; id < logits.length; id++) {
|
|
238
|
+
if (suppressed?.has(id))
|
|
239
|
+
continue;
|
|
240
|
+
const value = logits[id];
|
|
241
|
+
if (value > bestValue) {
|
|
242
|
+
bestValue = value;
|
|
243
|
+
best = id;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return best;
|
|
247
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
import { type OnnxRuntime } from './onnx-runtime.js';
|
|
3
|
+
/** What the engine gets back: something that turns samples into words. */
|
|
4
|
+
export interface ReadingTranscriber {
|
|
5
|
+
/** 16 kHz mono samples in, the child's words out. Empty when the model answered nothing. */
|
|
6
|
+
transcribe(samples: Float32Array): Promise<string>;
|
|
7
|
+
}
|
|
8
|
+
export interface ReadingRuntimeDeps {
|
|
9
|
+
/** The page's address, which `heavy/` is resolved against. */
|
|
10
|
+
readonly base: string;
|
|
11
|
+
/** The child's language tag (`pt-BR`). A language the project has no model for is refused BY NAME. */
|
|
12
|
+
readonly language: string;
|
|
13
|
+
/**
|
|
14
|
+
* The `fetch` of the realm this runs in, lent by whoever opens the reading (ADR-0232 D4): the root's window where there is
|
|
15
|
+
* no worker, the worker's own scope inside `platform/reading-worker`. REQUIRED — this module reaches no global.
|
|
16
|
+
*/
|
|
17
|
+
readonly fetch: (url: string) => Promise<Response>;
|
|
18
|
+
/** Injected so a case can replay a real model's numbers without its 378 MiB; the default is the delivery's runtime. */
|
|
19
|
+
readonly ort?: OnnxRuntime;
|
|
20
|
+
readonly importModule?: (url: string) => Promise<unknown>;
|
|
21
|
+
/** A ceiling on the answer, so a model that loops does not hold the microphone for ever. */
|
|
22
|
+
readonly maxTokens?: number;
|
|
23
|
+
}
|
|
24
|
+
export declare function loadReadingRuntime(d: ReadingRuntimeDeps): Promise<ReadingTranscriber>;
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// platform/reading-runtime — THE READING, RUN (ADR-0216 §2; issues #185, #200): sound in, the child's words out.
|
|
3
|
+
//
|
|
4
|
+
// The model of the child's language is opened from the delivery (`heavy/` on the page's own origin, ADR-0177) and run here:
|
|
5
|
+
// the encoder once, then the decoder token by token until it says it is done. What each graph is called, what it takes and what
|
|
6
|
+
// comes back is not guessed — `scratchpad/leitura-de-referencia-3.py` ran the real files and its trace is what
|
|
7
|
+
// `tests/reading-runtime.node.test.js` replays.
|
|
8
|
+
//
|
|
9
|
+
// THREE FAMILIES, and they differ in ways that matter:
|
|
10
|
+
// · Whisper (pt) eats a log-mel spectrogram and opens with four tokens — «transcribe, in Portuguese, without timestamps» — and
|
|
11
|
+
// refuses 88 tokens that are not speech. Without that opening it answers in whatever language it feels like.
|
|
12
|
+
// · Moonshine (es) eats the waveform and opens with one token, in two decoder graphs like Whisper's.
|
|
13
|
+
// · Moonshine (en) is the same model with ONE MERGED decoder graph, told which pass it is on by `use_cache_branch` and fed empty
|
|
14
|
+
// caches on the first. It is the shape most easily written wrong from a guess, which is why it is traced too.
|
|
15
|
+
//
|
|
16
|
+
// ⚠️ THE CROSS-ATTENTION CACHE IS COMPUTED ONCE AND NEVER AGAIN. The first pass answers a `present.*.encoder.*` per layer that
|
|
17
|
+
// depends only on the sound; feeding back the new one each step is how a loop gets slower with every token AND drifts.
|
|
18
|
+
import { atDelivery, loadOnnxRuntime } from './onnx-runtime.js';
|
|
19
|
+
import { logMel, nextToken, readingModelFor, readingTextOf, suppressedTokens, tokenIdOf, WHISPER_BANDS, WHISPER_FRAMES, } from './reading-model.js';
|
|
20
|
+
const MAX_TOKENS = 200;
|
|
21
|
+
export async function loadReadingRuntime(d) {
|
|
22
|
+
const plan = readingModelFor(d.language);
|
|
23
|
+
if (!plan)
|
|
24
|
+
throw new Error(`reading: the project has no model for ${d.language}`);
|
|
25
|
+
const get = d.fetch;
|
|
26
|
+
const ort = d.ort ?? await loadOnnxRuntime({ base: d.base, importModule: d.importModule });
|
|
27
|
+
const asJson = async (id) => {
|
|
28
|
+
const r = await get(atDelivery(id, d.base));
|
|
29
|
+
if (!r.ok)
|
|
30
|
+
throw new Error(`reading: HTTP ${r.status} for ${id} — the delivery does not carry this language's model`);
|
|
31
|
+
return await r.json();
|
|
32
|
+
};
|
|
33
|
+
const asSession = async (id) => {
|
|
34
|
+
const r = await get(atDelivery(id, d.base));
|
|
35
|
+
if (!r.ok)
|
|
36
|
+
throw new Error(`reading: HTTP ${r.status} for ${id} — the delivery does not carry this language's model`);
|
|
37
|
+
return ort.InferenceSession.create(new Uint8Array(await r.arrayBuffer()), { executionProviders: ['wasm'] });
|
|
38
|
+
};
|
|
39
|
+
const [tokenizer, generation, config] = await Promise.all([
|
|
40
|
+
asJson(plan.tokenizer),
|
|
41
|
+
asJson(plan.generation),
|
|
42
|
+
asJson(plan.config),
|
|
43
|
+
]);
|
|
44
|
+
const melFilters = plan.input === 'log-mel' && plan.preprocessor
|
|
45
|
+
? (await asJson(plan.preprocessor)).mel_filters
|
|
46
|
+
: null;
|
|
47
|
+
const [encoder, decoder, decoderPast] = await Promise.all([
|
|
48
|
+
asSession(plan.encoder),
|
|
49
|
+
asSession(plan.decoder),
|
|
50
|
+
plan.decoderPast ? asSession(plan.decoderPast) : Promise.resolve(null),
|
|
51
|
+
]);
|
|
52
|
+
const start = openingOf(plan, tokenizer, generation);
|
|
53
|
+
const eos = generation.eos_token_id ?? -1;
|
|
54
|
+
const { heads, width } = cacheSizeOf(config);
|
|
55
|
+
const ceiling = d.maxTokens ?? MAX_TOKENS;
|
|
56
|
+
return {
|
|
57
|
+
async transcribe(samples) {
|
|
58
|
+
const encoded = await encoder.run(soundFor(ort, plan, samples, melFilters));
|
|
59
|
+
const hidden = encoded[encoder.outputNames[0]];
|
|
60
|
+
const shape = {
|
|
61
|
+
ort, start, hidden, heads, width,
|
|
62
|
+
frames: hidden.dims?.[1] ?? 0,
|
|
63
|
+
// the merged graph wants the caches present and EMPTY on the first pass, and the flag that says which pass this is
|
|
64
|
+
merged: !decoderPast,
|
|
65
|
+
};
|
|
66
|
+
const past = {};
|
|
67
|
+
const ids = [];
|
|
68
|
+
for (let step = 0; step < ceiling; step++) {
|
|
69
|
+
const first = step === 0;
|
|
70
|
+
const session = first || shape.merged ? decoder : decoderPast;
|
|
71
|
+
const answered = await session.run(stepInputs(shape, session, first, ids, past));
|
|
72
|
+
keepPast(past, session, answered, first);
|
|
73
|
+
const chosen = nextToken(lastLogits(answered[session.outputNames[0]]), suppressedTokens(generation, first));
|
|
74
|
+
// no token at all (a row of NaN) is an end, like the end-of-text: pushing it would ask for a word the vocabulary lacks
|
|
75
|
+
if (chosen < 0 || chosen === eos)
|
|
76
|
+
break;
|
|
77
|
+
ids.push(chosen);
|
|
78
|
+
}
|
|
79
|
+
return readingTextOf(tokenizer, ids).trim();
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* WHAT THE ENCODER HEARS. Whisper a log-mel picture of 30 s drawn with the model's own filters (80 bands × 3000 frames), Moonshine
|
|
85
|
+
* the wave itself, every sample. The names are the graphs' own: a real session refuses any other.
|
|
86
|
+
*/
|
|
87
|
+
function soundFor(ort, plan, samples, melFilters) {
|
|
88
|
+
return plan.input === 'log-mel'
|
|
89
|
+
? { input_features: new ort.Tensor('float32', logMel(samples, melFilters ?? []), [1, WHISPER_BANDS, WHISPER_FRAMES]) }
|
|
90
|
+
: { input_values: new ort.Tensor('float32', samples, [1, samples.length]) };
|
|
91
|
+
}
|
|
92
|
+
/** What one decoder step is handed: the opening on the first, then only the newest token, and the caches so far. */
|
|
93
|
+
function stepInputs(shape, session, first, ids, past) {
|
|
94
|
+
const { ort } = shape;
|
|
95
|
+
const inputIds = first ? shape.start : [ids[ids.length - 1]];
|
|
96
|
+
const inputs = {
|
|
97
|
+
input_ids: new ort.Tensor('int64', BigInt64Array.from(inputIds, BigInt), [1, inputIds.length]),
|
|
98
|
+
encoder_hidden_states: shape.hidden,
|
|
99
|
+
...(first && shape.merged ? emptyCaches(ort, session, shape.heads, shape.width, shape.frames) : past),
|
|
100
|
+
};
|
|
101
|
+
if (shape.merged)
|
|
102
|
+
inputs.use_cache_branch = new ort.Tensor('bool', Uint8Array.of(first ? 0 : 1), [1]);
|
|
103
|
+
return inputs;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* The caches carried to the next step: each `present.*` the decoder answered, renamed to the `past_key_values.*` it takes. The
|
|
107
|
+
* `logits` beside them are the answer, never a cache.
|
|
108
|
+
* ⚠️ ONLY the self-attention half grows. The cross-attention half is the sound, and the sound does not change.
|
|
109
|
+
*/
|
|
110
|
+
function keepPast(past, session, answered, first) {
|
|
111
|
+
for (const name of session.outputNames) {
|
|
112
|
+
if (!name.startsWith('present.'))
|
|
113
|
+
continue;
|
|
114
|
+
if (first || name.includes('.decoder.'))
|
|
115
|
+
past[name.replace('present.', 'past_key_values.')] = answered[name];
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* HOW THE DECODER IS TOLD WHAT TO DO. Moonshine opens with one token and reads what it hears. Whisper opens with four, and they
|
|
120
|
+
* are an instruction: transcribe (not translate), in THIS language, without timestamps. 📏 Measured: without them it answers in
|
|
121
|
+
* whatever language it decides, and with `<|translate|>` it answers in English — a child reading Portuguese would be corrected
|
|
122
|
+
* against an English transcript.
|
|
123
|
+
*/
|
|
124
|
+
function openingOf(plan, tokenizer, generation) {
|
|
125
|
+
const first = generation.decoder_start_token_id ?? 0;
|
|
126
|
+
if (plan.input !== 'log-mel')
|
|
127
|
+
return [first];
|
|
128
|
+
const named = [`<|${plan.language}|>`, '<|transcribe|>', '<|notimestamps|>'].map((c) => tokenIdOf(tokenizer, c));
|
|
129
|
+
if (named.some((id) => id === null))
|
|
130
|
+
throw new Error(`reading: this model has no token for ${plan.language} transcription`);
|
|
131
|
+
return [first, ...named];
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* How big one cache is, from `config.json`: the KEY-VALUE heads (a model with grouped attention keeps fewer than it attends with),
|
|
135
|
+
* and a width of `hidden_size / heads`, 64 where the config does not say.
|
|
136
|
+
*/
|
|
137
|
+
function cacheSizeOf(config) {
|
|
138
|
+
return {
|
|
139
|
+
heads: config.num_key_value_heads ?? config.num_attention_heads ?? 0,
|
|
140
|
+
width: config.hidden_size && config.num_attention_heads ? config.hidden_size / config.num_attention_heads : 64,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
/** The caches a merged decoder wants on its first pass: the self-attention halves empty, the cross-attention ones the sound's. */
|
|
144
|
+
function emptyCaches(ort, session, heads, width, frames) {
|
|
145
|
+
const out = {};
|
|
146
|
+
for (const name of session.inputNames ?? []) {
|
|
147
|
+
if (!name.startsWith('past_key_values.'))
|
|
148
|
+
continue;
|
|
149
|
+
const length = name.includes('.decoder.') ? 0 : frames;
|
|
150
|
+
out[name] = new ort.Tensor('float32', new Float32Array(heads * length * width), [1, heads, length, width]);
|
|
151
|
+
}
|
|
152
|
+
return out;
|
|
153
|
+
}
|
|
154
|
+
/** The logits of the LAST position: the decoder answers one row per token it was given, and only the newest one is a choice. */
|
|
155
|
+
function lastLogits(tensor) {
|
|
156
|
+
const t = tensor;
|
|
157
|
+
const vocab = t.dims?.[2] ?? t.data.length;
|
|
158
|
+
return t.data.subarray(t.data.length - vocab);
|
|
159
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
import { loadReadingRuntime } from './reading-runtime.js';
|
|
3
|
+
/** What the client asks for. */
|
|
4
|
+
export type ReadingRequest = {
|
|
5
|
+
readonly kind: 'load';
|
|
6
|
+
readonly base: string;
|
|
7
|
+
readonly language: string;
|
|
8
|
+
} | {
|
|
9
|
+
readonly kind: 'transcribe';
|
|
10
|
+
readonly id: number;
|
|
11
|
+
readonly samples: Float32Array;
|
|
12
|
+
};
|
|
13
|
+
/** What the worker answers. `id` is absent only on the answer to `load`, which nobody can ask twice. */
|
|
14
|
+
export type ReadingAnswer = {
|
|
15
|
+
readonly kind: 'ready';
|
|
16
|
+
} | {
|
|
17
|
+
readonly kind: 'text';
|
|
18
|
+
readonly id: number;
|
|
19
|
+
readonly text: string;
|
|
20
|
+
} | {
|
|
21
|
+
readonly kind: 'failed';
|
|
22
|
+
readonly id?: number;
|
|
23
|
+
readonly message: string;
|
|
24
|
+
};
|
|
25
|
+
/** The side of the conversation this file needs, so a gate can run the whole protocol without a thread. */
|
|
26
|
+
export interface WorkerScope {
|
|
27
|
+
onmessage: ((event: {
|
|
28
|
+
data: ReadingRequest;
|
|
29
|
+
}) => void) | null;
|
|
30
|
+
postMessage(answer: ReadingAnswer): void;
|
|
31
|
+
/**
|
|
32
|
+
* The worker realm's own `fetch`, which the model's files are fetched with (ADR-0232 D4): the runtime receives it from the
|
|
33
|
+
* scope, so the worker's entry reaches no bare global either.
|
|
34
|
+
*/
|
|
35
|
+
fetch(url: string): Promise<Response>;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Wires the protocol onto a scope. Exported — and not only run at import — so the gate exercises the REAL handler instead of a
|
|
39
|
+
* copy of it written in the test, which is the difference between measuring the protocol and measuring a description of it.
|
|
40
|
+
*/
|
|
41
|
+
export declare function serveReading(scope: WorkerScope, load?: typeof loadReadingRuntime): void;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// platform/reading-worker — THE TRANSCRIPTION, OFF THE MAIN THREAD (ADR-0216 §2; issue #185).
|
|
3
|
+
//
|
|
4
|
+
// 🔴 WHY A THREAD OF ITS OWN, and it is measured and not hygiene: `platform/reading-runtime` runs an encoder and then a decoder
|
|
5
|
+
// token by token, each one a wasm call of tens of milliseconds. On the main thread that is the thread that also draws the game
|
|
6
|
+
// and feeds the microphone — 📏 measured in the lab, Whisper transcribing on the main thread CUT THE AUDIO IN GAPS OF 4 s; the
|
|
7
|
+
// same work in a worker left a biggest gap of 264 ms. A child who reads aloud while the page transcribes loses the words she
|
|
8
|
+
// said during the pause, and nothing anywhere says so.
|
|
9
|
+
//
|
|
10
|
+
// The protocol is three messages and no state the caller cannot see:
|
|
11
|
+
// · `{ kind: 'load', base, language }` → `{ kind: 'ready' }` or `{ kind: 'failed', message }`
|
|
12
|
+
// · `{ kind: 'transcribe', id, samples }` → `{ kind: 'text', id, text }` or `{ kind: 'failed', id, message }`
|
|
13
|
+
// Every answer carries the `id` of the question, because a second reading may be asked for while the first is still running and
|
|
14
|
+
// an answer that cannot be matched to its question is an answer given to the wrong child.
|
|
15
|
+
//
|
|
16
|
+
// ⚠️ THE MODEL IS OPENED HERE, INSIDE THE WORKER, and that is the whole point: `loadReadingRuntime` fetches the graphs from the
|
|
17
|
+
// delivery and the ONNX runtime compiles them. Doing that on the main thread freezes the game for as long as it takes to open
|
|
18
|
+
// 378 MiB of model, which is the other half of the defect this file exists to remove.
|
|
19
|
+
import { loadReadingRuntime } from './reading-runtime.js';
|
|
20
|
+
const reason = (e) => (e instanceof Error ? e.message : String(e));
|
|
21
|
+
/**
|
|
22
|
+
* Wires the protocol onto a scope. Exported — and not only run at import — so the gate exercises the REAL handler instead of a
|
|
23
|
+
* copy of it written in the test, which is the difference between measuring the protocol and measuring a description of it.
|
|
24
|
+
*/
|
|
25
|
+
export function serveReading(scope, load = loadReadingRuntime) {
|
|
26
|
+
let transcriber = null;
|
|
27
|
+
scope.onmessage = (event) => {
|
|
28
|
+
const msg = event.data;
|
|
29
|
+
if (msg.kind === 'load') {
|
|
30
|
+
void (async () => {
|
|
31
|
+
try {
|
|
32
|
+
// a METHOD call on the scope: `fetch` detached from its realm throws «Illegal invocation»
|
|
33
|
+
transcriber = await load({ base: msg.base, language: msg.language, fetch: (url) => scope.fetch(url) });
|
|
34
|
+
scope.postMessage({ kind: 'ready' });
|
|
35
|
+
}
|
|
36
|
+
catch (e) {
|
|
37
|
+
scope.postMessage({ kind: 'failed', message: reason(e) });
|
|
38
|
+
}
|
|
39
|
+
})();
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
// ⚠️ A REQUEST BEFORE THE MODEL IS OPEN IS ANSWERED, not dropped: a dropped message leaves the caller's promise pending for
|
|
43
|
+
// ever, and a child waiting for words that will never come has no way to know she should try again.
|
|
44
|
+
void (async () => {
|
|
45
|
+
try {
|
|
46
|
+
if (!transcriber)
|
|
47
|
+
throw new Error('reading worker: asked to transcribe before the model was opened');
|
|
48
|
+
scope.postMessage({ kind: 'text', id: msg.id, text: await transcriber.transcribe(msg.samples) });
|
|
49
|
+
}
|
|
50
|
+
catch (e) {
|
|
51
|
+
scope.postMessage({ kind: 'failed', id: msg.id, message: reason(e) });
|
|
52
|
+
}
|
|
53
|
+
})();
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
if (typeof WorkerGlobalScope !== 'undefined' && typeof self !== 'undefined' && self instanceof WorkerGlobalScope) {
|
|
57
|
+
serveReading(self);
|
|
58
|
+
}
|