@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
package/dist-pkg/ui/layout.js
CHANGED
|
@@ -1,117 +1,135 @@
|
|
|
1
1
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
-
// ui/layout.ts —
|
|
3
|
-
//
|
|
4
|
-
//
|
|
2
|
+
// ui/layout.ts — the game's SCALE. Locks #game-region to a whole multiple of REAL PIXELS of 320×180 (per player) and
|
|
3
|
+
// rescales the UI variables scoped to the canvas. Deps: core/screens. The pure arithmetic (`stageScale`, `applyScale`,
|
|
4
|
+
// the target floor, the reporters' rules) is exported as functions; `createLayout` is the shell that measures a
|
|
5
|
+
// document it is HANDED and calls `afterScale` (the root's CRT re-anchoring its scanlines) — ADR-0232 D4, issue #207.
|
|
5
6
|
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
// enquanto um áudio toca, e sumir. E a reserva estava sendo aplicada O TEMPO TODO, porque o detector de
|
|
9
|
-
// "aberto" lia um div vazio (ver o cabeçalho de ui/vlibras): o canvas vivia em `left: -136`, fora da tela,
|
|
10
|
-
// com ou sem modo pessoa surda. O acoplamento vlibras↔layout desaparece junto.
|
|
11
|
-
import { $ } from './dom.js';
|
|
7
|
+
// IT RESERVES NO SPACE FOR THE INTERPRETER: in deaf mode the interpreter appears IN FRONT of the screen when the sonar
|
|
8
|
+
// calls it (ADR-0234, see ui/vlibras), so the layout does not shift the game for it.
|
|
12
9
|
import { screenBaseSize } from '../core/screens.js';
|
|
13
|
-
import { crtScanVars } from '../render/crt.js';
|
|
14
10
|
/**
|
|
15
|
-
*
|
|
11
|
+
* THE TOUCH TARGET FLOOR, in CSS px, for a CSS scale factor `k` (ADR-0163).
|
|
16
12
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
*
|
|
13
|
+
* 🔴 IT WAS A RULER BY VIEWPORT HEIGHT (ADR-0095: 24 px under 540, 34 under 720, 44 above), because a 640×360 pause
|
|
14
|
+
* card with 44 px items scrolled. ADR-0163 made the resolution the engine's — never under 640×360 — and the Dev set the
|
|
15
|
+
* target — «44px é o correto, eu errei quando disse 42px» — so the floor is 44 px at the minimum (k = 2) and
|
|
16
|
+
* grows with the scale, like the text; there is no smaller screen left to shrink for.
|
|
17
|
+
*/
|
|
18
|
+
export function minimumTarget(k) {
|
|
19
|
+
return 22 * (Number.isFinite(k) && k > 2 ? k : 2);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* WHAT A CARTRIDGE DRAWS UNDER THE FLOOR (ADR-0163 rule 4): text under 8·k px (16 at 640×360) and targets whose SMALLER
|
|
23
|
+
* side is under 22·k px (44 at 640×360). The engine's own nodes are not the cartridge's to answer for, and a node with no
|
|
24
|
+
* area is not drawn. Half a pixel of slack absorbs subpixel layout.
|
|
25
|
+
*/
|
|
26
|
+
export function belowFloor(nodes, k) {
|
|
27
|
+
const ratio = minimumTarget(k) / 22;
|
|
28
|
+
const smallText = [];
|
|
29
|
+
const smallTargets = [];
|
|
30
|
+
for (const n of nodes) {
|
|
31
|
+
if (n.daEngine)
|
|
32
|
+
continue;
|
|
33
|
+
if (n.fontPx !== null && n.fontPx > 0 && n.fontPx < 8 * ratio - 0.5)
|
|
34
|
+
smallText.push(n.name);
|
|
35
|
+
if (n.target && n.target.w > 0 && n.target.h > 0 && Math.min(n.target.w, n.target.h) < minimumTarget(k) - 0.5)
|
|
36
|
+
smallTargets.push(n.name);
|
|
37
|
+
}
|
|
38
|
+
return { text: smallText, targets: smallTargets };
|
|
39
|
+
}
|
|
40
|
+
export function barIntruders(barBox, nodes) {
|
|
41
|
+
// A bar with no area reserves nothing — and accusing against a zero rectangle would accuse everyone.
|
|
42
|
+
if (!barBox || barBox.w <= 0 || barBox.h <= 0)
|
|
43
|
+
return [];
|
|
44
|
+
// ⚠️ THE ZERO-AREA GUARD DOES WORK: the strict inequalities exclude a node that is DEGENERATE AT THE BOUNDARY, not one
|
|
45
|
+
// in general — a zero-width stripe crossing the bar passes all four comparisons. 📌 Containers of zero height or width
|
|
46
|
+
// are common in generated markup, and accusing them would be pure noise — which is how a consumer learns to ignore
|
|
47
|
+
// the line.
|
|
48
|
+
return nodes
|
|
49
|
+
.filter((n) => !n.isBar && n.box.w > 0 && n.box.h > 0)
|
|
50
|
+
.filter((n) => n.box.x < barBox.x + barBox.w && barBox.x < n.box.x + n.box.w
|
|
51
|
+
&& n.box.y < barBox.y + barBox.h && barBox.y < n.box.y + n.box.h)
|
|
52
|
+
.map((n) => n.name);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* THE SHELL THAT GIVES THE AVAILABLE SPACE — by id OR by class, and both forms count the same.
|
|
24
56
|
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* altura < 540 24 px WCAG 2.2 · 2.5.8 Target Size (Minimum) — AA
|
|
57
|
+
* ⚠️ BY ID ONLY, a host with `<div class="stage-wrap">` (the quiz) got no scale at all: `layout()` returned early and
|
|
58
|
+
* nothing reported anything — a silent `return` is indistinguishable from "there was nothing to do".
|
|
28
59
|
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* 6,4 mm, abaixo do alvo de polegar de 9,6 mm que o painel de toque deste jogo cita. É por isso que o
|
|
32
|
-
* ESPAÇAMENTO entre alvos passa a ser o que protege o dedo onde o tamanho não pode — a mesma saída que a
|
|
33
|
-
* própria 2.5.8 dá na sua exceção de spacing.
|
|
60
|
+
* The consumer is not wrong to use the class: a document can have several screens, and an id is unique. Accepting both
|
|
61
|
+
* is what makes the engine consumable by whoever did not copy its markup.
|
|
34
62
|
*/
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
{ altura: 0, alvo: 24 },
|
|
39
|
-
]);
|
|
63
|
+
function findStageWrap(doc) {
|
|
64
|
+
return doc.querySelector('#stage-wrap') ?? doc.querySelector('.stage-wrap');
|
|
65
|
+
}
|
|
40
66
|
/**
|
|
41
|
-
*
|
|
67
|
+
* ADR-0001 AS A PURE FUNCTION — so the engine can apply it to every cartridge (ADR-0163) and a test can pin it.
|
|
42
68
|
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
69
|
+
* Integer multiple of 320×180 (per the screen grid) in REAL pixels; up to 5 logical px of crop per side when that buys one
|
|
70
|
+
* more step (the `−10`: `base·kDev − avail·dpr ≤ 10·kDev`). Never under 2 CSS px per logical pixel — 640×360 CSS, text 16 px,
|
|
71
|
+
* targets 44 px: where the whole multiple gives less (a fractional display scale on the minimum window) the scale is exactly
|
|
72
|
+
* 2 CSS px, whole in real pixels or not, because WCAG 2.2 AA decides (ADR-0179).
|
|
45
73
|
*/
|
|
46
|
-
export function
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
return
|
|
74
|
+
export function stageScale(availW, availH, dpr, baseW, baseH) {
|
|
75
|
+
const MIN_K = 2;
|
|
76
|
+
const integerK = Math.floor(Math.min(availW * dpr / (baseW - 10), availH * dpr / (baseH - 10)));
|
|
77
|
+
const kDev = integerK / dpr >= MIN_K ? integerK : MIN_K * dpr;
|
|
78
|
+
const k = kDev / dpr;
|
|
79
|
+
return { kDev, k, width: baseW * k, height: baseH * k };
|
|
52
80
|
}
|
|
53
|
-
// A CONTAGEM DE JOGADORES entra por injeção desde 2026-08-26. Era `numPlayers`, um `let` de `core/state`
|
|
54
|
-
// importado como binding vivo — e um `let` de módulo é compartilhado por qualquer segundo jogo que a
|
|
55
|
-
// mesma página carregue (D13 do `demos`, ADR-0038). O que entra aqui é o GETTER da rodada que a raiz
|
|
56
|
-
// possui; o `let` que sobra guarda a função, não o número.
|
|
57
|
-
let _numJogadores = () => 1;
|
|
58
|
-
/** Liga a contagem de jogadores. Chamado uma vez pela raiz, antes do primeiro `layout()`. */
|
|
59
|
-
export function initLayout(deps) { _numJogadores = deps.numJogadores; }
|
|
60
81
|
/**
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
* ⚠️ ERA SÓ `#stage-wrap`, E ISSO DEIXOU A ENGINE SEM ESCALA NO PRÓPRIO HOST. O `app/index.html` do jogo
|
|
64
|
-
* trazia `<div id="stage-wrap">`; quando o cartucho saiu (issue #111) sobrou o `app/quiz.html`, que tem
|
|
65
|
-
* `<div class="stage-wrap">`. A procura por id falhava, `layout()` fazia early-return, e **nada reportava
|
|
66
|
-
* nada**: um `return` silencioso é indistinguível de «não havia o que fazer». O comentário do próprio
|
|
67
|
-
* `quiz.html` dizia «mesmo id que ui/layout escala» — quem o escreveu acreditava que corria.
|
|
82
|
+
* Writes a scale onto the game region: its size, and the UI variables that grow with it.
|
|
68
83
|
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
84
|
+
* ⚠️ HOST-INJECTED (the region is passed in): `createGame` serves documents that
|
|
85
|
+
* are not the global one — the fault the root's finding 15 names about reaching `document` from under the injection.
|
|
71
86
|
*/
|
|
72
|
-
function
|
|
73
|
-
|
|
87
|
+
export function applyScale(region, e) {
|
|
88
|
+
region.style.width = e.width + 'px';
|
|
89
|
+
region.style.height = e.height + 'px';
|
|
90
|
+
region.style.setProperty('--hud-fs', Math.max(9, Math.round(180 * e.k * 0.052)) + 'px');
|
|
91
|
+
region.style.setProperty('--ui-fs', (8 * e.k) + 'px'); // LOGICAL base 8px × k (16px at k=2)
|
|
92
|
+
// One ruler (plan phase 5b): `--tap` is the name three sibling games read, `--alvo-min` the engine's (ADR-0163); both come
|
|
93
|
+
// from `minimumTarget`, so a display scale that gives k under 2 (Windows 110%) no longer drops `--tap` under 44 px.
|
|
94
|
+
region.style.setProperty('--tap', minimumTarget(e.k) + 'px');
|
|
95
|
+
region.style.setProperty('--alvo-min', minimumTarget(e.k) + 'px'); // 44 px at 640×360, growing with k (ADR-0163)
|
|
96
|
+
// THE EDGE MARGIN (interface log 2026-09-26): 4 logical px between the region's edge and every text and button but the quick
|
|
97
|
+
// bar's — 8 px at 640×360, growing with k like the text. The stylesheet reads it; the quick bar keeps the edge (ADR-0180).
|
|
98
|
+
region.style.setProperty('--margem-borda', (4 * e.k) + 'px');
|
|
74
99
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
gr
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
//
|
|
109
|
-
// ⚠️ A ALTURA É A DO ESPAÇO DISPONÍVEL, e não a da sub-tela de um jogador: o dedo toca o aparelho, não
|
|
110
|
-
// o viewport lógico. Em quatro telas divididas cada uma tem 180 px de alto, e encolher o alvo por causa
|
|
111
|
-
// disso seria ler o número errado — o aparelho continua o mesmo.
|
|
112
|
-
gr.style.setProperty('--alvo-min', alvoMinimoDeToque(availH) + 'px');
|
|
100
|
+
/**
|
|
101
|
+
* Builds a stage scaler over the document and window it is handed. It keeps nothing between calls: every `layout()`
|
|
102
|
+
* measures again, reads the player count again, and writes onto the region.
|
|
103
|
+
*/
|
|
104
|
+
export function createLayout(ctx) {
|
|
105
|
+
function layout() {
|
|
106
|
+
const wrap = findStageWrap(ctx.doc);
|
|
107
|
+
if (!wrap)
|
|
108
|
+
return;
|
|
109
|
+
wrap.style.paddingRight = '0px';
|
|
110
|
+
const availW = wrap.clientWidth || 320;
|
|
111
|
+
const availH = wrap.clientHeight || 180;
|
|
112
|
+
// The screen grid sets the base (1=320×180, 2=640×180, 3-4=640×360).
|
|
113
|
+
const n = ctx.numPlayers();
|
|
114
|
+
const { w: baseW, h: baseH } = screenBaseSize(n);
|
|
115
|
+
// Floor k=2: EACH viewport is at least 640×360, so 2×2 = 1280×720 fits a government Chromebook (1366×768).
|
|
116
|
+
// ADR-0001: SCALE locked to WHOLE REAL PIXELS. Each art pixel = kDev PHYSICAL pixels (whole) → scanlines ALWAYS regular
|
|
117
|
+
// and uniform art at ANY dpr. Tolerates ≤5 logical px of crop per side (the −10): base·kDev − avail·dpr ≤ 10·kDev ⇒
|
|
118
|
+
// kDev ≤ avail·dpr/(base−10). (The Dev chose whole REAL pixels.) The arithmetic lives in `stageScale` (ADR-0163), so the
|
|
119
|
+
// engine applies it to every cartridge.
|
|
120
|
+
const dpr = ctx.win.devicePixelRatio || 1;
|
|
121
|
+
const ratio = stageScale(availW, availH, dpr, baseW, baseH);
|
|
122
|
+
const { kDev, k } = ratio;
|
|
123
|
+
// The UI variables' SCALE is SCOPED to #game-region: only the UI INSIDE the canvas (menus/HUD/pause/quiz) scales with
|
|
124
|
+
// k. Outside the canvas (top bar, debug panel) it inherits :root → text ALWAYS 16px, touch 44px (the Dev).
|
|
125
|
+
const gr = ctx.doc.querySelector('#game-region');
|
|
126
|
+
if (gr) {
|
|
127
|
+
// `--tap` is the PREFERRED size (22·k) and `--alvo-min` the FLOOR (22·k, 44 px at 640×360 — ADR-0163), written in `applyScale`.
|
|
128
|
+
applyScale(gr, ratio);
|
|
129
|
+
}
|
|
130
|
+
ctx.afterScale(); // scanlines realign when the scale k changes
|
|
131
|
+
if (ctx.debug?.())
|
|
132
|
+
console.info(`[escala] kDev=${kDev}× px REAIS (canvas físico ${baseW * kDev}×${baseH * kDev} = múltiplo INTEIRO de ${baseW}×${baseH}); CSS ${Math.round(baseW * k)}×${Math.round(baseH * k)} (k=${k.toFixed(3)}, dpr=${dpr})`);
|
|
113
133
|
}
|
|
114
|
-
|
|
115
|
-
if (/[?&]debug=true/.test(location.search))
|
|
116
|
-
console.info(`[escala] kDev=${kDev}× px REAIS (canvas físico ${baseW * kDev}×${baseH * kDev} = múltiplo INTEIRO de ${baseW}×${baseH}); CSS ${Math.round(baseW * k)}×${Math.round(baseH * k)} (k=${k.toFixed(3)}, dpr=${dpr})`);
|
|
134
|
+
return { layout };
|
|
117
135
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
/** One track of a three.js AnimationClip JSON, as the export writes it. */
|
|
3
|
+
export interface TrackJson {
|
|
4
|
+
readonly name: string;
|
|
5
|
+
readonly type: string;
|
|
6
|
+
readonly times: readonly number[];
|
|
7
|
+
readonly values: readonly number[];
|
|
8
|
+
readonly interpolation?: number;
|
|
9
|
+
}
|
|
10
|
+
/** A three.js AnimationClip JSON (`AnimationClip.parse`), as the export writes it. */
|
|
11
|
+
export interface ClipJson {
|
|
12
|
+
readonly name: string;
|
|
13
|
+
readonly duration: number;
|
|
14
|
+
readonly tracks: readonly TrackJson[];
|
|
15
|
+
}
|
|
16
|
+
/** A track's value at `time`, as three.js interpolates it: held before the first key and after the last. */
|
|
17
|
+
export declare function valueAt(track: TrackJson, time: number): number[];
|
|
18
|
+
/**
|
|
19
|
+
* The clip from `from` seconds on, its times shifted to start at zero: each track keeps its value AT `from` as its first key,
|
|
20
|
+
* then its later keys. A track whose keys all lie before `from` holds its last value. `from` of zero is the clip itself.
|
|
21
|
+
*/
|
|
22
|
+
export declare function clipFrom(clip: ClipJson, from: number): ClipJson;
|
|
23
|
+
/**
|
|
24
|
+
* The face's tracks, bound to every mesh that carries the morph targets. The export names a morph track after the head's NODE
|
|
25
|
+
* (`cabecaModifAlisson.morphTargetInfluences[Sorriso]`), and three.js loads a node whose mesh has two primitives as a GROUP of
|
|
26
|
+
* two meshes, each with its own copy of the targets — a track on the group moves neither. `meshesOf(node)` answers the meshes
|
|
27
|
+
* a track on that node must reach (by the names three.js binds, e.g. their uuids), or `null` where the node is itself the mesh;
|
|
28
|
+
* the track is written once for each.
|
|
29
|
+
*/
|
|
30
|
+
export declare function bindFaceTracks(clip: ClipJson, meshesOf: (node: string) => readonly string[] | null): ClipJson;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// ui/libras-avatar-clip — A SIGN CLIP, BEFORE three.js READS IT (ADR-0234, route B, phase B2). Pure: no three.js, no DOM.
|
|
3
|
+
//
|
|
4
|
+
// The B1 export hands each sign over as three.js AnimationClip JSON (`scripts/libras-export.mjs`), and two things about it are
|
|
5
|
+
// answered here, on the JSON, where node can hold them (`tests/libras-avatar-plan.node.test.js`): a clip whose sign starts
|
|
6
|
+
// later than the clip does — FALA, whose export starts at a stray keyframe — is played from its sign (`clipFrom`), and the
|
|
7
|
+
// face's tracks, named after the head's node, are written for each mesh three.js loads the head as (`bindFaceTracks`).
|
|
8
|
+
// `ui/libras-avatar-stage` parses what comes out.
|
|
9
|
+
/** three.js `InterpolateDiscrete`: the export marks a STEP track with it. */
|
|
10
|
+
const DISCRETE = 2300;
|
|
11
|
+
function slerp(a, b, u) {
|
|
12
|
+
let dot = a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3];
|
|
13
|
+
const bb = dot < 0 ? b.map((x) => -x) : [...b];
|
|
14
|
+
dot = Math.abs(dot);
|
|
15
|
+
if (dot > 0.9995) {
|
|
16
|
+
const q = a.map((x, c) => x + (bb[c] - x) * u);
|
|
17
|
+
const l = Math.hypot(...q);
|
|
18
|
+
return q.map((x) => x / l);
|
|
19
|
+
}
|
|
20
|
+
const th = Math.acos(dot);
|
|
21
|
+
const s = Math.sin(th);
|
|
22
|
+
return a.map((x, c) => (Math.sin((1 - u) * th) * x + Math.sin(u * th) * bb[c]) / s);
|
|
23
|
+
}
|
|
24
|
+
/** A track's value at `time`, as three.js interpolates it: held before the first key and after the last. */
|
|
25
|
+
export function valueAt(track, time) {
|
|
26
|
+
const { times, values } = track;
|
|
27
|
+
const width = values.length / times.length;
|
|
28
|
+
const key = (i) => values.slice(i * width, i * width + width);
|
|
29
|
+
const last = times.length - 1;
|
|
30
|
+
if (time <= times[0] || last === 0)
|
|
31
|
+
return key(0);
|
|
32
|
+
if (time >= times[last])
|
|
33
|
+
return key(last);
|
|
34
|
+
let i = 0;
|
|
35
|
+
while (times[i + 1] <= time)
|
|
36
|
+
i += 1;
|
|
37
|
+
if (time === times[i] || track.interpolation === DISCRETE)
|
|
38
|
+
return key(i);
|
|
39
|
+
const u = (time - times[i]) / (times[i + 1] - times[i]);
|
|
40
|
+
const a = key(i);
|
|
41
|
+
const b = key(i + 1);
|
|
42
|
+
return track.type === 'quaternion' ? slerp(a, b, u) : a.map((x, c) => x + (b[c] - x) * u);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The clip from `from` seconds on, its times shifted to start at zero: each track keeps its value AT `from` as its first key,
|
|
46
|
+
* then its later keys. A track whose keys all lie before `from` holds its last value. `from` of zero is the clip itself.
|
|
47
|
+
*/
|
|
48
|
+
export function clipFrom(clip, from) {
|
|
49
|
+
if (!(from > 0))
|
|
50
|
+
return clip;
|
|
51
|
+
const tracks = clip.tracks.map((t) => {
|
|
52
|
+
const times = [0];
|
|
53
|
+
const values = [...valueAt(t, from)];
|
|
54
|
+
const width = t.values.length / t.times.length;
|
|
55
|
+
t.times.forEach((time, i) => {
|
|
56
|
+
if (time <= from)
|
|
57
|
+
return;
|
|
58
|
+
times.push(time - from);
|
|
59
|
+
values.push(...t.values.slice(i * width, i * width + width));
|
|
60
|
+
});
|
|
61
|
+
return { ...t, times, values };
|
|
62
|
+
});
|
|
63
|
+
return { name: clip.name, duration: Math.max(0, clip.duration - from), tracks };
|
|
64
|
+
}
|
|
65
|
+
const MORPH_TRACK = /^(.+)\.morphTargetInfluences\[(.+)\]$/u;
|
|
66
|
+
/**
|
|
67
|
+
* The face's tracks, bound to every mesh that carries the morph targets. The export names a morph track after the head's NODE
|
|
68
|
+
* (`cabecaModifAlisson.morphTargetInfluences[Sorriso]`), and three.js loads a node whose mesh has two primitives as a GROUP of
|
|
69
|
+
* two meshes, each with its own copy of the targets — a track on the group moves neither. `meshesOf(node)` answers the meshes
|
|
70
|
+
* a track on that node must reach (by the names three.js binds, e.g. their uuids), or `null` where the node is itself the mesh;
|
|
71
|
+
* the track is written once for each.
|
|
72
|
+
*/
|
|
73
|
+
export function bindFaceTracks(clip, meshesOf) {
|
|
74
|
+
const tracks = [];
|
|
75
|
+
for (const t of clip.tracks) {
|
|
76
|
+
const m = MORPH_TRACK.exec(t.name);
|
|
77
|
+
const meshes = m ? meshesOf(m[1]) : null;
|
|
78
|
+
if (!m || !meshes) {
|
|
79
|
+
tracks.push(t);
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
for (const mesh of meshes)
|
|
83
|
+
tracks.push({ ...t, name: `${mesh}.morphTargetInfluences[${m[2]}]` });
|
|
84
|
+
}
|
|
85
|
+
return { ...clip, tracks };
|
|
86
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
import { type AvatarManifest } from './libras-avatar-plan.js';
|
|
3
|
+
import type { AvatarStage } from './libras-avatar-stage.js';
|
|
4
|
+
/** The host's `fetch`, lent unbound — and called bare, never as a method of what carries it. */
|
|
5
|
+
export type FetchFile = ((url: string) => Promise<Response>) | undefined;
|
|
6
|
+
/**
|
|
7
|
+
* Why a delivery with no avatar cannot sign, and the fix (ADR-0169): the reason `NO_INTERPRETER` gives, word for word — the
|
|
8
|
+
* root's interpreter answers a delivery built without `--libras` exactly as a root with no player would.
|
|
9
|
+
*/
|
|
10
|
+
export declare const NOT_SHIPPED = "a Libras player is not installed in this delivery \u2014 build it with `inclusionist-heavy <folder> --libras`";
|
|
11
|
+
/** Why a request that waited across a `hide()` or the root's end was not signed. */
|
|
12
|
+
export declare const RELEASED = "the interpreter was taken off the screen before it signed";
|
|
13
|
+
/** Where the delivery's files are: the avatar's folder, its manifest, and the glosses `--libras` writes beside them. */
|
|
14
|
+
export interface AvatarPlace {
|
|
15
|
+
readonly folder: string;
|
|
16
|
+
readonly manifest: string;
|
|
17
|
+
readonly glosses: string;
|
|
18
|
+
}
|
|
19
|
+
/** The addresses under `base`, or `null` where it resolves nothing — a host whose document has no address finds no avatar. */
|
|
20
|
+
export declare function avatarPlace(base: string, folder?: string): AvatarPlace | null;
|
|
21
|
+
/** The delivery's manifest, or `null` for a delivery that did not ship the avatar. */
|
|
22
|
+
export declare const readManifest: (fetchFile: FetchFile, place: AvatarPlace) => Promise<AvatarManifest | null>;
|
|
23
|
+
/** The canvas the avatar is drawn on: the bottom right, in front of the screen, watched and never operated. */
|
|
24
|
+
export declare function avatarCanvas(doc: Document, title: string): HTMLCanvasElement;
|
|
25
|
+
export interface AvatarOpening {
|
|
26
|
+
readonly fetchFile: FetchFile;
|
|
27
|
+
/** The avatar's address, from the manifest. */
|
|
28
|
+
readonly avatarUrl: string;
|
|
29
|
+
readonly loadStage: (canvas: HTMLCanvasElement, avatar: ArrayBuffer) => Promise<AvatarStage>;
|
|
30
|
+
readonly timeoutMs: number;
|
|
31
|
+
readonly win: Pick<Window, 'setTimeout' | 'clearTimeout'>;
|
|
32
|
+
}
|
|
33
|
+
/** A stage, or why there is none. */
|
|
34
|
+
export type Opened = {
|
|
35
|
+
readonly stage: AvatarStage;
|
|
36
|
+
} | {
|
|
37
|
+
readonly reason: string;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Builds the stage on `canvas` from the delivery's avatar: the stage, or why not — an avatar the delivery cannot give, a device
|
|
41
|
+
* that cannot draw it, a load past `timeoutMs`. `wanted()` is asked when the stage arrives: false, and it is released at once.
|
|
42
|
+
* `cancel()` answers «released» now and clears the timer, so nothing of a released root outlives it.
|
|
43
|
+
*/
|
|
44
|
+
export declare function openAvatar(canvas: HTMLCanvasElement, o: AvatarOpening, wanted: () => boolean): {
|
|
45
|
+
readonly done: Promise<Opened>;
|
|
46
|
+
readonly cancel: () => void;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Makes each clip of `names` playable on `stage`, fetching the ones it has not got — once each, remembered in `ready` — and
|
|
50
|
+
* answers the names whose file could not be read or prepared.
|
|
51
|
+
*/
|
|
52
|
+
export declare function prepareClips(stage: AvatarStage, names: readonly string[], manifest: AvatarManifest, place: AvatarPlace, fetchFile: FetchFile, ready: Map<string, Promise<boolean>>): Promise<string[]>;
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// ui/libras-avatar-load — WHAT THE DELIVERY SHIPPED FOR THE FREE PLAYER, AND OPENING IT (ADR-0234, route B, phase B2).
|
|
3
|
+
//
|
|
4
|
+
// The half of `ui/libras-avatar-player` that talks to the delivery: where its files are, whether it shipped the avatar at all
|
|
5
|
+
// (its manifest, which only `inclusionist-heavy --libras` writes), the canvas the avatar is drawn on, the stage built on it from
|
|
6
|
+
// the avatar's bytes — within a time, or not at all — and the clips, each fetched and prepared once. Every answer is a value,
|
|
7
|
+
// never a throw: a delivery without the avatar, a device without WebGL and a clip that cannot be read are reasons, which the
|
|
8
|
+
// player hands to deaf mode and deaf mode to `problems` (ADR-0169).
|
|
9
|
+
import { LIBRAS_GLOSSES_FILE } from './libras-glosses.js';
|
|
10
|
+
import { LIBRAS_NOT_INSTALLED } from './vlibras.js';
|
|
11
|
+
import { LIBRAS_AVATAR_FOLDER, LIBRAS_AVATAR_MANIFEST, avatarManifestOf } from './libras-avatar-plan.js';
|
|
12
|
+
/**
|
|
13
|
+
* Why a delivery with no avatar cannot sign, and the fix (ADR-0169): the reason `NO_INTERPRETER` gives, word for word — the
|
|
14
|
+
* root's interpreter answers a delivery built without `--libras` exactly as a root with no player would.
|
|
15
|
+
*/
|
|
16
|
+
export const NOT_SHIPPED = LIBRAS_NOT_INSTALLED;
|
|
17
|
+
/** Why a request that waited across a `hide()` or the root's end was not signed. */
|
|
18
|
+
export const RELEASED = 'the interpreter was taken off the screen before it signed';
|
|
19
|
+
/** The addresses under `base`, or `null` where it resolves nothing — a host whose document has no address finds no avatar. */
|
|
20
|
+
export function avatarPlace(base, folder = LIBRAS_AVATAR_FOLDER) {
|
|
21
|
+
try {
|
|
22
|
+
const at = new URL(folder, base);
|
|
23
|
+
return { folder: at.href, manifest: new URL(LIBRAS_AVATAR_MANIFEST, at).href, glosses: new URL(LIBRAS_GLOSSES_FILE, at).href };
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/** A JSON file of the delivery, or `null` — no fetch, no file, not JSON. */
|
|
30
|
+
async function readJson(fetchFile, url) {
|
|
31
|
+
if (!fetchFile)
|
|
32
|
+
return null;
|
|
33
|
+
try {
|
|
34
|
+
const resp = await fetchFile(url);
|
|
35
|
+
return resp.ok ? await resp.json() : null;
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/** The delivery's manifest, or `null` for a delivery that did not ship the avatar. */
|
|
42
|
+
export const readManifest = async (fetchFile, place) => avatarManifestOf(await readJson(fetchFile, place.manifest));
|
|
43
|
+
/** The canvas the avatar is drawn on: the bottom right, in front of the screen, watched and never operated. */
|
|
44
|
+
export function avatarCanvas(doc, title) {
|
|
45
|
+
const canvas = doc.createElement('canvas');
|
|
46
|
+
canvas.setAttribute('role', 'img');
|
|
47
|
+
canvas.setAttribute('aria-label', title);
|
|
48
|
+
canvas.tabIndex = -1; // the keyboard stays with the game
|
|
49
|
+
canvas.hidden = true;
|
|
50
|
+
Object.assign(canvas.style, {
|
|
51
|
+
position: 'fixed', right: '0', bottom: '0', width: 'max(25vw, 200px)', height: 'max(50vh, 200px)', border: '0',
|
|
52
|
+
background: 'transparent', pointerEvents: 'none', zIndex: '2147483000',
|
|
53
|
+
});
|
|
54
|
+
doc.body.append(canvas);
|
|
55
|
+
return canvas;
|
|
56
|
+
}
|
|
57
|
+
const reasonOf = (failure) => (failure instanceof Error ? failure.message : String(failure));
|
|
58
|
+
async function build(canvas, o, wanted) {
|
|
59
|
+
const { fetchFile } = o; // called bare, as `FetchFile` says
|
|
60
|
+
if (!fetchFile)
|
|
61
|
+
return { reason: NOT_SHIPPED };
|
|
62
|
+
try {
|
|
63
|
+
const resp = await fetchFile(o.avatarUrl);
|
|
64
|
+
if (!resp.ok) {
|
|
65
|
+
return { reason: `the Libras avatar could not be read from the delivery (HTTP ${resp.status}) — build it again with `
|
|
66
|
+
+ '`inclusionist-heavy <folder> --libras`' };
|
|
67
|
+
}
|
|
68
|
+
const stage = await o.loadStage(canvas, await resp.arrayBuffer());
|
|
69
|
+
if (!wanted()) {
|
|
70
|
+
stage.dispose();
|
|
71
|
+
return { reason: RELEASED };
|
|
72
|
+
} // given up on or released meanwhile: nothing is kept
|
|
73
|
+
return { stage };
|
|
74
|
+
}
|
|
75
|
+
catch (failure) {
|
|
76
|
+
return { reason: `the Libras avatar cannot be drawn here: ${reasonOf(failure)}` };
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Builds the stage on `canvas` from the delivery's avatar: the stage, or why not — an avatar the delivery cannot give, a device
|
|
81
|
+
* that cannot draw it, a load past `timeoutMs`. `wanted()` is asked when the stage arrives: false, and it is released at once.
|
|
82
|
+
* `cancel()` answers «released» now and clears the timer, so nothing of a released root outlives it.
|
|
83
|
+
*/
|
|
84
|
+
export function openAvatar(canvas, o, wanted) {
|
|
85
|
+
let answer = () => { };
|
|
86
|
+
const stopped = new Promise((resolve) => { answer = resolve; });
|
|
87
|
+
const timer = o.win.setTimeout(() => {
|
|
88
|
+
answer({ reason: `the Libras avatar did not load within ${Math.round(o.timeoutMs / 1000)} s` });
|
|
89
|
+
}, o.timeoutMs);
|
|
90
|
+
const done = Promise.race([build(canvas, o, wanted), stopped]).then((r) => { o.win.clearTimeout(timer); return r; });
|
|
91
|
+
return { done, cancel: () => { o.win.clearTimeout(timer); answer({ reason: RELEASED }); } };
|
|
92
|
+
}
|
|
93
|
+
const isClipJson = (data) => {
|
|
94
|
+
const c = data;
|
|
95
|
+
return !!c && typeof c.duration === 'number' && Array.isArray(c.tracks);
|
|
96
|
+
};
|
|
97
|
+
/**
|
|
98
|
+
* A clip's address under the avatar's folder, SPELLED AS A URL SPELLS ITS NAME — which is the address the delivery's list keeps it
|
|
99
|
+
* under offline (`libras/offline-avatar.json`, ADR-0234 phase B3). Escaping the name first (`&` as `%26`, as `encodeURIComponent`
|
|
100
|
+
* does) asks another address, which the server answers online and the checked cache never holds: PRIMEIRO&ORDINAL and
|
|
101
|
+
* SEGUNDO&ORDINAL went unsigned offline. 📌 No clip name carries `%`, `#`, `?` or `\`, which a URL reads as something other than
|
|
102
|
+
* a name — `tests/libras-avatar-delivery.node.test.js` holds that over the pins.
|
|
103
|
+
*/
|
|
104
|
+
const clipAddress = (file, folder) => new URL(file, folder).href;
|
|
105
|
+
/**
|
|
106
|
+
* Makes each clip of `names` playable on `stage`, fetching the ones it has not got — once each, remembered in `ready` — and
|
|
107
|
+
* answers the names whose file could not be read or prepared.
|
|
108
|
+
*/
|
|
109
|
+
export async function prepareClips(stage, names, manifest, place, fetchFile, ready) {
|
|
110
|
+
const one = async (name) => {
|
|
111
|
+
const w = manifest.clips.get(name);
|
|
112
|
+
if (!w)
|
|
113
|
+
return false;
|
|
114
|
+
const json = await readJson(fetchFile, clipAddress(w.file, place.folder));
|
|
115
|
+
if (!isClipJson(json))
|
|
116
|
+
return false;
|
|
117
|
+
try {
|
|
118
|
+
stage.prepare(name, json, w.from);
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
const read = await Promise.all(names.map((name) => {
|
|
126
|
+
let loading = ready.get(name);
|
|
127
|
+
if (!loading) {
|
|
128
|
+
loading = one(name);
|
|
129
|
+
ready.set(name, loading);
|
|
130
|
+
}
|
|
131
|
+
return loading;
|
|
132
|
+
}));
|
|
133
|
+
return names.filter((_, i) => !read[i]);
|
|
134
|
+
}
|