@the-inclusionist/engine 6.36.1
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/LICENSE +661 -0
- package/README.md +107 -0
- package/app/css/style.css +622 -0
- package/app/public/vendor/fonts/andika-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/andika-400.woff2 +0 -0
- package/app/public/vendor/fonts/andika-700-ext.woff2 +0 -0
- package/app/public/vendor/fonts/andika-700.woff2 +0 -0
- package/app/public/vendor/fonts/atkinson-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/atkinson-400.woff2 +0 -0
- package/app/public/vendor/fonts/atkinson-700-ext.woff2 +0 -0
- package/app/public/vendor/fonts/atkinson-700.woff2 +0 -0
- package/app/public/vendor/fonts/atkinson-mono-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/atkinson-mono-400.woff2 +0 -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/greatvibes-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/lexend-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/lexend-400.woff2 +0 -0
- package/app/public/vendor/fonts/lexend-700-ext.woff2 +0 -0
- package/app/public/vendor/fonts/lexend-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/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/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/ufcook-700.woff2 +0 -0
- package/app/public/vendor/fonts/ufmag-400.woff2 +0 -0
- package/app/public/vendor/fonts.css +88 -0
- package/dist-pkg/boot/create-game.d.ts +159 -0
- package/dist-pkg/boot/create-game.js +266 -0
- package/dist-pkg/core/a11y-sr.d.ts +4 -0
- package/dist-pkg/core/a11y-sr.js +20 -0
- package/dist-pkg/core/actions.d.ts +100 -0
- package/dist-pkg/core/actions.js +149 -0
- package/dist-pkg/core/anel.d.ts +17 -0
- package/dist-pkg/core/anel.js +31 -0
- package/dist-pkg/core/collision.d.ts +21 -0
- package/dist-pkg/core/collision.js +62 -0
- package/dist-pkg/core/constants.d.ts +84 -0
- package/dist-pkg/core/constants.js +64 -0
- package/dist-pkg/core/contract.d.ts +227 -0
- package/dist-pkg/core/contract.js +184 -0
- package/dist-pkg/core/dom-query.d.ts +9 -0
- package/dist-pkg/core/dom-query.js +25 -0
- package/dist-pkg/core/entity.d.ts +150 -0
- package/dist-pkg/core/entity.js +38 -0
- package/dist-pkg/core/escape-html.d.ts +16 -0
- package/dist-pkg/core/escape-html.js +32 -0
- package/dist-pkg/core/i18n.d.ts +71 -0
- package/dist-pkg/core/i18n.js +236 -0
- package/dist-pkg/core/layers.d.ts +39 -0
- package/dist-pkg/core/layers.js +71 -0
- package/dist-pkg/core/letter-grid.d.ts +55 -0
- package/dist-pkg/core/letter-grid.js +116 -0
- package/dist-pkg/core/loop.d.ts +17 -0
- package/dist-pkg/core/loop.js +36 -0
- package/dist-pkg/core/password.d.ts +34 -0
- package/dist-pkg/core/password.js +205 -0
- package/dist-pkg/core/rng.d.ts +20 -0
- package/dist-pkg/core/rng.js +61 -0
- package/dist-pkg/core/rotulo-acessivel.d.ts +17 -0
- package/dist-pkg/core/rotulo-acessivel.js +41 -0
- package/dist-pkg/core/run-state.d.ts +115 -0
- package/dist-pkg/core/run-state.js +40 -0
- package/dist-pkg/core/scenes.d.ts +68 -0
- package/dist-pkg/core/scenes.js +64 -0
- package/dist-pkg/core/screens.d.ts +16 -0
- package/dist-pkg/core/screens.js +28 -0
- package/dist-pkg/core/state.d.ts +138 -0
- package/dist-pkg/core/state.js +290 -0
- package/dist-pkg/core/tiles.d.ts +12 -0
- package/dist-pkg/core/tiles.js +61 -0
- package/dist-pkg/core/world.d.ts +4 -0
- package/dist-pkg/core/world.js +58 -0
- package/dist-pkg/educational/activities-registry.d.ts +47 -0
- package/dist-pkg/educational/activities-registry.js +95 -0
- package/dist-pkg/i18n/en.d.ts +3 -0
- package/dist-pkg/i18n/en.js +560 -0
- package/dist-pkg/i18n/es.d.ts +3 -0
- package/dist-pkg/i18n/es.js +558 -0
- package/dist-pkg/i18n/pt.d.ts +3 -0
- package/dist-pkg/i18n/pt.js +618 -0
- package/dist-pkg/input/default-bindings.d.ts +24 -0
- package/dist-pkg/input/default-bindings.js +122 -0
- package/dist-pkg/input/devices.d.ts +15 -0
- package/dist-pkg/input/devices.js +31 -0
- package/dist-pkg/input/edges.d.ts +58 -0
- package/dist-pkg/input/edges.js +49 -0
- package/dist-pkg/input/gamepad.d.ts +203 -0
- package/dist-pkg/input/gamepad.js +531 -0
- package/dist-pkg/input/keyboard-runtime.d.ts +61 -0
- package/dist-pkg/input/keyboard-runtime.js +83 -0
- package/dist-pkg/input/keyboard.d.ts +45 -0
- package/dist-pkg/input/keyboard.js +80 -0
- package/dist-pkg/input/keydown.d.ts +270 -0
- package/dist-pkg/input/keydown.js +381 -0
- package/dist-pkg/input/latch.d.ts +30 -0
- package/dist-pkg/input/latch.js +63 -0
- package/dist-pkg/input/pointer-space.d.ts +40 -0
- package/dist-pkg/input/pointer-space.js +51 -0
- package/dist-pkg/input/state.d.ts +11 -0
- package/dist-pkg/input/state.js +12 -0
- package/dist-pkg/input/touch-bindings.d.ts +210 -0
- package/dist-pkg/input/touch-bindings.js +365 -0
- package/dist-pkg/input/touch.d.ts +173 -0
- package/dist-pkg/input/touch.js +314 -0
- package/dist-pkg/input/transports.d.ts +90 -0
- package/dist-pkg/input/transports.js +96 -0
- package/dist-pkg/input/vocabulary-migration.d.ts +58 -0
- package/dist-pkg/input/vocabulary-migration.js +125 -0
- package/dist-pkg/platform/audio-ambient.d.ts +27 -0
- package/dist-pkg/platform/audio-ambient.js +89 -0
- package/dist-pkg/platform/audio-earcons.d.ts +24 -0
- package/dist-pkg/platform/audio-earcons.js +62 -0
- package/dist-pkg/platform/audio-jingles.d.ts +17 -0
- package/dist-pkg/platform/audio-jingles.js +54 -0
- package/dist-pkg/platform/audio-mixer.d.ts +14 -0
- package/dist-pkg/platform/audio-mixer.js +62 -0
- package/dist-pkg/platform/audio-nav.d.ts +51 -0
- package/dist-pkg/platform/audio-nav.js +99 -0
- package/dist-pkg/platform/audio-sonar.d.ts +71 -0
- package/dist-pkg/platform/audio-sonar.js +173 -0
- package/dist-pkg/platform/audio.d.ts +32 -0
- package/dist-pkg/platform/audio.js +191 -0
- package/dist-pkg/platform/interruptible-speech.d.ts +19 -0
- package/dist-pkg/platform/interruptible-speech.js +79 -0
- package/dist-pkg/platform/speech.d.ts +2 -0
- package/dist-pkg/platform/speech.js +35 -0
- package/dist-pkg/platform/storage.d.ts +87 -0
- package/dist-pkg/platform/storage.js +148 -0
- package/dist-pkg/platform/tts.d.ts +33 -0
- package/dist-pkg/platform/tts.js +142 -0
- package/dist-pkg/render/camera.d.ts +58 -0
- package/dist-pkg/render/camera.js +105 -0
- package/dist-pkg/render/canvas.d.ts +13 -0
- package/dist-pkg/render/canvas.js +26 -0
- package/dist-pkg/render/cenario-data.d.ts +127 -0
- package/dist-pkg/render/cenario-data.js +145 -0
- package/dist-pkg/render/city-tex.d.ts +63 -0
- package/dist-pkg/render/city-tex.js +228 -0
- package/dist-pkg/render/city-tiles.d.ts +11 -0
- package/dist-pkg/render/city-tiles.js +140 -0
- package/dist-pkg/render/crt.d.ts +19 -0
- package/dist-pkg/render/crt.js +92 -0
- package/dist-pkg/render/cvd-matrices.d.ts +35 -0
- package/dist-pkg/render/cvd-matrices.js +91 -0
- package/dist-pkg/render/draw.d.ts +149 -0
- package/dist-pkg/render/draw.js +221 -0
- package/dist-pkg/render/fx.d.ts +63 -0
- package/dist-pkg/render/fx.js +123 -0
- package/dist-pkg/render/hc-role-data.d.ts +14 -0
- package/dist-pkg/render/hc-role-data.js +24 -0
- package/dist-pkg/render/high-contrast.d.ts +109 -0
- package/dist-pkg/render/high-contrast.js +254 -0
- package/dist-pkg/render/lq-filter.d.ts +37 -0
- package/dist-pkg/render/lq-filter.js +94 -0
- package/dist-pkg/render/minimap.d.ts +11 -0
- package/dist-pkg/render/minimap.js +94 -0
- package/dist-pkg/render/parallax.d.ts +85 -0
- package/dist-pkg/render/parallax.js +158 -0
- package/dist-pkg/render/player-anim.d.ts +71 -0
- package/dist-pkg/render/player-anim.js +152 -0
- package/dist-pkg/render/port.d.ts +143 -0
- package/dist-pkg/render/port.js +29 -0
- package/dist-pkg/render/recycling-tex.d.ts +48 -0
- package/dist-pkg/render/recycling-tex.js +164 -0
- package/dist-pkg/render/scene-city.d.ts +77 -0
- package/dist-pkg/render/scene-city.js +181 -0
- package/dist-pkg/render/scene-parallax.d.ts +72 -0
- package/dist-pkg/render/scene-parallax.js +426 -0
- package/dist-pkg/render/scene-sky.d.ts +126 -0
- package/dist-pkg/render/scene-sky.js +295 -0
- package/dist-pkg/render/screen-pipeline.d.ts +134 -0
- package/dist-pkg/render/screen-pipeline.js +177 -0
- package/dist-pkg/render/set-cenario.d.ts +54 -0
- package/dist-pkg/render/set-cenario.js +103 -0
- package/dist-pkg/render/sprite-fx.d.ts +3 -0
- package/dist-pkg/render/sprite-fx.js +40 -0
- package/dist-pkg/render/textures.d.ts +75 -0
- package/dist-pkg/render/textures.js +240 -0
- package/dist-pkg/render/title-scene.d.ts +46 -0
- package/dist-pkg/render/title-scene.js +75 -0
- package/dist-pkg/render/viewports.d.ts +52 -0
- package/dist-pkg/render/viewports.js +185 -0
- package/dist-pkg/render/viz-axes.d.ts +98 -0
- package/dist-pkg/render/viz-axes.js +184 -0
- package/dist-pkg/render/viz-modes.d.ts +61 -0
- package/dist-pkg/render/viz-modes.js +74 -0
- package/dist-pkg/render/viz-setters.d.ts +130 -0
- package/dist-pkg/render/viz-setters.js +244 -0
- package/dist-pkg/render/weather.d.ts +96 -0
- package/dist-pkg/render/weather.js +171 -0
- package/dist-pkg/render/wheelchair-sprites.d.ts +24 -0
- package/dist-pkg/render/wheelchair-sprites.js +63 -0
- package/dist-pkg/render/world-tex.d.ts +18 -0
- package/dist-pkg/render/world-tex.js +87 -0
- package/dist-pkg/ui/activities-menu.d.ts +258 -0
- package/dist-pkg/ui/activities-menu.js +648 -0
- package/dist-pkg/ui/caa-sets.d.ts +39 -0
- package/dist-pkg/ui/caa-sets.js +65 -0
- package/dist-pkg/ui/changed-mark.d.ts +15 -0
- package/dist-pkg/ui/changed-mark.js +96 -0
- package/dist-pkg/ui/debug-panel.d.ts +67 -0
- package/dist-pkg/ui/debug-panel.js +173 -0
- package/dist-pkg/ui/dom.d.ts +24 -0
- package/dist-pkg/ui/dom.js +47 -0
- package/dist-pkg/ui/focus-trap.d.ts +71 -0
- package/dist-pkg/ui/focus-trap.js +99 -0
- package/dist-pkg/ui/fonts.d.ts +41 -0
- package/dist-pkg/ui/fonts.js +59 -0
- package/dist-pkg/ui/hud.d.ts +137 -0
- package/dist-pkg/ui/hud.js +229 -0
- package/dist-pkg/ui/item-announcement.d.ts +19 -0
- package/dist-pkg/ui/item-announcement.js +36 -0
- package/dist-pkg/ui/layout.d.ts +6 -0
- package/dist-pkg/ui/layout.js +54 -0
- package/dist-pkg/ui/loop-crash.d.ts +19 -0
- package/dist-pkg/ui/loop-crash.js +60 -0
- package/dist-pkg/ui/map-hub.d.ts +56 -0
- package/dist-pkg/ui/map-hub.js +138 -0
- package/dist-pkg/ui/menu-nav.d.ts +141 -0
- package/dist-pkg/ui/menu-nav.js +390 -0
- package/dist-pkg/ui/pause-icons.d.ts +327 -0
- package/dist-pkg/ui/pause-icons.js +620 -0
- package/dist-pkg/ui/reach-notice.d.ts +34 -0
- package/dist-pkg/ui/reach-notice.js +71 -0
- package/dist-pkg/ui/settings-audio.d.ts +139 -0
- package/dist-pkg/ui/settings-audio.js +544 -0
- package/dist-pkg/ui/settings-caa.d.ts +45 -0
- package/dist-pkg/ui/settings-caa.js +137 -0
- package/dist-pkg/ui/settings-controls.d.ts +100 -0
- package/dist-pkg/ui/settings-controls.js +152 -0
- package/dist-pkg/ui/settings-empathy.d.ts +62 -0
- package/dist-pkg/ui/settings-empathy.js +137 -0
- package/dist-pkg/ui/settings-motion.d.ts +95 -0
- package/dist-pkg/ui/settings-motion.js +282 -0
- package/dist-pkg/ui/settings-motor.d.ts +77 -0
- package/dist-pkg/ui/settings-motor.js +215 -0
- package/dist-pkg/ui/settings-panel.d.ts +76 -0
- package/dist-pkg/ui/settings-panel.js +187 -0
- package/dist-pkg/ui/settings-typo.d.ts +74 -0
- package/dist-pkg/ui/settings-typo.js +150 -0
- package/dist-pkg/ui/settings-visual.d.ts +111 -0
- package/dist-pkg/ui/settings-visual.js +252 -0
- package/dist-pkg/ui/shell.d.ts +239 -0
- package/dist-pkg/ui/shell.js +430 -0
- package/dist-pkg/ui/title.d.ts +36 -0
- package/dist-pkg/ui/title.js +60 -0
- package/dist-pkg/ui/vlibras.d.ts +11 -0
- package/dist-pkg/ui/vlibras.js +87 -0
- package/dist-pkg/ui/webcam.d.ts +7 -0
- package/dist-pkg/ui/webcam.js +88 -0
- package/docs/CREDITS.md +64 -0
- package/docs/LICENSES.md +132 -0
- package/package.json +115 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// ui/layout.ts — ESCALA do jogo (Estágio 4, Tier 1). Trava o #game-region num múltiplo inteiro de PIXELS REAIS
|
|
3
|
+
// de 320×180 (por jogador) e reescala as vars de UI escopadas ao canvas. Deps: ui/dom ($), core/screens,
|
|
4
|
+
// render/crt (crtScanVars). A contagem de jogadores entra por `initLayout`. fpsTick/configureRender seguem no main.js.
|
|
5
|
+
//
|
|
6
|
+
// JÁ NÃO RESERVA ESPAÇO PARA O INTÉRPRETE. Reservava 380px à direita quando o painel do VLibras "abria", e o
|
|
7
|
+
// jogo deslocava para a esquerda — decisão revista pelo Dev: o intérprete deve aparecer NA FRENTE da tela
|
|
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';
|
|
12
|
+
import { screenBaseSize } from '../core/screens.js';
|
|
13
|
+
import { crtScanVars } from '../render/crt.js';
|
|
14
|
+
// A CONTAGEM DE JOGADORES entra por injeção desde 2026-08-26. Era `numPlayers`, um `let` de `core/state`
|
|
15
|
+
// importado como binding vivo — e um `let` de módulo é compartilhado por qualquer segundo jogo que a
|
|
16
|
+
// mesma página carregue (D13 do `demos`, ADR-0038). O que entra aqui é o GETTER da rodada que a raiz
|
|
17
|
+
// possui; o `let` que sobra guarda a função, não o número.
|
|
18
|
+
let _numJogadores = () => 1;
|
|
19
|
+
/** Liga a contagem de jogadores. Chamado uma vez pela raiz, antes do primeiro `layout()`. */
|
|
20
|
+
export function initLayout(deps) { _numJogadores = deps.numJogadores; }
|
|
21
|
+
export function layout() {
|
|
22
|
+
const wrap = $('#stage-wrap');
|
|
23
|
+
if (!wrap)
|
|
24
|
+
return;
|
|
25
|
+
wrap.style.paddingRight = '0px';
|
|
26
|
+
const availW = wrap.clientWidth || 320;
|
|
27
|
+
const availH = wrap.clientHeight || 180;
|
|
28
|
+
// E11: a grade de telas define a base (1=320×180, 2=640×180, 3-4=640×360)
|
|
29
|
+
const n = _numJogadores();
|
|
30
|
+
// (`screenGrid(n)` SAIU em 2026-08-26: `cols`/`rows` eram desestruturados e nunca lidos — a escala sai de
|
|
31
|
+
// `screenBaseSize`, logo abaixo. Era uma chamada paga a cada `layout()` por nada. `noUnusedLocals` achou.)
|
|
32
|
+
const { w: baseW, h: baseH } = screenBaseSize(n);
|
|
33
|
+
// Piso k=2: CADA viewport tem no mínimo 640×360. Assim 2×2 = 1280×720 cabe num Chromebook do governo (1366×768).
|
|
34
|
+
const MIN_K = 2;
|
|
35
|
+
// ADR-001 (CORRIGIDO 2026-07-04): ESCALA travada em PIXELS REAIS INTEIROS. Cada pixel de arte = kDev pixels
|
|
36
|
+
// FÍSICOS (inteiro) → scanlines SEMPRE regulares e arte uniforme em QUALQUER dpr. Tolera ≤5px lógicos de corte
|
|
37
|
+
// por lado (o −10): base·kDev − avail·dpr ≤ 10·kDev ⇒ kDev ≤ avail·dpr/(base−10). (José escolheu inteiro-REAL.)
|
|
38
|
+
const dpr = window.devicePixelRatio || 1;
|
|
39
|
+
const kDev = Math.max(Math.round(MIN_K * dpr), Math.floor(Math.min(availW * dpr / (baseW - 10), availH * dpr / (baseH - 10))));
|
|
40
|
+
const k = kDev / dpr; // fator LÓGICO/CSS (kDev = fator em pixels REAIS, inteiro)
|
|
41
|
+
// ESCALA das vars de UI é ESCOPADA ao #game-region: só a UI DENTRO do canvas (menus/HUD/pausa/quiz) escala com o
|
|
42
|
+
// k. Fora do canvas (barra de topo, painel de debug) herda o :root → texto SEMPRE 16px, toque 44px (José).
|
|
43
|
+
const gr = $('#game-region');
|
|
44
|
+
if (gr) {
|
|
45
|
+
gr.style.width = (baseW * k) + 'px';
|
|
46
|
+
gr.style.height = (baseH * k) + 'px';
|
|
47
|
+
gr.style.setProperty('--hud-fs', Math.max(9, Math.round(180 * k * 0.052)) + 'px');
|
|
48
|
+
gr.style.setProperty('--ui-fs', (8 * k) + 'px'); // base LÓGICA 8px × k (16px em k=2)
|
|
49
|
+
gr.style.setProperty('--tap', (22 * k) + 'px'); // toque 22px × k (44px em k=2, piso WCAG)
|
|
50
|
+
}
|
|
51
|
+
crtScanVars(); // scanlines re-alinham quando a escala k muda
|
|
52
|
+
if (/[?&]debug=true/.test(location.search))
|
|
53
|
+
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})`);
|
|
54
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
export interface AvisoDeQuedaCtx {
|
|
3
|
+
/** `querySelector` do documento deste jogo. Injetado: a engine recebe o dela, `main.ts` passa o `$` global. */
|
|
4
|
+
procurar: (sel: string) => HTMLElement | null;
|
|
5
|
+
/** A narração falada, quando o jogo tiver uma. Ausente = o aviso escrito basta. */
|
|
6
|
+
narrar?: (texto: string) => void;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Monta o `aoFalhar` para entrar em `startLoop(ticker, quadro, maxDt, { aoFalhar })`.
|
|
10
|
+
*
|
|
11
|
+
* A ORDEM DOS CANAIS É DELIBERADA e vale escrever: o console PRIMEIRO, porque quem depura não pode perder o
|
|
12
|
+
* erro se o DOM estiver quebrado — e um DOM quebrado é um dos motivos plausíveis para o quadro ter lançado.
|
|
13
|
+
* Depois o leitor de tela, que é quem não tem outra pista nenhuma. O visível por último, porque quem o vê já
|
|
14
|
+
* viu a tela parar.
|
|
15
|
+
*
|
|
16
|
+
* Cada canal é isolado do seguinte: se a narração lançar, o aviso escrito já saiu. Um aviso que falha pela
|
|
17
|
+
* metade tem de entregar a outra metade — é a mesma regra que o `startLoop` aplica a este próprio callback.
|
|
18
|
+
*/
|
|
19
|
+
export declare function criarAvisoDeQueda(ctx: AvisoDeQuedaCtx): (erro: unknown) => void;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// ui/loop-crash.ts — O ANÚNCIO DE QUE O LAÇO PAROU. A outra metade do ADR-0054.
|
|
3
|
+
//
|
|
4
|
+
// `core/loop.startLoop` já PARA quando um quadro lança, e já chama `aoFalhar`. O que faltava era alguém que
|
|
5
|
+
// passasse um `aoFalhar` — e a `confirmation` do ADR-0054 diz isso por escrito: *"a raiz de composição ainda
|
|
6
|
+
// não liga o `aoFalhar`, então hoje o laço para em silêncio... enquanto isto não existir este registro é só
|
|
7
|
+
// metade verdadeiro."*
|
|
8
|
+
//
|
|
9
|
+
// ⚠️ E A METADE QUE FALTAVA É A QUE IMPORTA. Tela congelada é sintoma VISUAL. No modo cego, um jogo parado e
|
|
10
|
+
// um jogo pensando produzem a mesma coisa: silêncio. A criança fica a esperar por um jogo que já morreu, sem
|
|
11
|
+
// nada que lhe diga para recarregar — e o único aviso que existia era um erro no console, que ela não lê.
|
|
12
|
+
//
|
|
13
|
+
// ⚠️ POR QUE UM MÓDULO E NÃO UMA LINHA DENTRO DE `createGame`. Porque o jogo de plataforma NÃO PASSA POR
|
|
14
|
+
// `createGame` — `main.ts` monta a engine à mão, e é ele quem chama `startLoop`. Uma solução que vivesse só
|
|
15
|
+
// na raiz de composição da engine deixaria de fora exatamente o jogo que existe. As duas raízes importam
|
|
16
|
+
// daqui.
|
|
17
|
+
//
|
|
18
|
+
// ⚠️ E NÃO USA `srAlert` DE `core/a11y-sr`, o que é uma escolha e não um esquecimento:
|
|
19
|
+
//
|
|
20
|
+
// · `srAlert` limpa, espera um `requestAnimationFrame` e só então escreve. Essa dança existe para forçar o
|
|
21
|
+
// leitor a REANUNCIAR texto repetido; uma queda é anunciada uma vez e nunca se repete, então não compra
|
|
22
|
+
// nada aqui — e faz o aviso depender de um quadro futuro, no instante em que os quadros pararam.
|
|
23
|
+
// · `srAlert` busca no documento GLOBAL. Quem recebe o documento por injeção (a engine, um teste, um
|
|
24
|
+
// segundo jogo na mesma página) perderia o aviso. O anúncio de que tudo parou é o último lugar que
|
|
25
|
+
// deveria depender de uma busca global.
|
|
26
|
+
import { t } from '../core/i18n.js';
|
|
27
|
+
/**
|
|
28
|
+
* Monta o `aoFalhar` para entrar em `startLoop(ticker, quadro, maxDt, { aoFalhar })`.
|
|
29
|
+
*
|
|
30
|
+
* A ORDEM DOS CANAIS É DELIBERADA e vale escrever: o console PRIMEIRO, porque quem depura não pode perder o
|
|
31
|
+
* erro se o DOM estiver quebrado — e um DOM quebrado é um dos motivos plausíveis para o quadro ter lançado.
|
|
32
|
+
* Depois o leitor de tela, que é quem não tem outra pista nenhuma. O visível por último, porque quem o vê já
|
|
33
|
+
* viu a tela parar.
|
|
34
|
+
*
|
|
35
|
+
* Cada canal é isolado do seguinte: se a narração lançar, o aviso escrito já saiu. Um aviso que falha pela
|
|
36
|
+
* metade tem de entregar a outra metade — é a mesma regra que o `startLoop` aplica a este próprio callback.
|
|
37
|
+
*/
|
|
38
|
+
export function criarAvisoDeQueda(ctx) {
|
|
39
|
+
return (erro) => {
|
|
40
|
+
try {
|
|
41
|
+
console.error('[inclusionist] the frame threw; the loop stopped.', erro);
|
|
42
|
+
}
|
|
43
|
+
catch { /* noop */ }
|
|
44
|
+
const msg = t('sr.laco.parou');
|
|
45
|
+
// O leitor de tela: assertivo, porque interromper é justamente o ponto.
|
|
46
|
+
const alerta = ctx.procurar('#sr-alert');
|
|
47
|
+
if (alerta)
|
|
48
|
+
alerta.textContent = msg;
|
|
49
|
+
try {
|
|
50
|
+
ctx.narrar?.(msg);
|
|
51
|
+
}
|
|
52
|
+
catch { /* noop: a narração falhou; o aviso escrito já saiu */ }
|
|
53
|
+
// O VISÍVEL. O texto entra no ATRIBUTO e a folha de estilo o mostra com `content: attr(...)` — é o que
|
|
54
|
+
// mantém a frase LOCALIZADA. Um aviso cravado no CSS estaria em inglês para uma criança brasileira, que
|
|
55
|
+
// é o defeito que o pilar 3 existe para impedir.
|
|
56
|
+
const regiao = ctx.procurar('#game-region');
|
|
57
|
+
if (regiao)
|
|
58
|
+
regiao.setAttribute('data-incl-parou', msg);
|
|
59
|
+
};
|
|
60
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
import type { DomQuery } from '../core/dom-query.js';
|
|
3
|
+
/** Assinatura mínima do seletor de DOM (a mesma de `ui/dom.ts`). */
|
|
4
|
+
export type { DomQuery } from '../core/dom-query.js';
|
|
5
|
+
/** Qual painel a linha abre. `null` = ainda não existe subsistema para abrir. */
|
|
6
|
+
export type MapHubAction = 'options' | 'padwiz' | null;
|
|
7
|
+
/** Uma linha do painel. */
|
|
8
|
+
export interface MapHubRow {
|
|
9
|
+
/** Rótulo com o emoji na frente — o emoji é o que `soonMessage` corta para falar. */
|
|
10
|
+
lbl: string;
|
|
11
|
+
/** Nº de telas exigido; `undefined` = vale em qualquer modo. */
|
|
12
|
+
mode?: number;
|
|
13
|
+
/** Em construção: aparece cinza, com "Em breve" no botão. */
|
|
14
|
+
soon?: boolean;
|
|
15
|
+
/** O painel que o botão abre, por nome. */
|
|
16
|
+
act?: MapHubAction;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A TABELA — congelada. A ordem é a da tela: os quatro modos de teclado, o gamepad, e as três promessas.
|
|
20
|
+
* As três últimas são o roadmap deste jogo (webcam e fala), e é por causa delas que o módulo não é engine.
|
|
21
|
+
*/
|
|
22
|
+
export declare const MAP_HUB_ROWS: readonly MapHubRow[];
|
|
23
|
+
/** O estado visual de uma linha, para o número de telas atual. */
|
|
24
|
+
export interface MapHubRowState {
|
|
25
|
+
/** A linha exige um modo diferente do atual. */
|
|
26
|
+
off: boolean;
|
|
27
|
+
/** A linha está cinza (em construção OU modo errado) — é o que vira `row-off` + `disabled`. */
|
|
28
|
+
dis: boolean;
|
|
29
|
+
}
|
|
30
|
+
/** O estado de cada linha em `np` telas. Fonte única do markup e das duas mensagens de `srAlert`. */
|
|
31
|
+
export declare function mapHubStates(np: number, rows?: readonly MapHubRow[]): MapHubRowState[];
|
|
32
|
+
/** O HTML do painel inteiro — cabeçalho + uma `.ctrl-row` por linha. */
|
|
33
|
+
export declare function mapHubMarkup(np: number, rows?: readonly MapHubRow[]): string;
|
|
34
|
+
/** O rótulo sem o emoji da frente — "👁 Mapear olhos e boca" → "Mapear olhos e boca". */
|
|
35
|
+
export declare function soonName(lbl: string): string;
|
|
36
|
+
/** O aviso falado de uma linha em construção. */
|
|
37
|
+
export declare function soonMessage(lbl: string): string;
|
|
38
|
+
/** O aviso falado de uma linha cujo modo não é o número de telas atual. */
|
|
39
|
+
export declare function wrongModeMessage(mode: number): string;
|
|
40
|
+
export interface MapHubCtx {
|
|
41
|
+
/** Seletor de DOM injetado (`ui/dom.ts:$`) — o módulo nunca alcança `document` sozinho. */
|
|
42
|
+
$: DomQuery;
|
|
43
|
+
/** Anúncio assertivo ao leitor de tela (`core/a11y-sr.ts:srAlert`). */
|
|
44
|
+
srAlert: (msg: string) => void;
|
|
45
|
+
/** Nº de telas vivo (`core/state.ts:numPlayers`) — GETTER, porque é reatribuído. */
|
|
46
|
+
getNumPlayers: () => number;
|
|
47
|
+
/** Abre o painel de remapeamento de teclado (`openOptions` do main.js). */
|
|
48
|
+
openOptions: () => void;
|
|
49
|
+
/** Abre o assistente de gamepad (`input/gamepad.ts:openPadWiz`). */
|
|
50
|
+
openPadWiz: () => void;
|
|
51
|
+
}
|
|
52
|
+
export interface MapHubApi {
|
|
53
|
+
/** Redesenha `#map-hub` e reamarra os botões. Chamado por `openMovement`, a cada abertura do menu. */
|
|
54
|
+
render(): void;
|
|
55
|
+
}
|
|
56
|
+
export declare function initMapHub(ctx: MapHubCtx): MapHubApi;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// ui/map-hub — O PAINEL "MAPEAR CONTROLES", dentro do menu de Movimento.
|
|
3
|
+
//
|
|
4
|
+
// Este módulo é o antigo `renderMapHub()` do main.js (mais o seu ajudante `mapSoon`, que não tinha outro
|
|
5
|
+
// chamador). Ele desenha a lista de formas de comandar o jogo — teclado (uma linha por modo de 1 a 4 telas),
|
|
6
|
+
// gamepad, e três entradas ainda por construir (olhos e boca, setores de olhar, palavras faladas) — e amarra
|
|
7
|
+
// o botão de cada linha ao painel que ela abre.
|
|
8
|
+
//
|
|
9
|
+
// ======================= É DESTE JOGO, E NÃO ENGINE =======================
|
|
10
|
+
// Um segundo jogo da coleção NÃO reusaria este arquivo, e a razão não é o assunto (mapear controles é
|
|
11
|
+
// universal) — é o CONTEÚDO. Este painel enumera exatamente os subsistemas de entrada que *The Inclusionist*
|
|
12
|
+
// tem hoje e os que ele PROMETE ter: a redação "em construção — chega junto com os subsistemas de webcam e
|
|
13
|
+
// fala" é uma declaração de roadmap deste jogo, não uma capacidade do engine. Ele também depende do `#map-hub`
|
|
14
|
+
// deste `index.html`, das classes `ctrl-row`/`mode-btn`/`row-off` desta folha de estilo, e de dois painéis
|
|
15
|
+
// concretos (`openOptions`, `openPadWiz`). O que É engine já está extraído e mora noutro lugar:
|
|
16
|
+
// `input/keyboard.ts` (o esquema), `input/gamepad.ts` (o assistente de mapeamento), `core/screens.ts` (a
|
|
17
|
+
// grade). Aqui sobra a VITRINE — e vitrine é do jogo. Daí `ui/`, ao lado de `ui/settings-motor.ts`, que é o
|
|
18
|
+
// resto do mesmo menu de Movimento.
|
|
19
|
+
//
|
|
20
|
+
// ======================= DECIDIR ≠ EXECUTAR =======================
|
|
21
|
+
// Três coisas eram uma só, e as duas primeiras são puras:
|
|
22
|
+
// · `MAP_HUB_ROWS` — a TABELA. Rótulo, modo exigido (ou nenhum) e a ação nomeada (`'options'`, `'padwiz'`
|
|
23
|
+
// ou nada, para as três em construção). A ação virou um NOME e não uma função: é o que permite a tabela
|
|
24
|
+
// ser um dado congelado, testável, e o `init` decidir qual painel cada nome abre.
|
|
25
|
+
// · `mapHubStates(np)` / `mapHubMarkup(np)` — o estado de cada linha (desabilitada por estar em construção,
|
|
26
|
+
// ou por o modo dela não ser o número de telas atual) e o HTML que sai disso. A regra que importa e que
|
|
27
|
+
// ninguém via no meio da interpolação: "⌨ Mapear teclado para modo 3 jogadores" só habilita quando o jogo
|
|
28
|
+
// ESTÁ em 3 telas — trocar o mapa de um modo em que você não está mostraria teclas que não valem.
|
|
29
|
+
// · `initMapHub(ctx).render()` — a metade impura: `innerHTML`, `querySelectorAll`, `addEventListener`.
|
|
30
|
+
//
|
|
31
|
+
// ======================= O AVISO É A PARTE DE ACESSIBILIDADE =======================
|
|
32
|
+
// Um botão `disabled` é invisível para quem navega por teclado com leitor de tela: o foco simplesmente pula.
|
|
33
|
+
// Por isso as duas mensagens de `srAlert` existem, e por isso elas são exportadas como funções puras
|
|
34
|
+
// (`soonMessage`, `wrongModeMessage`): quem clica na linha cinza — por toque, ou porque o leitor lê a linha
|
|
35
|
+
// inteira e não só o botão — recebe o MOTIVO em palavras, não silêncio.
|
|
36
|
+
// Já foi assim: os botões nasciam com o atributo `disabled`, o `click` deles não disparava, e as duas
|
|
37
|
+
// mensagens eram inalcançáveis — medido no navegador: das oito linhas, seis ficavam fora da ordem de foco e
|
|
38
|
+
// clicar nelas não dizia nada. Agora é `aria-disabled`, que anuncia o estado sem esconder o botão.
|
|
39
|
+
//
|
|
40
|
+
// ======================= O QUE FICOU DE FORA, E POR QUÊ =======================
|
|
41
|
+
// · Abrir/fechar o overlay `#movement` (`openMovement`/`closeMovement`, `frontOverlay`, o foco no primeiro
|
|
42
|
+
// botão) FICA no main.js: é a casca compartilhada por TODOS os painéis de configuração, não deste.
|
|
43
|
+
// · `openOptions` e `gamepadApi.openPadWiz()` entram por injeção. O primeiro é do main.js (edita o controle
|
|
44
|
+
// do jogador que abriu a pausa), o segundo é de `input/gamepad.ts`.
|
|
45
|
+
// · O número de telas entra por GETTER (`getNumPlayers`) porque `numPlayers` é reatribuído por
|
|
46
|
+
// `setNumPlayers` a cada troca — e é justamente ele que decide quais linhas ficam cinzas.
|
|
47
|
+
//
|
|
48
|
+
// ======================= ARMADILHAS DE ORDEM DE BOOT =======================
|
|
49
|
+
// · Zero I/O no import: nada toca o DOM no corpo do módulo. `initMapHub` também não — ele só devolve
|
|
50
|
+
// `render()`, e é `render()` que procura `#map-hub`. Se o elemento não existir, `render()` desiste em
|
|
51
|
+
// silêncio (guard clause), como o original.
|
|
52
|
+
// · `render()` é chamado de dentro de `openMovement`, que é uma DECLARAÇÃO DE FUNÇÃO içada e aparece ANTES
|
|
53
|
+
// deste ponto no main.js. Não há TDZ: `openMovement` só roda a partir de um clique, muito depois do
|
|
54
|
+
// módulo ter avaliado. Ainda assim, a religação deve manter um invólucro `function renderMapHub()` no
|
|
55
|
+
// main.js — declaração içada, como manda a casa — para que a leitura de `openMovement` continue honesta.
|
|
56
|
+
// · O `np` é lido UMA vez por `render()` e fica preso no manipulador de clique daquela renderização. Isso é
|
|
57
|
+
// verbatim e é seguro hoje porque `openMovement` re-renderiza a cada abertura; se um dia o painel puder
|
|
58
|
+
// ficar aberto enquanto o nº de telas muda, esta é a linha a mexer.
|
|
59
|
+
/**
|
|
60
|
+
* A TABELA — congelada. A ordem é a da tela: os quatro modos de teclado, o gamepad, e as três promessas.
|
|
61
|
+
* As três últimas são o roadmap deste jogo (webcam e fala), e é por causa delas que o módulo não é engine.
|
|
62
|
+
*/
|
|
63
|
+
export const MAP_HUB_ROWS = Object.freeze([
|
|
64
|
+
{ lbl: '⌨ Mapear teclado para modo 1 jogador', mode: 1, act: 'options' },
|
|
65
|
+
{ lbl: '⌨ Mapear teclado para modo 2 jogadores', mode: 2, act: 'options' },
|
|
66
|
+
{ lbl: '⌨ Mapear teclado para modo 3 jogadores', mode: 3, act: 'options' },
|
|
67
|
+
{ lbl: '⌨ Mapear teclado para modo 4 jogadores', mode: 4, act: 'options' },
|
|
68
|
+
{ lbl: '🎮 Mapear gamepad', act: 'padwiz' }, // L1: assistente (DirectInput e afins) — mapa salvo por modelo
|
|
69
|
+
{ lbl: '👁 Mapear olhos e boca', soon: true },
|
|
70
|
+
{ lbl: '🎯 Mapear setores de olhar', soon: true },
|
|
71
|
+
{ lbl: '🎤 Mapear palavras (fala)', soon: true },
|
|
72
|
+
].map((r) => Object.freeze(r)));
|
|
73
|
+
/** O estado de cada linha em `np` telas. Fonte única do markup e das duas mensagens de `srAlert`. */
|
|
74
|
+
export function mapHubStates(np, rows = MAP_HUB_ROWS) {
|
|
75
|
+
return rows.map((r) => {
|
|
76
|
+
const off = !!r.mode && r.mode !== np; // teclado: só o modo em que o jogo ESTÁ fica clicável
|
|
77
|
+
return { off, dis: !!r.soon || off };
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
/** O HTML do painel inteiro — cabeçalho + uma `.ctrl-row` por linha. */
|
|
81
|
+
export function mapHubMarkup(np, rows = MAP_HUB_ROWS) {
|
|
82
|
+
const st = mapHubStates(np, rows);
|
|
83
|
+
return '<h3 class="panel-sub">Mapear controles <span class="panel-sub__tag">por jogador</span></h3>' +
|
|
84
|
+
rows.map((it, i) => {
|
|
85
|
+
const dis = st[i].dis;
|
|
86
|
+
const note = it.soon ? ' <em style="opacity:.7">(em construção)</em>' : '';
|
|
87
|
+
// `aria-disabled` e NAO `disabled`: o atributo real tira o botao da ordem de foco e engole o clique,
|
|
88
|
+
// entao quem navega por teclado pulava seis das oito linhas e nunca ficava sabendo por que elas nao
|
|
89
|
+
// funcionam — enquanto quem enxerga ve a linha cinza e o "(em construcao)". Com `aria-disabled` o
|
|
90
|
+
// estado e anunciado, o botao continua alcancavel, e as duas frases de srAlert que ja existiam aqui
|
|
91
|
+
// finalmente chegam a alguem. O visual nao muda: quem pinta a linha cinza e a classe `row-off`.
|
|
92
|
+
return `<div class="ctrl-row${dis ? ' row-off' : ''}"><span>${it.lbl}${note}</span>` +
|
|
93
|
+
`<button class="mode-btn" type="button" data-map="${i}"${dis ? ' aria-disabled="true"' : ''}>${it.soon ? 'Em breve' : 'Abrir'}</button></div>`;
|
|
94
|
+
}).join('');
|
|
95
|
+
}
|
|
96
|
+
/** O rótulo sem o emoji da frente — "👁 Mapear olhos e boca" → "Mapear olhos e boca". */
|
|
97
|
+
export function soonName(lbl) {
|
|
98
|
+
return lbl.replace(/^\S+\s/, '');
|
|
99
|
+
}
|
|
100
|
+
/** O aviso falado de uma linha em construção. */
|
|
101
|
+
export function soonMessage(lbl) {
|
|
102
|
+
return soonName(lbl) + ': em construção — chega junto com os subsistemas de webcam e fala.';
|
|
103
|
+
}
|
|
104
|
+
/** O aviso falado de uma linha cujo modo não é o número de telas atual. */
|
|
105
|
+
export function wrongModeMessage(mode) {
|
|
106
|
+
return 'Disponível só no modo ' + mode + ' jogador' + (mode > 1 ? 'es' : '') + '. Troque o nº de telas na barra do topo.';
|
|
107
|
+
}
|
|
108
|
+
export function initMapHub(ctx) {
|
|
109
|
+
/** Nome da ação → o painel de verdade. Fora da tabela, para a tabela poder ser dado congelado. */
|
|
110
|
+
function run(act) {
|
|
111
|
+
if (act === 'options')
|
|
112
|
+
ctx.openOptions();
|
|
113
|
+
else if (act === 'padwiz')
|
|
114
|
+
ctx.openPadWiz();
|
|
115
|
+
}
|
|
116
|
+
function render() {
|
|
117
|
+
const el = ctx.$('#map-hub');
|
|
118
|
+
if (!el)
|
|
119
|
+
return; // o painel não está neste documento — nada a fazer
|
|
120
|
+
const np = ctx.getNumPlayers(); // lido UMA vez; preso no manipulador desta renderização (ver o cabeçalho)
|
|
121
|
+
el.innerHTML = mapHubMarkup(np);
|
|
122
|
+
el.querySelectorAll('button[data-map]').forEach((b) => b.addEventListener('click', () => {
|
|
123
|
+
const it = MAP_HUB_ROWS[Number(b.dataset['map'])];
|
|
124
|
+
if (!it)
|
|
125
|
+
return;
|
|
126
|
+
if (it.soon) {
|
|
127
|
+
ctx.srAlert(soonMessage(it.lbl));
|
|
128
|
+
return;
|
|
129
|
+
} // em construção: diz o porquê
|
|
130
|
+
if (it.mode && it.mode !== np) {
|
|
131
|
+
ctx.srAlert(wrongModeMessage(it.mode));
|
|
132
|
+
return;
|
|
133
|
+
} // modo errado: idem
|
|
134
|
+
run(it.act ?? null);
|
|
135
|
+
}));
|
|
136
|
+
}
|
|
137
|
+
return { render };
|
|
138
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
/** ui/dom.ts `$` — injetado; o módulo nunca alcança `document`. */
|
|
3
|
+
export type { DomQuery } from '../core/dom-query.js';
|
|
4
|
+
/** A intenção, não a tecla. Definição única em input/edges; aqui só reexportada. */
|
|
5
|
+
import type { NavKeys } from '../input/edges.js';
|
|
6
|
+
export type { NavKeys } from '../input/edges.js';
|
|
7
|
+
/** O que este módulo lê de um `KeyboardEvent`. */
|
|
8
|
+
export interface NavKeyEvent {
|
|
9
|
+
code: string;
|
|
10
|
+
preventDefault(): void;
|
|
11
|
+
stopPropagation(): void;
|
|
12
|
+
}
|
|
13
|
+
/** Confirma/entra. `NumpadEnter` conta aqui (mas NÃO pausa: ver o ouvinte de bolha do game.js). */
|
|
14
|
+
export declare const KEY_YES: ReadonlySet<string>;
|
|
15
|
+
/** Volta. ⚠️ DEFEITO 2: `Escape` é a MESMA intenção que a ação "especial" do gamepad. Verbatim. */
|
|
16
|
+
export declare const KEY_NO: ReadonlySet<string>;
|
|
17
|
+
export declare const KEY_UP: ReadonlySet<string>;
|
|
18
|
+
export declare const KEY_DOWN: ReadonlySet<string>;
|
|
19
|
+
export declare const KEY_LEFT: ReadonlySet<string>;
|
|
20
|
+
export declare const KEY_RIGHT: ReadonlySet<string>;
|
|
21
|
+
/**
|
|
22
|
+
* Traduz (tecla física, ação remapeada do dono da tecla) → intenção. `act` é `null` quando a tecla não é de
|
|
23
|
+
* nenhum jogador (tecla genérica). Verbatim das seis linhas de `menuNavKey`.
|
|
24
|
+
*/
|
|
25
|
+
export declare function menuKeyIntent(code: string, act: string | null): NavKeys;
|
|
26
|
+
import type { EventTargetLike } from '../input/touch-bindings.js';
|
|
27
|
+
import type { DomQuery } from '../core/dom-query.js';
|
|
28
|
+
export { hasNavIntent as hasIntent } from '../input/edges.js';
|
|
29
|
+
export { passoNoAnel } from '../core/anel.js';
|
|
30
|
+
/** `select` com esquerda/direita: um passo, SEM dar a volta — ajustar VALOR não é navegar lista (ver acima). */
|
|
31
|
+
export declare function selectStep(selectedIndex: number, optionsLen: number, delta: number): number;
|
|
32
|
+
/** `select` com "sim": um passo, COM volta. É a diferença deliberada entre confirmar e ajustar. */
|
|
33
|
+
export declare function selectWrap(selectedIndex: number, optionsLen: number): number;
|
|
34
|
+
/** `input[type=range]` com esquerda/direita: um `step` (default 1), preso entre `min` e `max`. */
|
|
35
|
+
export declare function rangeStep(value: number, min: number, max: number, step: number, delta: number): number;
|
|
36
|
+
/**
|
|
37
|
+
* O PASSO DO CURSOR NO MENU DE PAUSA — um ANEL, agora que a pausa é uma lista (ADR-0044, itens 1 e 7).
|
|
38
|
+
*
|
|
39
|
+
* ISTO SUBSTITUI `pauseGridMove`, e a substituição é o desfecho do ADR-0044, não uma limpeza. O que havia
|
|
40
|
+
* era uma GRADE de duas zonas — a barra de dez ícones em cima, a lista de itens em duas colunas embaixo — e
|
|
41
|
+
* uma fronteira entre elas com quatro regras próprias ("de cima, 'baixo' cai sempre no primeiro item", "da
|
|
42
|
+
* primeira linha, 'cima' sobe para o ícone de mesmo índice", …). Cada regra dessas era uma coisa a mais para
|
|
43
|
+
* a criança descobrir sem ver, e nenhuma delas era descobrível: só se aprendia esbarrando.
|
|
44
|
+
*
|
|
45
|
+
* A XAG 106 permite laço para menu LINEAR e o PROÍBE para grade de duas dimensões — numa grade, dar a volta
|
|
46
|
+
* teleporta o cursor para o outro canto e a pessoa perde a noção de onde está. Era por isso que o anel do
|
|
47
|
+
* item 1 valia para todo menu do jogo MENOS este. Com a barra no HUD (item 7), o cartão passou a ter uma
|
|
48
|
+
* lista só, e o laço deixou de ser proibido para virar o recomendado.
|
|
49
|
+
*
|
|
50
|
+
* O que se ganha em troca das quatro regras: `quit` fica a UMA tecla para CIMA de `resume`. Último na
|
|
51
|
+
* leitura, vizinho no dedo.
|
|
52
|
+
*/
|
|
53
|
+
export declare function passoNaPausa(len: number, idx: number, k: NavKeys): number;
|
|
54
|
+
export interface MenuNavCtx {
|
|
55
|
+
/** ui/dom.ts `$` — só para achar `#padwiz`. */
|
|
56
|
+
$: DomQuery;
|
|
57
|
+
/** `document.activeElement` — injetado para o teste node poder simular foco sem DOM. */
|
|
58
|
+
getActiveElement: () => Element | null;
|
|
59
|
+
/** O overlay VISÍVEL de z-index mais alto dentro de `#game-region`. É o `sharedDialogOpen` original. */
|
|
60
|
+
topVisibleOverlay: () => HTMLElement | null;
|
|
61
|
+
/** `overlays.closeById(id)` — true se havia entrada registrada para aquele diálogo. */
|
|
62
|
+
closeById: (id: string) => boolean;
|
|
63
|
+
/** `vpPause[i]` — o menu de pausa da tela do jogador `i`. `buildGameHud` REATRIBUI a array. */
|
|
64
|
+
getPauseMenu: (playerIndex: number) => HTMLElement | null | undefined;
|
|
65
|
+
/** ui/shell.ts via o envelope içado do game.js. `no` na raiz do menu de pausa volta ao jogo. */
|
|
66
|
+
setPhase: (p: 'title' | 'playing' | 'paused') => void;
|
|
67
|
+
/** `let pauseActor` do game.js: "sim" no menu de pausa marca QUEM agiu (o submenu abre na aba dele).
|
|
68
|
+
* ui/pause-icons.ts já recebe o mesmo setter — o `let` continua sendo do game.js, com seis leitores. */
|
|
69
|
+
setPauseActor: (playerIndex: number) => void;
|
|
70
|
+
/**
|
|
71
|
+
* ESTE É O MOMENTO DE NAVEGAR MENU? Se `false`, toda tecla passa direto.
|
|
72
|
+
*
|
|
73
|
+
* Era `if (phase !== 'paused') return`, com `phase` vindo por IMPORTAÇÃO de `core/state` — e o segundo
|
|
74
|
+
* consumidor mostrou o preço (achado 10 de `consumer-quiz`): um quiz cujos ajustes estão SEMPRE disponíveis
|
|
75
|
+
* precisava se declarar "pausado" para poder navegar os próprios menus. Não havia como trazer o próprio
|
|
76
|
+
* modelo de fases, porque não havia por onde.
|
|
77
|
+
*
|
|
78
|
+
* A pergunta injetada é PROPOSITALMENTE um booleano, e não a fase. Injetar `getPhase()` teria consertado a
|
|
79
|
+
* importação e mantido a mentira: o consumidor continuaria obrigado a devolver a string `'paused'`, que é
|
|
80
|
+
* um conceito do jogo de plataforma. Perguntando "dá para navegar agora?", o jogo de plataforma responde
|
|
81
|
+
* `phase === 'paused'` e um quiz responde `true` — cada um na sua língua, e nenhum dos dois mentindo.
|
|
82
|
+
*/
|
|
83
|
+
isNavigable: () => boolean;
|
|
84
|
+
/**
|
|
85
|
+
* `core/a11y-sr.srSay` — a região `aria-live` "polite". INJETADO porque ela alcança o `document` na hora da
|
|
86
|
+
* chamada, e este módulo não pode alcançar documento nenhum por conta própria.
|
|
87
|
+
*
|
|
88
|
+
* O menu de PAUSA precisa dela e os diálogos não: eles selecionam por FOCO, e o leitor de tela anuncia o
|
|
89
|
+
* foco sozinho. A pausa seleciona por CLASSE (`.pm-sel`), porque é desenhada dentro da tela do jogador —
|
|
90
|
+
* e classe nenhuma dispara anúncio. Sem esta injeção o menu é mudo para quem o navega por escuta.
|
|
91
|
+
*/
|
|
92
|
+
srSay: (texto: string) => void;
|
|
93
|
+
/**
|
|
94
|
+
* O índice "N de M" está ligado? (ADR-0044, item 3, e a XAG 106 exige que ele possa ser desligado.)
|
|
95
|
+
*
|
|
96
|
+
* PERGUNTADO ao ctx e não lido de `core/state`, e o motivo é o mesmo do `isNavigable`: enquanto este módulo
|
|
97
|
+
* importava `core/state`, um segundo jogo não tinha como trazer o próprio modelo — o quiz teve de se
|
|
98
|
+
* declarar "pausado" para navegar os menus dele. `tests/menu-nav.node.test.js` reprova se a aresta voltar,
|
|
99
|
+
* e ela quase voltou por aqui: eu tinha escrito o `import` antes de o gate me lembrar.
|
|
100
|
+
*/
|
|
101
|
+
comIndice: () => boolean;
|
|
102
|
+
/**
|
|
103
|
+
* A tela `i` está no modo `accessibility` (ADR-0044, item 7)? Perguntado ANTES de `isNavigable`, porque
|
|
104
|
+
* esse modo roda com o jogo ANDANDO — é a única coisa deste módulo que age fora da pausa.
|
|
105
|
+
*/
|
|
106
|
+
naBarraDe: (i: number) => boolean;
|
|
107
|
+
/** Um passo dentro da barra rápida da tela `i`. */
|
|
108
|
+
navBar: (i: number, k: NavKeys) => void;
|
|
109
|
+
/** ui/settings-controls.ts: um remap em andamento consome a tecla — o menu não pode roubá-la. */
|
|
110
|
+
isCapturing: () => boolean;
|
|
111
|
+
/** input/gamepad.ts: o assistente de mapeamento fica POR CIMA de tudo; só Escape (cancela) o alcança. */
|
|
112
|
+
closePadWiz: (save: boolean) => void;
|
|
113
|
+
/** input/keyboard-runtime.ts: de quem é esta tecla? (-1 = genérica). */
|
|
114
|
+
whichPlayer: (code: string) => number;
|
|
115
|
+
/** input/keyboard-runtime.ts: que ação esta tecla é PARA aquele jogador (respeitando o remap)? */
|
|
116
|
+
actionOf: (code: string, playerIndex: number) => string | null;
|
|
117
|
+
/** `window` — só para `attach()` instalar o ouvinte em CAPTURA, exatamente como o game.js fazia. */
|
|
118
|
+
/** A escuta de teclado dos menus. Ver `EventTargetLike` em input/touch-bindings. */
|
|
119
|
+
win: EventTargetLike;
|
|
120
|
+
}
|
|
121
|
+
export interface MenuNavApi {
|
|
122
|
+
/** O overlay visível mais alto (alias de `topVisibleOverlay` — ver "a duplicação que esta extração curou"). */
|
|
123
|
+
sharedDialogOpen: () => HTMLElement | null;
|
|
124
|
+
/** Os controles NAVEGÁVEIS de um menu: habilitados E visíveis (`offsetParent !== null`). */
|
|
125
|
+
menuItems: (menu: HTMLElement) => HTMLElement[];
|
|
126
|
+
/** Foca o item atual (se já for um deles) ou o primeiro. Sai calado com `menu` nulo — ver o DEFEITO 1. */
|
|
127
|
+
menuFocus: (menu: HTMLElement | null) => void;
|
|
128
|
+
/** Fecha o diálogo e tenta devolver o foco ao que ficou por baixo. */
|
|
129
|
+
dialogBack: (menu: HTMLElement) => void;
|
|
130
|
+
/** Um passo de navegação DENTRO de um diálogo de configuração. */
|
|
131
|
+
navDialog: (menu: HTMLElement, k: NavKeys) => void;
|
|
132
|
+
/** Marca `el` como o item selecionado de `menu` e atualiza a legenda dos ícones. */
|
|
133
|
+
pauseSetSel: (menu: HTMLElement, el: HTMLElement | null | undefined) => void;
|
|
134
|
+
/** Um passo de navegação DENTRO de um menu de pausa (barra de ícones + grade de itens). */
|
|
135
|
+
navPause: (menu: HTMLElement, playerIndex: number, k: NavKeys) => void;
|
|
136
|
+
/** O tradutor de teclado. Exportado à parte para o teste disparar sem depender da fase de propagação. */
|
|
137
|
+
menuNavKey: (e: NavKeyEvent) => void;
|
|
138
|
+
/** Instala `menuNavKey` em CAPTURA na janela — verbatim do game.js. */
|
|
139
|
+
attach: () => void;
|
|
140
|
+
}
|
|
141
|
+
export declare function initMenuNav(ctx: MenuNavCtx): MenuNavApi;
|