@the-inclusionist/engine 10.0.0 → 11.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -1
- package/app/css/style.css +216 -42
- package/app/public/vendor/fonts-licences/NOTICE.txt +166 -0
- package/app/public/vendor/fonts-licences/OFL-1.1.txt +86 -0
- package/app/public/vendor/fonts.css +11 -1175
- package/dist-pkg/boot/create-game.d.ts +84 -33
- package/dist-pkg/boot/create-game.js +708 -307
- package/dist-pkg/core/a11y-sr.d.ts +1 -1
- package/dist-pkg/core/a11y-sr.js +3 -5
- package/dist-pkg/core/accommodations.d.ts +26 -8
- package/dist-pkg/core/accommodations.js +27 -10
- package/dist-pkg/core/actions.d.ts +39 -10
- package/dist-pkg/core/actions.js +28 -6
- package/dist-pkg/core/contract.d.ts +0 -2
- package/dist-pkg/core/contract.js +0 -2
- package/dist-pkg/core/i18n.d.ts +36 -71
- package/dist-pkg/core/i18n.js +131 -198
- package/dist-pkg/core/session-clock.d.ts +23 -0
- package/dist-pkg/core/session-clock.js +37 -0
- package/dist-pkg/i18n/en.js +44 -49
- package/dist-pkg/i18n/es.js +44 -49
- package/dist-pkg/i18n/pt.js +49 -54
- package/dist-pkg/input/gamepad.d.ts +42 -11
- package/dist-pkg/input/gamepad.js +50 -15
- package/dist-pkg/input/key-default.d.ts +30 -0
- package/dist-pkg/input/key-default.js +60 -0
- package/dist-pkg/input/keyboard-runtime.d.ts +0 -4
- package/dist-pkg/input/keyboard.d.ts +0 -1
- package/dist-pkg/input/keydown.d.ts +5 -11
- package/dist-pkg/input/keydown.js +3 -3
- package/dist-pkg/input/latch-edge.d.ts +10 -2
- package/dist-pkg/input/latch-edge.js +4 -3
- package/dist-pkg/input/latch-scope.d.ts +27 -22
- package/dist-pkg/input/latch-scope.js +25 -37
- package/dist-pkg/input/latch-store.d.ts +9 -12
- package/dist-pkg/input/latch-store.js +13 -18
- package/dist-pkg/input/latch-sync.d.ts +5 -4
- package/dist-pkg/input/latch-sync.js +5 -5
- package/dist-pkg/input/pad-reading.js +7 -2
- package/dist-pkg/input/pad-wizard.d.ts +4 -1
- package/dist-pkg/input/pad-wizard.js +60 -9
- package/dist-pkg/input/switch-scan.d.ts +29 -12
- package/dist-pkg/input/switch-scan.js +15 -2
- package/dist-pkg/input/touch-bindings.d.ts +3 -6
- package/dist-pkg/input/touch.d.ts +17 -2
- package/dist-pkg/input/touch.js +55 -17
- package/dist-pkg/input/transport-in-use.d.ts +5 -5
- package/dist-pkg/input/transport-in-use.js +5 -5
- package/dist-pkg/input/transports.d.ts +0 -9
- package/dist-pkg/input/transports.js +1 -1
- package/dist-pkg/input/virtual-controller.d.ts +39 -3
- package/dist-pkg/input/virtual-controller.js +32 -4
- package/dist-pkg/input/voice-map.d.ts +27 -11
- package/dist-pkg/input/voice-map.js +41 -43
- package/dist-pkg/platform/audio-ambient.d.ts +0 -1
- package/dist-pkg/platform/audio-ambient.js +3 -1
- package/dist-pkg/platform/audio-mixer.js +4 -9
- package/dist-pkg/platform/audio-sonar.d.ts +8 -94
- package/dist-pkg/platform/audio-sonar.js +22 -207
- package/dist-pkg/platform/audio.d.ts +10 -1
- package/dist-pkg/platform/audio.js +35 -2
- package/dist-pkg/platform/choose-by-voice.d.ts +70 -0
- package/dist-pkg/platform/choose-by-voice.js +157 -0
- package/dist-pkg/platform/font-library.d.ts +77 -0
- package/dist-pkg/platform/font-library.js +98 -0
- package/dist-pkg/platform/font-library.json +721 -0
- package/dist-pkg/platform/heavy-catalogue.d.ts +46 -0
- package/dist-pkg/platform/heavy-catalogue.js +47 -14
- package/dist-pkg/platform/heavy-mirror.d.ts +3 -9
- package/dist-pkg/platform/heavy-mirror.js +14 -17
- package/dist-pkg/platform/heavy.d.ts +40 -8
- package/dist-pkg/platform/heavy.js +177 -22
- package/dist-pkg/platform/interruptible-speech.d.ts +15 -8
- package/dist-pkg/platform/interruptible-speech.js +37 -13
- package/dist-pkg/platform/listener-scope.js +22 -13
- package/dist-pkg/platform/locale-host.d.ts +7 -3
- package/dist-pkg/platform/locale-host.js +13 -2
- package/dist-pkg/platform/reading-in-worker.d.ts +24 -0
- package/dist-pkg/platform/reading-in-worker.js +38 -0
- package/dist-pkg/platform/reading-model.d.ts +21 -0
- package/dist-pkg/platform/reading-model.js +25 -0
- package/dist-pkg/platform/reading-runtime.js +13 -8
- package/dist-pkg/platform/reading.d.ts +12 -1
- package/dist-pkg/platform/reading.js +9 -1
- package/dist-pkg/platform/speech-recognition.d.ts +10 -1
- package/dist-pkg/platform/speech-recognition.js +29 -9
- package/dist-pkg/platform/speech.d.ts +10 -1
- package/dist-pkg/platform/speech.js +24 -13
- package/dist-pkg/platform/tts.d.ts +37 -4
- package/dist-pkg/platform/tts.js +279 -57
- package/dist-pkg/platform/voice-listener.d.ts +6 -2
- package/dist-pkg/platform/voice-listener.js +1 -1
- package/dist-pkg/platform/voice-plan.d.ts +13 -0
- package/dist-pkg/platform/voice-plan.js +57 -2
- package/dist-pkg/platform/vosk-runtime.d.ts +25 -0
- package/dist-pkg/platform/vosk-runtime.js +59 -1
- package/dist-pkg/platform/vosk-vocabulary.d.ts +6 -0
- package/dist-pkg/platform/vosk-vocabulary.js +178 -0
- package/dist-pkg/render/crt.d.ts +11 -9
- package/dist-pkg/render/crt.js +6 -10
- package/dist-pkg/render/cvd-matrices.d.ts +5 -1
- package/dist-pkg/render/cvd-matrices.js +5 -1
- package/dist-pkg/render/high-contrast.d.ts +1 -2
- package/dist-pkg/render/high-contrast.js +5 -1
- package/dist-pkg/render/viz-axes.d.ts +9 -0
- package/dist-pkg/render/viz-axes.js +15 -2
- package/dist-pkg/ui/aac-sets.d.ts +29 -24
- package/dist-pkg/ui/aac-sets.js +22 -30
- package/dist-pkg/ui/audio-choices.d.ts +10 -5
- package/dist-pkg/ui/audio-choices.js +14 -10
- package/dist-pkg/ui/camera-control.d.ts +47 -0
- package/dist-pkg/ui/camera-control.js +35 -1
- package/dist-pkg/ui/declared-words.d.ts +21 -0
- package/dist-pkg/ui/declared-words.js +66 -0
- package/dist-pkg/ui/drawing-problems.js +4 -3
- package/dist-pkg/ui/eye-control.js +17 -17
- package/dist-pkg/ui/face-control.js +15 -15
- package/dist-pkg/ui/fonts.d.ts +42 -8
- package/dist-pkg/ui/fonts.js +58 -24
- package/dist-pkg/ui/footer-scroll-driver.d.ts +14 -0
- package/dist-pkg/ui/footer-scroll-driver.js +201 -0
- package/dist-pkg/ui/footer-scroll.d.ts +38 -0
- package/dist-pkg/ui/footer-scroll.js +55 -0
- package/dist-pkg/ui/game-options.d.ts +14 -9
- package/dist-pkg/ui/game-options.js +41 -21
- package/dist-pkg/ui/hand-control.js +15 -15
- package/dist-pkg/ui/help-panel.d.ts +19 -8
- package/dist-pkg/ui/help-panel.js +15 -5
- package/dist-pkg/ui/hud-bands.d.ts +29 -12
- package/dist-pkg/ui/hud-bands.js +63 -25
- package/dist-pkg/ui/hud-row.d.ts +34 -0
- package/dist-pkg/ui/hud-row.js +80 -0
- package/dist-pkg/ui/hud.d.ts +0 -3
- package/dist-pkg/ui/layout.js +5 -2
- package/dist-pkg/ui/libras-avatar-clip.d.ts +30 -0
- package/dist-pkg/ui/libras-avatar-clip.js +86 -0
- package/dist-pkg/ui/libras-avatar-load.d.ts +52 -0
- package/dist-pkg/ui/libras-avatar-load.js +134 -0
- package/dist-pkg/ui/libras-avatar-plan.d.ts +102 -0
- package/dist-pkg/ui/libras-avatar-plan.js +211 -0
- package/dist-pkg/ui/libras-avatar-player.d.ts +28 -0
- package/dist-pkg/ui/libras-avatar-player.js +226 -0
- package/dist-pkg/ui/libras-avatar-stage.d.ts +37 -0
- package/dist-pkg/ui/libras-avatar-stage.js +208 -0
- package/dist-pkg/ui/libras-glosses.d.ts +28 -0
- package/dist-pkg/ui/libras-glosses.js +161 -0
- package/dist-pkg/ui/menu-intent.d.ts +29 -0
- package/dist-pkg/ui/menu-intent.js +35 -1
- package/dist-pkg/ui/menu-items.d.ts +10 -0
- package/dist-pkg/ui/menu-items.js +29 -0
- package/dist-pkg/ui/menu-nav.d.ts +40 -4
- package/dist-pkg/ui/menu-nav.js +132 -47
- package/dist-pkg/ui/panel-widgets.d.ts +34 -0
- package/dist-pkg/ui/panel-widgets.js +86 -0
- package/dist-pkg/ui/pause-icons.d.ts +30 -14
- package/dist-pkg/ui/pause-icons.js +106 -45
- package/dist-pkg/ui/scan-overlay.d.ts +3 -3
- package/dist-pkg/ui/scan-overlay.js +11 -3
- package/dist-pkg/ui/screen-text.d.ts +25 -0
- package/dist-pkg/ui/screen-text.js +152 -0
- package/dist-pkg/ui/session-clock.d.ts +32 -0
- package/dist-pkg/ui/session-clock.js +67 -0
- package/dist-pkg/ui/settings-aac.d.ts +3 -1
- package/dist-pkg/ui/settings-aac.js +32 -13
- package/dist-pkg/ui/settings-audio.d.ts +10 -5
- package/dist-pkg/ui/settings-audio.js +101 -35
- package/dist-pkg/ui/settings-controls.d.ts +0 -4
- package/dist-pkg/ui/settings-mobility.d.ts +3 -6
- package/dist-pkg/ui/settings-mobility.js +4 -46
- package/dist-pkg/ui/settings-motion.js +2 -2
- package/dist-pkg/ui/settings-panel.d.ts +10 -0
- package/dist-pkg/ui/settings-panel.js +25 -1
- package/dist-pkg/ui/settings-typo.d.ts +5 -6
- package/dist-pkg/ui/settings-typo.js +20 -11
- package/dist-pkg/ui/settings-visual.js +2 -2
- package/dist-pkg/ui/shell.d.ts +1 -3
- package/dist-pkg/ui/shell.js +8 -4
- package/dist-pkg/ui/title.d.ts +2 -11
- package/dist-pkg/ui/title.js +39 -6
- package/dist-pkg/ui/top-band.d.ts +5 -3
- package/dist-pkg/ui/top-band.js +21 -25
- package/dist-pkg/ui/typo-choices.d.ts +3 -3
- package/dist-pkg/ui/typo-choices.js +9 -8
- package/dist-pkg/ui/vlibras.d.ts +71 -27
- package/dist-pkg/ui/vlibras.js +116 -44
- package/dist-pkg/ui/voice-control.d.ts +23 -1
- package/dist-pkg/ui/voice-control.js +149 -16
- package/dist-pkg/ui/voice-settings.d.ts +2 -3
- package/dist-pkg/ui/voice-settings.js +53 -3
- package/dist-pkg/ui/where-the-child-is.js +2 -2
- package/docs/CREDITS.md +122 -10
- package/docs/LICENSES.md +36 -7
- package/package.json +34 -9
- package/scripts/check-cartridge.mjs +78 -0
- package/scripts/game-build.d.mts +25 -0
- package/scripts/game-build.mjs +144 -0
- package/scripts/heavy-into-the-delivery.mjs +284 -40
- package/scripts/libras-avatar.json +680 -0
- package/scripts/libras-avatar.mjs +246 -0
- package/scripts/libras-glosses/gloss.py +95 -0
- package/scripts/libras-glosses/pyproject.toml +32 -0
- package/scripts/libras-glosses/uv.lock +781 -0
- package/scripts/libras-glosses.mjs +351 -0
- package/scripts/licences/OFL-1.1.txt +86 -0
- package/scripts/licences/UFL-1.0.txt +96 -0
- package/scripts/licences/fonts.mjs +389 -0
- package/scripts/licences/third-party.mjs +35 -21
- 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/fondamento-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/fondamento-400.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/inter-400.woff2 +0 -0
- package/app/public/vendor/fonts/inter-700.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/lato-400.woff2 +0 -0
- package/app/public/vendor/fonts/lato-700.woff2 +0 -0
- package/app/public/vendor/fonts/literata-400.woff2 +0 -0
- package/app/public/vendor/fonts/literata-700.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/newsreader-400.woff2 +0 -0
- package/app/public/vendor/fonts/newsreader-700.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/opendyslexic-400.woff2 +0 -0
- package/app/public/vendor/fonts/opensans-400.woff2 +0 -0
- package/app/public/vendor/fonts/opensans-700.woff2 +0 -0
- package/app/public/vendor/fonts/pinyon-400.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/pressstart-400.woff2 +0 -0
- package/app/public/vendor/fonts/quattro-400.woff2 +0 -0
- package/app/public/vendor/fonts/quattro-700.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/sourcesans-400.woff2 +0 -0
- package/app/public/vendor/fonts/sourcesans-700.woff2 +0 -0
- package/app/public/vendor/fonts/sourceserif-400.woff2 +0 -0
- package/app/public/vendor/fonts/sourceserif-700.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/ufmag-400.woff2 +0 -0
- package/dist-pkg/platform/guide-intensity.d.ts +0 -39
- package/dist-pkg/platform/guide-intensity.js +0 -71
- package/dist-pkg/ui/latch-refusal.d.ts +0 -33
- package/dist-pkg/ui/latch-refusal.js +0 -60
|
@@ -44,8 +44,17 @@ export interface Audio {
|
|
|
44
44
|
setHearingLossGraph(on: boolean): void;
|
|
45
45
|
/** The pause and title silence everything; playing brings it back. Acts only if the master exists — never makes one. */
|
|
46
46
|
setMasterMuted(muted: boolean): void;
|
|
47
|
-
/**
|
|
47
|
+
/**
|
|
48
|
+
* The mixer's live state per category, loaded from the store when the audio is built. Written in place — `on`, `vol`, or the
|
|
49
|
+
* whole category — by the panels, the bar and any game, and EVERY write that changes a value is told to `onCatChange`.
|
|
50
|
+
*/
|
|
48
51
|
readonly audioCat: Record<string, CatState>;
|
|
52
|
+
/**
|
|
53
|
+
* Listens to every change of a category — its `on` or its `vol` — WHOEVER made it: the engine's panels and bar, and a game
|
|
54
|
+
* writing `audio.audioCat.tts.on = false` itself (ADR-0247). Called with the category's key, after the value changed; a write
|
|
55
|
+
* of the value already there is no change. Answers the function that stops listening.
|
|
56
|
+
*/
|
|
57
|
+
onCatChange(listener: (cat: string) => void): () => void;
|
|
49
58
|
/** A category's bus, hanging off the master node. */
|
|
50
59
|
catNode(cat: string): GainNode | null;
|
|
51
60
|
/** Moves a category's bus to its state now, and keeps that state in the store. */
|
|
@@ -23,6 +23,32 @@ function buildHearingChain(ac) {
|
|
|
23
23
|
sp.connect(ac.destination);
|
|
24
24
|
return { input: lp };
|
|
25
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* THE CATEGORIES AS THE MIXER HOLDS THEM, each write heard (ADR-0247). A game reaches the mixer itself (`Engine.audio`), and
|
|
28
|
+
* its door to a category is a plain property write — `audioCat.tts.on = false` — with no call after it that an event could
|
|
29
|
+
* hang on. So the write IS the door: `on` and `vol` are accessors that tell `changed`, and so is the category itself, whose
|
|
30
|
+
* replacement (`audioCat.tts = {…}`) is copied INTO the mixer's object — replaced, the object every surface reads would come
|
|
31
|
+
* apart from the one the engine and the game write, and no later write would be heard.
|
|
32
|
+
*/
|
|
33
|
+
function heardCategories(stored, changed) {
|
|
34
|
+
const cats = {};
|
|
35
|
+
for (const [k, first] of Object.entries(stored)) {
|
|
36
|
+
let on = first.on, vol = first.vol;
|
|
37
|
+
const live = {};
|
|
38
|
+
Object.defineProperties(live, {
|
|
39
|
+
on: { enumerable: true, get: () => on, set: (v) => { if (v !== on) {
|
|
40
|
+
on = v;
|
|
41
|
+
changed(k);
|
|
42
|
+
} } },
|
|
43
|
+
vol: { enumerable: true, get: () => vol, set: (v) => { if (v !== vol) {
|
|
44
|
+
vol = v;
|
|
45
|
+
changed(k);
|
|
46
|
+
} } },
|
|
47
|
+
});
|
|
48
|
+
Object.defineProperty(cats, k, { enumerable: true, get: () => live, set: (v) => { live.on = v.on; live.vol = v.vol; } });
|
|
49
|
+
}
|
|
50
|
+
return cats;
|
|
51
|
+
}
|
|
26
52
|
/** The last node before the output: a panner clamped to the two ears when a pan is asked, the gain itself when not. */
|
|
27
53
|
function panned(ac, g, pan) {
|
|
28
54
|
if (pan == null || !ac.createStereoPanner)
|
|
@@ -42,8 +68,15 @@ export function createAudio(deps) {
|
|
|
42
68
|
let hearingLoss = false;
|
|
43
69
|
let masterGain = null, hlChain = null;
|
|
44
70
|
let footCount = 0;
|
|
45
|
-
const
|
|
71
|
+
const catListeners = new Set();
|
|
72
|
+
// a copy of the listeners, so one that stops listening while it is told does not skip the next
|
|
73
|
+
const audioCat = heardCategories(loadAudioCat(deps.store), (cat) => { for (const l of [...catListeners])
|
|
74
|
+
l(cat); });
|
|
46
75
|
const catNodes = {};
|
|
76
|
+
function onCatChange(listener) {
|
|
77
|
+
catListeners.add(listener);
|
|
78
|
+
return () => { catListeners.delete(listener); };
|
|
79
|
+
}
|
|
47
80
|
function ensureAC() {
|
|
48
81
|
if (!audioCtx)
|
|
49
82
|
audioCtx = deps.newContext();
|
|
@@ -174,7 +207,7 @@ export function createAudio(deps) {
|
|
|
174
207
|
get volume() { return volume; }, setVolume: (v) => { volume = v; },
|
|
175
208
|
get audioCtx() { return audioCtx; }, ensureAC, audioOut,
|
|
176
209
|
get hearingLoss() { return hearingLoss; }, setHearingLossGraph, setMasterMuted,
|
|
177
|
-
audioCat, catNode, setCatGain,
|
|
210
|
+
audioCat, onCatChange, catNode, setCatGain,
|
|
178
211
|
tone, tonePan, noiseHit,
|
|
179
212
|
get footCount() { return footCount; },
|
|
180
213
|
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
import { type VoskDeps, type VoskLoad } from './vosk-runtime.js';
|
|
3
|
+
import { type VoiceListenerDeps } from './voice-listener.js';
|
|
4
|
+
/** What a choice gives back: the index of the option said, or `null`; what was heard; why it ended. */
|
|
5
|
+
export interface ChoiceHeard {
|
|
6
|
+
readonly chosen: number | null;
|
|
7
|
+
readonly heard: string;
|
|
8
|
+
/** One option said · more than one said in one sentence · no option in time · asked to stop. */
|
|
9
|
+
readonly ended: 'chosen' | 'unclear' | 'timeout' | 'asked';
|
|
10
|
+
}
|
|
11
|
+
export interface ChooseSettings {
|
|
12
|
+
/** The language the options are in — the content's, when it is not the page's (ADR-0225). Default: the page's. */
|
|
13
|
+
readonly language?: string;
|
|
14
|
+
/** How long to wait for an option. Default 10 s: time to think, and a microphone that is never forgotten open. */
|
|
15
|
+
readonly maxMs?: number;
|
|
16
|
+
/**
|
|
17
|
+
* Called once, when the ear is OPEN and a word said now would be heard — at once when the 👄's ear is lent, after the model
|
|
18
|
+
* has loaded when one had to be opened. Before it, a game says «loading», not «say the answer»: the first choice in a language
|
|
19
|
+
* waits a few seconds for its model, and a child told to speak into that wait is not heard (the Dev, 2026-09-27).
|
|
20
|
+
*/
|
|
21
|
+
readonly onListening?: () => void;
|
|
22
|
+
}
|
|
23
|
+
/** A recogniser listening with a grammar: it can be stopped, and it may say which words its model knows. */
|
|
24
|
+
export interface OpenedChoice {
|
|
25
|
+
stop(): Promise<void>;
|
|
26
|
+
/** The words the language's model knows, when they can be read — an option with a word outside them can never be chosen. */
|
|
27
|
+
readonly vocabulary?: () => Promise<ReadonlySet<string> | null>;
|
|
28
|
+
}
|
|
29
|
+
export interface VoiceChooserDeps {
|
|
30
|
+
/** The page's language (`core/i18n.bcp47`). */
|
|
31
|
+
readonly language: () => string;
|
|
32
|
+
/**
|
|
33
|
+
* Opens the command recogniser for `language` with this grammar and the microphone, calling `onHeard` with every text it hears
|
|
34
|
+
* — `final` when the sentence ended. REJECTS, with the reason, when the model or the microphone cannot be had.
|
|
35
|
+
*/
|
|
36
|
+
readonly open: (language: string, grammar: readonly string[], onHeard: (text: string, final: boolean) => void) => Promise<OpenedChoice>;
|
|
37
|
+
readonly after: (fn: () => void, ms: number) => unknown;
|
|
38
|
+
readonly cancel: (handle: unknown) => void;
|
|
39
|
+
/** A line for `problems`, written once per reason (ADR-0169). */
|
|
40
|
+
readonly report: (line: string) => void;
|
|
41
|
+
/**
|
|
42
|
+
* An ear ALREADY LISTENING in `language`, lent for this choice with the grammar given — the 👄's (`ui/voice-control.borrow`) — or
|
|
43
|
+
* `null`. Borrowed, nothing is opened and nothing paused: one model and one microphone, however many choices.
|
|
44
|
+
*/
|
|
45
|
+
readonly borrow?: (language: string, grammar: readonly string[], onHeard: (text: string, final: boolean) => void) => OpenedChoice | null;
|
|
46
|
+
/** Called before an ear of its own is opened and after it is let go: the root pauses the 👄 in between (one ear per word). */
|
|
47
|
+
readonly pause?: () => void;
|
|
48
|
+
readonly resume?: () => void;
|
|
49
|
+
}
|
|
50
|
+
export interface VoiceChooser {
|
|
51
|
+
choose(options: readonly string[], settings?: ChooseSettings): Promise<ChoiceHeard>;
|
|
52
|
+
/** Ends the choice in hand now: it answers `null`, `ended: 'asked'`. */
|
|
53
|
+
stop(): void;
|
|
54
|
+
}
|
|
55
|
+
/** What opening the command recogniser for a choice uses of the delivery and the browser, lent by the root (ADR-0232 D4). */
|
|
56
|
+
export type CommandRecogniserDeps = Omit<VoskDeps, 'language'> & Pick<VoiceListenerDeps, 'getUserMedia' | 'createContext'> & {
|
|
57
|
+
/** How a language's model is had — the root's `keepVoskModels(…).load`, so it is loaded once; default a fresh load. */
|
|
58
|
+
readonly load?: (d: VoskDeps) => Promise<VoskLoad>;
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* THE `open` A CHOOSER IS GIVEN WHERE THE COMMAND RECOGNISER IS THE EAR: the language's model from the delivery, then the
|
|
62
|
+
* microphone with the grammar. A model that opened for a microphone that did not is let go before the refusal travels.
|
|
63
|
+
*/
|
|
64
|
+
export declare function openWithCommandRecogniser(d: CommandRecogniserDeps): VoiceChooserDeps['open'];
|
|
65
|
+
/**
|
|
66
|
+
* The options a sentence names, in the order said, each once: read word by word, the LONGEST option that starts at each word —
|
|
67
|
+
* so «cavalo marinho» is one option and not also «cavalo» — accents and case aside, whole words only.
|
|
68
|
+
*/
|
|
69
|
+
export declare function optionsNamed(options: readonly string[], sentence: string): number[];
|
|
70
|
+
export declare function createVoiceChooser(d: VoiceChooserDeps): VoiceChooser;
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// platform/choose-by-voice — A CHILD ANSWERS BY SAYING ONE OF THE OPTIONS THE GAME SHOWS (ADR-0256).
|
|
3
|
+
//
|
|
4
|
+
// The game passes the options it is showing and gets back which one was said. The recogniser hears a grammar of exactly those
|
|
5
|
+
// options plus `[unk]`, so it can only answer with one of them or with «none of these» — never with the nearest option to a word
|
|
6
|
+
// that was not one. 📏 Why not the reading (Whisper): the lab measured it at 1 of 7 on words said alone, and an option is one or
|
|
7
|
+
// two words; the command recogniser, closed, heard 7 of 7.
|
|
8
|
+
//
|
|
9
|
+
// 🔴 TWO OPTIONS IN ONE SENTENCE ARE NOT AN ANSWER (the quiz's own rule, which moved here with the matching): a child who says
|
|
10
|
+
// «gato ou galinha» is thinking out loud, and picking one of them would answer FOR her — and mark it wrong. So the choice is
|
|
11
|
+
// decided at the END of a sentence, never on its first word: one option chooses, two or more end the choice unanswered, none
|
|
12
|
+
// goes on listening. 📏 It costs the end of the sentence — the lab measured 1.3 s after the word against 0.4 s on the partial.
|
|
13
|
+
//
|
|
14
|
+
// What this module decides, and nothing else: the grammar an option set becomes, which options a sentence names, when the
|
|
15
|
+
// listening ends, and that one choice at a time holds the microphone. Opening the recogniser and the microphone is the root's
|
|
16
|
+
// (`open`), which lends the delivery, the cache and the browser (ADR-0232 D4).
|
|
17
|
+
import { spokenText, compareKey } from './speech-recognition.js';
|
|
18
|
+
import { loadVoskRuntime } from './vosk-runtime.js';
|
|
19
|
+
import { startVoiceListening } from './voice-listener.js';
|
|
20
|
+
const MAX_MS = 10_000;
|
|
21
|
+
/**
|
|
22
|
+
* THE `open` A CHOOSER IS GIVEN WHERE THE COMMAND RECOGNISER IS THE EAR: the language's model from the delivery, then the
|
|
23
|
+
* microphone with the grammar. A model that opened for a microphone that did not is let go before the refusal travels.
|
|
24
|
+
*/
|
|
25
|
+
export function openWithCommandRecogniser(d) {
|
|
26
|
+
return async (language, grammar, onHeard) => {
|
|
27
|
+
// a device with no microphone is refused before 31 MiB of model are opened for nothing
|
|
28
|
+
if (!d.getUserMedia)
|
|
29
|
+
throw new Error('this device cannot open a microphone');
|
|
30
|
+
const load = await (d.load ?? loadVoskRuntime)({ ...d, language });
|
|
31
|
+
if (!load.ok)
|
|
32
|
+
throw new Error(`${load.missing.join(', ')} not on this device`);
|
|
33
|
+
try {
|
|
34
|
+
const listening = await startVoiceListening({
|
|
35
|
+
model: load.model, grammar, getUserMedia: d.getUserMedia, createContext: d.createContext,
|
|
36
|
+
onPartial: (text) => onHeard(text, false), onFinal: (text) => onHeard(text, true),
|
|
37
|
+
});
|
|
38
|
+
return { stop: () => listening.stop(), ...(load.vocabulary ? { vocabulary: load.vocabulary } : {}) };
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
load.model.terminate?.();
|
|
42
|
+
throw e;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The options a sentence names, in the order said, each once: read word by word, the LONGEST option that starts at each word —
|
|
48
|
+
* so «cavalo marinho» is one option and not also «cavalo» — accents and case aside, whole words only.
|
|
49
|
+
*/
|
|
50
|
+
export function optionsNamed(options, sentence) {
|
|
51
|
+
const keys = options.map((o) => compareKey(o).split(' ').filter(Boolean));
|
|
52
|
+
const said = compareKey(sentence).split(' ').filter(Boolean);
|
|
53
|
+
const named = [];
|
|
54
|
+
for (let i = 0; i < said.length;) {
|
|
55
|
+
let best = -1;
|
|
56
|
+
keys.forEach((k, o) => {
|
|
57
|
+
if (k.length && k.every((w, j) => said[i + j] === w) && (best < 0 || k.length > keys[best].length))
|
|
58
|
+
best = o;
|
|
59
|
+
});
|
|
60
|
+
if (best < 0) {
|
|
61
|
+
i += 1;
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
if (!named.includes(best))
|
|
65
|
+
named.push(best);
|
|
66
|
+
i += keys[best].length;
|
|
67
|
+
}
|
|
68
|
+
return named;
|
|
69
|
+
}
|
|
70
|
+
export function createVoiceChooser(d) {
|
|
71
|
+
const said = new Set();
|
|
72
|
+
const once = (kind, line) => { if (!said.has(kind)) {
|
|
73
|
+
said.add(kind);
|
|
74
|
+
d.report(line);
|
|
75
|
+
} };
|
|
76
|
+
let inFlight = null;
|
|
77
|
+
let askToStop = null;
|
|
78
|
+
/** An option with a word the model does not know can never be heard: said to the adult, once per option and language. */
|
|
79
|
+
const reportUnsayable = async (opened, options, language) => {
|
|
80
|
+
const known = await opened.vocabulary?.().catch(() => null);
|
|
81
|
+
if (!known)
|
|
82
|
+
return;
|
|
83
|
+
for (const option of options) {
|
|
84
|
+
const lacking = spokenText(option).split(' ').filter((w) => w && !known.has(w));
|
|
85
|
+
if (lacking.length) {
|
|
86
|
+
once(`unsayable:${language}:${option}`, `choosing by voice: the option "${option}" has ${lacking.length === 1 ? 'a word' : 'words'} `
|
|
87
|
+
+ `the ${language} speech model does not know (${lacking.map((w) => `"${w}"`).join(', ')}) — the child cannot choose it by saying `
|
|
88
|
+
+ 'it, only with the arrows; reword it with words the model knows (ADR-0256)');
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
const run = async (options, settings) => {
|
|
93
|
+
const language = settings.language ?? d.language();
|
|
94
|
+
const grammar = [...new Set(options.map((o) => spokenText(o)).filter(Boolean))];
|
|
95
|
+
let heard = '';
|
|
96
|
+
let settle = () => { };
|
|
97
|
+
const answer = new Promise((resolve) => { settle = resolve; });
|
|
98
|
+
const end = (chosen, ended) => settle({ chosen, heard, ended });
|
|
99
|
+
// asked to stop while the microphone is still opening counts too: it is let go as soon as it opens
|
|
100
|
+
askToStop = () => end(null, 'asked');
|
|
101
|
+
const onHeard = (text, final) => {
|
|
102
|
+
const clean = text.replace(/\[unk\]/g, ' ').replace(/\s+/g, ' ').trim();
|
|
103
|
+
// 🔴 THE END OF A SENTENCE IS WHAT WAS HEARD, even empty: a partial the recogniser took back («happy», then nothing) is not
|
|
104
|
+
// an answer to show the child — measured on 2026-09-27, the quiz said «I heard happy» for a word the model had withdrawn
|
|
105
|
+
if (final || clean)
|
|
106
|
+
heard = clean;
|
|
107
|
+
if (!final || !clean)
|
|
108
|
+
return;
|
|
109
|
+
const named = optionsNamed(options, clean);
|
|
110
|
+
if (named.length === 1)
|
|
111
|
+
end(named[0], 'chosen');
|
|
112
|
+
else if (named.length > 1)
|
|
113
|
+
end(null, 'unclear');
|
|
114
|
+
};
|
|
115
|
+
const borrowed = d.borrow?.(language, grammar, onHeard) ?? null;
|
|
116
|
+
if (!borrowed)
|
|
117
|
+
d.pause?.();
|
|
118
|
+
try {
|
|
119
|
+
let opened;
|
|
120
|
+
try {
|
|
121
|
+
opened = borrowed ?? await d.open(language, grammar, onHeard);
|
|
122
|
+
}
|
|
123
|
+
catch (e) {
|
|
124
|
+
const why = e instanceof Error ? e.message : String(e);
|
|
125
|
+
once(`open:${language}`, `choosing by voice: the recogniser did not open for ${language} (${why}) — the child cannot answer an `
|
|
126
|
+
+ `option by saying it; build the delivery with \`--commands ${language.split('-')[0]}\` and allow the microphone (ADR-0256)`);
|
|
127
|
+
throw e;
|
|
128
|
+
}
|
|
129
|
+
void reportUnsayable(opened, options, language);
|
|
130
|
+
// a game's listener that throws must not take the child's answer with it
|
|
131
|
+
try {
|
|
132
|
+
settings.onListening?.();
|
|
133
|
+
}
|
|
134
|
+
catch { /* the game's own line, not the choice */ }
|
|
135
|
+
const timer = d.after(() => end(null, 'timeout'), settings.maxMs ?? MAX_MS);
|
|
136
|
+
const result = await answer;
|
|
137
|
+
d.cancel(timer);
|
|
138
|
+
await opened.stop().catch(() => { });
|
|
139
|
+
return result;
|
|
140
|
+
}
|
|
141
|
+
finally {
|
|
142
|
+
askToStop = null;
|
|
143
|
+
if (!borrowed)
|
|
144
|
+
d.resume?.();
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
return {
|
|
148
|
+
choose(options, settings = {}) {
|
|
149
|
+
// one microphone, one choice: a second caller waits on the first
|
|
150
|
+
if (inFlight)
|
|
151
|
+
return inFlight;
|
|
152
|
+
inFlight = run(options, settings).finally(() => { inFlight = null; });
|
|
153
|
+
return inFlight;
|
|
154
|
+
},
|
|
155
|
+
stop() { askToStop?.(); },
|
|
156
|
+
};
|
|
157
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
import type { HeavyFile } from './heavy-catalogue.js';
|
|
3
|
+
/** One face of a family: one file, and what its `@font-face` rule says of it. */
|
|
4
|
+
export interface LibraryFace {
|
|
5
|
+
readonly file: string;
|
|
6
|
+
/** A weight (`400`) or a variable range (`100 900`), as `font-weight` takes it. */
|
|
7
|
+
readonly weight: string;
|
|
8
|
+
readonly style: string;
|
|
9
|
+
/** A key of `FontLibrary.ranges`; absent = the file covers the whole family (an author's original, ADR-0254). */
|
|
10
|
+
readonly range?: string;
|
|
11
|
+
readonly bytes: number;
|
|
12
|
+
readonly sha256: string;
|
|
13
|
+
/** The author's original this file was compressed from, for a family with a Reserved Font Name (ADR-0254). */
|
|
14
|
+
readonly from?: string;
|
|
15
|
+
}
|
|
16
|
+
/** A family of the library: its folder in the mirror, the licence its fonts state, and its faces. */
|
|
17
|
+
export interface LibraryFamily {
|
|
18
|
+
readonly folder: string;
|
|
19
|
+
readonly licence: string;
|
|
20
|
+
readonly faces: readonly LibraryFace[];
|
|
21
|
+
}
|
|
22
|
+
/** The catalogue: the mirror's address, the unicode-ranges the faces share, and the families by name. */
|
|
23
|
+
export interface FontLibrary {
|
|
24
|
+
readonly mirror: string;
|
|
25
|
+
readonly ranges: Readonly<Record<string, string>>;
|
|
26
|
+
readonly families: Readonly<Record<string, LibraryFamily>>;
|
|
27
|
+
}
|
|
28
|
+
/** The heavy-file id of a face: `font:<folder>:<file>`. */
|
|
29
|
+
export declare const libraryFaceId: (family: LibraryFamily, face: LibraryFace) => string;
|
|
30
|
+
/**
|
|
31
|
+
* Each FILE of these families as a heavy file: its address, its bytes and the sha256 its body must have to be kept. Once each —
|
|
32
|
+
* a variable file may be declared at two weights, and is kept once.
|
|
33
|
+
*/
|
|
34
|
+
export declare function libraryFiles(library: FontLibrary, families: readonly string[]): HeavyFile[];
|
|
35
|
+
/**
|
|
36
|
+
* The `@font-face` rules of these families, each `src` at `href(address)` — the delivery's `heavy/` path, resolved against the
|
|
37
|
+
* page, where the service worker answers from the checked cache. A face with no range covers the whole family (ADR-0254).
|
|
38
|
+
*/
|
|
39
|
+
export declare function libraryFaceRules(library: FontLibrary, families: readonly string[], href: (url: string) => string): string;
|
|
40
|
+
/**
|
|
41
|
+
* THE FAMILIES A CARTRIDGE DECLARED (`uses.fonts`), less the engine's own — they are packaged, and declaring one asks for
|
|
42
|
+
* nothing. `problem` names a declaration that is not a list of family names, which the engine cannot read.
|
|
43
|
+
*/
|
|
44
|
+
export declare function declaredFamilies(value: unknown, engineFamilies: ReadonlySet<string>): {
|
|
45
|
+
readonly asked: string[];
|
|
46
|
+
readonly problem: string | null;
|
|
47
|
+
};
|
|
48
|
+
/** The line of `problems` for a declared family the library does not hold (ADR-0169: the subject, the cost, the fix). */
|
|
49
|
+
export declare function unknownFamilyLine(family: string): string;
|
|
50
|
+
/** What the root lends the library's start: where the rules go, how a delivery path becomes an address, and where lines go. */
|
|
51
|
+
export interface LibraryFontsPorts {
|
|
52
|
+
/** The families declared, the engine's own left out (`declaredFamilies`). */
|
|
53
|
+
readonly asked: readonly string[];
|
|
54
|
+
/** Writes the rules into the document (a `<style>` of the root's). */
|
|
55
|
+
readonly declare: (css: string) => void;
|
|
56
|
+
/** An upstream address → the address the page asks for it at (`heavy/` under the page). */
|
|
57
|
+
readonly href: (url: string) => string;
|
|
58
|
+
/** A line of `problems`. */
|
|
59
|
+
readonly report: (line: string) => void;
|
|
60
|
+
/** The catalogue; `loadFontLibrary` unless a case hands its own. */
|
|
61
|
+
readonly load?: () => Promise<FontLibrary>;
|
|
62
|
+
}
|
|
63
|
+
/** What the start answers: the files to keep in the checked cache, and the listener that turns their refusals into lines. */
|
|
64
|
+
export interface LibraryFontsStarted {
|
|
65
|
+
readonly files: readonly HeavyFile[];
|
|
66
|
+
readonly onReport: (r: {
|
|
67
|
+
readonly id: string;
|
|
68
|
+
readonly outcome: string;
|
|
69
|
+
readonly error?: string;
|
|
70
|
+
}) => void;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* DECLARES THE ASKED FAMILIES' FACES, and answers the files to keep. A family the library does not hold is said in `problems`;
|
|
74
|
+
* a file the delivery did not carry, or whose sha256 differs, is said once per family when its report arrives. Never throws: a
|
|
75
|
+
* catalogue that does not load is one line, and the game plays in its stack's next face.
|
|
76
|
+
*/
|
|
77
|
+
export declare function startLibraryFonts(p: LibraryFontsPorts): Promise<LibraryFontsStarted>;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
/** Loads the catalogue — its own chunk, fetched from the page's origin the first time a cartridge declares a family. */
|
|
3
|
+
async function loadFontLibrary() {
|
|
4
|
+
return (await import('./font-library.json')).default;
|
|
5
|
+
}
|
|
6
|
+
/** The address a face is kept under in the checked cache — the mirror's, as every heavy file's upstream address is. */
|
|
7
|
+
function libraryFaceUrl(library, family, face) {
|
|
8
|
+
return `${library.mirror}/${family.folder}/${face.file}`;
|
|
9
|
+
}
|
|
10
|
+
/** The heavy-file id of a face: `font:<folder>:<file>`. */
|
|
11
|
+
export const libraryFaceId = (family, face) => `font:${family.folder}:${face.file}`;
|
|
12
|
+
/**
|
|
13
|
+
* Each FILE of these families as a heavy file: its address, its bytes and the sha256 its body must have to be kept. Once each —
|
|
14
|
+
* a variable file may be declared at two weights, and is kept once.
|
|
15
|
+
*/
|
|
16
|
+
export function libraryFiles(library, families) {
|
|
17
|
+
const files = families.flatMap((name) => {
|
|
18
|
+
const family = library.families[name];
|
|
19
|
+
return family ? family.faces.map((face) => ({
|
|
20
|
+
id: libraryFaceId(family, face), url: libraryFaceUrl(library, family, face), bytes: face.bytes, sha256: face.sha256,
|
|
21
|
+
})) : [];
|
|
22
|
+
});
|
|
23
|
+
return files.filter((f, i) => files.findIndex((g) => g.id === f.id) === i);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The `@font-face` rules of these families, each `src` at `href(address)` — the delivery's `heavy/` path, resolved against the
|
|
27
|
+
* page, where the service worker answers from the checked cache. A face with no range covers the whole family (ADR-0254).
|
|
28
|
+
*/
|
|
29
|
+
export function libraryFaceRules(library, families, href) {
|
|
30
|
+
return families.flatMap((name) => {
|
|
31
|
+
const family = library.families[name];
|
|
32
|
+
if (!family)
|
|
33
|
+
return [];
|
|
34
|
+
return family.faces.map((face) => {
|
|
35
|
+
const range = face.range ? `unicode-range:${library.ranges[face.range]};` : '';
|
|
36
|
+
return `@font-face{font-family:'${name}';font-style:${face.style};font-weight:${face.weight};font-display:swap;`
|
|
37
|
+
+ `src:url('${href(libraryFaceUrl(library, family, face))}') format('woff2');${range}}`;
|
|
38
|
+
});
|
|
39
|
+
}).join('\n');
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* THE FAMILIES A CARTRIDGE DECLARED (`uses.fonts`), less the engine's own — they are packaged, and declaring one asks for
|
|
43
|
+
* nothing. `problem` names a declaration that is not a list of family names, which the engine cannot read.
|
|
44
|
+
*/
|
|
45
|
+
export function declaredFamilies(value, engineFamilies) {
|
|
46
|
+
if (value === undefined || value === null)
|
|
47
|
+
return { asked: [], problem: null };
|
|
48
|
+
if (!Array.isArray(value) || value.some((f) => typeof f !== 'string' || !f.trim())) {
|
|
49
|
+
return { asked: [], problem: `fonts: \`uses.fonts\` is ${JSON.stringify(value)}, not a list of family names — no library face was `
|
|
50
|
+
+ 'loaded, so the text the game draws in them falls back to the next face of its stack; declare `uses: { fonts: [\'Lato\'] }`' };
|
|
51
|
+
}
|
|
52
|
+
return { asked: [...new Set(value.map((f) => f.trim()))].filter((f) => !engineFamilies.has(f)), problem: null };
|
|
53
|
+
}
|
|
54
|
+
/** The line of `problems` for a declared family the library does not hold (ADR-0169: the subject, the cost, the fix). */
|
|
55
|
+
export function unknownFamilyLine(family) {
|
|
56
|
+
return `font «${family}»: the game declares it in \`uses.fonts\`, and the engine's font library has no such family — the text `
|
|
57
|
+
+ 'the game draws in it falls back to the next face of its stack; declare a family the library holds '
|
|
58
|
+
+ '(`platform/font-library.json`), or leave it out';
|
|
59
|
+
}
|
|
60
|
+
/** The line of `problems` for a declared family the delivery did not carry, or whose file was refused. */
|
|
61
|
+
function undeliveredLine(family, error) {
|
|
62
|
+
return `font «${family}»: the game declares it in \`uses.fonts\`, and it was not kept (${error}) — the text the game draws in it `
|
|
63
|
+
+ 'falls back to the next face of its stack, and on a day without a network the face is not there at all; build the delivery '
|
|
64
|
+
+ `with \`inclusionist-heavy <folder> --fonts "${family}"\``;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* DECLARES THE ASKED FAMILIES' FACES, and answers the files to keep. A family the library does not hold is said in `problems`;
|
|
68
|
+
* a file the delivery did not carry, or whose sha256 differs, is said once per family when its report arrives. Never throws: a
|
|
69
|
+
* catalogue that does not load is one line, and the game plays in its stack's next face.
|
|
70
|
+
*/
|
|
71
|
+
export async function startLibraryFonts(p) {
|
|
72
|
+
try {
|
|
73
|
+
const library = await (p.load ?? loadFontLibrary)();
|
|
74
|
+
const held = p.asked.filter((f) => library.families[f]);
|
|
75
|
+
for (const f of p.asked)
|
|
76
|
+
if (!library.families[f])
|
|
77
|
+
p.report(unknownFamilyLine(f));
|
|
78
|
+
if (held.length)
|
|
79
|
+
p.declare(libraryFaceRules(library, held, p.href));
|
|
80
|
+
const familyOf = new Map(held.flatMap((f) => library.families[f].faces.map((face) => [libraryFaceId(library.families[f], face), f])));
|
|
81
|
+
const said = new Set();
|
|
82
|
+
return {
|
|
83
|
+
files: libraryFiles(library, held),
|
|
84
|
+
onReport: (r) => {
|
|
85
|
+
const family = familyOf.get(r.id);
|
|
86
|
+
if (!family || r.outcome !== 'falhou' || said.has(family))
|
|
87
|
+
return;
|
|
88
|
+
said.add(family);
|
|
89
|
+
p.report(undeliveredLine(family, r.error ?? 'refused'));
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
catch (e) {
|
|
94
|
+
for (const f of p.asked)
|
|
95
|
+
p.report(undeliveredLine(f, `the font catalogue did not load: ${e instanceof Error ? e.message : String(e)}`));
|
|
96
|
+
return { files: [], onReport: () => { } };
|
|
97
|
+
}
|
|
98
|
+
}
|