@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
|
@@ -1,117 +1,145 @@
|
|
|
1
1
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
-
// ui/pause-icons — the PER-SCREEN pause
|
|
3
|
-
// Extracted VERBATIM from game.js (Estágio 4): PAUSE_ICONS, buildScreenPause, calmMode/applyCalm, iconAct,
|
|
4
|
-
// iconLabel, reflectIconBtn, reflectPauseIcons and hasPrivateOutput.
|
|
2
|
+
// ui/pause-icons — the PER-SCREEN pause card (`.screen-pause`) and the accessibility quick bar (`.pi-btn`).
|
|
5
3
|
//
|
|
6
|
-
// WHY THE
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
4
|
+
// WHY THE QUICK BAR IS ITS OWN MODULE: its buttons are the ONLY place where a state owned by another subsystem (blind
|
|
5
|
+
// mode, TTS, Libras, calm mode, the input mode, contrast, colour correction, camera, voice, speed, language) is both
|
|
6
|
+
// changed AND read back as an `aria-label`. That round-trip — «the label must tell the truth about the state» — is why
|
|
7
|
+
// `computeIconLabel` exists, and it is what the pure functions below make testable in node.
|
|
10
8
|
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
// guards, same announcements — only the shape changed.
|
|
9
|
+
// Each icon's action is a row of a TABLE (`ICON_ACTS`), not a chain of `if`s: the list of subsystems is long by nature,
|
|
10
|
+
// one button each.
|
|
14
11
|
//
|
|
15
|
-
//
|
|
16
|
-
// · `
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
import {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
import {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
import
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
12
|
+
// INJECTED, not owned here:
|
|
13
|
+
// · `setPauseActor` — who opened the card; the panels open scoped to that player. This module only WRITES it.
|
|
14
|
+
// · `getPauseActs` — the `.pm-btn` action table (`ui/shell` or the host's own), asked when an item is pressed, so it
|
|
15
|
+
// can be wired after this module.
|
|
16
|
+
// · the pause screens, asked through a getter: the host rebuilds them when the number of screens changes.
|
|
17
|
+
// · `rm`/`saveRM` — the reduced-motion flags, shared with ui/settings-motion (same reference).
|
|
18
|
+
// · `matchMedia` — the browser's media query, for the reduced-motion default when nothing is stored (ADR-0232).
|
|
19
|
+
// · `PM_BTNS` and the other lists of buttons are `ui/pause-buttons`', imported, never copied.
|
|
20
|
+
import { flagOf, nextLocale, LANGUAGE_NAME } from './locale-flags.js';
|
|
21
|
+
/*
|
|
22
|
+
* 🔴 THE TWO VISUAL CYCLES MOVED HOUSE to `core/visual-cycles` (ADR-0221, issue #203). They lived in TWO modules — the list of
|
|
23
|
+
* levels in the panel, the steps and the names here — and the thing that costs most to maintain is the ASYMMETRY between them,
|
|
24
|
+
* which only reads with both in sight. See the header there.
|
|
25
|
+
*/
|
|
26
|
+
import { SHORT_THEME, SHORT_CORRECTION } from './visual-axes-panel.js';
|
|
27
|
+
/*
|
|
28
|
+
* 🔴 CALM MODE MOVED HOUSE (ADR-0221, issue #203). It is not about ICONS: it is about what a child who cannot bear noise needs
|
|
29
|
+
* the engine to silence, and the icon is only one of the surfaces she asks through. It lives in `core/calm-mode`, a leaf
|
|
30
|
+
* module — zero imports, zero DOM — which is why the destructive clamp of level 1 can be measured without mounting anything.
|
|
31
|
+
*/
|
|
32
|
+
import { CALM_NAMES, CALM_AUDIO_CATS, nextCalmMode, sanitiseTeaLevel, calmAudioPlan, calmMotionPlan } from '../core/calm-mode.js';
|
|
33
|
+
/*
|
|
34
|
+
* 🔴 THE MARKUP MOVED HOUSE to `ui/pause-markup` (ADR-0221, issue #203). Building the strings and wiring the elements the
|
|
35
|
+
* browser makes from them are two jobs that do not need each other; what is left in this file is the second one. See the
|
|
36
|
+
* header there for why the icon catalogue had to leave before the markup could.
|
|
37
|
+
*/
|
|
38
|
+
import { quickBarMarkup, screenPauseMarkup, } from './pause-markup.js';
|
|
39
|
+
/*
|
|
40
|
+
* 🔴 THE ICON CATALOGUE MOVED HOUSE to `core/pause-icon-catalogue` (ADR-0221, issue #203): WHICH icons exist and in what order
|
|
41
|
+
* is DATA, and this file is about what they DO. See the header there for why the data had to leave first.
|
|
42
|
+
*/
|
|
43
|
+
import { PAUSE_ICONS, pauseIcon } from '../core/pause-icon-catalogue.js';
|
|
44
|
+
import { nextTheme, nextCorrection, hasHighContrast, DEFAULT_VISUAL, } from '../render/viz-axes.js';
|
|
45
|
+
import { announceItem } from './item-announcement.js';
|
|
46
|
+
import { accessibleLabel } from '../core/accessible-label.js';
|
|
47
|
+
import { stepInRing } from '../core/ring.js'; // from the LEAF, not from ui/menu-nav: see the note there
|
|
48
|
+
// A LIVE BINDING (ESM): the index can be turned off in the menu, and the value here follows without a subscription.
|
|
49
|
+
// The stateless half (ADR-0232): the defaults, the reduced-motion question and the 📷 cycle are vocabulary, not the store.
|
|
50
|
+
import { DEFAULTS, defaultReducedMotion } from '../core/setting-defaults.js';
|
|
51
|
+
import { nextCameraControl } from '../core/camera-cycle.js';
|
|
52
|
+
/** The word for each position of the 📷 cycle (ADR-0215). */
|
|
53
|
+
const CAMERA_MODE_NAME = { off: 'state.off', hands: 'camera.hands', face: 'camera.face', eyes: 'camera.eyes' };
|
|
46
54
|
/**
|
|
47
|
-
*
|
|
55
|
+
* The word for each position. Private again since 2026-09-21: it was exported for the motor panel's row, and the Dev took that
|
|
56
|
+
* row out («Tire a linha de acessibilidade motora») — the icon is the only surface of this setting now.
|
|
57
|
+
*/
|
|
58
|
+
const INPUT_MODE_NAME = { standard: 'input.standard', sticky: 'input.sticky', scan: 'input.scan' };
|
|
59
|
+
/**
|
|
60
|
+
* Which of the three is showing. The SCAN WINS over the latch on purpose: with one button there is nothing to hold, so a stored
|
|
61
|
+
* latch would otherwise make the icon claim a position the child is not in.
|
|
62
|
+
*/
|
|
63
|
+
export function inputModeOf(s) {
|
|
64
|
+
return s.switchScan ? 'scan' : s.toggleMove ? 'sticky' : 'standard';
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* The next position. Two things take one away, and neither is an error to report — a cycle simply never stops where nothing
|
|
68
|
+
* would happen (ADR-0155), and a word that promised what this game cannot do would teach a child that her setting is broken
|
|
69
|
+
* (ADR-0106 §5):
|
|
48
70
|
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
71
|
+
* · A GAME THAT HOLDS NO KEY has nothing for the latch to hold, so the icon offers standard and one button.
|
|
72
|
+
* · A DEVICE THAT SENDS ONE COMMAND AT A TIME always latches and cannot be asked not to (ADR-0113 clause 3, ADR-0211), so
|
|
73
|
+
* `standard` is unreachable there. 🔴 That used to grey the whole icon out, and with three positions that would have cost a
|
|
74
|
+
* child playing with her eyes the one-button scan as well — the lock is the latch's, not the scan's.
|
|
75
|
+
*/
|
|
76
|
+
function inputModeOrder(holdsKeys, latchRequired = false) {
|
|
77
|
+
if (!holdsKeys)
|
|
78
|
+
return ['standard', 'scan'];
|
|
79
|
+
return latchRequired ? ['sticky', 'scan'] : ['standard', 'sticky', 'scan'];
|
|
80
|
+
}
|
|
81
|
+
export function nextInputMode(m, holdsKeys, latchRequired = false) {
|
|
82
|
+
const order = inputModeOrder(holdsKeys, latchRequired);
|
|
83
|
+
const i = order.indexOf(m);
|
|
84
|
+
return order[(i < 0 ? 0 : i + 1) % order.length];
|
|
85
|
+
}
|
|
86
|
+
// The rule for applying an input mode is written in the icon's own action, where it happens: the ☝️ is the only surface of
|
|
87
|
+
// this setting.
|
|
88
|
+
import { nextGameSpeed } from '../core/game-speed.js';
|
|
89
|
+
import { KEYS } from '../platform/storage-keys.js';
|
|
90
|
+
import { setMoveLatch } from './settings-mobility.js';
|
|
91
|
+
import { latchRefusal } from './latch-refusal.js';
|
|
92
|
+
import { PM_BTNS, PM_OPTIONS_BTNS, PM_GAME_BTNS } from './pause-buttons.js';
|
|
93
|
+
import { SCENE_KEYS, CHARACTER_ANIMATIONS, readStoredScene, storeScene } from './motion-scene.js';
|
|
94
|
+
/**
|
|
95
|
+
* Hover and focus on a bar's icons write its NAME in the bar's `.pause-icons-cap` and ask for its EXPLANATION.
|
|
53
96
|
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
97
|
+
* Shared by the per-screen quick bars and the bar the engine mounts in `#title-icons`: two copies of this wiring is
|
|
98
|
+
* how one bar showed the name and the other did not — the Dev found the engine's bar silent on hover and on the
|
|
99
|
+
* cursor. Leaving an icon falls back to the CURSOR of the bar's mode when there is one (`.pi-sel`): it is the only
|
|
100
|
+
* thing saying where that cursor is.
|
|
56
101
|
*/
|
|
57
|
-
export function
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
102
|
+
export function wireBarCaption(bar, explain) {
|
|
103
|
+
const cap = bar.querySelector('.pause-icons-cap');
|
|
104
|
+
const captionFor = (b) => {
|
|
105
|
+
if (cap)
|
|
106
|
+
cap.textContent = accessibleLabel(b); // name and state only: «N de M» is spoken, never written (ADR-0167)
|
|
107
|
+
explain(b.dataset.pi ?? null);
|
|
108
|
+
};
|
|
109
|
+
const restoreCaption = () => {
|
|
110
|
+
const cursor = bar.querySelector('.pi-sel');
|
|
111
|
+
if (cursor) {
|
|
112
|
+
captionFor(cursor);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
if (cap)
|
|
116
|
+
cap.textContent = '';
|
|
117
|
+
explain(null);
|
|
118
|
+
};
|
|
119
|
+
bar.querySelectorAll('.pi-btn').forEach((b) => {
|
|
120
|
+
b.addEventListener('mouseenter', () => captionFor(b));
|
|
121
|
+
b.addEventListener('focus', () => captionFor(b));
|
|
122
|
+
b.addEventListener('mouseleave', restoreCaption);
|
|
123
|
+
b.addEventListener('blur', restoreCaption);
|
|
124
|
+
});
|
|
63
125
|
}
|
|
64
|
-
/** The accessibility shortcut bar at the top of every pause screen (and of the splash `#title-icons`).
|
|
65
|
-
* Sound-bound icons (blind/TTS) require a private audio output; webcam/voice are still `soon`.
|
|
66
|
-
* VERBATIM from game.js in order and behaviour; the names became i18n keys in the Fase-5 pass. */
|
|
67
|
-
// `n` é a CHAVE i18n do nome do ícone (o emoji `e` não traduz — é o mesmo glifo em toda língua).
|
|
68
|
-
export const PAUSE_ICONS = [
|
|
69
|
-
{ k: 'blind', e: '🦯', n: 'icon.blind' },
|
|
70
|
-
{ k: 'tts', e: '🗨️', n: 'icon.tts' },
|
|
71
|
-
{ k: 'libras', e: '🤟', n: 'icon.libras' },
|
|
72
|
-
{ k: 'tea', e: '🧩', n: 'icon.tea' },
|
|
73
|
-
{ k: 'altmove', e: '☝️', n: 'icon.altmove' }, // ☝️ e não 🦾: o gesto é UM DEDO tocando, que é o que a alternância pede (pedido do Dev)
|
|
74
|
-
{ k: 'contrast', e: '🌗', n: 'icon.contrast' },
|
|
75
|
-
{ k: 'cvd', e: '🚥', n: 'icon.cvd' },
|
|
76
|
-
{ k: 'face', e: '🧑', n: 'icon.face', soon: true },
|
|
77
|
-
{ k: 'eyes', e: '👀', n: 'icon.eyes', soon: true },
|
|
78
|
-
{ k: 'voice', e: '👄', n: 'icon.voice', soon: true },
|
|
79
|
-
];
|
|
80
|
-
const ICON_BY_KEY = new Map(PAUSE_ICONS.map((ic) => [ic.k, ic]));
|
|
81
|
-
export function pauseIcon(k) { return ICON_BY_KEY.get(k); }
|
|
82
|
-
/** TEA cycle: 0 = normal · 1 = calmo (reduces) · 2 = silencioso (switches off). Never touches TTS/blind mode. */
|
|
83
|
-
export const CALM_NAMES = ['calm.off', 'calm.quiet', 'calm.silent'];
|
|
84
126
|
/**
|
|
85
|
-
*
|
|
86
|
-
*
|
|
127
|
+
* What an icon of the quick bar SAYS — one function, not three copies of the same expression. It is spoken at moments
|
|
128
|
+
* that look different and are the same (the cursor lands on the icon, a finger activates it), and the `aria-label` it
|
|
129
|
+
* reads already carries the STATE («Alto contraste, ativado»): `reflectIconBtn` rewrites it on every change, which is why
|
|
130
|
+
* this reads it back instead of rebuilding the text.
|
|
87
131
|
*/
|
|
88
|
-
|
|
89
|
-
|
|
132
|
+
function iconCaption(t, barEl, el, menuIndexOn) {
|
|
133
|
+
const icons = [...barEl.querySelectorAll('.pi-btn')];
|
|
134
|
+
// «A declared label wins» is `core/accessible-label`'s rule, shared with the pause list and every menu: one answer to
|
|
135
|
+
// «what is this control called».
|
|
136
|
+
return announceItem(t, { label: accessibleLabel(el), position: icons.indexOf(el) + 1, total: icons.length }, menuIndexOn);
|
|
90
137
|
}
|
|
91
|
-
/**
|
|
92
|
-
function
|
|
93
|
-
return
|
|
138
|
+
/** Reads the calm level from storage, sanitised. Called in `init`, never on import. */
|
|
139
|
+
function readTeaLevel(store) {
|
|
140
|
+
return sanitiseTeaLevel(store.getNum(KEYS.tea, DEFAULTS.calmMode), DEFAULTS.calmMode);
|
|
94
141
|
}
|
|
95
|
-
/**
|
|
96
|
-
export const CALM_AUDIO_CATS = ['ambient', 'music', 'earcons', 'other', 'interact'];
|
|
97
|
-
/** Colour-vision-deficiency cycle, in `player.viz` values. */
|
|
98
|
-
export const CVD_SEQ = ['normal', 'fix-protan', 'fix-deuter', 'fix-tritan'];
|
|
99
|
-
/** i18n keys of the CVD announcement names, indexed the same as CVD_SEQ.
|
|
100
|
-
*
|
|
101
|
-
* ⚠️ POSITION 0 IS `cvd.tricro` AND NOT `cvd.off`, AND THE TWO KEYS ARE NOT INTERCHANGEABLE.
|
|
102
|
-
* This list names the four CHOICES of the cycle, so position 0 is a way of seeing — trichromatic
|
|
103
|
-
* vision, the one that needs no correction — and it is said as one. `cvd.off` below is a FALLBACK
|
|
104
|
-
* for `s.viz` values that are not corrections at all, and 13 of the 16 viz modes are exactly that:
|
|
105
|
-
* the three simulations, the three contrast levels, the five low-vision modes and blind mode.
|
|
106
|
-
* Announcing "trichromatic vision" there would have the software assert what the child sees, while
|
|
107
|
-
* she is simulating not seeing it. The choice is named; the fallback is switched off. */
|
|
108
|
-
export const CVD_NAMES = ['cvd.tricro', 'cvd.protan', 'cvd.deuter', 'cvd.tritan'];
|
|
109
|
-
/** `player.viz` → i18n key of the label used by iconLabel (anything else falls back to the 'off' key). */
|
|
110
|
-
export const CVD_LABELS = {
|
|
111
|
-
'fix-protan': 'cvd.protan', 'fix-deuter': 'cvd.deuter', 'fix-tritan': 'cvd.tritan',
|
|
112
|
-
};
|
|
113
|
-
/** A player has private output when nobody else is on the same sink. Single screen ⇒ always private.
|
|
114
|
-
* Pure form of game.js's hasPrivateOutput (see the report: it had no other caller left). */
|
|
142
|
+
/** A player has private output when nobody else is on the same sink. Single screen ⇒ always private. */
|
|
115
143
|
export function hasPrivateOutputIn(list, count, i) {
|
|
116
144
|
if (count <= 1)
|
|
117
145
|
return true;
|
|
@@ -120,234 +148,173 @@ export function hasPrivateOutputIn(list, count, i) {
|
|
|
120
148
|
return false;
|
|
121
149
|
return !list.some((q, j) => j !== i && q && q.audioSink === p.audioSink);
|
|
122
150
|
}
|
|
123
|
-
/** TEA cycle step. */
|
|
124
|
-
export function nextCalmMode(cur) { return (cur + 1) % 3; }
|
|
125
|
-
/** Next high-contrast level. An unlisted `viz` (e.g. a CVD filter) is treated as index 0 ⇒ jumps to 'hc-direto'. */
|
|
126
|
-
export function nextContrast(cur) {
|
|
127
|
-
let idx = CONTRAST_LEVELS.indexOf(cur);
|
|
128
|
-
idx = idx < 0 ? 0 : idx;
|
|
129
|
-
return CONTRAST_LEVELS[(idx + 1) % CONTRAST_LEVELS.length];
|
|
130
|
-
}
|
|
131
|
-
/** Next CVD filter. NOTE the asymmetry with nextContrast: an unlisted `viz` maps to index 1 ('fix-protan'),
|
|
132
|
-
* not 0 — verbatim from game.js (`idx = idx<0 ? 1 : (idx+1)%seq.length`). */
|
|
133
|
-
export function nextCvd(cur) {
|
|
134
|
-
let idx = CVD_SEQ.indexOf(cur);
|
|
135
|
-
idx = idx < 0 ? 1 : (idx + 1) % CVD_SEQ.length;
|
|
136
|
-
return { idx, mode: CVD_SEQ[idx] };
|
|
137
|
-
}
|
|
138
|
-
/** What `applyCalm` does to ONE audio category, given the TEA level. Extracted so the (destructive) volume
|
|
139
|
-
* clamp at level 1 is visible and testable — see the report. */
|
|
140
|
-
export function calmAudioPlan(calmMode, vol) {
|
|
141
|
-
if (calmMode === 0)
|
|
142
|
-
return { on: true, vol };
|
|
143
|
-
if (calmMode === 1)
|
|
144
|
-
return { on: true, vol: Math.min(vol, 0.3) };
|
|
145
|
-
return { on: false, vol };
|
|
146
|
-
}
|
|
147
|
-
/** What `applyCalm` does to the scene/character reduced-motion flags. */
|
|
148
|
-
export function calmMotionPlan(calmMode) {
|
|
149
|
-
return { sceneReduced: calmMode >= 1, charFrozen: calmMode === 2 };
|
|
150
|
-
}
|
|
151
151
|
/** The `aria-label` of one icon — it MUST reflect the current state, on/off or level. This is the whole
|
|
152
152
|
* point of the function: a toggle that looks pressed but does not say so is invisible to a screen reader. */
|
|
153
|
-
export function computeIconLabel(k, s) {
|
|
154
|
-
const ic =
|
|
153
|
+
export function computeIconLabel(t, k, s) {
|
|
154
|
+
const ic = pauseIcon(k);
|
|
155
155
|
if (!ic)
|
|
156
156
|
return '';
|
|
157
|
-
|
|
158
|
-
//
|
|
159
|
-
//
|
|
160
|
-
|
|
161
|
-
return t('icon.soon', { nome: t(ic.n) });
|
|
162
|
-
const rotulo = (v) => t('icon.state', { nome: t(ic.n), v: t(v) });
|
|
163
|
-
if (k === 'blind')
|
|
164
|
-
return rotulo(s.modoCego ? 'state.on' : 'state.off');
|
|
165
|
-
if (k === 'tts')
|
|
166
|
-
return rotulo(s.ttsOn ? 'state.on' : 'state.off');
|
|
167
|
-
if (k === 'libras')
|
|
168
|
-
return rotulo(s.librasOn ? 'state.on' : 'state.off');
|
|
169
|
-
// TEA e daltonismo usam um nome CURTO aqui, diferente do nome do botão: o rótulo já diz o nível, e
|
|
170
|
-
// repetir a lista de níveis do nome ("(calmo / silencioso)", "(protan/deutan/tritan)") a diria duas vezes.
|
|
171
|
-
// Era assim antes da conversão, com o texto curto embutido — preservado, não reinventado.
|
|
172
|
-
if (k === 'tea')
|
|
173
|
-
return t('icon.state', { nome: t('icon.tea.short'), v: t(CALM_NAMES[s.calmMode]) });
|
|
174
|
-
if (k === 'altmove')
|
|
175
|
-
return rotulo(s.toggleMove ? 'state.on' : 'state.off');
|
|
176
|
-
if (k === 'contrast')
|
|
177
|
-
return rotulo(CURTO_DO_TEMA[s.visual.tema]);
|
|
178
|
-
if (k === 'cvd')
|
|
179
|
-
return t('icon.state', { nome: t('icon.cvd.short'), v: t(CURTO_DA_CORRECAO[s.visual.correcao]) });
|
|
180
|
-
return t(ic.n);
|
|
157
|
+
const state = STATE_OF_ICON[k];
|
|
158
|
+
// The state is ALWAYS a parameter (`{v}`), never a concatenation: a language that puts the state before the name needs
|
|
159
|
+
// the dictionary to reorder it. «name: state» is the frame; the state is the content, and it is translated too.
|
|
160
|
+
return state ? t('icon.state', { nome: t(SHORT_NAME[k] ?? ic.n), v: state(t, s) }) : t(ic.n);
|
|
181
161
|
}
|
|
182
|
-
|
|
162
|
+
const onOff = (t, on) => t(on ? 'state.on' : 'state.off');
|
|
163
|
+
/** What each icon with a state says after its name, already in the child's language. An icon not here has no state to say. */
|
|
164
|
+
const STATE_OF_ICON = {
|
|
165
|
+
blind: (t, s) => onOff(t, s.blindMode),
|
|
166
|
+
tts: (t, s) => onOff(t, s.ttsOn),
|
|
167
|
+
libras: (t, s) => onOff(t, s.librasOn),
|
|
168
|
+
tea: (t, s) => t(CALM_NAMES[s.calmMode]),
|
|
169
|
+
altmove: (t, s) => t(INPUT_MODE_NAME[inputModeOf(s)]),
|
|
170
|
+
contrast: (t, s) => t(SHORT_THEME[s.visual.tema]),
|
|
171
|
+
cvd: (t, s) => t(SHORT_CORRECTION[s.visual.correcao]),
|
|
172
|
+
camera: (t, s) => t(CAMERA_MODE_NAME[s.camera ?? 'off']),
|
|
173
|
+
voice: (t, s) => onOff(t, s.voice),
|
|
174
|
+
// a language's own name is not translated: «Español» reads the same in every interface
|
|
175
|
+
idioma: (_t, s) => LANGUAGE_NAME[(s.locale ?? 'pt')] ?? LANGUAGE_NAME.pt,
|
|
176
|
+
velocidade: (t, s) => t('icon.velocidade.valor', { pct: Math.round((s.speed ?? 1) * 100) }),
|
|
177
|
+
};
|
|
178
|
+
/**
|
|
179
|
+
* Calm mode and colour correction have a SHORT name key for the label, because the button's name once carried the list of
|
|
180
|
+
* levels («(calmo / silencioso)», «(protan/deutan/tritan)») and the label already says the level. ⚠️ In the three
|
|
181
|
+
* dictionaries the short key now equals the long one word for word: the distinction has no subject left, and merging the
|
|
182
|
+
* keys is a dictionary change.
|
|
183
|
+
*/
|
|
184
|
+
const SHORT_NAME = { tea: 'icon.tea.short', cvd: 'icon.cvd.short' };
|
|
185
|
+
const ICON_VISUAL = Object.freeze({
|
|
186
|
+
blind: (s) => ({ on: s.blindMode, dis: !s.privateOutput }),
|
|
187
|
+
tts: (s) => ({ on: s.ttsOn, dis: !s.privateOutput || !!s.noVoice }),
|
|
188
|
+
libras: (s) => ({ on: s.librasOn }),
|
|
189
|
+
tea: (s) => ({ on: s.calmMode === 2, calm: s.calmMode === 1 }),
|
|
190
|
+
// ⚠️ AND IT IS NEVER GREYED OUT. `latchRequired` says the DEVICE in use sends one command at a time and the latch cannot be
|
|
191
|
+
// turned off (ADR-0113 clause 3) — which the CYCLE tells, since `standard` simply does not appear in it. Greying the icon
|
|
192
|
+
// would take the one-button scan away from the child playing with their eyes, who is the likeliest to need it.
|
|
193
|
+
altmove: (s) => ({ on: inputModeOf(s) !== 'standard' }),
|
|
194
|
+
contrast: (s) => ({ on: hasHighContrast(s.visual) }),
|
|
195
|
+
velocidade: (s) => ({ on: (s.speed ?? 1) < 1 }),
|
|
196
|
+
camera: (s) => ({ on: (s.camera ?? 'off') !== 'off' }),
|
|
197
|
+
voice: (s) => ({ on: !!s.voice }),
|
|
198
|
+
// ⚠️ THE TWO-TONE BACKGROUND IS THIS ICON'S «ON» SIGNAL, and it reads the correction AXIS — so it still says the same when
|
|
199
|
+
// the high-contrast theme is on too, and the child's correction does not vanish from the icon that exists to show it.
|
|
200
|
+
cvd: (s) => ({ cvd: s.visual.correcao !== 'tricro' ? `pi-cvd-${s.visual.correcao}` : '' }),
|
|
201
|
+
});
|
|
202
|
+
/** At rest: an icon the table does not name shows nothing. */
|
|
203
|
+
const ICON_VISUAL_AT_REST = Object.freeze({ on: false, dis: false, calm: false, cvd: '', active: false });
|
|
204
|
+
/** Pure form of reflectIconBtn's branching. */
|
|
183
205
|
export function computeIconVisual(k, s) {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
}
|
|
189
|
-
else if (k === 'tts') {
|
|
190
|
-
on = s.ttsOn;
|
|
191
|
-
dis = !s.privateOutput;
|
|
192
|
-
}
|
|
193
|
-
else if (k === 'libras') {
|
|
194
|
-
on = s.librasOn;
|
|
195
|
-
}
|
|
196
|
-
else if (k === 'tea') {
|
|
197
|
-
on = s.calmMode === 2;
|
|
198
|
-
calm = s.calmMode === 1;
|
|
199
|
-
}
|
|
200
|
-
else if (k === 'altmove') {
|
|
201
|
-
on = s.toggleMove;
|
|
202
|
-
dis = !!s.alternanciaExigida;
|
|
203
|
-
}
|
|
204
|
-
else if (k === 'contrast') {
|
|
205
|
-
on = temAltoContraste(s.visual);
|
|
206
|
-
}
|
|
207
|
-
else if (k === 'cvd') {
|
|
208
|
-
// ⚠️ O FUNDO DE DUAS CORES É O SINAL DE LIGADO deste ícone, e agora ele lê o EIXO da correção — que
|
|
209
|
-
// continua a dizer o mesmo quando o tema também está ligado, coisa que a chave única não conseguia: com
|
|
210
|
-
// `hc-direto-7` no campo, a correção da criança desaparecia do ícone que existe para a mostrar.
|
|
211
|
-
if (s.visual.correcao !== 'tricro')
|
|
212
|
-
cvd = 'pi-cvd-' + s.visual.correcao;
|
|
213
|
-
}
|
|
214
|
-
return { on, dis, calm, cvd, active: on || calm || !!cvd };
|
|
206
|
+
const v = { ...ICON_VISUAL_AT_REST, ...ICON_VISUAL[k]?.(s) };
|
|
207
|
+
// 📌 `active` is DERIVED, declared by no rule: it is the `aria-pressed`, and no icon should be able to say it is pressed
|
|
208
|
+
// without showing why.
|
|
209
|
+
return { ...v, active: v.on || v.calm || !!v.cvd };
|
|
215
210
|
}
|
|
216
211
|
/** The CSS classes reflectIconBtn clears before applying a fresh visual — in the original order. */
|
|
217
212
|
export const ICON_STATE_CLASSES = ['pi-calm', 'pi-cvd-protan', 'pi-cvd-deuter', 'pi-cvd-tritan'];
|
|
218
|
-
// --- markup (pure string builders; the DOM shell below just assigns them) ---
|
|
219
|
-
/** One `.pi-btn`. `soon` icons get `.pi-soon` and the "under construction" suffix baked into the aria-label.
|
|
220
|
-
* The label is the RESTING one: reflectIconBtn overwrites it with the stateful label as soon as the bar is
|
|
221
|
-
* reflected. `ic.n` is an i18n key, so it must be resolved here too — the markup is rendered once at build
|
|
222
|
-
* time and would otherwise ship the raw key to a screen reader. */
|
|
223
|
-
export function iconBtnMarkup(ic) {
|
|
224
|
-
return '<button class="pi-btn' + (ic.soon ? ' pi-soon' : '') + '" type="button" data-pi="' + ic.k +
|
|
225
|
-
'" aria-label="' + (ic.soon ? t('icon.soon', { nome: t(ic.n) }) : t(ic.n)) + '">' + ic.e + '</button>';
|
|
226
|
-
}
|
|
227
|
-
export function iconesQueAccionam(escritores) {
|
|
228
|
-
// ⚠️ POR ÍCONE, e não um booleano para os dois — e foi uma MUTAÇÃO SOBREVIVENTE que o mostrou. Com uma
|
|
229
|
-
// única bandeira, `&&` e `||` produziam o mesmo resultado nos casos que eu tinha escrito, porque todos
|
|
230
|
-
// tiravam os DOIS escritores. O `&&` escondia um ícone que FUNCIONA quando só um escritor falta, e o `||`
|
|
231
|
-
// mostrava um que NÃO funciona. Os dois erram, em direcções opostas, e a pergunta certa nunca foi «este
|
|
232
|
-
// jogo tem escritores visuais» — é «este ÍCONE tem quem o accione».
|
|
233
|
-
// 📌 E o `altmove` entra pela MESMA porta, que é o achado: «este jogo segura teclas?» é a mesma pergunta
|
|
234
|
-
// que «este ícone tem quem o accione», feita a um campo do contrato em vez de a um escritor injectado.
|
|
235
|
-
// Um terceiro ramo, e não uma regra nova.
|
|
236
|
-
return PAUSE_ICONS.filter((ic) => (ic.k === 'contrast' ? escritores.tema
|
|
237
|
-
: ic.k === 'cvd' ? escritores.correcao
|
|
238
|
-
: ic.k === 'altmove' ? escritores.seguraTeclas
|
|
239
|
-
: true));
|
|
240
|
-
}
|
|
241
213
|
/**
|
|
242
|
-
*
|
|
214
|
+
* WHO ACTIVATES EACH ICON — one question per icon, and an icon nothing locks is offered.
|
|
243
215
|
*
|
|
244
|
-
*
|
|
245
|
-
*
|
|
246
|
-
*
|
|
216
|
+
* PER ICON, not one flag for the whole bar: with one flag, a single missing writer would either hide an icon that WORKS
|
|
217
|
+
* or show one that does NOT. The question is «does this ICON have something to activate it». A table, so adding an icon
|
|
218
|
+
* is adding a row.
|
|
247
219
|
*/
|
|
248
|
-
|
|
220
|
+
const ICON_IS_ACTIONABLE = Object.freeze({
|
|
221
|
+
contrast: (w) => w.theme,
|
|
222
|
+
cvd: (w) => w.correction,
|
|
223
|
+
// «Does this game hold keys?» is the same question, asked of a contract field rather than an injected writer.
|
|
224
|
+
// AND A SECOND HALF (ADR-0218): «one button only» has a subject wherever the game declares a position to scan, so a game
|
|
225
|
+
// holding no key (the quiz demo) still has a ☝️. A game that declares nothing has none: a scan of nothing is the dead
|
|
226
|
+
// button of ADR-0106 §5 paid for in seconds.
|
|
227
|
+
altmove: (w) => w.holdsKeys() || (w.declaredPositions?.() ?? 0) > 0,
|
|
228
|
+
// The same question asked of the typography cycle (ADR-0149): the icon exists when someone knows how to walk it.
|
|
229
|
+
// `Boolean(...)` and not the raw field: it is optional, and the conversion is written rather than left to a `filter`.
|
|
230
|
+
tipografia: (w) => Boolean(w.typography),
|
|
231
|
+
// the hourglass exists where time runs by itself (ADR-0180): a turn game has nothing to slow
|
|
232
|
+
velocidade: (w) => Boolean(w.clock?.()),
|
|
233
|
+
camera: (w) => Boolean(w.camera),
|
|
234
|
+
// 👄 exists where there is a MICROPHONE to ask for, the same rule as the camera's (ADR-0106 §5)
|
|
235
|
+
voice: (w) => Boolean(w.microphone),
|
|
236
|
+
menu: (w) => Boolean(w.menus),
|
|
237
|
+
});
|
|
238
|
+
export function iconsThatAct(writers) {
|
|
239
|
+
// ABSENCE FROM THE TABLE MEANS YES: an icon no rule locks depends on nobody to work, and hiding it for lack of a row
|
|
240
|
+
// would take away from the child a way that exists.
|
|
241
|
+
return PAUSE_ICONS.filter((ic) => ICON_IS_ACTIONABLE[ic.k]?.(writers) ?? true);
|
|
242
|
+
}
|
|
249
243
|
/**
|
|
250
|
-
*
|
|
251
|
-
*
|
|
252
|
-
*
|
|
253
|
-
*
|
|
254
|
-
* nada. Para quem vê, parece que o clique falhou; para quem navega por leitor de tela, o menu leu-lhe um
|
|
255
|
-
* item que não existe. É exactamente o que o §5 chama de pior do que a ausência: «uma barra que oferece um
|
|
256
|
-
* caminho e depois o recusa ensina-lhe que o caminho não é para ela».
|
|
244
|
+
* THE ITEMS THE ENGINE ACTIVATES BY ITSELF, which therefore never depend on a game's `getPauseActs`. Read from the
|
|
245
|
+
* dispatch, not decided here: `options`, `opcoesdojogo` and `pmback` change which list is on the card, and
|
|
246
|
+
* `acessibilidade` takes the cursor to the quick bar — all four are handled in this module before the game's table is
|
|
247
|
+
* consulted. What belongs to the game is the CONTENT of the list `opcoesdojogo` opens.
|
|
257
248
|
*/
|
|
258
|
-
|
|
259
|
-
return botoes.filter((b) => ITENS_DA_ENGINE.has(b.act) || typeof acts[b.act] === 'function');
|
|
260
|
-
}
|
|
249
|
+
const ENGINE_ITEMS = new Set(['options', 'opcoesdojogo', 'pmback', 'acessibilidade']);
|
|
261
250
|
/**
|
|
262
|
-
*
|
|
263
|
-
*
|
|
264
|
-
*
|
|
265
|
-
* ⚠️ Esta é a parte que um filtro item-a-item não apanha. Se todos os painéis de ajuste forem filtrados —
|
|
266
|
-
* um jogo que não monta nenhum —, o item `options` sobrevive (a engine acciona-o) e abre uma lista sem nada.
|
|
267
|
-
* A criança atravessa uma porta e fica presa num submenu vazio, cuja única saída é o `pmback` que também
|
|
268
|
-
* sumiu com ele.
|
|
251
|
+
* Why a pause item is locked, in the child's words (ADR-0161). A key per item where the reason is particular to it —
|
|
252
|
+
* «Número de jogadores»: the GAME decides how many (ADR-0147) — and one general reason for the rest. Resolved at every
|
|
253
|
+
* refresh, so it follows the language of the moment the card opens.
|
|
269
254
|
*/
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
return opcoesVivas.length > 0 ? viva : viva.filter((b) => b.act !== 'options');
|
|
274
|
-
}
|
|
275
|
-
/** The whole icon bar. Used by the pause screen AND by the splash `#title-icons` (which built the same string
|
|
276
|
-
* by hand in game.js — that duplication dies with this export).
|
|
277
|
-
* O parâmetro é ADITIVO e o padrão é a lista inteira: quem já chamava sem argumentos não muda de resultado. */
|
|
278
|
-
export function iconsMarkup(icones = PAUSE_ICONS) {
|
|
279
|
-
return icones.map(iconBtnMarkup).join('');
|
|
255
|
+
const OWN_REASONS = new Set(['ajuda', 'addplayer', 'opcoesdojogo']);
|
|
256
|
+
function itemReason(t, act) {
|
|
257
|
+
return t(OWN_REASONS.has(act) ? `pause.motivo.${act}` : 'pause.motivo');
|
|
280
258
|
}
|
|
281
|
-
/** One `.pm-btn`. Dynamic labels (`letra`/`nivel`) are rendered eagerly and carry NO `data-i18n`, so
|
|
282
|
-
* i18n.applyDom() cannot overwrite them. */
|
|
283
259
|
/**
|
|
284
|
-
*
|
|
285
|
-
*
|
|
286
|
-
*
|
|
287
|
-
* juntos:
|
|
288
|
-
*
|
|
289
|
-
* 1. FRONTEIRA. `level` vinha de `core/state.quizLevel` e `qlName` de uma tabela do jogo. Um menu de pausa
|
|
290
|
-
* da ENGINE montava o rótulo de uma atividade de alfabetização — conteúdo pedagógico, não mecânica.
|
|
291
|
-
* 2. IDIOMA. "Nível" é pt-BR CRU dentro de um módulo de engine. O gate do item 14 vigia o `main.js` e não
|
|
292
|
-
* alcança `ui/`, então esta linha atravessou a i18n inteira sem ser vista. Num build em inglês, o menu
|
|
293
|
-
* de pausa de uma criança dizia "📚 Nível 2 · …".
|
|
294
|
-
*
|
|
295
|
-
* Agora o jogo entrega a frase montada (`dynLabel`), e a engine só a coloca no botão. O jogo é quem sabe o
|
|
296
|
-
* que é um nível, quem sabe o nome dele e quem sabe em que idioma dizê-lo.
|
|
260
|
+
* THE MENU ITEMS THIS GAME CAN ACTUALLY ACTIVATE. The others are not hidden: they stay on the card, LOCKED with their
|
|
261
|
+
* reason said (ADR-0161) — an item that does nothing when pressed, and says nothing, reads to a screen-reader user as an
|
|
262
|
+
* item that does not exist.
|
|
297
263
|
*/
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
const lbl = dynLabel(b) ?? (dyn ? (b.lbl ?? '') : tr('pause.' + b.act));
|
|
301
|
-
return '<button class="pm-btn' + (b.letra ? ' pm-letra' : '') + (b.nivel ? ' pm-nivel' : '') +
|
|
302
|
-
'" role="menuitem" type="button" data-act="' + b.act + '"' +
|
|
303
|
-
(dyn ? '' : (' data-i18n="pause.' + b.act + '"')) + '>' + lbl + '</button>';
|
|
264
|
+
function itemsThatAct(buttons, acts) {
|
|
265
|
+
return buttons.filter((b) => ENGINE_ITEMS.has(b.act) || typeof acts[b.act] === 'function');
|
|
304
266
|
}
|
|
305
267
|
/**
|
|
306
|
-
*
|
|
307
|
-
*
|
|
308
|
-
*
|
|
309
|
-
* `ui/shell` e a troca de submenu aqui). Enquanto fosse `.pm-btn` escrito três vezes, bastaria um deles
|
|
310
|
-
* esquecer o `:not([hidden])` para o anel atravessar para a lista invisível — e a criança ouviria itens de um
|
|
311
|
-
* menu que não está na tela.
|
|
268
|
+
* THE ROOT LIST, with one more rule: `options` is a DOOR, and a door to an empty room acts no more than a dead button.
|
|
269
|
+
* An item-by-item filter misses this: the engine activates `options` itself, so it would pass and open a list with
|
|
270
|
+
* nothing in it.
|
|
312
271
|
*/
|
|
313
|
-
export
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
272
|
+
export function rootThatActs(rootEl, options, acts,
|
|
273
|
+
// An OPTIONAL fourth argument defaulting to the EMPTY list: a game that declares nothing of its own is exactly that
|
|
274
|
+
// case, so the default is also the right answer.
|
|
275
|
+
fromGame = []) {
|
|
276
|
+
const howManyAct = (bs) => itemsThatAct(bs, acts).filter((b) => b.act !== 'pmback').length;
|
|
277
|
+
let alive = itemsThatAct(rootEl, acts);
|
|
278
|
+
if (howManyAct(options) === 0)
|
|
279
|
+
alive = alive.filter((b) => b.act !== 'options');
|
|
280
|
+
// The same rule for the game's own door (ADR-0146): a game with nothing of its own gets no live «game options».
|
|
281
|
+
// ADR-0182: a door whose room the ENGINE draws from the cartridge's rows is live through its action, with no list behind it
|
|
282
|
+
if (howManyAct(fromGame) === 0 && typeof acts.opcoesdojogo !== 'function')
|
|
283
|
+
alive = alive.filter((b) => b.act !== 'opcoesdojogo');
|
|
284
|
+
return alive;
|
|
318
285
|
}
|
|
319
286
|
/**
|
|
320
|
-
*
|
|
287
|
+
* The navigable items of a pause card — those of the VISIBLE list, and only those.
|
|
321
288
|
*
|
|
322
|
-
*
|
|
323
|
-
*
|
|
324
|
-
*
|
|
289
|
+
* One constant because THREE modules ask (navigation in `ui/menu-nav`, the first selection in `ui/shell` and the list
|
|
290
|
+
* switch here): if one of them forgot `:not([hidden])`, the ring would step into the invisible list and the child would
|
|
291
|
+
* hear items of a menu that is not on screen. `:not([hidden])` on the ITEM too: a hidden item must not be reached nor
|
|
292
|
+
* counted in «N de M» (a locked one is not hidden — it is reached, and says why).
|
|
325
293
|
*/
|
|
326
|
-
export
|
|
327
|
-
sp.querySelectorAll('.pause-menu').forEach((m) => { m.hidden = m.dataset.sub !== sub; });
|
|
328
|
-
const primeiro = sp.querySelector(PM_ITENS_VISIVEIS);
|
|
329
|
-
sp.querySelectorAll('.pm-sel,.pi-sel').forEach((b) => b.classList.remove('pm-sel', 'pi-sel'));
|
|
330
|
-
if (primeiro)
|
|
331
|
-
primeiro.classList.add('pm-sel');
|
|
332
|
-
return primeiro;
|
|
333
|
-
}
|
|
294
|
+
export const PM_VISIBLE_ITEMS = '.pause-menu:not([hidden]) .pm-btn:not([hidden])';
|
|
334
295
|
/**
|
|
335
|
-
*
|
|
336
|
-
*
|
|
337
|
-
*
|
|
338
|
-
*
|
|
339
|
-
|
|
340
|
-
|
|
296
|
+
* THE DOORS INSIDE THE CARD, and which list each opens. They do nothing to the game — they change which list is on
|
|
297
|
+
* screen — so they live here and not in the action table, which lives in `ui/shell` and does not know the card.
|
|
298
|
+
* `pmback` always returns to the ROOT, from either list. A table and not a chain of `if`s: a fourth list is one more
|
|
299
|
+
* row, not one more branch.
|
|
300
|
+
*/
|
|
301
|
+
const DOOR_TO_LIST = Object.freeze({ options: 'opcoes', opcoesdojogo: 'jogo', pmback: 'raiz' });
|
|
302
|
+
/**
|
|
303
|
+
* Switches the visible list of ONE pause card, and puts the cursor on the FIRST item of the list that came in.
|
|
341
304
|
*
|
|
342
|
-
*
|
|
343
|
-
*
|
|
305
|
+
* Free-standing (not a method of `init`) on purpose: `ui/menu-nav` needs it for «no» to go back to the root, and it has no
|
|
306
|
+
* access to the button tables. Every list already exists in the markup, so the switch is DOM only.
|
|
344
307
|
*/
|
|
345
|
-
export function
|
|
346
|
-
|
|
347
|
-
|
|
308
|
+
export function showPauseOptions(sp, sub) {
|
|
309
|
+
sp.querySelectorAll('.pause-menu').forEach((m) => { m.hidden = m.dataset.sub !== sub; });
|
|
310
|
+
const first = sp.querySelector(PM_VISIBLE_ITEMS);
|
|
311
|
+
sp.querySelectorAll('.pm-sel,.pi-sel').forEach((b) => b.classList.remove('pm-sel', 'pi-sel'));
|
|
312
|
+
if (first)
|
|
313
|
+
first.classList.add('pm-sel');
|
|
314
|
+
return first;
|
|
348
315
|
}
|
|
349
|
-
export function
|
|
350
|
-
if (
|
|
316
|
+
export function barAction(k, hasStart) {
|
|
317
|
+
if (hasStart || k.no)
|
|
351
318
|
return 'sair';
|
|
352
319
|
if (k.yes)
|
|
353
320
|
return 'ativar';
|
|
@@ -355,130 +322,101 @@ export function acaoNaBarra(k, temStart) {
|
|
|
355
322
|
return 'andar';
|
|
356
323
|
return 'nada';
|
|
357
324
|
}
|
|
358
|
-
/** The full innerHTML of a `.screen-pause`. Pure — every input is a parameter. */
|
|
359
|
-
export function screenPauseMarkup(o) {
|
|
360
|
-
// O NOME ACESSÍVEL DO DIÁLOGO passa pelo dicionário. Era texto cru, e MEDIDO num jogo em inglês o efeito
|
|
361
|
-
// era este: o título visível dizia "Paused" e o nome do diálogo, "Menu de pausa do jogador 1". Quem enxerga
|
|
362
|
-
// lia em inglês; quem escuta recebia o menu anunciado em português — a mesma assimetria do item 4 do
|
|
363
|
-
// ADR-0044, um nível acima. E `aria-label`, não `aria-labelledby`: o `<h2>` é rótulo VISUAL, e é por isso
|
|
364
|
-
// que escondê-lo num quadro apertado não tira o nome do diálogo de quem escuta.
|
|
365
|
-
return '<div class="pause-card" role="dialog" aria-modal="true" aria-label="' + t('pause.cardAria', { n: o.player + 1 }) + '">' +
|
|
366
|
-
'<h2><span data-i18n="pause.title">' + o.t('pause.title') + '</span>' + (o.numPlayers > 1 ? ' · Jogador ' + (o.player + 1) : '') + '</h2>' +
|
|
367
|
-
pauseMenuHtml(o.pmButtons, 'raiz', o.dynLabel, o.t) +
|
|
368
|
-
pauseMenuHtml(o.optionsButtons, 'opcoes', o.dynLabel, o.t) +
|
|
369
|
-
'<p class="pause-legend"></p></div>';
|
|
370
|
-
}
|
|
371
325
|
// ---------------------------------------------------------------------------------------------
|
|
372
326
|
// DOM-facing shell
|
|
373
327
|
// ---------------------------------------------------------------------------------------------
|
|
374
328
|
export function initPauseIcons(ctx) {
|
|
375
|
-
|
|
376
|
-
//
|
|
377
|
-
//
|
|
378
|
-
// este é um controlo de menu que vive na barra rápida.
|
|
379
|
-
//
|
|
380
|
-
// O custo de não persistir era da criança que mais precisa dele: quem usa o modo SILENCIOSO voltava a
|
|
381
|
-
// pô-lo a cada sessão — e é para quem o barulho inesperado custa mais. Um ajuste que se esquece não é um
|
|
382
|
-
// ajuste, é uma tarefa diária.
|
|
329
|
+
const { t } = ctx.translator;
|
|
330
|
+
// THE CALM LEVEL PERSISTS (issue #61; ADR-0028: every menu setting persists). The child who uses the SILENT mode is the
|
|
331
|
+
// one for whom unexpected noise costs most, and a setting that is forgotten is a daily chore, not a setting.
|
|
383
332
|
//
|
|
384
|
-
//
|
|
385
|
-
//
|
|
386
|
-
|
|
387
|
-
let calmMode = lerNivelTea();
|
|
333
|
+
// READ IN INIT, NEVER ON IMPORT: a test importing this module would otherwise depend on the environment's storage, and a
|
|
334
|
+
// level inherited from another case is a failure that shows up far from its cause.
|
|
335
|
+
let calmMode = readTeaLevel(ctx.store);
|
|
388
336
|
const P = () => ctx.getPlayers();
|
|
389
337
|
/*
|
|
390
|
-
*
|
|
391
|
-
*
|
|
392
|
-
*
|
|
393
|
-
* O `store` vem do import directo deste módulo e não do `ctx`, porque é assim que este ficheiro já persiste
|
|
394
|
-
* o resto: uma chave injectada é um campo que um consumidor pode omitir, e omiti-la faria escrever num nome
|
|
395
|
-
* torto.
|
|
338
|
+
* THE MOVEMENT LATCH HAS AN ENGINE DEFAULT (ADR-0106 §4): a host that injects one still rules; a host that does not is
|
|
339
|
+
* no longer left without it. The `store` is the page's, from the ctx (ADR-0232).
|
|
396
340
|
*/
|
|
397
341
|
const setToggleMove = ctx.setToggleMove
|
|
398
|
-
?? ((i, on) =>
|
|
399
|
-
players: P(), store, srSay: ctx.srSay, getNumPlayers: ctx.getNumPlayers,
|
|
400
|
-
//
|
|
401
|
-
//
|
|
402
|
-
|
|
342
|
+
?? ((i, on) => setMoveLatch({
|
|
343
|
+
t, players: P(), store: ctx.store, srSay: ctx.srSay, getNumPlayers: ctx.getNumPlayers,
|
|
344
|
+
// Passed through, not resolved here: the icon and the panel must write the SAME thing, and resolving the
|
|
345
|
+
// device in one of them only is how two surfaces of the same engine come to disagree.
|
|
346
|
+
transportInUse: ctx.transportInUse,
|
|
403
347
|
}, i, on));
|
|
404
348
|
/*
|
|
405
|
-
*
|
|
406
|
-
*
|
|
407
|
-
*
|
|
408
|
-
* `sr.icon.blindOn`/`Off` por si, logo abaixo.
|
|
349
|
+
* BLIND MODE LIKEWISE: the default is exactly what `core/state` decided a setter does — write, persist, notify — and
|
|
350
|
+
* nothing more. Game effects are REACTIONS, subscribed with `on('blindMode', …)`. The announcement is not lost for a host
|
|
351
|
+
* that injects nothing: this icon says `sr.icon.blindOn`/`Off` itself, below.
|
|
409
352
|
*/
|
|
410
|
-
const
|
|
411
|
-
/** O documento onde se constroi. Resolvido a cada uso, e por globalThis — em node o identificador
|
|
412
|
-
* document nem existe, e um ?? sobre ele lançaria ReferenceError em vez de cair no padrão. */
|
|
413
|
-
const docDaMontagem = () => ctx.doc ?? globalThis.document;
|
|
353
|
+
const writeBlindMode = ctx.setBlindMode ?? ((on) => { ctx.settings.setBlindModeValue(on); });
|
|
414
354
|
/*
|
|
415
|
-
*
|
|
355
|
+
* CONTRAST AND COLOUR ONLY APPEAR IF SOMEONE CAN WRITE THEM (ADR-0106 §5): the theme and the correction are written by
|
|
356
|
+
* the host (`setPlayerTheme`/`setPlayerCorrection`), and without a writer the engine does not pretend.
|
|
416
357
|
*
|
|
417
|
-
*
|
|
418
|
-
*
|
|
419
|
-
* `render/viz-setters`, cujo contexto pede **34 campos** do grafo de render de UM jogo — `parallaxLayers`,
|
|
420
|
-
* `decoSprites`, `getPowerups`, `rebuildCoins`, `worldSprite`. O `createGame` não monta isso, e um quiz não
|
|
421
|
-
* tem nada disso para montar. A engine não pode dar um padrão aqui; o que ela pode é não FINGIR.
|
|
422
|
-
*
|
|
423
|
-
* Decidido uma vez, no arranque, e não a cada montagem de barra: o conjunto de escritores de um consumidor
|
|
424
|
-
* não muda a meio de uma partida, e recalcular por tela faria as telas discordarem entre si.
|
|
358
|
+
* Decided once, at start, not per bar: a host's set of writers does not change mid-game, and recomputing per screen
|
|
359
|
+
* would let the screens disagree.
|
|
425
360
|
*/
|
|
426
|
-
const
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
//
|
|
430
|
-
//
|
|
431
|
-
|
|
432
|
-
|
|
361
|
+
const gameIcons = iconsThatAct({
|
|
362
|
+
theme: Boolean(ctx.setPlayerTheme),
|
|
363
|
+
correction: Boolean(ctx.setPlayerCorrection),
|
|
364
|
+
// No `Boolean(...)`: the two above ask an optional field «is there a writer?»; this is an ANSWER the game gave, and
|
|
365
|
+
// wrapping it would turn a badly wired ctx's `undefined` into `false` — hiding the control silently.
|
|
366
|
+
holdsKeys: ctx.holdsKeys,
|
|
367
|
+
// the same question the scan asks (ADR-0218): how many positions this game would give it to offer
|
|
368
|
+
declaredPositions: ctx.declaredPositions,
|
|
369
|
+
typography: Boolean(ctx.cycleTypography),
|
|
370
|
+
// mounted when the root can answer the clock question; shown or hidden per cartridge in `reflectIconBtn` (ADR-0142)
|
|
371
|
+
clock: () => Boolean(ctx.clock),
|
|
372
|
+
camera: Boolean(ctx.camera),
|
|
373
|
+
microphone: Boolean(ctx.microphone),
|
|
374
|
+
menus: Boolean(ctx.openMenus),
|
|
433
375
|
});
|
|
434
376
|
/*
|
|
435
|
-
*
|
|
436
|
-
*
|
|
437
|
-
*
|
|
438
|
-
*
|
|
439
|
-
* 📌 O `getPauseActs` fica FUNÇÃO e não valor, porque a laziness dele é a razão de ele existir assim: no
|
|
440
|
-
* cartucho a tabela é um `const` declarado ~1200 linhas abaixo, e lê-la aqui cairia na zona morta temporal.
|
|
377
|
+
* RESOLVED ONCE, at start, as in `settings-motion`: `rm` is mutated in place and shared by REFERENCE with whoever draws
|
|
378
|
+
* the scene, and resolving it on each use would make a new object per call — the switch would stop reaching the drawing,
|
|
379
|
+
* with no error at all.
|
|
441
380
|
*/
|
|
442
|
-
const rm = ctx.rm ??
|
|
443
|
-
const rmKeys = ctx.rmKeys ??
|
|
444
|
-
const rmChar = ctx.rmChar ??
|
|
445
|
-
const saveRM = ctx.saveRM ?? (() =>
|
|
381
|
+
const rm = ctx.rm ?? readStoredScene(ctx.store, defaultReducedMotion(ctx.matchMedia));
|
|
382
|
+
const rmKeys = ctx.rmKeys ?? SCENE_KEYS;
|
|
383
|
+
const rmChar = ctx.rmChar ?? CHARACTER_ANIMATIONS;
|
|
384
|
+
const saveRM = ctx.saveRM ?? (() => storeScene(ctx.store, rm));
|
|
446
385
|
/*
|
|
447
|
-
*
|
|
448
|
-
*
|
|
449
|
-
*
|
|
450
|
-
* precisamente por a tabela chegar tarde. Um padrão não pode custar a ligação tardia que o campo tem.
|
|
386
|
+
* THESE THREE ARE READ ON EACH CALL, not resolved once like `rm` above: a host may replace the action table after `init`
|
|
387
|
+
* (it can arrive late), and a default must not cost that late binding. `rm` is the opposite — what matters there is the
|
|
388
|
+
* object's IDENTITY, shared by reference with whoever draws the scene.
|
|
451
389
|
*
|
|
452
|
-
*
|
|
453
|
-
*
|
|
454
|
-
*
|
|
455
|
-
* A anotação de tipo é necessária: sem ela o padrão `() => ({})` infere `{}`, que não aceita indexação por
|
|
456
|
-
* string, e o compilador passaria a recusar `acts[act]` — o despacho inteiro.
|
|
390
|
+
* The type annotation is needed: without it the default `() => ({})` infers `{}`, which does not accept string indexing,
|
|
391
|
+
* and the compiler would refuse `acts[act]` — the whole dispatch.
|
|
457
392
|
*/
|
|
458
393
|
const dynLabel = (b) => (ctx.dynLabel ? ctx.dynLabel(b) : null);
|
|
459
394
|
const getPauseActs = () => (ctx.getPauseActs ? ctx.getPauseActs() : {});
|
|
460
395
|
const setPauseActor = (i) => { if (ctx.setPauseActor)
|
|
461
396
|
ctx.setPauseActor(i); };
|
|
462
397
|
function hasPrivateOutput(i) { return hasPrivateOutputIn(P(), ctx.getNumPlayers(), i); }
|
|
463
|
-
/**
|
|
464
|
-
function
|
|
398
|
+
/** The latch refusal for this player now, or `null`. Recomputed: the device in use changes. */
|
|
399
|
+
function refusalNow(i) { return ctx.transportInUse ? latchRefusal(ctx.transportInUse(i)) : null; }
|
|
465
400
|
function iconState(i) {
|
|
466
401
|
const p = P()[i] || {};
|
|
467
402
|
const cat = ctx.getAudioCat();
|
|
468
403
|
return {
|
|
469
|
-
|
|
404
|
+
blindMode: ctx.getBlindMode(),
|
|
470
405
|
ttsOn: !!(cat && cat.tts && cat.tts.on),
|
|
471
406
|
librasOn: ctx.isLibrasOn(),
|
|
472
407
|
calmMode,
|
|
473
408
|
toggleMove: !!p.toggleMove,
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
//
|
|
477
|
-
//
|
|
478
|
-
|
|
479
|
-
|
|
409
|
+
switchScan: ctx.settings.switchScan,
|
|
410
|
+
voice: ctx.settings.voiceControl,
|
|
411
|
+
// The NAMED default, not an empty value that only happened to match nothing: ADR-0029's changed-mark reads the
|
|
412
|
+
// default and nothing else.
|
|
413
|
+
visual: p.visual ?? DEFAULT_VISUAL,
|
|
414
|
+
speed: ctx.settings.gameSpeed,
|
|
415
|
+
camera: ctx.settings.cameraControl,
|
|
416
|
+
locale: ctx.translator.locale(),
|
|
480
417
|
privateOutput: hasPrivateOutput(i),
|
|
481
|
-
|
|
418
|
+
latchRequired: refusalNow(i) !== null,
|
|
419
|
+
noVoice: !!ctx.noVoice?.(),
|
|
482
420
|
};
|
|
483
421
|
}
|
|
484
422
|
// --- TEA ---------------------------------------------------------------------------------
|
|
@@ -505,14 +443,15 @@ export function initPauseIcons(ctx) {
|
|
|
505
443
|
}
|
|
506
444
|
// --- icon actions (the dispatcher, as a table) ---------------------------------------------
|
|
507
445
|
const ICON_ACTS = {
|
|
446
|
+
menu: (i) => { ctx.openMenus?.(i); },
|
|
508
447
|
blind: () => {
|
|
509
|
-
|
|
510
|
-
ctx.srSay(t(ctx.
|
|
448
|
+
writeBlindMode(!ctx.getBlindMode());
|
|
449
|
+
ctx.srSay(t(ctx.getBlindMode() ? 'sr.icon.blindOn' : 'sr.icon.blindOff'));
|
|
511
450
|
},
|
|
512
451
|
tts: () => {
|
|
513
452
|
const cat = ctx.getAudioCat();
|
|
514
453
|
if (!cat || !cat.tts)
|
|
515
|
-
return; //
|
|
454
|
+
return; // the same guard `iconLabel` and `applyCalm` have
|
|
516
455
|
cat.tts.on = !cat.tts.on;
|
|
517
456
|
ctx.setCatGain('tts');
|
|
518
457
|
if (ctx.reflectTtsPanelEnabled)
|
|
@@ -525,66 +464,127 @@ export function initPauseIcons(ctx) {
|
|
|
525
464
|
},
|
|
526
465
|
tea: () => {
|
|
527
466
|
calmMode = nextCalmMode(calmMode);
|
|
528
|
-
store.set(
|
|
467
|
+
ctx.store.set(KEYS.tea, calmMode); // ADR-0028: every menu setting persists. See the note at the `let` above.
|
|
529
468
|
applyCalm();
|
|
530
469
|
ctx.srSay(t('sr.icon.tea', { v: t(CALM_NAMES[calmMode]) }));
|
|
531
470
|
},
|
|
471
|
+
/*
|
|
472
|
+
* ☝️ CYCLES THREE READINGS OF A PRESS (ADR-0218): standard · no holding needed · one button only.
|
|
473
|
+
*
|
|
474
|
+
* 📌 ENTERING THE SCAN LEAVES THE LATCH WHERE IT IS, and leaving it turns the latch off. The scan wins in `inputModeOf`, so
|
|
475
|
+
* the position shown is never ambiguous; and because the latch writer announces by itself, it is called ONLY where it
|
|
476
|
+
* changes something — otherwise the child would hear «no holding needed, off» when what ended was the scan.
|
|
477
|
+
*/
|
|
532
478
|
altmove: (i) => {
|
|
533
479
|
// verbatim: `players[i].toggleMove` with no `||{}` guard (unlike contrast/cvd below).
|
|
534
|
-
|
|
480
|
+
const latched = !!P()[i].toggleMove;
|
|
481
|
+
const next = nextInputMode(inputModeOf({ toggleMove: latched, switchScan: ctx.settings.switchScan }), ctx.holdsKeys(), refusalNow(i) !== null);
|
|
482
|
+
ctx.settings.setSwitchScanValue(next === 'scan');
|
|
483
|
+
// 📌 ENTERING THE SCAN LEAVES THE LATCH WHERE SHE PUT IT — the scan wins in `inputModeOf`, so the position shown is never
|
|
484
|
+
// ambiguous and coming back out returns her to the choice she had made. Leaving it writes the position she walked to.
|
|
485
|
+
// ⚠️ And the latch writer ANNOUNCES BY ITSELF, so it is called only where it changes something: otherwise the child would
|
|
486
|
+
// hear «não precisa segurar, desligado» when what ended was the scan.
|
|
487
|
+
if (next !== 'scan' && latched !== (next === 'sticky')) {
|
|
488
|
+
// ⚠️ THE REFUSAL, over the WRITE, which is the only thing it ever meant. The cycle above already skips `standard` on a
|
|
489
|
+
// device that always latches, so this should be unreachable — and it stays because `iconAct` is exported and the two
|
|
490
|
+
// rules could drift apart, which is the same reason the guards below give for not being belt and braces.
|
|
491
|
+
const refusal = refusalNow(i);
|
|
492
|
+
if (refusal) {
|
|
493
|
+
ctx.srAlert(t(refusal.key));
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
setToggleMove(i, next === 'sticky');
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
ctx.srSay(t('sr.icon.inputMode', { v: t(INPUT_MODE_NAME[next]) }));
|
|
535
500
|
},
|
|
536
|
-
//
|
|
537
|
-
//
|
|
538
|
-
//
|
|
539
|
-
//
|
|
540
|
-
// ⚠️ AS DUAS GUARDAS NÃO SÃO CINTO E SUSPENSÓRIOS. Sem escritor, o ícone nem sequer é montado
|
|
541
|
-
// (`iconesDoJogo`), então este ramo não deveria ser alcançável pela barra — mas `iconAct` é EXPORTADO e
|
|
542
|
-
// qualquer consumidor pode chamá-lo por chave. Sem a guarda, essa chamada rebentaria; com ela, não faz
|
|
543
|
-
// nada e não anuncia — que é o mesmo que dizer a verdade: este jogo não tem por onde.
|
|
501
|
+
// Each of the two icons writes its OWN axis (#104), so changing one leaves the other where it was.
|
|
502
|
+
// ⚠️ THE TWO GUARDS ARE NOT BELT AND BRACES. Without a writer the icon is not even mounted (`gameIcons`), so the bar
|
|
503
|
+
// cannot reach this branch — but `iconAct` is EXPORTED and any host can call it by key. Without the guard that call
|
|
504
|
+
// would throw; with it, nothing happens and nothing is announced — which is the truth: this game has no way to do it.
|
|
544
505
|
contrast: (i) => {
|
|
545
|
-
if (!ctx.
|
|
506
|
+
if (!ctx.setPlayerTheme)
|
|
507
|
+
return;
|
|
508
|
+
const v = nextTheme((P()[i] || {}).visual ?? DEFAULT_VISUAL);
|
|
509
|
+
ctx.setPlayerTheme(i, v.tema);
|
|
510
|
+
ctx.srSay(t('sr.visual.contrast', { v: t(SHORT_THEME[v.tema]) }));
|
|
511
|
+
},
|
|
512
|
+
/*
|
|
513
|
+
* THE TYPOGRAPHY CYCLE (ADR-0149 §1): one press changes the letter CASE and the FACE at once. The guard is the one
|
|
514
|
+
* written at `contrast` above, for the same reason.
|
|
515
|
+
*
|
|
516
|
+
* It ANNOUNCES THE FACE, NOT THE POSITION: «position 3 of 5» tells nobody anything; the face's name is what the child
|
|
517
|
+
* recognises — the same reason ADR-0074 forbids `action2` from reaching a person.
|
|
518
|
+
*/
|
|
519
|
+
tipografia: () => {
|
|
520
|
+
if (!ctx.cycleTypography)
|
|
546
521
|
return;
|
|
547
|
-
const
|
|
548
|
-
|
|
549
|
-
|
|
522
|
+
const face = ctx.cycleTypography();
|
|
523
|
+
if (face)
|
|
524
|
+
ctx.srSay(t('sr.typo.font', { fam: face }));
|
|
525
|
+
},
|
|
526
|
+
// PLAYING THROUGH THE WEBCAM (ADR-0215): off → hands → face → eyes → off; stored in one key, so one mode at a time.
|
|
527
|
+
camera: () => {
|
|
528
|
+
const v = nextCameraControl(ctx.settings.cameraControl);
|
|
529
|
+
ctx.settings.setCameraControlValue(v);
|
|
530
|
+
ctx.srSay(t('sr.icon.camera', { v: t(CAMERA_MODE_NAME[v]) }));
|
|
531
|
+
},
|
|
532
|
+
// PLAYING BY SPEAKING (ADR-0189, issue #184): on or off, one stored key. What cannot start puts it back to off and says why,
|
|
533
|
+
// which is the control's own job (`ui/voice-control`) — this only writes the child's answer.
|
|
534
|
+
voice: () => {
|
|
535
|
+
const v = !ctx.settings.voiceControl;
|
|
536
|
+
ctx.settings.setVoiceControlValue(v);
|
|
537
|
+
ctx.srSay(t('sr.icon.voice', { v: t(v ? 'state.on' : 'state.off') }));
|
|
538
|
+
},
|
|
539
|
+
// THE LANGUAGE (the Dev, 2026-09-16): the next flag; `setLocale` stores it and every surface redraws on `i18n:change`. Said in the NEW
|
|
540
|
+
// language, once it has loaded.
|
|
541
|
+
idioma: () => {
|
|
542
|
+
const v = nextLocale(ctx.translator.locale());
|
|
543
|
+
void ctx.translator.setLocale(v).then(() => ctx.srSay(t('sr.icon.idioma', { v: LANGUAGE_NAME[v] })));
|
|
544
|
+
},
|
|
545
|
+
// THE GAME SPEED (ADR-0180): one step down, wrapping at 50%; stored, and felt on the next frame of `startLoop`.
|
|
546
|
+
velocidade: () => {
|
|
547
|
+
const v = nextGameSpeed(ctx.settings.gameSpeed);
|
|
548
|
+
ctx.settings.setGameSpeedValue(v);
|
|
549
|
+
ctx.srSay(t('sr.icon.velocidade', { pct: Math.round(v * 100) }));
|
|
550
550
|
},
|
|
551
551
|
cvd: (i) => {
|
|
552
|
-
if (!ctx.
|
|
552
|
+
if (!ctx.setPlayerCorrection)
|
|
553
553
|
return;
|
|
554
|
-
const v =
|
|
555
|
-
ctx.
|
|
556
|
-
ctx.srSay(t('sr.icon.cvd', { v: t(
|
|
554
|
+
const v = nextCorrection((P()[i] || {}).visual ?? DEFAULT_VISUAL);
|
|
555
|
+
ctx.setPlayerCorrection(i, v.correcao);
|
|
556
|
+
ctx.srSay(t('sr.icon.cvd', { v: t(SHORT_CORRECTION[v.correcao]) }));
|
|
557
557
|
},
|
|
558
558
|
};
|
|
559
559
|
function iconAct(k, i) {
|
|
560
|
-
|
|
561
|
-
if (
|
|
562
|
-
ctx.srAlert(t('
|
|
560
|
+
// locked like the panel's row (ADR-0185): the same reason, said, and nothing turned on
|
|
561
|
+
if (k === 'tts' && ctx.noVoice?.()) {
|
|
562
|
+
ctx.srAlert(t('audio.semVoz'));
|
|
563
563
|
return;
|
|
564
564
|
}
|
|
565
565
|
if ((k === 'blind' || k === 'tts') && !hasPrivateOutput(i)) {
|
|
566
566
|
ctx.srAlert(t('sr.icon.needsPrivateOutput'));
|
|
567
567
|
return;
|
|
568
568
|
}
|
|
569
|
-
//
|
|
570
|
-
//
|
|
571
|
-
//
|
|
572
|
-
// botões que discordam sobre o mesmo ajuste.
|
|
573
|
-
if (k === 'altmove') {
|
|
574
|
-
const recusa = recusaAgora(i);
|
|
575
|
-
if (recusa) {
|
|
576
|
-
ctx.srAlert(t(recusa.chave));
|
|
577
|
-
return;
|
|
578
|
-
}
|
|
579
|
-
}
|
|
569
|
+
// No blanket refusal for `altmove` here (ADR-0218): refusing the whole icon on a device that always latches would also
|
|
570
|
+
// lock «one button only» away from the child playing with their eyes. The lock lives in the CYCLE, which skips
|
|
571
|
+
// `standard` on that device, and the refusal lives inside the action, over the latch write only.
|
|
580
572
|
const act = ICON_ACTS[k];
|
|
581
573
|
if (act)
|
|
582
574
|
act(i);
|
|
583
575
|
}
|
|
584
576
|
// --- reflection --------------------------------------------------------------------------
|
|
585
|
-
function iconLabel(k, i) { return computeIconLabel(k, iconState(i)); }
|
|
577
|
+
function iconLabel(k, i) { return computeIconLabel(t, k, iconState(i)); }
|
|
586
578
|
function reflectIconBtn(b, i) {
|
|
587
579
|
const k = b.dataset.pi || '';
|
|
580
|
+
// the hourglass follows the CURRENT cartridge's clock (ADR-0180): a turn game mounted later hides it, a clock game shows it
|
|
581
|
+
if (k === 'velocidade')
|
|
582
|
+
b.hidden = !ctx.clock?.();
|
|
583
|
+
if (k === 'idioma') {
|
|
584
|
+
const flag = flagOf(ctx.translator.locale());
|
|
585
|
+
if (b.innerHTML !== flag)
|
|
586
|
+
b.innerHTML = flag;
|
|
587
|
+
}
|
|
588
588
|
const st = iconState(i);
|
|
589
589
|
const v = computeIconVisual(k, st);
|
|
590
590
|
b.classList.remove(...ICON_STATE_CLASSES);
|
|
@@ -595,26 +595,25 @@ export function initPauseIcons(ctx) {
|
|
|
595
595
|
b.classList.toggle('pi-on', v.on);
|
|
596
596
|
b.classList.toggle('pi-dis', v.dis);
|
|
597
597
|
/*
|
|
598
|
-
*
|
|
599
|
-
*
|
|
600
|
-
*
|
|
601
|
-
*
|
|
602
|
-
* ⚠️ E `aria-disabled` e NÃO `disabled`: o segundo tira o botão da ordem de tabulação, e quem navega
|
|
603
|
-
* por teclado deixaria de o alcançar — logo deixaria de poder ouvir POR QUE ele não responde. É a
|
|
604
|
-
* mesma escolha que o `#opt-altmove` faz no painel, pela mesma razão.
|
|
598
|
+
* `pi-dis` is a CSS class (issue #128): a child who sees gets a greyed icon, a child on a screen reader gets nothing — the
|
|
599
|
+
* button announces itself actionable and does not respond. `aria-disabled` mirrors the same fact for whoever listens.
|
|
600
|
+
* `aria-disabled` and NOT `disabled`: the latter takes the button out of the tab order, and a keyboard user could no
|
|
601
|
+
* longer reach it — nor hear WHY it does not respond.
|
|
605
602
|
*/
|
|
606
603
|
if (v.dis)
|
|
607
604
|
b.setAttribute('aria-disabled', 'true');
|
|
608
605
|
else
|
|
609
606
|
b.removeAttribute('aria-disabled');
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
//
|
|
616
|
-
//
|
|
617
|
-
|
|
607
|
+
// the ☰ opens something and holds no state: a pressed/unpressed button would announce a toggle
|
|
608
|
+
if (k === 'menu')
|
|
609
|
+
b.removeAttribute('aria-pressed');
|
|
610
|
+
else
|
|
611
|
+
b.setAttribute('aria-pressed', String(v.active));
|
|
612
|
+
// ⚠️ EVERY icon is relabelled here, including the ones that carry no state — because the markup and this reflection do not
|
|
613
|
+
// run in the same language. `initI18n` loads en/es asynchronously, so the markup is born in pt and only the reflection
|
|
614
|
+
// corrects it. 📌 Measured in a browser back when a guard skipped some of them: five icons in English and three still in
|
|
615
|
+
// Portuguese, on the same bar.
|
|
616
|
+
b.setAttribute('aria-label', computeIconLabel(t, k, st));
|
|
618
617
|
}
|
|
619
618
|
function reflectIconsIn(root, i) {
|
|
620
619
|
if (!root)
|
|
@@ -622,111 +621,136 @@ export function initPauseIcons(ctx) {
|
|
|
622
621
|
root.querySelectorAll('.pi-btn').forEach((b) => reflectIconBtn(b, i));
|
|
623
622
|
}
|
|
624
623
|
/**
|
|
625
|
-
*
|
|
626
|
-
*
|
|
624
|
+
* THE CARDS THIS INSTANCE BUILT. Private: whoever built them keeps them, which asks nothing of any host — a ctx field
|
|
625
|
+
* would be one more thing every game had to remember to pass.
|
|
627
626
|
*/
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
*
|
|
631
|
-
*
|
|
632
|
-
*
|
|
633
|
-
*
|
|
634
|
-
* cada um dos 300 jogos teria de se lembrar de passar.
|
|
627
|
+
const cards = [];
|
|
628
|
+
/*
|
|
629
|
+
* `refreshPauseItems` LOCKS THE ITEMS THIS GAME CANNOT ACTIVATE — recomputed, not decided at start. The action table can
|
|
630
|
+
* arrive after the card is built (`getPauseActs` is asked when needed), so deciding at build time would lose for good
|
|
631
|
+
* every item whose action exists only later. The host calls `reflectPauseIcons()` when the pause opens and when a
|
|
632
|
+
* cartridge is mounted, so the card is right at the moment the child sees it.
|
|
635
633
|
*/
|
|
636
|
-
const cartoes = [];
|
|
637
634
|
/**
|
|
638
|
-
*
|
|
635
|
+
* THE CARD'S NAME — what is SEEN and what is HEARD — repainted in the current language.
|
|
639
636
|
*
|
|
640
|
-
*
|
|
641
|
-
*
|
|
642
|
-
*
|
|
643
|
-
*
|
|
637
|
+
* The markup is born before an asynchronously loaded dictionary arrives, and an `aria-label` set once cannot be corrected
|
|
638
|
+
* later by `applyDom`, which only reaches `[data-i18n]` and `[data-i18n-aria]`. Nor would `data-i18n-aria` do: `applyDom`
|
|
639
|
+
* calls `t(k)` without parameters, and this key carries the seat number — the child would hear «Player {n} pause menu»,
|
|
640
|
+
* braces included. So it is repainted on each `reflectPauseIcons()`, i.e. when the pause opens.
|
|
644
641
|
*
|
|
645
|
-
*
|
|
646
|
-
*
|
|
647
|
-
* item que não acciona —, e agora também vê os que passaram a accionar.
|
|
642
|
+
* The one who lost was the one who LISTENS (ADR-0044 item 4): to a sighted child the card was entirely in the right
|
|
643
|
+
* language, and nothing looked wrong.
|
|
648
644
|
*/
|
|
649
|
-
function
|
|
645
|
+
function renameCard(cardEl, i) {
|
|
646
|
+
const card = cardEl.querySelector('.pause-card');
|
|
647
|
+
if (card)
|
|
648
|
+
card.setAttribute('aria-label', t('pause.cardAria', { n: i + 1 }));
|
|
649
|
+
const seat = cardEl.querySelector('h2 .pause-seat');
|
|
650
|
+
if (seat)
|
|
651
|
+
seat.textContent = ctx.getNumPlayers() > 1 ? t('pause.cardSeat', { n: i + 1 }) : '';
|
|
652
|
+
}
|
|
653
|
+
function refreshPauseItems() {
|
|
650
654
|
const acts = getPauseActs();
|
|
651
|
-
const
|
|
652
|
-
const
|
|
653
|
-
const
|
|
654
|
-
|
|
655
|
-
...
|
|
655
|
+
const rootEl = ctx.pmButtons ?? PM_BTNS;
|
|
656
|
+
const options = ctx.optionsButtons ?? PM_OPTIONS_BTNS;
|
|
657
|
+
const fromGame = ctx.gameButtons ?? PM_GAME_BTNS;
|
|
658
|
+
const actingItems = new Set([
|
|
659
|
+
...rootThatActs(rootEl, options, acts, fromGame).map((b) => b.act),
|
|
660
|
+
...itemsThatAct(options, acts).map((b) => b.act),
|
|
661
|
+
...itemsThatAct(fromGame, acts).map((b) => b.act),
|
|
656
662
|
]);
|
|
657
|
-
//
|
|
658
|
-
//
|
|
659
|
-
for (
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
663
|
+
// An explicit index, not `filter(Boolean)`: the cards are indexed by PLAYER, building only screen 2 leaves a hole at
|
|
664
|
+
// index 0, and closing the hole would renumber the seats in the card's name. The `undefined` guard is written by hand.
|
|
665
|
+
for (let i = 0; i < cards.length; i++) {
|
|
666
|
+
const cardEl = cards[i];
|
|
667
|
+
if (!cardEl)
|
|
668
|
+
continue;
|
|
669
|
+
renameCard(cardEl, i);
|
|
670
|
+
for (const btn of cardEl.querySelectorAll('.pm-btn')) {
|
|
671
|
+
// LOCKED WITH ITS REASON, NOT HIDDEN (ADR-0161, superseding ADR-0106 §5 here): a card that changed shape from game
|
|
672
|
+
// to game hid from the child that an option exists. The cursor still stops on the item and it still counts;
|
|
673
|
+
// reaching or pressing it says why. And not `remove()`: the table can grow later.
|
|
674
|
+
const act = btn.dataset.act ?? '';
|
|
675
|
+
if (actingItems.has(act)) {
|
|
676
|
+
btn.removeAttribute('aria-disabled');
|
|
677
|
+
delete btn.dataset.motivo;
|
|
678
|
+
}
|
|
679
|
+
else {
|
|
680
|
+
btn.setAttribute('aria-disabled', 'true');
|
|
681
|
+
btn.dataset.motivo = itemReason(t, act);
|
|
682
|
+
}
|
|
664
683
|
}
|
|
665
684
|
}
|
|
666
685
|
}
|
|
667
686
|
function reflectPauseIcons() {
|
|
668
687
|
ctx.getA11yBars().forEach((bar, i) => reflectIconsIn(bar, i));
|
|
669
|
-
|
|
688
|
+
refreshPauseItems();
|
|
670
689
|
}
|
|
671
690
|
// --- the pause screen ----------------------------------------------------------------------
|
|
672
691
|
/**
|
|
673
|
-
*
|
|
674
|
-
*
|
|
675
|
-
*
|
|
676
|
-
* a fala, a única pista de que a tela mudou seria o silêncio. O índice "N de M" vem junto (item 3), e é ele
|
|
677
|
-
* que diz de quantos itens é a lista nova.
|
|
692
|
+
* Switches the visible list AND ANNOUNCES the first item of the list that came in. Not decoration: a child who cannot see
|
|
693
|
+
* has just changed menus and the cursor jumped; without speech the only clue would be silence. The «N de M» index comes
|
|
694
|
+
* with it (ADR-0044 item 3) and says how many items the new list has.
|
|
678
695
|
*/
|
|
679
|
-
function
|
|
680
|
-
const
|
|
681
|
-
if (!
|
|
696
|
+
function announceList(sp, sub) {
|
|
697
|
+
const first = showPauseOptions(sp, sub);
|
|
698
|
+
if (!first)
|
|
682
699
|
return;
|
|
683
|
-
const
|
|
684
|
-
ctx.srSay(
|
|
700
|
+
const items = [...sp.querySelectorAll(PM_VISIBLE_ITEMS)];
|
|
701
|
+
ctx.srSay(announceItem(t, { label: first.textContent || '', position: 1, total: items.length }, ctx.settings.menuIndexOn));
|
|
685
702
|
}
|
|
686
|
-
/* =====================
|
|
703
|
+
/* ===================== THE `accessibility` MODE (ADR-0044 item 7) ===================== */
|
|
687
704
|
/**
|
|
688
|
-
*
|
|
689
|
-
*
|
|
690
|
-
*
|
|
691
|
-
* modo de entrada que sobrevivesse ao reinício seria a armadilha voltando pela porta dos fundos — a criança
|
|
692
|
-
* abriria o jogo no dia seguinte e o personagem não andaria.
|
|
705
|
+
* Who has the d-pad steering the BAR instead of the character. ROUND lifetime (ADR-0038): it lives in this instance's
|
|
706
|
+
* closure, is not persisted, and goes with the round — an input mode that survived a restart would be the trap coming
|
|
707
|
+
* back: the child would open the game the next day and the character would not move.
|
|
693
708
|
*/
|
|
694
|
-
const
|
|
695
|
-
/**
|
|
696
|
-
function
|
|
709
|
+
const onBar = new Set();
|
|
710
|
+
/** Screen `i`'s bar cursor, or the first icon when there is no cursor yet. */
|
|
711
|
+
function selectedIcon(bar) {
|
|
697
712
|
return bar.querySelector('.pi-sel') || bar.querySelector('.pi-btn');
|
|
698
713
|
}
|
|
699
|
-
/**
|
|
700
|
-
function
|
|
714
|
+
/** Puts the cursor on an icon, writes the caption and ANNOUNCES — speech is the channel of whoever cannot see the icon. */
|
|
715
|
+
function selectIcon(i, bar, el) {
|
|
701
716
|
bar.querySelectorAll('.pi-sel').forEach((x) => x.classList.remove('pi-sel'));
|
|
702
717
|
el.classList.add('pi-sel');
|
|
703
718
|
const cap = bar.querySelector('.pause-icons-cap');
|
|
704
719
|
if (cap)
|
|
705
|
-
cap.textContent =
|
|
706
|
-
ctx.srSay(
|
|
720
|
+
cap.textContent = accessibleLabel(el);
|
|
721
|
+
ctx.srSay(iconCaption(t, bar, el, ctx.settings.menuIndexOn)); // the spoken one carries the place (ADR-0167)
|
|
722
|
+
ctx.explainIcon?.(i, el.dataset.pi ?? null);
|
|
707
723
|
}
|
|
708
724
|
/**
|
|
709
|
-
*
|
|
725
|
+
* ENTERS the mode: the d-pad now steers screen `i`'s bar.
|
|
726
|
+
*
|
|
727
|
+
* It does NOT resume the game (ADR-0155): START is the QUICK PAUSE, the bar is used with the game FROZEN, and resuming on
|
|
728
|
+
* entry would unfreeze the world the instant the child asked it to stop. Whoever enters decides the phase.
|
|
710
729
|
*
|
|
711
|
-
*
|
|
712
|
-
*
|
|
713
|
-
*
|
|
730
|
+
* The announcement says how to LEAVE, at the moment of entering — the line that disarms the trap ADR-0044 recorded as a
|
|
731
|
+
* negative consequence: a child who cannot see presses a direction, the character does not move, and nothing on screen
|
|
732
|
+
* explains why, because the screen is not that child's channel.
|
|
714
733
|
*/
|
|
715
|
-
function
|
|
734
|
+
function enterBarMode(i) {
|
|
716
735
|
const bar = ctx.getA11yBars()[i];
|
|
717
|
-
const
|
|
718
|
-
if (!bar || !
|
|
736
|
+
const first = bar && selectedIcon(bar);
|
|
737
|
+
if (!bar || !first)
|
|
719
738
|
return;
|
|
720
|
-
|
|
721
|
-
const acts = getPauseActs();
|
|
722
|
-
if (acts.resume)
|
|
723
|
-
acts.resume(); // volta à tela normal: o modo é para usar DURANTE a partida
|
|
739
|
+
onBar.add(i);
|
|
724
740
|
ctx.srSay(t('sr.a11y.barEnter'));
|
|
725
|
-
|
|
741
|
+
selectIcon(i, bar, first);
|
|
726
742
|
}
|
|
727
|
-
/**
|
|
728
|
-
|
|
729
|
-
|
|
743
|
+
/**
|
|
744
|
+
* LEAVES the mode and gives the d-pad back to the character. It announces, because giving it back is information too.
|
|
745
|
+
*
|
|
746
|
+
* `silent` is for leaving the bar for ANOTHER screen, not for the game — SELECT, which swaps the quick pause for the card
|
|
747
|
+
* (ADR-0155): «back to the game» said there would be a lie, with the card opening on top.
|
|
748
|
+
*
|
|
749
|
+
* `onLeaveBar` runs on EVERY exit — Back, START or whoever calls this — because the root must unfreeze the game by any
|
|
750
|
+
* door. An exit only one path knew would leave the child on the other back at the character in a stopped world.
|
|
751
|
+
*/
|
|
752
|
+
function leaveBarMode(i, silent = false) {
|
|
753
|
+
if (!onBar.delete(i))
|
|
730
754
|
return;
|
|
731
755
|
const bar = ctx.getA11yBars()[i];
|
|
732
756
|
if (bar) {
|
|
@@ -735,113 +759,112 @@ export function initPauseIcons(ctx) {
|
|
|
735
759
|
if (cap)
|
|
736
760
|
cap.textContent = '';
|
|
737
761
|
}
|
|
738
|
-
ctx.
|
|
762
|
+
ctx.explainIcon?.(i, null);
|
|
763
|
+
if (!silent)
|
|
764
|
+
ctx.srSay(t('sr.a11y.barExit'));
|
|
765
|
+
ctx.onLeaveBar?.(i, silent);
|
|
739
766
|
}
|
|
740
|
-
/**
|
|
741
|
-
const
|
|
767
|
+
/** Is screen `i`'s d-pad on the bar? What input routing asks every frame. */
|
|
768
|
+
const isOnBar = (i) => onBar.has(i);
|
|
742
769
|
/**
|
|
743
|
-
*
|
|
770
|
+
* ONE STEP inside the mode. `hasStart` is the edge of the button that opens the pause — the second way out.
|
|
744
771
|
*
|
|
745
|
-
*
|
|
746
|
-
*
|
|
772
|
+
* The bar is one row, so all FOUR directions move along it: for someone navigating without sight, «up» in a one-row list
|
|
773
|
+
* must not be a dead end. And it moves in a RING, like every menu of the game (ADR-0044 item 1).
|
|
747
774
|
*/
|
|
748
|
-
function navBar(i, k,
|
|
749
|
-
if (!
|
|
775
|
+
function navBar(i, k, hasStart = false) {
|
|
776
|
+
if (!onBar.has(i))
|
|
750
777
|
return;
|
|
751
778
|
const bar = ctx.getA11yBars()[i];
|
|
752
779
|
if (!bar)
|
|
753
780
|
return;
|
|
754
|
-
const
|
|
755
|
-
if (
|
|
756
|
-
|
|
781
|
+
const action = barAction(k, hasStart);
|
|
782
|
+
if (action === 'sair') {
|
|
783
|
+
leaveBarMode(i);
|
|
757
784
|
return;
|
|
758
785
|
}
|
|
759
|
-
const
|
|
760
|
-
if (!
|
|
786
|
+
const icons = [...bar.querySelectorAll('.pi-btn')];
|
|
787
|
+
if (!icons.length)
|
|
761
788
|
return;
|
|
762
|
-
|
|
763
|
-
const
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
cur.click();
|
|
789
|
+
// never null here: the bar has an icon, and `selectedIcon` falls back to the first one
|
|
790
|
+
const cur = selectedIcon(bar);
|
|
791
|
+
// the click goes through the bar's own listener, which records who pressed it (`setPauseActor`)
|
|
792
|
+
if (action === 'ativar') {
|
|
793
|
+
cur.click();
|
|
768
794
|
return;
|
|
769
795
|
}
|
|
770
|
-
if (
|
|
771
|
-
|
|
772
|
-
selecionarIcone(bar, icones[passoNoAnel(icones.length, idx, d)]);
|
|
773
|
-
}
|
|
796
|
+
if (action === 'andar')
|
|
797
|
+
selectIcon(i, bar, icons[stepInRing(icons.length, icons.indexOf(cur), (k.down || k.right) ? 1 : -1)]);
|
|
774
798
|
}
|
|
775
799
|
function buildScreenPause(i) {
|
|
776
|
-
const sp =
|
|
800
|
+
const sp = ctx.doc.createElement('div');
|
|
777
801
|
sp.className = 'screen-pause';
|
|
778
802
|
sp.hidden = true;
|
|
779
803
|
sp.dataset.player = String(i);
|
|
780
804
|
/*
|
|
781
|
-
*
|
|
782
|
-
*
|
|
783
|
-
* em que a resposta existe»; não é. O `getPauseActs` é um getter precisamente porque a tabela chega
|
|
784
|
-
* TARDE — «`pauseActs` is a `const` declared far below the init site», diz o próprio campo —, e o
|
|
785
|
-
* `createGame` monta durante o próprio `createGame(...)`. Filtrar aqui apagava para sempre todo item cuja
|
|
786
|
-
* acção só passou a existir depois do boot.
|
|
787
|
-
*
|
|
788
|
-
* Quem decide o que se VÊ é o `refrescarItensDaPausa`, a cada `reflectPauseIcons()` — que o `ui/shell`
|
|
789
|
-
* dispara quando a fase vira `pause-menu`, ou seja quando a pausa ABRE. O §5 continua respeitado (a
|
|
790
|
-
* criança nunca vê um item que não acciona) e agora também vê os que passaram a accionar.
|
|
805
|
+
* BUILDS THE WHOLE LIST, and `refreshPauseItems` decides which items act — here and on every `reflectPauseIcons()`.
|
|
806
|
+
* Filtering at build time would lose for good every item whose action exists only after the boot (see above).
|
|
791
807
|
*/
|
|
792
808
|
sp.innerHTML = screenPauseMarkup({
|
|
793
809
|
player: i,
|
|
794
810
|
numPlayers: ctx.getNumPlayers(),
|
|
795
811
|
pmButtons: ctx.pmButtons ?? PM_BTNS,
|
|
796
812
|
optionsButtons: ctx.optionsButtons ?? PM_OPTIONS_BTNS,
|
|
813
|
+
gameButtons: ctx.gameButtons ?? PM_GAME_BTNS,
|
|
797
814
|
dynLabel: dynLabel, t,
|
|
798
815
|
});
|
|
799
|
-
|
|
800
|
-
|
|
816
|
+
cards[i] = sp;
|
|
817
|
+
refreshPauseItems(); // the lock holds from the build, not only from the first opening
|
|
801
818
|
sp.addEventListener('click', (e) => {
|
|
802
|
-
const
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
setPauseActor(i);
|
|
806
|
-
const act = b.dataset.act || '';
|
|
807
|
-
// NAVEGAÇÃO DENTRO DO CARTÃO fica aqui, e não na tabela de ações: `options` e `pmback` não fazem nada
|
|
808
|
-
// ao jogo — trocam qual lista está na tela. A tabela vive em `ui/shell`, que não conhece este `sp`.
|
|
809
|
-
if (act === 'options' || act === 'pmback') {
|
|
810
|
-
anunciarLista(sp, act === 'options' ? 'opcoes' : 'raiz');
|
|
811
|
-
return;
|
|
812
|
-
}
|
|
813
|
-
// `acessibilidade` leva o cursor à BARRA RÁPIDA. Enquanto ela mora dentro do cartão, "entrar no modo"
|
|
814
|
-
// é pôr o cursor nela — e a saída continua sendo a saída da pausa, que é a mesma de sempre. Quando o
|
|
815
|
-
// item 7 levar a barra para o HUD, esta linha o segue; o que o item SIGNIFICA não muda.
|
|
816
|
-
if (act === 'acessibilidade') {
|
|
817
|
-
entrarNaBarra(i);
|
|
818
|
-
return;
|
|
819
|
-
}
|
|
820
|
-
const acts = getPauseActs();
|
|
821
|
-
const fn = acts[act];
|
|
822
|
-
if (fn)
|
|
823
|
-
fn();
|
|
824
|
-
return;
|
|
825
|
-
}
|
|
819
|
+
const b = e.target?.closest('.pm-btn');
|
|
820
|
+
if (b)
|
|
821
|
+
pressPauseItem(i, sp, b);
|
|
826
822
|
});
|
|
827
823
|
return sp;
|
|
828
824
|
}
|
|
825
|
+
/** One item of screen `i`'s pause card pressed — a locked one, a door between its lists, the quick bar, or an action. */
|
|
826
|
+
function pressPauseItem(i, sp, b) {
|
|
827
|
+
// LOCKED (ADR-0161): pressing it SAYS the reason and does nothing — neither door nor action.
|
|
828
|
+
if (b.getAttribute('aria-disabled') === 'true') {
|
|
829
|
+
const reason = b.dataset.motivo ?? '';
|
|
830
|
+
ctx.srSay(reason);
|
|
831
|
+
ctx.explainItem?.(reason);
|
|
832
|
+
return;
|
|
833
|
+
}
|
|
834
|
+
setPauseActor(i);
|
|
835
|
+
const act = b.dataset.act || '';
|
|
836
|
+
// «Opções do jogo» with the cartridge's rows opens the engine's panel (ADR-0182), not the list a host may pass
|
|
837
|
+
const doorWithPanel = act === 'opcoesdojogo' && typeof getPauseActs().opcoesdojogo === 'function';
|
|
838
|
+
if (DOOR_TO_LIST[act] && !doorWithPanel) {
|
|
839
|
+
announceList(sp, DOOR_TO_LIST[act]);
|
|
840
|
+
return;
|
|
841
|
+
}
|
|
842
|
+
// `acessibilidade` takes the cursor to the QUICK BAR. The item is not in the engine's root (ADR-0151); only a list a
|
|
843
|
+
// GAME passes reaches it, and for that list the card closes and the bar is used — explicitly, since `enterBar` does
|
|
844
|
+
// not resume by itself (ADR-0155).
|
|
845
|
+
if (act === 'acessibilidade') {
|
|
846
|
+
getPauseActs().resume?.();
|
|
847
|
+
enterBarMode(i);
|
|
848
|
+
return;
|
|
849
|
+
}
|
|
850
|
+
const fn = getPauseActs()[act];
|
|
851
|
+
if (fn)
|
|
852
|
+
fn();
|
|
853
|
+
}
|
|
829
854
|
/**
|
|
830
|
-
*
|
|
855
|
+
* One screen's QUICK BAR: the toggles, the caption, and their wiring.
|
|
831
856
|
*
|
|
832
|
-
*
|
|
833
|
-
*
|
|
834
|
-
* facilidade não existe —, e degradar o que existe para DAR acesso seria o contrário do que ele serve.
|
|
857
|
+
* A SIBLING of `.screen-exp`, not a child (#82): the bar is CONTROL, not experience. Empathy mode degrades the experience
|
|
858
|
+
* on purpose — a simulation creates difficulty — and degrading what exists to GIVE access would be the opposite.
|
|
835
859
|
*/
|
|
836
860
|
function buildQuickBar(i) {
|
|
837
|
-
const bar =
|
|
861
|
+
const bar = ctx.doc.createElement('div');
|
|
838
862
|
bar.className = 'screen-a11y';
|
|
839
863
|
bar.dataset.player = String(i);
|
|
840
|
-
bar.innerHTML = quickBarMarkup(
|
|
841
|
-
//
|
|
842
|
-
//
|
|
843
|
-
//
|
|
844
|
-
// `tabindex` é posto AQUI, no elemento, e não no markup que as duas compartilham.
|
|
864
|
+
bar.innerHTML = quickBarMarkup(ctx.translator, gameIcons);
|
|
865
|
+
// OUT OF THE TAB ORDER during play (ADR-0044 item 7): a stop per icon between the child and the game would be the price
|
|
866
|
+
// of leaving them in, and keyboard reach is not lost — it becomes the `accessibility` mode, opened from the pause. The
|
|
867
|
+
// bar the root mounts in `#title-icons` is unaffected: the `tabindex` is set HERE, on the element, not in the shared markup.
|
|
845
868
|
bar.querySelectorAll('.pi-btn').forEach((b) => { b.tabIndex = -1; });
|
|
846
869
|
const cap = bar.querySelector('.pause-icons-cap');
|
|
847
870
|
bar.addEventListener('click', (e) => {
|
|
@@ -852,39 +875,25 @@ export function initPauseIcons(ctx) {
|
|
|
852
875
|
iconAct(ib.dataset.pi || '', i);
|
|
853
876
|
reflectPauseIcons(); // must run BEFORE reading the label back — that is what makes the caption honest
|
|
854
877
|
if (cap)
|
|
855
|
-
cap.textContent =
|
|
856
|
-
});
|
|
857
|
-
// Legenda = o `aria-label` do botão, para que passar o mouse ou focar diga a MESMA verdade que um leitor
|
|
858
|
-
// de tela anunciaria. Uma fonte só para quem vê e para quem escuta.
|
|
859
|
-
//
|
|
860
|
-
// E ELA SOME AO SAIR. Antes ficava: a última explicação apontada permanecia por cima do jogo até alguém
|
|
861
|
-
// apontar outra. Numa barra que agora vive na TELA DE JOGO isso é uma faixa de texto parada em cima da
|
|
862
|
-
// partida — o Dev viu e disse o que é: explicação só enquanto o mouse estiver no botão.
|
|
863
|
-
//
|
|
864
|
-
// A EXCEÇÃO É O CURSOR DO MODO `accessibility`: quando ele está pousado num ícone, a legenda é a única
|
|
865
|
-
// coisa que diz onde ele está, e apagá-la ao mexer o mouse cegaria o modo. Daí a pergunta pelo `.pi-sel`.
|
|
866
|
-
const limpar = () => { if (cap && !bar.querySelector('.pi-sel'))
|
|
867
|
-
cap.textContent = ''; };
|
|
868
|
-
bar.querySelectorAll('.pi-btn').forEach((b) => {
|
|
869
|
-
const show = () => { if (cap)
|
|
870
|
-
cap.textContent = legendaDoIcone(bar, b); };
|
|
871
|
-
b.addEventListener('mouseenter', show);
|
|
872
|
-
b.addEventListener('focus', show);
|
|
873
|
-
b.addEventListener('mouseleave', limpar);
|
|
874
|
-
b.addEventListener('blur', limpar);
|
|
878
|
+
cap.textContent = accessibleLabel(ib);
|
|
875
879
|
});
|
|
880
|
+
// Caption = the button's `aria-label`, so hovering or focusing says the SAME truth a screen reader would announce: one
|
|
881
|
+
// source for whoever sees and whoever listens. It GOES AWAY on leaving — a bar on the play screen must not leave a strip
|
|
882
|
+
// of text over the game (the Dev: an explanation only while the pointer is on the button). The exception is the
|
|
883
|
+
// `accessibility` mode's CURSOR: when it sits on an icon the caption is the only thing saying where it is, hence the
|
|
884
|
+
// `.pi-sel` check in `wireBarCaption`.
|
|
885
|
+
wireBarCaption(bar, (k) => ctx.explainIcon?.(i, k));
|
|
876
886
|
return bar;
|
|
877
887
|
}
|
|
878
888
|
return {
|
|
879
|
-
buildScreenPause, buildQuickBar,
|
|
880
|
-
|
|
889
|
+
buildScreenPause, buildQuickBar, enterBar: enterBarMode, leaveBar: leaveBarMode, onBar: isOnBar, navBar,
|
|
890
|
+
mountedIcons: gameIcons,
|
|
881
891
|
iconAct, iconLabel, reflectIconBtn, reflectIconsIn, reflectPauseIcons,
|
|
882
|
-
//
|
|
883
|
-
//
|
|
884
|
-
// pior do defeito: o ajuste parece ter pegado e some depois.
|
|
892
|
+
// `setCalmMode` PERSISTS TOO, and sanitises: it is the other door to the same value, and a level set here that only
|
|
893
|
+
// lasted the session would look applied and then vanish.
|
|
885
894
|
applyCalm,
|
|
886
895
|
getCalmMode: () => calmMode,
|
|
887
|
-
setCalmMode: (n) => { calmMode =
|
|
896
|
+
setCalmMode: (n) => { calmMode = sanitiseTeaLevel(n, DEFAULTS.calmMode); ctx.store.set(KEYS.tea, calmMode); },
|
|
888
897
|
iconState,
|
|
889
898
|
};
|
|
890
899
|
}
|