@the-inclusionist/engine 7.0.1 → 8.0.0-rc.2

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.
Files changed (136) hide show
  1. package/README.md +5 -3
  2. package/app/css/style.css +22 -5
  3. package/app/public/vendor/fonts/fondamento-400-ext.woff2 +0 -0
  4. package/app/public/vendor/fonts/fondamento-400.woff2 +0 -0
  5. package/app/public/vendor/fonts/opendyslexic-400.woff2 +0 -0
  6. package/app/public/vendor/fonts/playwrite-ar.woff2 +0 -0
  7. package/app/public/vendor/fonts/playwrite-br.woff2 +0 -0
  8. package/app/public/vendor/fonts/playwrite-ca.woff2 +0 -0
  9. package/app/public/vendor/fonts/playwrite-cl.woff2 +0 -0
  10. package/app/public/vendor/fonts/playwrite-co.woff2 +0 -0
  11. package/app/public/vendor/fonts/playwrite-mx.woff2 +0 -0
  12. package/app/public/vendor/fonts/playwrite-us-modern.woff2 +0 -0
  13. package/app/public/vendor/fonts/playwrite-us-trad.woff2 +0 -0
  14. package/app/public/vendor/fonts/pressstart-400.woff2 +0 -0
  15. package/app/public/vendor/fonts.css +64 -2
  16. package/dist-pkg/boot/create-game.d.ts +107 -4
  17. package/dist-pkg/boot/create-game.js +337 -18
  18. package/dist-pkg/core/constants.d.ts +0 -28
  19. package/dist-pkg/core/constants.js +34 -17
  20. package/dist-pkg/core/contract.d.ts +99 -0
  21. package/dist-pkg/core/contract.js +78 -0
  22. package/dist-pkg/core/entity.d.ts +47 -4
  23. package/dist-pkg/core/layers.d.ts +18 -0
  24. package/dist-pkg/core/layers.js +18 -0
  25. package/dist-pkg/core/rng.js +14 -4
  26. package/dist-pkg/core/route.d.ts +42 -0
  27. package/dist-pkg/core/route.js +158 -0
  28. package/dist-pkg/core/state.d.ts +10 -1
  29. package/dist-pkg/core/state.js +13 -0
  30. package/dist-pkg/educational/adaptive-engine.d.ts +65 -0
  31. package/dist-pkg/educational/adaptive-engine.js +117 -0
  32. package/dist-pkg/educational/segment-bar.d.ts +96 -0
  33. package/dist-pkg/educational/segment-bar.js +89 -0
  34. package/dist-pkg/i18n/en.js +44 -0
  35. package/dist-pkg/i18n/es.js +44 -0
  36. package/dist-pkg/i18n/pt.js +60 -0
  37. package/dist-pkg/input/default-bindings.d.ts +40 -0
  38. package/dist-pkg/input/default-bindings.js +151 -9
  39. package/dist-pkg/input/gamepad.d.ts +33 -12
  40. package/dist-pkg/input/gamepad.js +103 -16
  41. package/dist-pkg/input/keyboard-runtime.d.ts +6 -8
  42. package/dist-pkg/input/keyboard-runtime.js +24 -5
  43. package/dist-pkg/input/keyboard.d.ts +42 -0
  44. package/dist-pkg/input/keyboard.js +87 -17
  45. package/dist-pkg/input/keydown.d.ts +40 -2
  46. package/dist-pkg/input/keydown.js +29 -6
  47. package/dist-pkg/input/latch-edge.d.ts +28 -0
  48. package/dist-pkg/input/latch-edge.js +45 -0
  49. package/dist-pkg/input/latch-scope.d.ts +70 -0
  50. package/dist-pkg/input/latch-scope.js +110 -0
  51. package/dist-pkg/input/latch-store.d.ts +45 -0
  52. package/dist-pkg/input/latch-store.js +74 -0
  53. package/dist-pkg/input/latch-sync.d.ts +47 -0
  54. package/dist-pkg/input/latch-sync.js +43 -0
  55. package/dist-pkg/input/origem-sintetica.d.ts +44 -0
  56. package/dist-pkg/input/origem-sintetica.js +60 -0
  57. package/dist-pkg/input/pad-defaults.d.ts +22 -0
  58. package/dist-pkg/input/pad-defaults.js +34 -0
  59. package/dist-pkg/input/pointer.d.ts +61 -0
  60. package/dist-pkg/input/pointer.js +71 -0
  61. package/dist-pkg/input/state.d.ts +86 -1
  62. package/dist-pkg/input/state.js +128 -1
  63. package/dist-pkg/input/touch-bindings.d.ts +21 -2
  64. package/dist-pkg/input/touch-bindings.js +15 -3
  65. package/dist-pkg/input/touch.js +9 -1
  66. package/dist-pkg/input/transporte-em-uso.d.ts +101 -0
  67. package/dist-pkg/input/transporte-em-uso.js +130 -0
  68. package/dist-pkg/input/transports.d.ts +88 -2
  69. package/dist-pkg/input/transports.js +60 -5
  70. package/dist-pkg/input/vocabulary-migration.d.ts +18 -7
  71. package/dist-pkg/platform/audio-earcons.d.ts +29 -2
  72. package/dist-pkg/platform/audio-earcons.js +10 -0
  73. package/dist-pkg/platform/audio-nav.d.ts +1 -1
  74. package/dist-pkg/platform/audio-sonar.d.ts +149 -9
  75. package/dist-pkg/platform/audio-sonar.js +232 -21
  76. package/dist-pkg/platform/guide-intensity.d.ts +40 -0
  77. package/dist-pkg/platform/guide-intensity.js +73 -0
  78. package/dist-pkg/platform/pesados-catalogo.d.ts +14 -0
  79. package/dist-pkg/platform/pesados-catalogo.js +177 -0
  80. package/dist-pkg/platform/pesados.d.ts +31 -0
  81. package/dist-pkg/platform/pesados.js +75 -0
  82. package/dist-pkg/platform/storage.d.ts +30 -0
  83. package/dist-pkg/platform/storage.js +35 -0
  84. package/dist-pkg/platform/tts.js +16 -1
  85. package/dist-pkg/platform/voice-plan.d.ts +109 -0
  86. package/dist-pkg/platform/voice-plan.js +156 -0
  87. package/dist-pkg/platform/vozes-prontas.d.ts +28 -0
  88. package/dist-pkg/platform/vozes-prontas.js +61 -0
  89. package/dist-pkg/render/draw.d.ts +1 -1
  90. package/dist-pkg/render/draw.js +15 -5
  91. package/dist-pkg/render/viz-axes.d.ts +17 -0
  92. package/dist-pkg/render/viz-axes.js +19 -0
  93. package/dist-pkg/render/viz-setters.d.ts +34 -2
  94. package/dist-pkg/render/viz-setters.js +189 -33
  95. package/dist-pkg/render/wheelchair-sprites.d.ts +11 -3
  96. package/dist-pkg/render/wheelchair-sprites.js +10 -4
  97. package/dist-pkg/ui/dom.js +20 -2
  98. package/dist-pkg/ui/fonts.d.ts +71 -1
  99. package/dist-pkg/ui/fonts.js +111 -8
  100. package/dist-pkg/ui/latch-refusal.d.ts +32 -0
  101. package/dist-pkg/ui/latch-refusal.js +60 -0
  102. package/dist-pkg/ui/layout.d.ts +32 -0
  103. package/dist-pkg/ui/layout.js +64 -1
  104. package/dist-pkg/ui/motion-scene.d.ts +41 -0
  105. package/dist-pkg/ui/motion-scene.js +76 -0
  106. package/dist-pkg/ui/panel-shell.d.ts +53 -0
  107. package/dist-pkg/ui/panel-shell.js +103 -0
  108. package/dist-pkg/ui/pause-icons.d.ts +168 -20
  109. package/dist-pkg/ui/pause-icons.js +315 -45
  110. package/dist-pkg/ui/reach-notice.js +8 -0
  111. package/dist-pkg/ui/settings-audio.d.ts +14 -1
  112. package/dist-pkg/ui/settings-audio.js +43 -3
  113. package/dist-pkg/ui/settings-controls.d.ts +66 -6
  114. package/dist-pkg/ui/settings-controls.js +179 -17
  115. package/dist-pkg/ui/settings-empathy.d.ts +15 -0
  116. package/dist-pkg/ui/settings-empathy.js +2 -0
  117. package/dist-pkg/ui/settings-motion.d.ts +25 -19
  118. package/dist-pkg/ui/settings-motion.js +32 -19
  119. package/dist-pkg/ui/settings-motor.d.ts +101 -3
  120. package/dist-pkg/ui/settings-motor.js +136 -9
  121. package/dist-pkg/ui/settings-typo.d.ts +49 -5
  122. package/dist-pkg/ui/settings-typo.js +74 -23
  123. package/dist-pkg/ui/settings-visual.d.ts +21 -1
  124. package/dist-pkg/ui/settings-visual.js +48 -4
  125. package/dist-pkg/ui/shell.d.ts +13 -3
  126. package/dist-pkg/ui/shell.js +3 -4
  127. package/dist-pkg/ui/simulation-refusal.d.ts +32 -0
  128. package/dist-pkg/ui/simulation-refusal.js +57 -0
  129. package/dist-pkg/ui/visual-axes-panel.d.ts +48 -0
  130. package/dist-pkg/ui/visual-axes-panel.js +95 -0
  131. package/dist-pkg/ui/webcam.js +6 -1
  132. package/docs/CREDITS.md +22 -1
  133. package/docs/LICENSES.md +170 -132
  134. package/package.json +25 -4
  135. package/app/public/vendor/fonts/greatvibes-400.woff2 +0 -0
  136. package/app/public/vendor/fonts/ufcook-700.woff2 +0 -0
@@ -1,6 +1,7 @@
1
1
  // SPDX-License-Identifier: AGPL-3.0-or-later
2
2
  import type { PlayerView } from '../core/entity.js';
3
3
  import type { NavKeys } from '../input/edges.js';
4
+ import { type Tema, type Correcao, type VisualState } from '../render/viz-axes.js';
4
5
  import type { MotionSceneFlags, MotionSceneKey, MotionCharDef } from './settings-motion.js';
5
6
  import type { AudioCatState } from './settings-audio.js';
6
7
  /**
@@ -32,6 +33,11 @@ export declare const PAUSE_ICONS: readonly PauseIcon[];
32
33
  export declare function pauseIcon(k: string): PauseIcon | undefined;
33
34
  /** TEA cycle: 0 = normal · 1 = calmo (reduces) · 2 = silencioso (switches off). Never touches TTS/blind mode. */
34
35
  export declare const CALM_NAMES: readonly string[];
36
+ /**
37
+ * O nível TEA guardado, saneado. Fora de 0..2 devolve o padrão — dado do navegador é dado de fora, e um
38
+ * nível inventado escolheria `CALM_NAMES[3]`, que é `undefined`, e o anúncio ao leitor de tela sairia vazio.
39
+ */
40
+ export declare function saneiaNivelTea(bruto: number): number;
35
41
  /** The audio categories `applyCalm` governs. TTS/sonar/guarda/guia stay untouched — a calm player still needs them. */
36
42
  export declare const CALM_AUDIO_CATS: readonly string[];
37
43
  /** Colour-vision-deficiency cycle, in `player.viz` values. */
@@ -62,7 +68,7 @@ export declare const CVD_LABELS: Readonly<Record<string, string>>;
62
68
  * tipar `core/state.players` como `Player[]`: o compilador recusou converter um `Player` — que não tem
63
69
  * assinatura de índice — para ela, e essa recusa é a informação.
64
70
  */
65
- export type PausePlayer = PlayerView<'viz' | 'toggleMove' | 'audioSink' | 'rmWalk' | 'rmBreath' | 'rmFlavor'>;
71
+ export type PausePlayer = PlayerView<'visual' | 'toggleMove' | 'walkDir' | 'audioSink' | 'rmWalk' | 'rmBreath' | 'rmFlavor'>;
66
72
  /** One `.pm-btn` descriptor — the shape of game.js's PM_BTNS (owned by ui/activities-menu). */
67
73
  export interface PauseMenuButton {
68
74
  act: string;
@@ -81,10 +87,32 @@ export interface IconStateSnapshot {
81
87
  librasOn: boolean;
82
88
  calmMode: number;
83
89
  toggleMove: boolean;
84
- /** `player.viz` — shared by the contrast and CVD icons (they overwrite each other; that is by design). */
85
- viz: string;
90
+ /**
91
+ * O estado visual em DOIS EIXOS (#104).
92
+ *
93
+ * ⚠️ ERA `viz: string`, E O COMENTÁRIO DIZIA: «shared by the contrast and CVD icons (they overwrite each
94
+ * other; **that is by design**)». Não era desenho — era o campo único a impor-se, e a frase é o defeito
95
+ * escrito como se fosse decisão. Os dois ícones sempre ciclaram DENTRO do seu eixo (`nextContrast` e
96
+ * `nextCvd` existem desde sempre, separados); só não tinham onde guardar o resultado sem apagar o vizinho.
97
+ */
98
+ visual: VisualState;
86
99
  /** False disables the blind/TTS icons: those need an audio output nobody else is listening to. */
87
100
  privateOutput: boolean;
101
+ /**
102
+ * O aparelho em uso EXIGE a alternância? (ADR-0113 cláusula 3.)
103
+ *
104
+ * ⚠️ Em olhos, rosto, gestos e fala ela é o que faz a entrada funcionar, logo não há escolha a oferecer.
105
+ * O ícone fica desabilitado COM MOTIVO, como o painel — e não pode divergir dele: as duas superfícies
106
+ * escrevem o mesmo valor, e uma que aceitasse o clique enquanto a outra recusa deixaria a criança com
107
+ * dois botões que discordam sobre o mesmo ajuste.
108
+ *
109
+ * 🔴 OPCIONAL PORQUE O GATE DA FORMA ME APANHOU: eu escrevi-o obrigatório, e o
110
+ * `tests/superficie-publica` reprovou com «ENTROU como obrigatório» — que é uma MUDANÇA QUEBRANTE do
111
+ * pacote, porque quem constrói este snapshot passa a ter de o preencher. Foi para isto que o gate da
112
+ * FORMA (`2f2582f`) foi escrito, e é a primeira vez que ele apanha um campo A ENTRAR e não a sair.
113
+ * Ausente significa «ninguém me disse», que degrada para «não exijo» — o comportamento de hoje.
114
+ */
115
+ alternanciaExigida?: boolean;
88
116
  }
89
117
  /** A player has private output when nobody else is on the same sink. Single screen ⇒ always private.
90
118
  * Pure form of game.js's hasPrivateOutput (see the report: it had no other caller left). */
@@ -135,9 +163,80 @@ export declare const ICON_STATE_CLASSES: readonly string[];
135
163
  * reflected. `ic.n` is an i18n key, so it must be resolved here too — the markup is rendered once at build
136
164
  * time and would otherwise ship the raw key to a screen reader. */
137
165
  export declare function iconBtnMarkup(ic: PauseIcon): string;
166
+ /**
167
+ * OS ÍCONES QUE ESTE JOGO CONSEGUE MESMO ACCIONAR (ADR-0106 §5).
168
+ *
169
+ * ⚠️ NENHUMA ETAPA PODE ENTREGAR BOTÃO MORTO, e o registo diz porquê com todas as letras: «uma barra que
170
+ * oferece a uma criança um caminho e depois o recusa é pior do que uma barra que ela vê que não está lá,
171
+ * porque a primeira ensina-lhe que o caminho não é para ela».
172
+ *
173
+ * ⚠️ E O CONTRASTE E A COR SÃO O CASO REAL, medido em 2026-09-08 e diferente dos outros seis campos
174
+ * «acidentais»: eles não são estado que um cartucho calhou de guardar — precisam de um `render/viz-setters`,
175
+ * cujo contexto tem **34 campos** do grafo de render de UM jogo (`parallaxLayers`, `decoSprites`,
176
+ * `getPowerups`, `rebuildCoins`, `worldSprite`…). O `createGame` não monta isso, e um quiz não tem nada
177
+ * disso para montar. Então aqui a engine não pode oferecer um padrão — o que ela pode é **não fingir**.
178
+ *
179
+ * ⚠️ Isto NÃO é o mesmo que `soon`. `soon` é «ainda não construímos»; isto é «este jogo não tem por onde», e
180
+ * um botão que anuncia «em breve» diria a coisa errada.
181
+ */
182
+ export interface AccionaveisDoJogo {
183
+ /** Há quem escreva o TEMA (o alto contraste)? Sem ele, o ícone `contrast` não é montado. */
184
+ readonly tema: boolean;
185
+ /** Há quem escreva a CORREÇÃO de cor? Sem ela, o ícone `cvd` não é montado. */
186
+ readonly correcao: boolean;
187
+ /**
188
+ * ESTE JOGO SEGURA ALGUMA TECLA? — `GameDeclaration.seguraTeclas`, o campo do ADR-0115.
189
+ *
190
+ * 🔴 Sem ele o ícone `altmove` não é montado, e a AUSÊNCIA é a decisão. A alternância existe para quem não
191
+ * consegue manter uma tecla premida; num jogo onde nada se segura ela não tem o que travar, e um controle
192
+ * que não faz nada ensina a uma criança que o ajuste de que ela depende está partido.
193
+ *
194
+ * ⚠️ E ISTO É UMA AUSÊNCIA DIFERENTE DA DO ADR-0113 cláusula 3, que também vive neste ficheiro: lá o
195
+ * controle fica DESABILITADO com o motivo, porque o aparelho EXIGE a alternância e ela não se pode
196
+ * desligar. Aqui não há nada a travar, e um controle que explica por que não faz nada continua a ser um
197
+ * controle que não faz nada.
198
+ */
199
+ readonly seguraTeclas: boolean;
200
+ }
201
+ /**
202
+ * @deprecated O nome dizia «escritores VISUAIS» e a pergunta deixou de ser só visual quando o `seguraTeclas`
203
+ * entrou (ADR-0115). Use `AccionaveisDoJogo`. O alias fica para o consumidor não pagar duas quebras no mesmo
204
+ * major — uma pelo campo novo e outra pelo nome.
205
+ */
206
+ export type EscritoresVisuais = AccionaveisDoJogo;
207
+ export declare function iconesQueAccionam(escritores: AccionaveisDoJogo): readonly PauseIcon[];
208
+ /**
209
+ * OS TRÊS ITENS QUE A ENGINE ACCIONA SOZINHA, e que por isso nunca dependem do `getPauseActs` de um jogo.
210
+ *
211
+ * 📏 Lidos do despacho, e não decididos aqui: `options`/`pmback` trocam qual lista está no cartão e
212
+ * `acessibilidade` leva o cursor à barra rápida — os três são tratados neste módulo e voltam antes de a
213
+ * tabela do jogo ser consultada.
214
+ */
215
+ export declare const ITENS_DA_ENGINE: ReadonlySet<string>;
216
+ /**
217
+ * OS ITENS DO MENU QUE ESTE JOGO CONSEGUE MESMO ACCIONAR (ADR-0106 §5).
218
+ *
219
+ * ⚠️ HOJE UM ITEM SEM ACÇÃO É UM BOTÃO MORTO, E EM SILÊNCIO. O despacho faz `const fn = acts[act]; if (fn)
220
+ * fn();` — quem carrega num item que o jogo não implementou não recebe erro, não recebe anúncio, não recebe
221
+ * nada. Para quem vê, parece que o clique falhou; para quem navega por leitor de tela, o menu leu-lhe um
222
+ * item que não existe. É exactamente o que o §5 chama de pior do que a ausência: «uma barra que oferece um
223
+ * caminho e depois o recusa ensina-lhe que o caminho não é para ela».
224
+ */
225
+ export declare function itensQueAccionam(botoes: readonly PauseMenuButton[], acts: Record<string, (() => void) | undefined>): readonly PauseMenuButton[];
226
+ /**
227
+ * A LISTA RAIZ, com uma regra a mais: `options` é uma PORTA, e uma porta para uma sala vazia também é um
228
+ * botão morto.
229
+ *
230
+ * ⚠️ Esta é a parte que um filtro item-a-item não apanha. Se todos os painéis de ajuste forem filtrados —
231
+ * um jogo que não monta nenhum —, o item `options` sobrevive (a engine acciona-o) e abre uma lista sem nada.
232
+ * A criança atravessa uma porta e fica presa num submenu vazio, cuja única saída é o `pmback` que também
233
+ * sumiu com ele.
234
+ */
235
+ export declare function raizQueAcciona(raiz: readonly PauseMenuButton[], opcoes: readonly PauseMenuButton[], acts: Record<string, (() => void) | undefined>): readonly PauseMenuButton[];
138
236
  /** The whole icon bar. Used by the pause screen AND by the splash `#title-icons` (which built the same string
139
- * by hand in game.js — that duplication dies with this export). */
140
- export declare function iconsMarkup(): string;
237
+ * by hand in game.js — that duplication dies with this export).
238
+ * O parâmetro é ADITIVO e o padrão é a lista inteira: quem já chamava sem argumentos não muda de resultado. */
239
+ export declare function iconsMarkup(icones?: readonly PauseIcon[]): string;
141
240
  /** One `.pm-btn`. Dynamic labels (`letra`/`nivel`) are rendered eagerly and carry NO `data-i18n`, so
142
241
  * i18n.applyDom() cannot overwrite them. */
143
242
  /**
@@ -194,7 +293,7 @@ export declare function mostrarSubmenuDaPausa(sp: HTMLElement, sub: PauseSub): H
194
293
  * A LEGENDA VIAJA JUNTO. Ela é a dica que substitui, para quem não vê, o `title` que só o mouse revela;
195
294
  * deixá-la no cartão tornaria a barra do HUD muda.
196
295
  */
197
- export declare function quickBarMarkup(): string;
296
+ export declare function quickBarMarkup(icones?: readonly PauseIcon[]): string;
198
297
  /**
199
298
  * O QUE UMA INTENÇÃO SIGNIFICA DENTRO DO MODO `accessibility` (ADR-0044, item 7).
200
299
  *
@@ -228,6 +327,21 @@ export interface ScreenPauseMarkupOpts {
228
327
  /** The full innerHTML of a `.screen-pause`. Pure — every input is a parameter. */
229
328
  export declare function screenPauseMarkup(o: ScreenPauseMarkupOpts): string;
230
329
  export interface PauseIconsCtx {
330
+ /**
331
+ * O DOCUMENTO onde a pausa e a barra são CONSTRUÍDAS. Ausente, vale o global.
332
+ *
333
+ * ⚠️ ISTO É O ACHADO 15 OUTRA VEZ, e o `boot/create-game` já o descreve no próprio cabeçalho: «`initI18n()`
334
+ * chamava `applyDom(document)`, o GLOBAL, por baixo de quem a chamasse. Num navegador dá no mesmo e por
335
+ * isso sobreviveu; num teste de lógica pura é a diferença entre bootar e não bootar, e num futuro com dois
336
+ * documentos (uma engine em iframe, um editor ao lado do jogo) seria a diferença entre traduzir o documento
337
+ * certo e o outro.»
338
+ *
339
+ * 📏 Medido em 2026-09-08: as duas metades que constroem DOM (`buildScreenPause`, `buildQuickBar`) faziam
340
+ * `document.createElement` no global. Enquanto a raiz de composição de cada jogo era um `main.ts` a correr
341
+ * num navegador, dava no mesmo — e foi por isso que sobreviveu. Deixa de dar assim que a ENGINE monta,
342
+ * porque o `createGame` recebe o documento por `host.doc` e pode estar a montar noutro.
343
+ */
344
+ doc?: Document;
231
345
  /** Quantos jogadores/telas. Estado de RODADA (ADR-0038): vem da instância que a raiz possui.
232
346
  * Era `numPlayers`, um `let` de `core/state` importado como binding vivo — e um `let` de módulo
233
347
  * é compartilhado por qualquer segundo jogo que a mesma página carregue (D13 do `demos`). */
@@ -248,9 +362,9 @@ export interface PauseIconsCtx {
248
362
  */
249
363
  getA11yBars: () => readonly HTMLElement[];
250
364
  /** PM_OPTIONS_BTNS — o submenu de opções. Mesma dona, mesmo motivo: ninguém tem duas cópias de uma lista. */
251
- optionsButtons: readonly PauseMenuButton[];
365
+ optionsButtons?: readonly PauseMenuButton[];
252
366
  /** PM_BTNS — the `.pm-btn` list. Owned by ui/activities-menu; injected, never copied. */
253
- pmButtons: readonly PauseMenuButton[];
367
+ pmButtons?: readonly PauseMenuButton[];
254
368
  /** QL_NAME — literacy-level names, for the (dormant) `nivel` button. Same owner as pmButtons. */
255
369
  /**
256
370
  * O RÓTULO de um botão dinâmico, pronto — ou `null` quando aquele botão não tem um (item 19).
@@ -259,16 +373,15 @@ export interface PauseIconsCtx {
259
373
  * frase. Um menu de pausa da ENGINE não sabe o que é nível de alfabetização, nem em que idioma dizê-lo.
260
374
  * Função e não valor, porque o rótulo muda em execução — de nível E de idioma.
261
375
  */
262
- dynLabel: (b: PauseMenuButton) => string | null;
376
+ /** O rótulo dinâmico, pronto — ou `null`. Ausente: nenhum botão deste jogo tem rótulo dinâmico. */
377
+ dynLabel?: (b: PauseMenuButton) => string | null;
263
378
  /** The `.pm-btn` action table. LAZY: `pauseActs` is a `const` declared far below the init site in game.js. */
264
- getPauseActs: () => Record<string, (() => void) | undefined>;
379
+ getPauseActs?: () => Record<string, (() => void) | undefined>;
265
380
  /** Records which player opened the menu. `pauseActor` itself stays in game.js — the gamepad, the keyboard
266
381
  * router, openHelp() and openOptions() all read it there. */
267
- setPauseActor: (i: number) => void;
268
- /** The live `vpPause` array (game.js rebuilds it on every buildGameHud). Getter, not the array. */
269
- getPauseScreens: () => readonly Element[];
382
+ setPauseActor?: (i: number) => void;
270
383
  getModoCego: () => boolean;
271
- setModoCego: (on: boolean) => void;
384
+ setModoCego?: (on: boolean) => void;
272
385
  /**
273
386
  * O mixer por categoria. NULO até `initAudioMixer()` — `platform/audio` o declara
274
387
  * `Record<string, CatState> | null` porque o import dele é PURO (não lê localStorage), e quem inicializa
@@ -286,18 +399,53 @@ export interface PauseIconsCtx {
286
399
  reflectTtsPanelEnabled: boolean;
287
400
  isLibrasOn: () => boolean;
288
401
  toggleLibras: () => void;
289
- rm: MotionSceneFlags;
290
- rmKeys: readonly MotionSceneKey[];
291
- rmChar: readonly MotionCharDef[];
292
- saveRM: () => void;
293
- setToggleMove: (i: number, on: boolean) => void;
294
- setPlayerViz: (i: number, mode: string) => void;
402
+ rm?: MotionSceneFlags;
403
+ rmKeys?: readonly MotionSceneKey[];
404
+ rmChar?: readonly MotionCharDef[];
405
+ saveRM?: () => void;
406
+ setToggleMove?: (i: number, on: boolean) => void;
407
+ /**
408
+ * QUAL APARELHO ESTE JOGADOR ESTÁ A USAR (ADR-0113).
409
+ *
410
+ * 📌 O ícone `altmove` desta barra é a OUTRA superfície que escreve a alternância — a mesma razão pela
411
+ * qual o escritor voltou para a engine (ADR-0106 §4). Sem este campo, ele escreveria só a chave antiga
412
+ * enquanto o painel escreve as duas, e as duas superfícies divergiriam em silêncio.
413
+ */
414
+ transporteEmUso?: (jogador: number) => string;
415
+ setPlayerViz?: (i: number, mode: string) => void;
416
+ /** Os escritores POR EIXO (#104): mexer no tema não apaga a correção, e vice-versa. */
417
+ setTemaDoJogador?: (i: number, tema: Tema) => void;
418
+ setCorrecaoDoJogador?: (i: number, correcao: Correcao) => void;
419
+ /**
420
+ * ESTE JOGO SEGURA ALGUMA TECLA? — o valor de `GameDeclaration.seguraTeclas` (ADR-0115). Sem ele o ícone
421
+ * `altmove` não é montado.
422
+ *
423
+ * ⚠️ OBRIGATÓRIO, e vai contra a direcção do ADR-0106, que passou a tornar campos deste ctx OPCIONAIS para
424
+ * a engine poder montar sozinha. A excepção tem razão medida: os campos que ganharam padrão têm um padrão
425
+ * SEGURO — o modo cego começa desligado, a lista de botões vem da engine. Aqui não há: `true` monta um
426
+ * controle que pode não fazer nada, e `false` esconde um de que uma criança depende. Os dois lados erram, e
427
+ * é essa a condição que o `holdsAtOnce` já usou para ser obrigatório também.
428
+ *
429
+ * 📌 Quem passa pelo `createGame` não escreve isto: a raiz lê a declaração, que já é obrigatória. O campo
430
+ * só é visível para um cartucho que chame `initPauseIcons` por fora — e é exactamente esse que não pode
431
+ * ficar em silêncio.
432
+ */
433
+ seguraTeclas: boolean;
295
434
  }
296
435
  export interface PauseIconsApi {
297
436
  /** Builds one `.screen-pause` (hidden), wired for click + hover/focus caption. Caller appends it. */
298
437
  buildScreenPause: (i: number) => HTMLElement;
299
438
  /** Monta a BARRA RÁPIDA (`.screen-a11y`) da tela `i`, já fiada. Chamada por ui/hud.ts, uma por tela. */
300
439
  buildQuickBar: (i: number) => HTMLElement;
440
+ /**
441
+ * OS ÍCONES QUE ESTA INSTÂNCIA MONTA — já filtrados pelo §5 do ADR-0106.
442
+ *
443
+ * ⚠️ Existe para que quem monta a barra do TÍTULO não repita o filtro. A barra do título não pode usar
444
+ * `buildQuickBar` (ele põe `tabIndex = -1`, e o próprio comentário lá diz porquê: no título não se está a
445
+ * jogar), então ela chama `iconsMarkup` directamente — e sem este acessor teria de recalcular quais ícones
446
+ * accionam, que é uma segunda cópia da mesma decisão.
447
+ */
448
+ iconesMontados: readonly PauseIcon[];
301
449
  /** ENTRA no modo `accessibility` da tela `i` — é o que o item `acessibilidade` da pausa faz. */
302
450
  entrarNaBarra: (i: number) => void;
303
451
  /** SAI do modo e devolve o direcional ao personagem. */