@the-inclusionist/engine 9.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 +120 -78
- package/app/css/style.css +761 -377
- 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-licences/NOTICE.txt +166 -0
- package/app/public/vendor/fonts-licences/OFL-1.1.txt +86 -0
- package/app/public/vendor/fonts.css +36 -84
- package/dist-pkg/boot/create-game.d.ts +499 -219
- package/dist-pkg/boot/create-game.js +3944 -562
- package/dist-pkg/core/a11y-sr.d.ts +25 -3
- package/dist-pkg/core/a11y-sr.js +38 -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 +125 -0
- package/dist-pkg/core/accommodations.js +195 -0
- package/dist-pkg/core/actions.d.ts +108 -55
- package/dist-pkg/core/actions.js +122 -71
- 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 +142 -164
- package/dist-pkg/core/contract.js +222 -208
- 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 +71 -60
- package/dist-pkg/core/i18n.js +186 -200
- 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/session-clock.d.ts +23 -0
- package/dist-pkg/core/session-clock.js +37 -0
- 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 +324 -223
- package/dist-pkg/i18n/es.js +323 -223
- package/dist-pkg/i18n/pt.js +348 -226
- 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 +195 -145
- package/dist-pkg/input/gamepad.js +383 -556
- 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/key-default.d.ts +30 -0
- package/dist-pkg/input/key-default.js +60 -0
- package/dist-pkg/input/keyboard-runtime.d.ts +15 -21
- package/dist-pkg/input/keyboard-runtime.js +23 -27
- package/dist-pkg/input/keyboard.d.ts +68 -63
- package/dist-pkg/input/keyboard.js +83 -105
- package/dist-pkg/input/keydown.d.ts +123 -137
- package/dist-pkg/input/keydown.js +284 -289
- package/dist-pkg/input/latch-edge.d.ts +33 -21
- package/dist-pkg/input/latch-edge.js +34 -35
- package/dist-pkg/input/latch-scope.d.ts +51 -50
- package/dist-pkg/input/latch-scope.js +61 -82
- package/dist-pkg/input/latch-store.d.ts +26 -30
- package/dist-pkg/input/latch-store.js +42 -50
- package/dist-pkg/input/latch-sync.d.ts +29 -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 +169 -0
- package/dist-pkg/input/pad-wizard.d.ts +99 -0
- package/dist-pkg/input/pad-wizard.js +256 -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 +59 -0
- package/dist-pkg/input/switch-scan.js +67 -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 -116
- package/dist-pkg/input/touch-bindings.js +158 -173
- package/dist-pkg/input/touch.d.ts +147 -72
- package/dist-pkg/input/touch.js +362 -126
- 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 +90 -117
- package/dist-pkg/input/transports.js +104 -106
- package/dist-pkg/input/virtual-controller.d.ts +82 -0
- package/dist-pkg/input/virtual-controller.js +69 -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 +59 -0
- package/dist-pkg/input/voice-map.js +123 -0
- package/dist-pkg/platform/audio-ambient.d.ts +13 -2
- package/dist-pkg/platform/audio-ambient.js +14 -14
- 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 +31 -39
- package/dist-pkg/platform/audio-sonar.d.ts +49 -152
- package/dist-pkg/platform/audio-sonar.js +124 -302
- package/dist-pkg/platform/audio.d.ts +62 -19
- package/dist-pkg/platform/audio.js +193 -168
- 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/flash-sampler.d.ts +19 -0
- package/dist-pkg/platform/flash-sampler.js +79 -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 +77 -0
- package/dist-pkg/platform/heavy-catalogue.js +256 -0
- package/dist-pkg/platform/heavy-mirror.d.ts +20 -0
- package/dist-pkg/platform/heavy-mirror.js +73 -0
- package/dist-pkg/platform/heavy.d.ts +136 -0
- package/dist-pkg/platform/heavy.js +324 -0
- package/dist-pkg/platform/interruptible-speech.d.ts +23 -16
- package/dist-pkg/platform/interruptible-speech.js +77 -54
- 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 +90 -0
- package/dist-pkg/platform/locale-host.d.ts +14 -0
- package/dist-pkg/platform/locale-host.js +45 -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 +64 -0
- package/dist-pkg/platform/reading-in-worker.js +125 -0
- package/dist-pkg/platform/reading-model.d.ts +94 -0
- package/dist-pkg/platform/reading-model.js +272 -0
- package/dist-pkg/platform/reading-runtime.d.ts +24 -0
- package/dist-pkg/platform/reading-runtime.js +164 -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 +80 -0
- package/dist-pkg/platform/reading.js +136 -0
- package/dist-pkg/platform/speech-recognition.d.ts +60 -0
- package/dist-pkg/platform/speech-recognition.js +104 -0
- package/dist-pkg/platform/speech.d.ts +23 -1
- package/dist-pkg/platform/speech.js +29 -18
- 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 +85 -39
- package/dist-pkg/platform/tts.js +447 -127
- 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 +40 -0
- package/dist-pkg/platform/voice-listener.js +88 -0
- package/dist-pkg/platform/voice-plan.d.ts +15 -98
- package/dist-pkg/platform/voice-plan.js +56 -143
- package/dist-pkg/platform/vosk-runtime.d.ts +96 -0
- package/dist-pkg/platform/vosk-runtime.js +151 -0
- package/dist-pkg/platform/vosk-vocabulary.d.ts +6 -0
- package/dist-pkg/platform/vosk-vocabulary.js +178 -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 +48 -12
- package/dist-pkg/render/crt.js +78 -80
- package/dist-pkg/render/cvd-matrices.d.ts +22 -18
- package/dist-pkg/render/cvd-matrices.js +37 -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 +67 -56
- package/dist-pkg/render/high-contrast.js +226 -217
- 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 +89 -86
- package/dist-pkg/render/viz-axes.js +128 -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 +45 -0
- package/dist-pkg/ui/aac-sets.js +41 -0
- package/dist-pkg/ui/audio-choices.d.ts +68 -0
- package/dist-pkg/ui/audio-choices.js +98 -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 +53 -0
- package/dist-pkg/ui/camera-control.js +48 -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/declared-words.d.ts +21 -0
- package/dist-pkg/ui/declared-words.js +66 -0
- 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 +94 -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 +157 -68
- package/dist-pkg/ui/fonts.js +305 -115
- 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 +47 -0
- package/dist-pkg/ui/game-options.js +158 -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 +87 -0
- package/dist-pkg/ui/help-panel.js +242 -0
- package/dist-pkg/ui/hud-bands.d.ts +62 -0
- package/dist-pkg/ui/hud-bands.js +146 -0
- 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 +73 -73
- 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/layout.d.ts +103 -28
- package/dist-pkg/ui/layout.js +117 -99
- 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/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 +61 -0
- package/dist-pkg/ui/menu-intent.js +88 -0
- package/dist-pkg/ui/menu-items.d.ts +20 -0
- package/dist-pkg/ui/menu-items.js +51 -0
- package/dist-pkg/ui/menu-nav.d.ts +98 -92
- package/dist-pkg/ui/menu-nav.js +398 -286
- 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 +150 -0
- package/dist-pkg/ui/panel-widgets.js +294 -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 +261 -347
- package/dist-pkg/ui/pause-icons.js +671 -601
- 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 +50 -0
- 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 +70 -0
- package/dist-pkg/ui/settings-aac.js +208 -0
- package/dist-pkg/ui/settings-audio.d.ts +97 -114
- package/dist-pkg/ui/settings-audio.js +493 -425
- package/dist-pkg/ui/settings-controls.d.ts +53 -96
- 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 +192 -0
- package/dist-pkg/ui/settings-mobility.js +332 -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 +58 -45
- package/dist-pkg/ui/settings-panel.js +189 -94
- package/dist-pkg/ui/settings-typo.d.ts +29 -78
- package/dist-pkg/ui/settings-typo.js +158 -129
- 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 +112 -135
- package/dist-pkg/ui/shell.js +148 -217
- 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 +3 -12
- package/dist-pkg/ui/title.js +49 -18
- package/dist-pkg/ui/top-band.d.ts +26 -0
- package/dist-pkg/ui/top-band.js +96 -0
- package/dist-pkg/ui/typo-choices.d.ts +71 -0
- package/dist-pkg/ui/typo-choices.js +85 -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 +77 -10
- package/dist-pkg/ui/vlibras.js +130 -81
- package/dist-pkg/ui/voice-control.d.ts +68 -0
- package/dist-pkg/ui/voice-control.js +276 -0
- package/dist-pkg/ui/voice-settings.d.ts +95 -0
- package/dist-pkg/ui/voice-settings.js +352 -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 +300 -44
- package/docs/LICENSES.md +189 -145
- package/package.json +51 -27
- 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 +388 -0
- 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/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/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 +301 -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/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/inter-400.woff2 +0 -0
- package/app/public/vendor/fonts/inter-700.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/newsreader-400.woff2 +0 -0
- package/app/public/vendor/fonts/newsreader-700.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/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/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/ufmag-400.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/guide-intensity.d.ts +0 -40
- package/dist-pkg/platform/guide-intensity.js +0 -73
- 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/latch-refusal.d.ts +0 -32
- package/dist-pkg/ui/latch-refusal.js +0 -60
- 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,4 +1,26 @@
|
|
|
1
1
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
-
|
|
3
|
-
export
|
|
4
|
-
|
|
2
|
+
/** What an announcer writes through: the document holding the two regions, and the host's next frame. */
|
|
3
|
+
export interface AnnouncerPorts {
|
|
4
|
+
/** The document whose `#sr-status` and `#sr-alert` are written. A document without them announces nothing, silently. */
|
|
5
|
+
readonly doc: Pick<Document, 'querySelector'>;
|
|
6
|
+
/**
|
|
7
|
+
* The host's `requestAnimationFrame`, AS THE HOST HAS IT: the clear-then-write dance needs one frame between. REQUIRED, and
|
|
8
|
+
* `undefined` is an answer — a host with no frames (a test double): the announcer then writes at once,
|
|
9
|
+
* which is the only honest thing left to do. The absence is answered HERE so that no composition root grows a branch for it
|
|
10
|
+
* (ADR-0221: the root carries wiring, not decisions).
|
|
11
|
+
*/
|
|
12
|
+
readonly raf: ((cb: FrameRequestCallback) => number) | undefined;
|
|
13
|
+
}
|
|
14
|
+
/** One root's announcer. */
|
|
15
|
+
export interface Announcer {
|
|
16
|
+
/** "Polite" announcement (status): does not interrupt what the reader is saying. */
|
|
17
|
+
readonly say: (text: string) => void;
|
|
18
|
+
/** "Assertive" announcement (alert): interrupts and speaks now (errors, important warnings). */
|
|
19
|
+
readonly alert: (text: string) => void;
|
|
20
|
+
/**
|
|
21
|
+
* Sends every announcement of THIS announcer also to `sink`, until the returned release
|
|
22
|
+
* is called. One sink at a time: a second call replaces the first, and releasing a replaced sink changes nothing.
|
|
23
|
+
*/
|
|
24
|
+
readonly mirrorTo: (sink: (text: string) => void) => () => void;
|
|
25
|
+
}
|
|
26
|
+
export declare function createAnnouncer({ doc, raf }: AnnouncerPorts): Announcer;
|
package/dist-pkg/core/a11y-sr.js
CHANGED
|
@@ -1,20 +1,39 @@
|
|
|
1
1
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
-
// core/a11y-sr.ts —
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
//
|
|
10
|
-
|
|
11
|
-
//
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
2
|
+
// core/a11y-sr.ts — announcements for the SCREEN READER. `say` = the "polite" aria-live region (`#sr-status`);
|
|
3
|
+
// `alert` = the "assertive" region (`#sr-alert`). Clear → next frame → write forces the reader to announce the same text
|
|
4
|
+
// again when it repeats.
|
|
5
|
+
//
|
|
6
|
+
// 🔴 A FACTORY (ADR-0232 D4, issue #207): the announcer used to be two module functions over the GLOBAL document, the
|
|
7
|
+
// global `requestAnimationFrame` and a module-level Libras mirror a host registered — so a root building in another
|
|
8
|
+
// document (an iframe, an editor beside the game) announced into the page's regions, and two roots shared one mirror.
|
|
9
|
+
// Now each root builds its own over the document and the frames its host lends. The factory stays PUBLISHED for a game
|
|
10
|
+
// that must announce with no root at all — its boot failed before `createGame` returned (game-chess's rootless alert).
|
|
11
|
+
//
|
|
12
|
+
// THE MIRROR IS AN EXPLICIT SINK OF EACH ANNOUNCER, and nothing connects it by default. It was the Libras interpreter's
|
|
13
|
+
// door; ADR-0234 closed that question (DD1): deaf mode signs what the sonar finds, when the child asks, and never the
|
|
14
|
+
// announcements — so no queue of messages. The sink arrives by injection so this core module never imports ui/ (ADR-0173).
|
|
15
|
+
const NO_MIRROR = () => { };
|
|
16
|
+
export function createAnnouncer({ doc, raf }) {
|
|
17
|
+
const nextFrame = (cb) => { if (raf)
|
|
18
|
+
raf(() => { cb(); });
|
|
19
|
+
else
|
|
20
|
+
cb(); };
|
|
21
|
+
let mirror = NO_MIRROR;
|
|
22
|
+
const write = (region, text) => {
|
|
23
|
+
const el = doc.querySelector(region);
|
|
24
|
+
if (el) {
|
|
25
|
+
el.textContent = '';
|
|
26
|
+
nextFrame(() => { el.textContent = text; });
|
|
27
|
+
}
|
|
28
|
+
mirror(text);
|
|
29
|
+
};
|
|
30
|
+
return {
|
|
31
|
+
say: (text) => { write('#sr-status', text); },
|
|
32
|
+
alert: (text) => { write('#sr-alert', text); },
|
|
33
|
+
mirrorTo: (sink) => {
|
|
34
|
+
mirror = sink;
|
|
35
|
+
return () => { if (mirror === sink)
|
|
36
|
+
mirror = NO_MIRROR; };
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
/** The minimal slice of `Element` this module reads. Structural, so a node test needs no real DOM. */
|
|
3
|
+
export interface LabelledElement {
|
|
4
|
+
getAttribute(name: string): string | null;
|
|
5
|
+
textContent: string | null;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* The control's name: `aria-label` when there is one, the visible text when not.
|
|
9
|
+
*
|
|
10
|
+
* THE ORDER IS THE DECISION, and it is not arbitrary: `aria-label` is what the accessibility platform is ALREADY going
|
|
11
|
+
* to announce. Narrating something else adds no information — it creates a second version of the same item, and whoever
|
|
12
|
+
* hears both has no way to know which is true.
|
|
13
|
+
*
|
|
14
|
+
* The visible text comes in when no label is declared, which is most buttons: there the two sources agree by
|
|
15
|
+
* construction.
|
|
16
|
+
*/
|
|
17
|
+
export declare function accessibleLabel(el: LabelledElement | null | undefined): string;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// core/accessible-label — WHAT A CONTROL IS CALLED, and one answer for whoever sees and whoever listens.
|
|
3
|
+
//
|
|
4
|
+
// ========================= THE DEFECT THIS CLOSES =========================
|
|
5
|
+
// MEASURED in the built game, with the cursor on the menu's number-of-players button:
|
|
6
|
+
//
|
|
7
|
+
// the game narrated: "◀ Number of players: 1 ▶, 1 of 4"
|
|
8
|
+
// the screen reader said: "Number of players: 1. Click on the left for fewer, on the right for more."
|
|
9
|
+
//
|
|
10
|
+
// Two different sentences for the SAME item at the same instant. A child using a screen reader AND the game's
|
|
11
|
+
// narration hears the item twice, two ways — and the game's version reads the `◀` and `▶` glyphs, the very noise
|
|
12
|
+
// item 4 of ADR-0044 took out of the pause legend. The rule already existed for the bar's icons (hovering or focusing
|
|
13
|
+
// says the SAME truth a screen reader would announce); here it becomes one function every menu calls.
|
|
14
|
+
//
|
|
15
|
+
// ========================= WHY A LEAF MODULE =========================
|
|
16
|
+
// Its consumers sit in different modules of `ui/`, and `ui/menu-nav` already reaches `ui/pause-icons` — putting the rule
|
|
17
|
+
// in one of them would close a cycle or make someone import from where it should not. The lesson of `core/ring`: a
|
|
18
|
+
// cycle in ESM does not blow up at once, it blows up at boot in the TDZ, once, in production.
|
|
19
|
+
//
|
|
20
|
+
// No dependencies, no I/O, no global `document`: it takes the element and returns text.
|
|
21
|
+
/** Markup whitespace becomes ONE space; the ends go. */
|
|
22
|
+
const tidy = (s) => (s || '').replace(/\s+/g, ' ').trim();
|
|
23
|
+
/**
|
|
24
|
+
* The control's name: `aria-label` when there is one, the visible text when not.
|
|
25
|
+
*
|
|
26
|
+
* THE ORDER IS THE DECISION, and it is not arbitrary: `aria-label` is what the accessibility platform is ALREADY going
|
|
27
|
+
* to announce. Narrating something else adds no information — it creates a second version of the same item, and whoever
|
|
28
|
+
* hears both has no way to know which is true.
|
|
29
|
+
*
|
|
30
|
+
* The visible text comes in when no label is declared, which is most buttons: there the two sources agree by
|
|
31
|
+
* construction.
|
|
32
|
+
*/
|
|
33
|
+
export function accessibleLabel(el) {
|
|
34
|
+
if (!el)
|
|
35
|
+
return '';
|
|
36
|
+
return tidy(el.getAttribute('aria-label')) || tidy(el.textContent);
|
|
37
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
import type { GameDeclaration } from './contract.js';
|
|
3
|
+
import type { Action } from './actions.js';
|
|
4
|
+
import { CONTRACT_KEYED } from './accommodations.js';
|
|
5
|
+
export type ContractKeyedAccommodation = (typeof CONTRACT_KEYED)[number];
|
|
6
|
+
/** What the derivation reads — and only that. */
|
|
7
|
+
export interface SubjectInputs {
|
|
8
|
+
readonly declaration: Pick<GameDeclaration, 'tick' | 'holdsKeys' | 'needsPointer' | 'world' | 'topology'>;
|
|
9
|
+
/** The positions this game's preset names (`presetActions`). Empty = the game is not played by actions. */
|
|
10
|
+
readonly actions: readonly Action[];
|
|
11
|
+
/** How many seats (`players().length` — a game that declares none has one child playing). */
|
|
12
|
+
readonly players: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* The contract-keyed accommodations with a subject in this game, NOW.
|
|
16
|
+
*
|
|
17
|
+
* The rules, each one the study's key (`docs/1-Discovery/study-accommodations-by-genre.md` §0.1) read off the contract:
|
|
18
|
+
* · `gameSpeed` ← time runs on its own (`tick === 'clock'`) — WCAG 2.2.1, GAG Basic «adjust the game speed»
|
|
19
|
+
* · `moveLatch`, `holdLatch` ← the game holds keys
|
|
20
|
+
* · `virtualPad`, `oneButton`, `inputCooldown`, `macros` ← the game is played by actions
|
|
21
|
+
* · `pointerSmoothing`, `pointerSensitivity`, `pointerStyle` ← the game needs a continuous pointer
|
|
22
|
+
* · `visionSimulation`, `audioDescription` ← there is a world (`world: none` refuses empathy and sonar, contract §8)
|
|
23
|
+
* · `blindMode`, `navigationSound` ← a world AND a direction: `bearing` answers `none` on `hotspots`
|
|
24
|
+
* · `perPlayerAudioOutput` ← more than one seat
|
|
25
|
+
*/
|
|
26
|
+
export declare function contractSubjects(i: SubjectInputs): ReadonlySet<ContractKeyedAccommodation>;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
import { CONTRACT_KEYED } from './accommodations.js';
|
|
3
|
+
/**
|
|
4
|
+
* The contract-keyed accommodations with a subject in this game, NOW.
|
|
5
|
+
*
|
|
6
|
+
* The rules, each one the study's key (`docs/1-Discovery/study-accommodations-by-genre.md` §0.1) read off the contract:
|
|
7
|
+
* · `gameSpeed` ← time runs on its own (`tick === 'clock'`) — WCAG 2.2.1, GAG Basic «adjust the game speed»
|
|
8
|
+
* · `moveLatch`, `holdLatch` ← the game holds keys
|
|
9
|
+
* · `virtualPad`, `oneButton`, `inputCooldown`, `macros` ← the game is played by actions
|
|
10
|
+
* · `pointerSmoothing`, `pointerSensitivity`, `pointerStyle` ← the game needs a continuous pointer
|
|
11
|
+
* · `visionSimulation`, `audioDescription` ← there is a world (`world: none` refuses empathy and sonar, contract §8)
|
|
12
|
+
* · `blindMode`, `navigationSound` ← a world AND a direction: `bearing` answers `none` on `hotspots`
|
|
13
|
+
* · `perPlayerAudioOutput` ← more than one seat
|
|
14
|
+
*/
|
|
15
|
+
export function contractSubjects(i) {
|
|
16
|
+
const d = i.declaration;
|
|
17
|
+
const hasWorld = d.world().kind === 'element';
|
|
18
|
+
const hasDirection = hasWorld && d.topology().kind !== 'hotspots';
|
|
19
|
+
const byActions = i.actions.length > 0;
|
|
20
|
+
const pointer = d.needsPointer?.() === true;
|
|
21
|
+
const holds = d.holdsKeys();
|
|
22
|
+
const rule = {
|
|
23
|
+
gameSpeed: d.tick === 'clock',
|
|
24
|
+
moveLatch: holds, holdLatch: holds,
|
|
25
|
+
virtualPad: byActions, oneButton: byActions, inputCooldown: byActions, macros: byActions,
|
|
26
|
+
pointerSmoothing: pointer, pointerSensitivity: pointer, pointerStyle: pointer,
|
|
27
|
+
visionSimulation: hasWorld, audioDescription: hasWorld,
|
|
28
|
+
blindMode: hasDirection, navigationSound: hasDirection,
|
|
29
|
+
perPlayerAudioOutput: i.players > 1,
|
|
30
|
+
};
|
|
31
|
+
return new Set(CONTRACT_KEYED.filter((k) => rule[k]));
|
|
32
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
/** Every accommodation the engine knows by name. None of these ids is a word a child reads. */
|
|
3
|
+
export declare const ACCOMMODATIONS: readonly ["typography", "letterCase", "narration", "spokenMenuIndex", "signLanguage", "soundMix", "hearingLossSimulation", "highContrast", "colorVisionCorrection", "reducedSceneMotion", "remapControls", "targetSize", "controlsHelp", "objectiveReminder", "repeatInstruction", "soundCaptions", "screenReader", "monoAudio", "interfaceScale", "interfaceLayout", "difficulty", "skipSection", "settingsProfiles", "visionSimulation", "audioDescription", "virtualPad", "oneButton", "inputCooldown", "macros", "gameSpeed", "blindMode", "navigationSound", "moveLatch", "holdLatch", "pointerSmoothing", "pointerSensitivity", "pointerStyle", "perPlayerAudioOutput", "cameraSway", "easyMode", "wheelchairMode", "detectionLeniency", "intensity", "hints", "reducedCharacterMotion", "caneSpacing", "textPace", "lexicalDifficulty", "wordHighlight", "pieceSets", "distinguishableSuits", "timingWindow", "aimAssist", "repeatedInput", "ownerColors", "contrastOutlines"];
|
|
4
|
+
export type Accommodation = (typeof ACCOMMODATIONS)[number];
|
|
5
|
+
/**
|
|
6
|
+
* Subject in EVERY game: there is text, sound, a screen, a menu and a way to confirm in all of them.
|
|
7
|
+
*
|
|
8
|
+
* 📌 `difficulty`, `skipSection` and `objectiveReminder` are here although the engine cannot make a game
|
|
9
|
+
* easier: what it can do is keep, persist and announce the choice, and the game reads it. The GAG asks for
|
|
10
|
+
* difficulty at Basic level, and «wide choice of difficulty levels» has a subject in any game with a goal.
|
|
11
|
+
*/
|
|
12
|
+
export declare const GENERAL: readonly ["typography", "letterCase", "narration", "spokenMenuIndex", "signLanguage", "soundMix", "hearingLossSimulation", "highContrast", "colorVisionCorrection", "reducedSceneMotion", "remapControls", "targetSize", "controlsHelp", "objectiveReminder", "repeatInstruction", "soundCaptions", "screenReader", "monoAudio", "interfaceScale", "interfaceLayout", "difficulty", "skipSection", "settingsProfiles"];
|
|
13
|
+
/**
|
|
14
|
+
* Subject answered by something the contract ALREADY asks, so the engine can derive it with no genre:
|
|
15
|
+
*
|
|
16
|
+
* · `gameSpeed` ← `tick === 'clock'` (WCAG 2.2.1; the GAG's Basic «adjust the game speed»)
|
|
17
|
+
* · `moveLatch`, `holdLatch` ← `holdsKeys()` and the positions the preset declares
|
|
18
|
+
* · `virtualPad`, `oneButton`, `inputCooldown`, `macros` ← action input (`needsPointer()` false)
|
|
19
|
+
* · `pointerSmoothing`, `pointerSensitivity`, `pointerStyle` ← `needsPointer()` true
|
|
20
|
+
* · `visionSimulation`, `audioDescription` ← `world().kind === 'element'`
|
|
21
|
+
* · `blindMode`, `navigationSound` ← a world AND a direction: `bearing` is `none` on `hotspots`
|
|
22
|
+
* · `perPlayerAudioOutput` ← more than one entry in `CreateGameOptions.players`
|
|
23
|
+
*/
|
|
24
|
+
export declare const CONTRACT_KEYED: readonly ["visionSimulation", "audioDescription", "virtualPad", "oneButton", "inputCooldown", "macros", "gameSpeed", "blindMode", "navigationSound", "moveLatch", "holdLatch", "pointerSmoothing", "pointerSensitivity", "pointerStyle", "perPlayerAudioOutput"];
|
|
25
|
+
/**
|
|
26
|
+
* Subject nothing in the engine can answer: the game declares it.
|
|
27
|
+
*
|
|
28
|
+
* ⚠️ `easyMode` and `wheelchairMode` are PLATFORM vocabulary — lower gravity, coins on the ground, no jump —
|
|
29
|
+
* and that is why they sit here and not in GENERAL: in a chess game they are switches with no subject, the
|
|
30
|
+
* exact defect ADR-0145 was written about.
|
|
31
|
+
*/
|
|
32
|
+
export declare const GAME_KEYED: readonly ["cameraSway", "easyMode", "wheelchairMode", "detectionLeniency", "intensity", "hints", "reducedCharacterMotion", "caneSpacing", "textPace", "lexicalDifficulty", "wordHighlight", "pieceSets", "distinguishableSuits", "timingWindow", "aimAssist", "repeatedInput", "ownerColors", "contrastOutlines"];
|
|
33
|
+
/**
|
|
34
|
+
* What the child reads and hears for an accommodation: the name, and the sentence that explains it — RESOLVED, in the page's
|
|
35
|
+
* language. A cartridge's ANSWER declares keys (`AccommodationKeys`), and `subjectWord` resolves them at every drawing.
|
|
36
|
+
*/
|
|
37
|
+
export interface AccommodationWord {
|
|
38
|
+
/** «Wheelchair mode», «Board pieces». NEVER `wheelchairMode` — an id that reaches a person is a defect. */
|
|
39
|
+
readonly label: string;
|
|
40
|
+
/** The SHORT form, for where the long one does not fit. Falls back to `label` when absent. */
|
|
41
|
+
readonly short?: string;
|
|
42
|
+
/** Optional: what this setting does, in one sentence — the footer text of the menu row (`CLAUDE.md` §4). */
|
|
43
|
+
readonly hint?: string;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* One game's vocabulary: for each accommodation it names, its word.
|
|
47
|
+
*
|
|
48
|
+
* ⚠️ PARTIAL ON PURPOSE, as `ActionPreset` is. Requiring all entries would make a quiz invent a word for a
|
|
49
|
+
* wheelchair it has no concept of, and an invented word ends up in a menu in front of a child.
|
|
50
|
+
*
|
|
51
|
+
* 📌 A game's OWN accommodation — one the catalogue does not contain (ADR-0145 §2) — is not declared here: a
|
|
52
|
+
* closed record cannot hold it, and that is what keeps an id typo from passing as an addition. The addition
|
|
53
|
+
* has its own place in the declaration (phase 2c).
|
|
54
|
+
*/
|
|
55
|
+
export type AccommodationPreset = Partial<Readonly<Record<Accommodation, AccommodationWord>>>;
|
|
56
|
+
/** Is it a known accommodation? Boundary guard for data that came from outside (a saved setting, a game). */
|
|
57
|
+
export declare function isAccommodation(x: unknown): x is Accommodation;
|
|
58
|
+
/** The accommodations this preset names, in the canonical order of `ACCOMMODATIONS`. */
|
|
59
|
+
export declare function presetAccommodations(p: AccommodationPreset): Accommodation[];
|
|
60
|
+
/**
|
|
61
|
+
* The question the engine asks a game when it must SHOW an accommodation: «what is this called here?».
|
|
62
|
+
*
|
|
63
|
+
* ⚠️ Returns `null` for an entry the game does not name, never the id — the `labellerFrom` rule. Whoever calls
|
|
64
|
+
* decides; for a GAME_KEYED entry the answer is not to mount the row, because a game that has no word for it
|
|
65
|
+
* has no subject for it either.
|
|
66
|
+
*/
|
|
67
|
+
export declare function accommodationLabellerFrom(p: AccommodationPreset): (a: Accommodation) => string | null;
|
|
68
|
+
/**
|
|
69
|
+
* Is a preset well formed? Returns the problems — EMPTY means conformant.
|
|
70
|
+
*
|
|
71
|
+
* ⚠️ AN ABSENT PRESET IS NOT A PROBLEM, and that is the difference from `presetProblems`. A game with no
|
|
72
|
+
* action cannot be played; a game that names no accommodation still gets every GENERAL one in the engine's
|
|
73
|
+
* words. What IS a problem is the silent kind: a blank label (a row with no name for a screen reader) and an
|
|
74
|
+
* unknown key (a typo that would vanish instead of mounting — or a game's own accommodation put in the wrong
|
|
75
|
+
* place, which the message says).
|
|
76
|
+
*/
|
|
77
|
+
export declare function accommodationPresetProblems(p: AccommodationPreset | null | undefined): string[];
|
|
78
|
+
/** The accommodations only the game can answer. */
|
|
79
|
+
export type GameKeyedAccommodation = (typeof GAME_KEYED)[number];
|
|
80
|
+
/**
|
|
81
|
+
* What a cartridge DECLARES for an accommodation it has a subject for: the KEYS of its words in its own dictionary
|
|
82
|
+
* (`CreateGameOptions.dictionaries`). Keys and not words, so the row follows a language change (ADR-0232 D3, erratum of
|
|
83
|
+
* 2026-09-25; the rule and its measured defect are written on `core/actions.ActionKeys`).
|
|
84
|
+
*/
|
|
85
|
+
export interface AccommodationKeys {
|
|
86
|
+
/** The key of the name: «Wheelchair mode», «Board pieces». */
|
|
87
|
+
readonly labelKey: string;
|
|
88
|
+
/** The key of the SHORT form. Falls back to the name when absent. */
|
|
89
|
+
readonly shortKey?: string;
|
|
90
|
+
/** The key of the sentence the menu row explains itself with, in the footer (`CLAUDE.md` §4). */
|
|
91
|
+
readonly hintKey?: string;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* THE CARTRIDGE'S ANSWER, for every GAME_KEYED accommodation: the KEYS of its word when the accommodation has a subject in
|
|
95
|
+
* this game, or `false` when it has none.
|
|
96
|
+
*
|
|
97
|
+
* 🔴 COMPLETE, NOT PARTIAL — and that is the difference from `AccommodationPreset`, and the decision. The Dev:
|
|
98
|
+
* «Gênero não precisa responder todas as acomodações, mas sim o cartucho, obrigatoriamente.» A partial map lets
|
|
99
|
+
* SILENCE answer, and silence here decides for the child: it would mount a wheelchair in chess, or hide one from a
|
|
100
|
+
* platformer, with nothing anywhere to say which. So a missing key is not «no»; it is a malformed declaration.
|
|
101
|
+
*
|
|
102
|
+
* 📌 `false` and not `null`/absence, so a «no» is always something somebody wrote. GENERAL accommodations mount
|
|
103
|
+
* always and CONTRACT_KEYED ones are derived from the contract, so neither is asked here — asking twice would let
|
|
104
|
+
* the two answers disagree.
|
|
105
|
+
*/
|
|
106
|
+
export type AccommodationAnswers = Readonly<Record<GameKeyedAccommodation, AccommodationKeys | false>>;
|
|
107
|
+
/** Is it one of the accommodations the cartridge must answer? */
|
|
108
|
+
export declare function isGameKeyed(x: unknown): x is GameKeyedAccommodation;
|
|
109
|
+
/**
|
|
110
|
+
* Is the cartridge's answer well formed? Returns the problems — EMPTY means conformant.
|
|
111
|
+
*
|
|
112
|
+
* ⚠️ UNLIKE `accommodationPresetProblems`, AN ABSENT ANSWER IS A PROBLEM: the answer is mandatory (ADR-0153), and a
|
|
113
|
+
* boot that accepted its absence would be the optional field the Dev refused. The messages go to whoever INTEGRATES
|
|
114
|
+
* the engine; each one names the accommodation and what to write.
|
|
115
|
+
*/
|
|
116
|
+
export declare function accommodationAnswersProblems(a: unknown): string[];
|
|
117
|
+
/**
|
|
118
|
+
* Does this accommodation have a subject in this game — and what is it called NOW? The question a panel asks before
|
|
119
|
+
* mounting a row, at every drawing.
|
|
120
|
+
*
|
|
121
|
+
* 📌 It returns the WORD, resolved through `word` (the root's translator, ADR-0232 D3), or `null` — never the id, never the
|
|
122
|
+
* key, never `true`: a row that mounts must have a name to show, and «has a subject» without a word is the `action2` defect
|
|
123
|
+
* one level up. A key the game's dictionaries lack is `null` here and a line of `problems` (`ui/declared-words`).
|
|
124
|
+
*/
|
|
125
|
+
export declare function subjectWord(answers: AccommodationAnswers, k: GameKeyedAccommodation, word: (key: string) => string | null): AccommodationWord | null;
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// core/accommodations — THE ACCOMMODATION CATALOGUE. The closed vocabulary of what the engine can adapt.
|
|
3
|
+
//
|
|
4
|
+
// ========================= WHAT THIS IS =========================
|
|
5
|
+
// ADR-0145 decided that the catalogue is GENERAL and that a game declares which entries have a subject in it,
|
|
6
|
+
// with its own words — «this record is that contract [`ActionPreset`] one level up». This module is the list
|
|
7
|
+
// and the partial map over it, and nothing else: zero imports, zero I/O, importable from both sides of the
|
|
8
|
+
// engine boundary. It is `core/actions.ts` copied shape for shape, on purpose.
|
|
9
|
+
//
|
|
10
|
+
// ========================= WHERE THE LIST COMES FROM =========================
|
|
11
|
+
// Not from what the engine happened to build. It is the catalogue of the study
|
|
12
|
+
// `docs/1-Discovery/study-accommodations-by-genre.md` — 380 games in 35 categories, keyed one accommodation at
|
|
13
|
+
// a time (section 0.1) and cross-checked against all 105 Game Accessibility Guidelines (section 0.2). The
|
|
14
|
+
// study scripts keep Portuguese ids; `tests/accommodations.node.test.js` ties the two by family size, so an
|
|
15
|
+
// accommodation added to one and not the other fails a gate instead of drifting.
|
|
16
|
+
//
|
|
17
|
+
// ========================= THE THREE FAMILIES — the study's finding, as types =========================
|
|
18
|
+
// What decides whether an accommodation has a SUBJECT in a game is not always the genre. Keyed one by one, the
|
|
19
|
+
// catalogue fell into three families, and they need three different answers from the engine:
|
|
20
|
+
//
|
|
21
|
+
// · GENERAL — there is text, sound, a screen and a menu in every game. The engine mounts these always.
|
|
22
|
+
// · CONTRACT_KEYED — the subject is answered by a declaration the contract ALREADY asks every game
|
|
23
|
+
// (`tick`, `holdsKeys()`, `needsPointer()`, `world()` × `topology()`, the players list). The engine can
|
|
24
|
+
// derive these without a genre, by the rule the engine keeps for what a contract already answers: absent ⇒ derive from the contract.
|
|
25
|
+
// · GAME_KEYED — nothing the engine holds answers it (genre, perspective, avatar, text, pieces, timing).
|
|
26
|
+
// The game — or the template of its genre — declares it.
|
|
27
|
+
//
|
|
28
|
+
// ⚠️ Being in the catalogue is NOT being built. `macros` is here and no writer exists; the row mounts when
|
|
29
|
+
// the action works, and not before (ADR-0106 §5). The catalogue names what can be asked for.
|
|
30
|
+
/** Every accommodation the engine knows by name. None of these ids is a word a child reads. */
|
|
31
|
+
export const ACCOMMODATIONS = [
|
|
32
|
+
// — general —
|
|
33
|
+
'typography', 'letterCase', 'narration', 'spokenMenuIndex', 'signLanguage', 'soundMix',
|
|
34
|
+
'hearingLossSimulation', 'highContrast', 'colorVisionCorrection', 'reducedSceneMotion', 'remapControls',
|
|
35
|
+
'targetSize', 'controlsHelp', 'objectiveReminder', 'repeatInstruction', 'soundCaptions', 'screenReader',
|
|
36
|
+
'monoAudio', 'interfaceScale', 'interfaceLayout', 'difficulty', 'skipSection', 'settingsProfiles',
|
|
37
|
+
// — keyed by the contract —
|
|
38
|
+
'visionSimulation', 'audioDescription', 'virtualPad', 'oneButton', 'inputCooldown', 'macros', 'gameSpeed',
|
|
39
|
+
'blindMode', 'navigationSound', 'moveLatch', 'holdLatch', 'pointerSmoothing', 'pointerSensitivity',
|
|
40
|
+
'pointerStyle', 'perPlayerAudioOutput',
|
|
41
|
+
// — keyed by the game —
|
|
42
|
+
'cameraSway', 'easyMode', 'wheelchairMode', 'detectionLeniency', 'intensity', 'hints',
|
|
43
|
+
'reducedCharacterMotion', 'caneSpacing', 'textPace', 'lexicalDifficulty', 'wordHighlight', 'pieceSets',
|
|
44
|
+
'distinguishableSuits', 'timingWindow', 'aimAssist', 'repeatedInput',
|
|
45
|
+
// ADR-0188, the Dev: «Criar os dois assuntos.» Only the game knows whether items have an owner, or a drawing to outline.
|
|
46
|
+
'ownerColors', 'contrastOutlines',
|
|
47
|
+
];
|
|
48
|
+
/**
|
|
49
|
+
* Subject in EVERY game: there is text, sound, a screen, a menu and a way to confirm in all of them.
|
|
50
|
+
*
|
|
51
|
+
* 📌 `difficulty`, `skipSection` and `objectiveReminder` are here although the engine cannot make a game
|
|
52
|
+
* easier: what it can do is keep, persist and announce the choice, and the game reads it. The GAG asks for
|
|
53
|
+
* difficulty at Basic level, and «wide choice of difficulty levels» has a subject in any game with a goal.
|
|
54
|
+
*/
|
|
55
|
+
export const GENERAL = [
|
|
56
|
+
'typography', 'letterCase', 'narration', 'spokenMenuIndex', 'signLanguage', 'soundMix',
|
|
57
|
+
'hearingLossSimulation', 'highContrast', 'colorVisionCorrection', 'reducedSceneMotion', 'remapControls',
|
|
58
|
+
'targetSize', 'controlsHelp', 'objectiveReminder', 'repeatInstruction', 'soundCaptions', 'screenReader',
|
|
59
|
+
'monoAudio', 'interfaceScale', 'interfaceLayout', 'difficulty', 'skipSection', 'settingsProfiles',
|
|
60
|
+
];
|
|
61
|
+
/**
|
|
62
|
+
* Subject answered by something the contract ALREADY asks, so the engine can derive it with no genre:
|
|
63
|
+
*
|
|
64
|
+
* · `gameSpeed` ← `tick === 'clock'` (WCAG 2.2.1; the GAG's Basic «adjust the game speed»)
|
|
65
|
+
* · `moveLatch`, `holdLatch` ← `holdsKeys()` and the positions the preset declares
|
|
66
|
+
* · `virtualPad`, `oneButton`, `inputCooldown`, `macros` ← action input (`needsPointer()` false)
|
|
67
|
+
* · `pointerSmoothing`, `pointerSensitivity`, `pointerStyle` ← `needsPointer()` true
|
|
68
|
+
* · `visionSimulation`, `audioDescription` ← `world().kind === 'element'`
|
|
69
|
+
* · `blindMode`, `navigationSound` ← a world AND a direction: `bearing` is `none` on `hotspots`
|
|
70
|
+
* · `perPlayerAudioOutput` ← more than one entry in `CreateGameOptions.players`
|
|
71
|
+
*/
|
|
72
|
+
export const CONTRACT_KEYED = [
|
|
73
|
+
'visionSimulation', 'audioDescription', 'virtualPad', 'oneButton', 'inputCooldown', 'macros', 'gameSpeed',
|
|
74
|
+
'blindMode', 'navigationSound', 'moveLatch', 'holdLatch', 'pointerSmoothing', 'pointerSensitivity',
|
|
75
|
+
'pointerStyle', 'perPlayerAudioOutput',
|
|
76
|
+
];
|
|
77
|
+
/**
|
|
78
|
+
* Subject nothing in the engine can answer: the game declares it.
|
|
79
|
+
*
|
|
80
|
+
* ⚠️ `easyMode` and `wheelchairMode` are PLATFORM vocabulary — lower gravity, coins on the ground, no jump —
|
|
81
|
+
* and that is why they sit here and not in GENERAL: in a chess game they are switches with no subject, the
|
|
82
|
+
* exact defect ADR-0145 was written about.
|
|
83
|
+
*/
|
|
84
|
+
export const GAME_KEYED = [
|
|
85
|
+
'cameraSway', 'easyMode', 'wheelchairMode', 'detectionLeniency', 'intensity', 'hints',
|
|
86
|
+
'reducedCharacterMotion', 'caneSpacing', 'textPace', 'lexicalDifficulty', 'wordHighlight', 'pieceSets',
|
|
87
|
+
'distinguishableSuits', 'timingWindow', 'aimAssist', 'repeatedInput',
|
|
88
|
+
'ownerColors', 'contrastOutlines',
|
|
89
|
+
];
|
|
90
|
+
/** Is it a known accommodation? Boundary guard for data that came from outside (a saved setting, a game). */
|
|
91
|
+
export function isAccommodation(x) {
|
|
92
|
+
return typeof x === 'string' && ACCOMMODATIONS.includes(x);
|
|
93
|
+
}
|
|
94
|
+
/** The accommodations this preset names, in the canonical order of `ACCOMMODATIONS`. */
|
|
95
|
+
export function presetAccommodations(p) {
|
|
96
|
+
return ACCOMMODATIONS.filter((a) => p[a] !== undefined);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* The question the engine asks a game when it must SHOW an accommodation: «what is this called here?».
|
|
100
|
+
*
|
|
101
|
+
* ⚠️ Returns `null` for an entry the game does not name, never the id — the `labellerFrom` rule. Whoever calls
|
|
102
|
+
* decides; for a GAME_KEYED entry the answer is not to mount the row, because a game that has no word for it
|
|
103
|
+
* has no subject for it either.
|
|
104
|
+
*/
|
|
105
|
+
export function accommodationLabellerFrom(p) {
|
|
106
|
+
return (a) => {
|
|
107
|
+
const w = p[a];
|
|
108
|
+
return w && w.label.trim() ? w.label : null;
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Is a preset well formed? Returns the problems — EMPTY means conformant.
|
|
113
|
+
*
|
|
114
|
+
* ⚠️ AN ABSENT PRESET IS NOT A PROBLEM, and that is the difference from `presetProblems`. A game with no
|
|
115
|
+
* action cannot be played; a game that names no accommodation still gets every GENERAL one in the engine's
|
|
116
|
+
* words. What IS a problem is the silent kind: a blank label (a row with no name for a screen reader) and an
|
|
117
|
+
* unknown key (a typo that would vanish instead of mounting — or a game's own accommodation put in the wrong
|
|
118
|
+
* place, which the message says).
|
|
119
|
+
*/
|
|
120
|
+
export function accommodationPresetProblems(p) {
|
|
121
|
+
if (p === null || p === undefined)
|
|
122
|
+
return [];
|
|
123
|
+
if (!isKeyed(p))
|
|
124
|
+
return [NOT_KEYED];
|
|
125
|
+
const strangers = Object.keys(p).filter((key) => !isAccommodation(key)).map((key) => `accommodations: «${key}» is not in the catalogue - a game's own accommodation is an addition (ADR-0145 §2), not a preset entry`);
|
|
126
|
+
const nameless = presetAccommodations(p).filter((a) => !isWord(p[a])).map((a) => `accommodations: ${a} has an empty label - the menu would show a nameless row`);
|
|
127
|
+
return [...strangers, ...nameless];
|
|
128
|
+
}
|
|
129
|
+
const NOT_KEYED = 'accommodations: must be an object keyed by accommodation';
|
|
130
|
+
/** An object keyed by name, and not a list: the one shape a preset and an answer both take. */
|
|
131
|
+
function isKeyed(x) {
|
|
132
|
+
return typeof x === 'object' && x !== null && !Array.isArray(x);
|
|
133
|
+
}
|
|
134
|
+
/** A WORD: an object whose label is text and not blank — a row that mounts must have a name to show. */
|
|
135
|
+
function isWord(v) {
|
|
136
|
+
return !!v && typeof v === 'object' && typeof v.label === 'string'
|
|
137
|
+
&& v.label.trim() !== '';
|
|
138
|
+
}
|
|
139
|
+
/** Is it one of the accommodations the cartridge must answer? */
|
|
140
|
+
export function isGameKeyed(x) {
|
|
141
|
+
return typeof x === 'string' && GAME_KEYED.includes(x);
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Is the cartridge's answer well formed? Returns the problems — EMPTY means conformant.
|
|
145
|
+
*
|
|
146
|
+
* ⚠️ UNLIKE `accommodationPresetProblems`, AN ABSENT ANSWER IS A PROBLEM: the answer is mandatory (ADR-0153), and a
|
|
147
|
+
* boot that accepted its absence would be the optional field the Dev refused. The messages go to whoever INTEGRATES
|
|
148
|
+
* the engine; each one names the accommodation and what to write.
|
|
149
|
+
*/
|
|
150
|
+
export function accommodationAnswersProblems(a) {
|
|
151
|
+
if (a === null || a === undefined) {
|
|
152
|
+
return ['accommodations: missing - the cartridge must answer every game-keyed accommodation (ADR-0153): the keys of its word, or false'];
|
|
153
|
+
}
|
|
154
|
+
if (!isKeyed(a))
|
|
155
|
+
return [NOT_KEYED];
|
|
156
|
+
const unanswered = GAME_KEYED.map((k) => answerProblem(a, k)).filter((p) => p !== null);
|
|
157
|
+
const strangers = Object.keys(a).filter((k) => !isGameKeyed(k)).map((k) => (isAccommodation(k)
|
|
158
|
+
? `accommodations: ${k} is not the cartridge's to answer - general ones mount always and contract ones are derived`
|
|
159
|
+
: `accommodations: «${k}» is not in the catalogue - a game's own accommodation is an addition (ADR-0145 §2)`));
|
|
160
|
+
return [...unanswered, ...strangers];
|
|
161
|
+
}
|
|
162
|
+
/** What is wrong with the cartridge's answer to ONE accommodation, or `null`: it must be written, and be `false` or keys. */
|
|
163
|
+
function answerProblem(answers, k) {
|
|
164
|
+
if (!(k in answers))
|
|
165
|
+
return `accommodations: ${k} is not answered - write the keys of its word if it has a subject in this game, or false`;
|
|
166
|
+
const v = answers[k];
|
|
167
|
+
return v === false || isDeclared(v) ? null : `accommodations: ${k} must be false or { labelKey } naming a key of the game's dictionary`;
|
|
168
|
+
}
|
|
169
|
+
/** DECLARED KEYS: an object whose `labelKey` is text and not blank, and whose optional keys are text when present. */
|
|
170
|
+
function isDeclared(v) {
|
|
171
|
+
if (!v || typeof v !== 'object')
|
|
172
|
+
return false;
|
|
173
|
+
const { labelKey, shortKey, hintKey } = v;
|
|
174
|
+
const optionalKey = (k) => k === undefined || typeof k === 'string';
|
|
175
|
+
return typeof labelKey === 'string' && labelKey.trim() !== '' && optionalKey(shortKey) && optionalKey(hintKey);
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Does this accommodation have a subject in this game — and what is it called NOW? The question a panel asks before
|
|
179
|
+
* mounting a row, at every drawing.
|
|
180
|
+
*
|
|
181
|
+
* 📌 It returns the WORD, resolved through `word` (the root's translator, ADR-0232 D3), or `null` — never the id, never the
|
|
182
|
+
* key, never `true`: a row that mounts must have a name to show, and «has a subject» without a word is the `action2` defect
|
|
183
|
+
* one level up. A key the game's dictionaries lack is `null` here and a line of `problems` (`ui/declared-words`).
|
|
184
|
+
*/
|
|
185
|
+
export function subjectWord(answers, k, word) {
|
|
186
|
+
const v = answers[k];
|
|
187
|
+
if (!v)
|
|
188
|
+
return null;
|
|
189
|
+
const label = word(v.labelKey);
|
|
190
|
+
if (!label || !label.trim())
|
|
191
|
+
return null;
|
|
192
|
+
const short = v.shortKey ? word(v.shortKey) : null;
|
|
193
|
+
const hint = v.hintKey ? word(v.hintKey) : null;
|
|
194
|
+
return { label, ...(short ? { short } : {}), ...(hint ? { hint } : {}) };
|
|
195
|
+
}
|