@the-inclusionist/engine 7.0.0 → 8.0.0-rc.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/README.md +5 -3
- package/app/css/style.css +22 -5
- 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/opendyslexic-400.woff2 +0 -0
- package/app/public/vendor/fonts/pressstart-400.woff2 +0 -0
- package/app/public/vendor/fonts.css +43 -2
- package/dist-pkg/boot/create-game.d.ts +76 -2
- package/dist-pkg/boot/create-game.js +271 -13
- package/dist-pkg/core/constants.d.ts +0 -28
- package/dist-pkg/core/constants.js +34 -17
- package/dist-pkg/core/contract.d.ts +39 -0
- package/dist-pkg/core/contract.js +36 -0
- package/dist-pkg/core/entity.d.ts +47 -4
- package/dist-pkg/core/layers.d.ts +18 -0
- package/dist-pkg/core/layers.js +18 -0
- package/dist-pkg/core/rng.js +14 -4
- package/dist-pkg/core/route.d.ts +42 -0
- package/dist-pkg/core/route.js +158 -0
- package/dist-pkg/core/state.d.ts +10 -1
- package/dist-pkg/core/state.js +13 -0
- package/dist-pkg/educational/adaptive-engine.d.ts +65 -0
- package/dist-pkg/educational/adaptive-engine.js +117 -0
- package/dist-pkg/educational/segment-bar.d.ts +96 -0
- package/dist-pkg/educational/segment-bar.js +89 -0
- package/dist-pkg/i18n/en.js +33 -0
- package/dist-pkg/i18n/es.js +33 -0
- package/dist-pkg/i18n/pt.js +46 -0
- package/dist-pkg/input/default-bindings.d.ts +40 -0
- package/dist-pkg/input/default-bindings.js +143 -9
- package/dist-pkg/input/gamepad.d.ts +18 -11
- package/dist-pkg/input/gamepad.js +75 -9
- package/dist-pkg/input/keyboard-runtime.d.ts +6 -8
- package/dist-pkg/input/keyboard-runtime.js +24 -5
- package/dist-pkg/input/keyboard.d.ts +10 -0
- package/dist-pkg/input/keyboard.js +41 -11
- package/dist-pkg/input/keydown.d.ts +27 -2
- package/dist-pkg/input/keydown.js +20 -6
- package/dist-pkg/input/latch-scope.d.ts +70 -0
- package/dist-pkg/input/latch-scope.js +110 -0
- package/dist-pkg/input/latch-store.d.ts +45 -0
- package/dist-pkg/input/latch-store.js +74 -0
- package/dist-pkg/input/origem-sintetica.d.ts +44 -0
- package/dist-pkg/input/origem-sintetica.js +60 -0
- package/dist-pkg/input/pointer.d.ts +61 -0
- package/dist-pkg/input/pointer.js +71 -0
- package/dist-pkg/input/state.d.ts +86 -1
- package/dist-pkg/input/state.js +128 -1
- package/dist-pkg/input/touch-bindings.d.ts +11 -2
- package/dist-pkg/input/touch-bindings.js +9 -3
- package/dist-pkg/input/touch.js +9 -1
- package/dist-pkg/input/transporte-em-uso.d.ts +101 -0
- package/dist-pkg/input/transporte-em-uso.js +130 -0
- package/dist-pkg/input/transports.d.ts +88 -2
- package/dist-pkg/input/transports.js +60 -5
- package/dist-pkg/input/vocabulary-migration.d.ts +18 -7
- package/dist-pkg/platform/audio-earcons.d.ts +29 -2
- package/dist-pkg/platform/audio-earcons.js +10 -0
- package/dist-pkg/platform/audio-nav.d.ts +1 -1
- package/dist-pkg/platform/audio-sonar.d.ts +149 -9
- package/dist-pkg/platform/audio-sonar.js +232 -21
- package/dist-pkg/platform/guide-intensity.d.ts +40 -0
- package/dist-pkg/platform/guide-intensity.js +73 -0
- package/dist-pkg/platform/storage.d.ts +30 -0
- package/dist-pkg/platform/storage.js +35 -0
- package/dist-pkg/platform/tts.js +16 -1
- package/dist-pkg/platform/voice-plan.d.ts +90 -0
- package/dist-pkg/platform/voice-plan.js +137 -0
- package/dist-pkg/render/draw.d.ts +1 -1
- package/dist-pkg/render/draw.js +15 -5
- package/dist-pkg/render/viz-axes.d.ts +17 -0
- package/dist-pkg/render/viz-axes.js +19 -0
- package/dist-pkg/render/viz-setters.d.ts +34 -2
- package/dist-pkg/render/viz-setters.js +189 -33
- package/dist-pkg/render/wheelchair-sprites.d.ts +11 -3
- package/dist-pkg/render/wheelchair-sprites.js +10 -4
- package/dist-pkg/ui/dom.js +20 -2
- package/dist-pkg/ui/fonts.d.ts +47 -1
- package/dist-pkg/ui/fonts.js +47 -9
- package/dist-pkg/ui/latch-refusal.d.ts +32 -0
- package/dist-pkg/ui/latch-refusal.js +60 -0
- package/dist-pkg/ui/layout.d.ts +32 -0
- package/dist-pkg/ui/layout.js +64 -1
- package/dist-pkg/ui/motion-scene.d.ts +41 -0
- package/dist-pkg/ui/motion-scene.js +76 -0
- package/dist-pkg/ui/panel-shell.d.ts +53 -0
- package/dist-pkg/ui/panel-shell.js +103 -0
- package/dist-pkg/ui/pause-icons.d.ts +134 -20
- package/dist-pkg/ui/pause-icons.js +307 -45
- package/dist-pkg/ui/reach-notice.js +8 -0
- package/dist-pkg/ui/settings-audio.d.ts +14 -1
- package/dist-pkg/ui/settings-audio.js +43 -3
- package/dist-pkg/ui/settings-controls.d.ts +66 -6
- package/dist-pkg/ui/settings-controls.js +179 -17
- package/dist-pkg/ui/settings-empathy.d.ts +15 -0
- package/dist-pkg/ui/settings-empathy.js +2 -0
- package/dist-pkg/ui/settings-motion.d.ts +25 -19
- package/dist-pkg/ui/settings-motion.js +32 -19
- package/dist-pkg/ui/settings-motor.d.ts +81 -3
- package/dist-pkg/ui/settings-motor.js +118 -9
- package/dist-pkg/ui/settings-typo.d.ts +32 -2
- package/dist-pkg/ui/settings-typo.js +64 -18
- package/dist-pkg/ui/settings-visual.d.ts +21 -1
- package/dist-pkg/ui/settings-visual.js +48 -4
- package/dist-pkg/ui/shell.d.ts +13 -3
- package/dist-pkg/ui/shell.js +3 -4
- package/dist-pkg/ui/simulation-refusal.d.ts +32 -0
- package/dist-pkg/ui/simulation-refusal.js +57 -0
- package/dist-pkg/ui/visual-axes-panel.d.ts +48 -0
- package/dist-pkg/ui/visual-axes-panel.js +95 -0
- package/dist-pkg/ui/webcam.js +6 -1
- package/docs/CREDITS.md +18 -0
- package/docs/LICENSES.md +12 -0
- package/package.json +24 -4
- package/app/public/vendor/fonts/greatvibes-400.woff2 +0 -0
- package/app/public/vendor/fonts/ufcook-700.woff2 +0 -0
|
@@ -123,6 +123,12 @@ export const KEYS = {
|
|
|
123
123
|
ttsEngine: 'incl_tts_engine', ttsVoice: 'incl_tts_voice', lang: 'incl_lang', // audiocat_{k}
|
|
124
124
|
// comunicação / legendas (ADR-0028: todo menu persiste)
|
|
125
125
|
letterCase: 'incl_lettercase', captions: 'incl_captions',
|
|
126
|
+
// ⚠️ O NÍVEL TEA (calmo / silencioso) PASSOU A PERSISTIR EM 2026-09-07, e antes não persistia: era um
|
|
127
|
+
// `let calmMode = 0` em `ui/pause-icons`, com o comentário «deliberately NOT persisted — verbatim: game.js
|
|
128
|
+
// never wrote it to storage». O «verbatim» é a chave — foi PRESERVADO na extração do monólito, não
|
|
129
|
+
// decidido. O custo era da criança que mais precisa dele: quem usa o modo silencioso voltava a pô-lo a
|
|
130
|
+
// cada sessão, e é para quem o barulho inesperado custa mais. O ADR-0028 diz que todo menu persiste.
|
|
131
|
+
tea: 'incl_tea',
|
|
126
132
|
menuIndex: 'incl_menuindex', // "6 de 10" no fim do anuncio de item (ADR-0044, item 3)
|
|
127
133
|
// tipografia / controles / toque
|
|
128
134
|
fontKey: 'incl_font_k', padDesign: 'incl_paddesign', padDir: 'incl_paddir', touchmap: 'incl_touchmap',
|
|
@@ -133,9 +139,38 @@ export const KEYS = {
|
|
|
133
139
|
reducedMotion: 'inclusionist.reducedmotion.v1', toggleMoveLegacy: 'inclusionist.togglemove',
|
|
134
140
|
// POR JOGADOR — parametrizadas pelo indice da tela. Eram sufixos '_p'+i montados a mao em varios
|
|
135
141
|
// pontos do game.js; virar funcao aqui e o que impede que um deles escreva num nome torto.
|
|
142
|
+
/**
|
|
143
|
+
* @deprecated ⚠️ A CHAVE LEGADA do modo visual — UM valor, do tempo em que só cabia um (issue #104).
|
|
144
|
+
*
|
|
145
|
+
* Continua a ser LIDA, e é isso que impede a criança de perder o que já escolheu; continua a ser ESCRITA
|
|
146
|
+
* enquanto os controles ainda escreverem um valor de cada vez, porque um leitor antigo (o cartucho na
|
|
147
|
+
* versão publicada) faz `if (v && VIZ_BY_KEY[v])` e rejeitaria um JSON — escrever a forma nova AQUI
|
|
148
|
+
* apagaria o ajuste dela em silêncio, que é exactamente o defeito que a migração existe para não cometer.
|
|
149
|
+
*/
|
|
136
150
|
vizP: (i) => 'incl_viz_p' + i,
|
|
151
|
+
/**
|
|
152
|
+
* O ESTADO VISUAL de dois eixos, em JSON (ADR-0076, issue #104).
|
|
153
|
+
*
|
|
154
|
+
* ⚠️ CHAVE NOVA AO LADO DA VELHA, e não a mesma chave com conteúdo novo. É o mesmo desenho que o campo
|
|
155
|
+
* `visual` usa ao lado do `viz`: as duas formas coexistem enquanto houver leitores das duas, cada um lê a
|
|
156
|
+
* que entende, e a velha só morre quando não sobrar quem a leia. `migrarVisual` aceita as duas, então o
|
|
157
|
+
* recuo — chave nova ausente, chave velha presente — devolve exactamente o que a criança escolheu.
|
|
158
|
+
*/
|
|
159
|
+
visualP: (i) => 'incl_visual_p' + i,
|
|
137
160
|
sinkP: (i) => 'incl_sink_p' + i,
|
|
138
161
|
easyP: (i) => 'incl_easy_p' + i,
|
|
162
|
+
/**
|
|
163
|
+
* ⚠️ AS DUAS DE BAIXO SÃO AS CHAVES LEGADAS desde 2026-09-08 (ADR-0104 §C, issue #114). Continuam a ser
|
|
164
|
+
* LIDAS — é o ajuste da criança, e a herança dele é o que a impede de o perder — e não voltam a ser
|
|
165
|
+
* escritas. O que se escreve é a chave COM TRANSPORTE, porque a alternância é do APARELHO e não da pessoa:
|
|
166
|
+
* ligá-la no controle de tela, onde ninguém segura um botão virtual com conforto, ligava-a também no
|
|
167
|
+
* teclado, onde segurar uma tecla é exactamente o que a criança sabe fazer.
|
|
168
|
+
*
|
|
169
|
+
* ⚠️ E A CHAVE NOVA NÃO MORA AQUI, de propósito. Ela é `chaveDaAlternancia`, em `input/latch-scope` — este
|
|
170
|
+
* ficheiro é módulo-FOLHA e `platform/` não importa de `input/`, que é a camada acima. Montá-la aqui
|
|
171
|
+
* exigiria ou uma aresta ao contrário ou uma segunda cópia do nome, e a segunda cópia é exactamente o que
|
|
172
|
+
* o comentário do bloco acima existe para impedir. O dono do nome é quem conhece a regra do transporte.
|
|
173
|
+
*/
|
|
139
174
|
toggleMoveP: (i) => 'incl_togglemove_p' + i,
|
|
140
175
|
toggleRunP: (i) => 'incl_togglerun_p' + i, // alternância do botão de CORRER (irmã da de movimento)
|
|
141
176
|
rmWalkP: (i) => 'incl_rmWalk_p' + i,
|
package/dist-pkg/platform/tts.js
CHANGED
|
@@ -10,7 +10,22 @@ import { t, bcp47 } from '../core/i18n.js';
|
|
|
10
10
|
import { criarFalaInterrompivel } from './interruptible-speech.js';
|
|
11
11
|
// ⚠️ MIGRAÇÃO PENDENTE (ADR-0022): a implementação neural de hoje é o @mintplex-labs/piper-tts-web e será SUBSTITUÍDA
|
|
12
12
|
// por sherpa-onnx-wasm (loader universal, modelos VITS/Piper + Kokoro-multi-lang carregados de qq URL R2 em runtime via
|
|
13
|
-
// FS.writeFile; lazy-fetch; eSpeak/Web Speech de fallback).
|
|
13
|
+
// FS.writeFile; lazy-fetch; eSpeak/Web Speech de fallback).
|
|
14
|
+
// 🔴 O MOTIVO ESCRITO AQUI ERA «@mintplex-labs foi descontinuado e só carrega 2 vozes pt-BR», E AS DUAS METADES
|
|
15
|
+
// FORAM MEDIDAS FALSAS EM 2026-09-09 — não pela leitura de um registo, que é como elas se propagaram por seis
|
|
16
|
+
// sítios, mas pelo registo npm e pelo pacote instalado:
|
|
17
|
+
// · DESCONTINUADO: nenhuma versão tem campo `deprecated`, e a 1.0.5 foi publicada em 2026-08-11 — depois de
|
|
18
|
+
// o ADR-0022 (2026-07-06) a dar por morta. A frase é de Julho e está atribuída ao Dev; o registo não a nega
|
|
19
|
+
// no passado, nega-a HOJE.
|
|
20
|
+
// · DUAS VOZES pt-BR: o `VoiceId` da 1.0.4 instalada traz 118 vozes, e entre elas as QUATRO exactas do
|
|
21
|
+
// ADR-0110 — `pt_BR-faber-medium`, `en_US-ryan-medium`, `en_US-amy-medium`, `es_MX-claude-high`.
|
|
22
|
+
// ⚠️ O QUE CONTINUA VERDADEIRO, e é mais afiado do que o motivo velho: o bundle prende `HF_BASE` a
|
|
23
|
+
// `huggingface.co/diffusionstudio/piper-voices` COM UMA GUARDA (`if (!url.match("https://huggingface.co")) return`),
|
|
24
|
+
// logo nenhum espelho de escola é alcançável por ele; e ele busca o PRÓPRIO runtime de cdnjs/jsDelivr por
|
|
25
|
+
// omissão, que é o que o ADR-0114 retira. A montante, o Piper mudou para `OHF-Voice/piper1-gpl` e a Open Home
|
|
26
|
+
// Foundation procura mantenedores — o risco existe, mas está noutro sítio.
|
|
27
|
+
// 📌 A ESCOLHA CONTINUA A SER A #129, e este comentário não a toma: descreve o que foi medido para que o
|
|
28
|
+
// próximo leitor não herde o motivo velho como se fosse medição.
|
|
14
29
|
// ⚠️ E DESDE O ADR-0094 ESTE MÓDULO NÃO NOMEIA FORNECEDOR NENHUM — quem o nomeia é o JOGO, por `ctx.carregarVozNeural`.
|
|
15
30
|
// O nome estava aqui num `import()` e o pacote em `devDependencies`, o que publicou uma engine que não compilava
|
|
16
31
|
// (ADR-0093); pô-lo em `dependencies` consertava o build e obrigava todo consumidor a 135,4 MB de `onnxruntime-web`,
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
/** Uma voz do catálogo. `voice` é o identificador no fornecedor; `engine` é o motor que a lê. */
|
|
3
|
+
export interface VozNeural {
|
|
4
|
+
readonly locale: string;
|
|
5
|
+
readonly engine: string;
|
|
6
|
+
readonly voice: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* AS QUATRO QUE A ENGINE GARANTE (ADR-0110), decididas pelo Dev em 2026-09-08.
|
|
10
|
+
*
|
|
11
|
+
* ⚠️ TODAS `vits-piper` E NENHUMA `ncnn`, que o ADR-0065 §5 já proibia: `ncnn` é outro MOTOR DE INFERÊNCIA —
|
|
12
|
+
* modelos `.param`/`.bin` e não `.onnx` — e este projeto corre sherpa-onnx. Dois dos links que chegaram em
|
|
13
|
+
* 2026-09-08 apontavam para os espelhos `ncnn`, e é por isso que a proibição tem gate e não só um comentário.
|
|
14
|
+
* ⚠️ E NENHUMA `int8`, pela razão MEDIDA do mesmo §5: no backend WASM o int8 corre ~3× MAIS LENTO que o fp32
|
|
15
|
+
* (RTF 5,6 contra 2,5, do log do próprio Dev) e produz saída incorrecta ou muda. Quantizar não encurta a
|
|
16
|
+
* espera aqui — alonga-a.
|
|
17
|
+
*/
|
|
18
|
+
export declare const VOZES_NEURAIS: readonly VozNeural[];
|
|
19
|
+
/**
|
|
20
|
+
* DE ONDE VÊM OS MODELOS (ADR-0114), num sítio só.
|
|
21
|
+
*
|
|
22
|
+
* 📏 MEDIDO EM 2026-09-08, e não escolhido: o `piper.ttstool.com` — que o Dev nomeou ao perguntar — serve o
|
|
23
|
+
* próprio runtime da própria origem e busca os modelos aqui. As quatro vozes deste catálogo respondem 200
|
|
24
|
+
* neste endereço, com `Access-Control-Allow-Origin: *`, logo um PWA pode buscá-las de outra origem.
|
|
25
|
+
*
|
|
26
|
+
* ⚠️ UM SÍTIO SÓ É A METADE QUE O REGISTO PEDE. Um endereço repetido no ponto de uso é como o CDN do
|
|
27
|
+
* WebGazer chegou ao `ui/webcam` — escrito à mão, sem política, e sem ninguém a poder mudá-lo de uma vez.
|
|
28
|
+
*/
|
|
29
|
+
export declare const HOST_DOS_MODELOS = "https://huggingface.co/rhasspy/piper-voices/resolve/main/";
|
|
30
|
+
/**
|
|
31
|
+
* O CAMINHO DO MODELO, DERIVADO DO IDENTIFICADOR — e não uma segunda tabela.
|
|
32
|
+
*
|
|
33
|
+
* `pt_BR-faber-medium` diz tudo o que o caminho precisa: `pt/pt_BR/faber/medium/pt_BR-faber-medium.onnx`.
|
|
34
|
+
* 🎯 DERIVAR EM VEZ DE TABELAR é a decisão inteira desta função: uma tabela de caminhos ao lado da tabela
|
|
35
|
+
* de vozes seria o mesmo facto escrito duas vezes, e este repositório já pagou isso três vezes — o
|
|
36
|
+
* `DomQuery`, os rótulos de movimento reduzido, as chaves de armazenamento. Duas listas divergem, e
|
|
37
|
+
* divergem uma entrada de cada vez.
|
|
38
|
+
*
|
|
39
|
+
* ⚠️ Devolve `null` para um identificador que não tenha a forma esperada, em vez de montar um caminho
|
|
40
|
+
* torto: uma URL inventada dá 404 na escola, e um `null` dá para reportar antes de sair de casa.
|
|
41
|
+
*/
|
|
42
|
+
export declare function caminhoDoModelo(v: VozNeural): string | null;
|
|
43
|
+
/** O endereço completo do modelo. `null` quando o identificador não se deixa ler. */
|
|
44
|
+
export declare function urlDoModelo(v: VozNeural): string | null;
|
|
45
|
+
/**
|
|
46
|
+
* A CONFIGURAÇÃO da voz, que o motor lê junto com o modelo.
|
|
47
|
+
*
|
|
48
|
+
* 📌 `.onnx.json` e não um segundo caminho: é o mesmo ficheiro com outro sufixo, medido a responder 200 no
|
|
49
|
+
* mesmo sítio. Escrevê-lo como derivação mantém a regra de que o identificador é a única fonte.
|
|
50
|
+
*/
|
|
51
|
+
export declare function urlDaConfig(v: VozNeural): string | null;
|
|
52
|
+
/** Em que pé está cada voz. `falhou` é um estado e não uma excepção — ver `vozEmUso`. */
|
|
53
|
+
export type EstadoDaVoz = 'ausente' | 'a-buscar' | 'pronta' | 'falhou';
|
|
54
|
+
/** O que se sabe de cada voz, por identificador. Uma voz ausente do mapa é `ausente`. */
|
|
55
|
+
export type EstadosDasVozes = Readonly<Record<string, EstadoDaVoz | undefined>>;
|
|
56
|
+
export declare const estadoDe: (estados: EstadosDasVozes, v: VozNeural) => EstadoDaVoz;
|
|
57
|
+
/**
|
|
58
|
+
* A ORDEM POR QUE SE BUSCAM: primeiro as do idioma corrente, depois as outras, cada grupo na ordem do
|
|
59
|
+
* catálogo. Só entram as que ainda não estão prontas nem em curso.
|
|
60
|
+
*
|
|
61
|
+
* ⚠️ O IDIOMA CORRENTE PRIMEIRO É A REGRA INTEIRA, e ela é sobre uma criança e não sobre eficiência: as
|
|
62
|
+
* outras são para «conforme interesse do jogador», mas a dela é a que ela precisa AGORA. Buscar em ordem de
|
|
63
|
+
* catálogo faria uma criança brasileira esperar por duas vozes inglesas na rede de uma escola.
|
|
64
|
+
*
|
|
65
|
+
* 📌 `falhou` VOLTA À FILA. Uma escola perde a rede a meio da manhã e recupera-a; uma voz marcada como
|
|
66
|
+
* falhada para sempre seria uma criança sem voz até alguém recarregar a página. Quem chama decide QUANDO
|
|
67
|
+
* tentar de novo — este módulo só diz que ainda há o que buscar.
|
|
68
|
+
*/
|
|
69
|
+
export declare function ordemDeBusca(estados: EstadosDasVozes, localeCorrente: string, catalogo?: readonly VozNeural[]): VozNeural[];
|
|
70
|
+
/** O que a criança ouve agora. `recuo` é a voz do sistema — eSpeak ou Web Speech. */
|
|
71
|
+
export type VozEmUso = {
|
|
72
|
+
readonly tipo: 'neural';
|
|
73
|
+
readonly voice: string;
|
|
74
|
+
} | {
|
|
75
|
+
readonly tipo: 'recuo';
|
|
76
|
+
readonly porque: 'a-buscar' | 'sem-voz-para-o-idioma' | 'falhou' | 'ausente';
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* QUEM ESTÁ A FALAR, E POR QUE — e esta função é o gate 3 do ADR-0110 em forma de código.
|
|
80
|
+
*
|
|
81
|
+
* ⚠️ O DEFEITO QUE ELA IMPEDE TEM NOME NESTE REPOSITÓRIO: «um recuo que se apresenta como a coisa real é a
|
|
82
|
+
* família do `reflectTTS`». Uma engine que dissesse «voz neural» enquanto o modelo ainda desce faria um adulto
|
|
83
|
+
* concluir que a qualidade que ouve É a qualidade final — e desistir de esperar por uma coisa que já vinha a
|
|
84
|
+
* caminho. Dizer `recuo` e dizer PORQUÊ custa uma linha e é a diferença entre informar e enganar.
|
|
85
|
+
*
|
|
86
|
+
* 📌 A PRIMEIRA VOZ PRONTA DO IDIOMA, e nada mais: onde há duas prontas, esta função NÃO escolhe entre elas —
|
|
87
|
+
* devolve a primeira do catálogo porque tem de devolver alguma, e qual delas a criança prefere é a pergunta
|
|
88
|
+
* que o ADR-0110 deixou em aberto. Ver o cabeçalho.
|
|
89
|
+
*/
|
|
90
|
+
export declare function vozEmUso(estados: EstadosDasVozes, localeCorrente: string, catalogo?: readonly VozNeural[]): VozEmUso;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// platform/voice-plan.ts — O PLANO DAS VOZES NEURAIS (ADR-0110), na metade PURA.
|
|
3
|
+
//
|
|
4
|
+
// ========================= O QUE ESTE MÓDULO É =========================
|
|
5
|
+
// O catálogo das quatro vozes que a engine GARANTE, a ordem por que se buscam, e — a parte que mais importa —
|
|
6
|
+
// a resposta honesta a «o que é que esta criança está a ouvir AGORA». Sem rede, sem cache, sem tempo. Quem
|
|
7
|
+
// busca de facto é a outra metade; quem decide O QUE buscar e o que DIZER é esta.
|
|
8
|
+
//
|
|
9
|
+
// ⚠️ POR QUE O CATÁLOGO VIVE AQUI E NÃO NO `TTS_SOURCES`. A tabela do `platform/tts` é indexada por idioma e
|
|
10
|
+
// tem UMA voz por idioma; este catálogo tem QUATRO vozes para TRÊS idiomas, porque o en-US tem duas. Ligá-las
|
|
11
|
+
// hoje mudaria comportamento: o `loadTTS` passaria a pedir `en_US-ryan-medium` à PORTA DO CARTUCHO
|
|
12
|
+
// (ADR-0094), que pode não ter essa voz. A tabela liga-se a isto quando o buscador existir, e nem um dia
|
|
13
|
+
// antes — uma lista ligada a um fornecedor que não a conhece é pior do que uma lista à espera.
|
|
14
|
+
//
|
|
15
|
+
// ⚠️ E QUAL DAS DUAS VOZES EN-US A CRIANÇA OUVE POR PADRÃO NÃO SE DECIDE AQUI. O ADR-0110 deixa-o
|
|
16
|
+
// explicitamente em aberto, e a ORDEM DESTE ARRAY NÃO É UMA PREFERÊNCIA — é a ordem em que o Dev as nomeou.
|
|
17
|
+
// Ler ordem como escolha seria decidir por omissão, que é o defeito que este projeto persegue.
|
|
18
|
+
/**
|
|
19
|
+
* AS QUATRO QUE A ENGINE GARANTE (ADR-0110), decididas pelo Dev em 2026-09-08.
|
|
20
|
+
*
|
|
21
|
+
* ⚠️ TODAS `vits-piper` E NENHUMA `ncnn`, que o ADR-0065 §5 já proibia: `ncnn` é outro MOTOR DE INFERÊNCIA —
|
|
22
|
+
* modelos `.param`/`.bin` e não `.onnx` — e este projeto corre sherpa-onnx. Dois dos links que chegaram em
|
|
23
|
+
* 2026-09-08 apontavam para os espelhos `ncnn`, e é por isso que a proibição tem gate e não só um comentário.
|
|
24
|
+
* ⚠️ E NENHUMA `int8`, pela razão MEDIDA do mesmo §5: no backend WASM o int8 corre ~3× MAIS LENTO que o fp32
|
|
25
|
+
* (RTF 5,6 contra 2,5, do log do próprio Dev) e produz saída incorrecta ou muda. Quantizar não encurta a
|
|
26
|
+
* espera aqui — alonga-a.
|
|
27
|
+
*/
|
|
28
|
+
export const VOZES_NEURAIS = Object.freeze([
|
|
29
|
+
Object.freeze({ locale: 'pt-BR', engine: 'piper', voice: 'pt_BR-faber-medium' }),
|
|
30
|
+
Object.freeze({ locale: 'en-US', engine: 'piper', voice: 'en_US-ryan-medium' }),
|
|
31
|
+
Object.freeze({ locale: 'en-US', engine: 'piper', voice: 'en_US-amy-medium' }),
|
|
32
|
+
Object.freeze({ locale: 'es-MX', engine: 'piper', voice: 'es_MX-claude-high' }),
|
|
33
|
+
]);
|
|
34
|
+
/**
|
|
35
|
+
* DE ONDE VÊM OS MODELOS (ADR-0114), num sítio só.
|
|
36
|
+
*
|
|
37
|
+
* 📏 MEDIDO EM 2026-09-08, e não escolhido: o `piper.ttstool.com` — que o Dev nomeou ao perguntar — serve o
|
|
38
|
+
* próprio runtime da própria origem e busca os modelos aqui. As quatro vozes deste catálogo respondem 200
|
|
39
|
+
* neste endereço, com `Access-Control-Allow-Origin: *`, logo um PWA pode buscá-las de outra origem.
|
|
40
|
+
*
|
|
41
|
+
* ⚠️ UM SÍTIO SÓ É A METADE QUE O REGISTO PEDE. Um endereço repetido no ponto de uso é como o CDN do
|
|
42
|
+
* WebGazer chegou ao `ui/webcam` — escrito à mão, sem política, e sem ninguém a poder mudá-lo de uma vez.
|
|
43
|
+
*/
|
|
44
|
+
export const HOST_DOS_MODELOS = 'https://huggingface.co/rhasspy/piper-voices/resolve/main/';
|
|
45
|
+
/**
|
|
46
|
+
* O CAMINHO DO MODELO, DERIVADO DO IDENTIFICADOR — e não uma segunda tabela.
|
|
47
|
+
*
|
|
48
|
+
* `pt_BR-faber-medium` diz tudo o que o caminho precisa: `pt/pt_BR/faber/medium/pt_BR-faber-medium.onnx`.
|
|
49
|
+
* 🎯 DERIVAR EM VEZ DE TABELAR é a decisão inteira desta função: uma tabela de caminhos ao lado da tabela
|
|
50
|
+
* de vozes seria o mesmo facto escrito duas vezes, e este repositório já pagou isso três vezes — o
|
|
51
|
+
* `DomQuery`, os rótulos de movimento reduzido, as chaves de armazenamento. Duas listas divergem, e
|
|
52
|
+
* divergem uma entrada de cada vez.
|
|
53
|
+
*
|
|
54
|
+
* ⚠️ Devolve `null` para um identificador que não tenha a forma esperada, em vez de montar um caminho
|
|
55
|
+
* torto: uma URL inventada dá 404 na escola, e um `null` dá para reportar antes de sair de casa.
|
|
56
|
+
*/
|
|
57
|
+
export function caminhoDoModelo(v) {
|
|
58
|
+
const partes = v.voice.split('-');
|
|
59
|
+
if (partes.length !== 3)
|
|
60
|
+
return null;
|
|
61
|
+
const [locale, nome, qualidade] = partes;
|
|
62
|
+
const idioma = locale.split('_')[0];
|
|
63
|
+
if (!idioma || !nome || !qualidade || idioma === locale)
|
|
64
|
+
return null;
|
|
65
|
+
return `${idioma}/${locale}/${nome}/${qualidade}/${v.voice}.onnx`;
|
|
66
|
+
}
|
|
67
|
+
/** O endereço completo do modelo. `null` quando o identificador não se deixa ler. */
|
|
68
|
+
export function urlDoModelo(v) {
|
|
69
|
+
const caminho = caminhoDoModelo(v);
|
|
70
|
+
return caminho === null ? null : HOST_DOS_MODELOS + caminho;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* A CONFIGURAÇÃO da voz, que o motor lê junto com o modelo.
|
|
74
|
+
*
|
|
75
|
+
* 📌 `.onnx.json` e não um segundo caminho: é o mesmo ficheiro com outro sufixo, medido a responder 200 no
|
|
76
|
+
* mesmo sítio. Escrevê-lo como derivação mantém a regra de que o identificador é a única fonte.
|
|
77
|
+
*/
|
|
78
|
+
export function urlDaConfig(v) {
|
|
79
|
+
const url = urlDoModelo(v);
|
|
80
|
+
return url === null ? null : url + '.json';
|
|
81
|
+
}
|
|
82
|
+
export const estadoDe = (estados, v) => estados[v.voice] ?? 'ausente';
|
|
83
|
+
/**
|
|
84
|
+
* A ORDEM POR QUE SE BUSCAM: primeiro as do idioma corrente, depois as outras, cada grupo na ordem do
|
|
85
|
+
* catálogo. Só entram as que ainda não estão prontas nem em curso.
|
|
86
|
+
*
|
|
87
|
+
* ⚠️ O IDIOMA CORRENTE PRIMEIRO É A REGRA INTEIRA, e ela é sobre uma criança e não sobre eficiência: as
|
|
88
|
+
* outras são para «conforme interesse do jogador», mas a dela é a que ela precisa AGORA. Buscar em ordem de
|
|
89
|
+
* catálogo faria uma criança brasileira esperar por duas vozes inglesas na rede de uma escola.
|
|
90
|
+
*
|
|
91
|
+
* 📌 `falhou` VOLTA À FILA. Uma escola perde a rede a meio da manhã e recupera-a; uma voz marcada como
|
|
92
|
+
* falhada para sempre seria uma criança sem voz até alguém recarregar a página. Quem chama decide QUANDO
|
|
93
|
+
* tentar de novo — este módulo só diz que ainda há o que buscar.
|
|
94
|
+
*/
|
|
95
|
+
export function ordemDeBusca(estados, localeCorrente, catalogo = VOZES_NEURAIS) {
|
|
96
|
+
const porBuscar = catalogo.filter((v) => {
|
|
97
|
+
const e = estadoDe(estados, v);
|
|
98
|
+
return e === 'ausente' || e === 'falhou';
|
|
99
|
+
});
|
|
100
|
+
return [
|
|
101
|
+
...porBuscar.filter((v) => v.locale === localeCorrente),
|
|
102
|
+
...porBuscar.filter((v) => v.locale !== localeCorrente),
|
|
103
|
+
];
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* QUEM ESTÁ A FALAR, E POR QUE — e esta função é o gate 3 do ADR-0110 em forma de código.
|
|
107
|
+
*
|
|
108
|
+
* ⚠️ O DEFEITO QUE ELA IMPEDE TEM NOME NESTE REPOSITÓRIO: «um recuo que se apresenta como a coisa real é a
|
|
109
|
+
* família do `reflectTTS`». Uma engine que dissesse «voz neural» enquanto o modelo ainda desce faria um adulto
|
|
110
|
+
* concluir que a qualidade que ouve É a qualidade final — e desistir de esperar por uma coisa que já vinha a
|
|
111
|
+
* caminho. Dizer `recuo` e dizer PORQUÊ custa uma linha e é a diferença entre informar e enganar.
|
|
112
|
+
*
|
|
113
|
+
* 📌 A PRIMEIRA VOZ PRONTA DO IDIOMA, e nada mais: onde há duas prontas, esta função NÃO escolhe entre elas —
|
|
114
|
+
* devolve a primeira do catálogo porque tem de devolver alguma, e qual delas a criança prefere é a pergunta
|
|
115
|
+
* que o ADR-0110 deixou em aberto. Ver o cabeçalho.
|
|
116
|
+
*/
|
|
117
|
+
export function vozEmUso(estados, localeCorrente, catalogo = VOZES_NEURAIS) {
|
|
118
|
+
const doIdioma = catalogo.filter((v) => v.locale === localeCorrente);
|
|
119
|
+
if (doIdioma.length === 0)
|
|
120
|
+
return { tipo: 'recuo', porque: 'sem-voz-para-o-idioma' };
|
|
121
|
+
const pronta = doIdioma.find((v) => estadoDe(estados, v) === 'pronta');
|
|
122
|
+
if (pronta)
|
|
123
|
+
return { tipo: 'neural', voice: pronta.voice };
|
|
124
|
+
// A PRECEDÊNCIA DOS TRÊS RECUOS, e cada degrau tem a sua razão:
|
|
125
|
+
//
|
|
126
|
+
// ⚠️ «A BUSCAR» GANHA DE «FALHOU» quando as duas coexistem: enquanto alguma ainda vem a caminho, dizer
|
|
127
|
+
// «falhou» seria anunciar uma derrota que ainda não aconteceu, e um adulto desligaria a espera cedo.
|
|
128
|
+
// ⚠️ E «AUSENTE» NÃO SE DISFARÇA DE «A BUSCAR», que era como esta função estava escrita à primeira. Nada
|
|
129
|
+
// começou é diferente de está a caminho: o primeiro é uma engine que ainda não pediu — possivelmente
|
|
130
|
+
// porque ninguém a mandou —, e chamar-lhe «a buscar» esconderia um buscador que nunca arrancou atrás de
|
|
131
|
+
// uma frase tranquilizadora. É o mesmo defeito de forma que o `neuralDisponivel` já custou à #91.
|
|
132
|
+
if (doIdioma.some((v) => estadoDe(estados, v) === 'a-buscar'))
|
|
133
|
+
return { tipo: 'recuo', porque: 'a-buscar' };
|
|
134
|
+
if (doIdioma.some((v) => estadoDe(estados, v) === 'falhou'))
|
|
135
|
+
return { tipo: 'recuo', porque: 'falhou' };
|
|
136
|
+
return { tipo: 'recuo', porque: 'ausente' };
|
|
137
|
+
}
|
|
@@ -65,7 +65,7 @@ interface PowerupWithSprite {
|
|
|
65
65
|
* `sq`/`sqT` são o squash&stretch (amplitude e relógio, escritos por render/fx.setSquash); `easy` desenha a
|
|
66
66
|
* hitbox de coleta tolerante translúcida; `viz` é o modo de visão, que recolore por viewport.
|
|
67
67
|
*/
|
|
68
|
-
export type DrawPlayer = AnimPlayer & PlayerView<'i' | 'x' | 'y' | 'facing' | 'hurtTimer' | 'sq' | 'sqT' | 'easy' | '
|
|
68
|
+
export type DrawPlayer = AnimPlayer & PlayerView<'i' | 'x' | 'y' | 'facing' | 'hurtTimer' | 'sq' | 'sqT' | 'easy' | 'visual' | 'sprite'>;
|
|
69
69
|
/** Flags de Movimento Reduzido que o DESENHO consulta (o objeto `rm` do game.js tem mais chaves). */
|
|
70
70
|
export interface ReducedMotion {
|
|
71
71
|
items?: boolean;
|
package/dist-pkg/render/draw.js
CHANGED
|
@@ -67,7 +67,7 @@ const rnd = rngDecoracao.rnd;
|
|
|
67
67
|
import { JUICE, easeOut3, shakeAmp, drawFx } from './fx.js';
|
|
68
68
|
import { criarCamera } from './camera.js';
|
|
69
69
|
import { drawCane, drawRunCane, drawChair } from './wheelchair-sprites.js';
|
|
70
|
-
import {
|
|
70
|
+
import { chaveDeTextura, ehBaixaVisao } from './viz-axes.js';
|
|
71
71
|
// (`game/powerups` e `game/coin-spawning` SAÍRAM daqui no item 19 — ver o bloco "ITENS DECLARADOS" abaixo.)
|
|
72
72
|
import { drawWeather } from './weather.js';
|
|
73
73
|
import { choosePlayerFrame } from './player-anim.js';
|
|
@@ -121,8 +121,11 @@ export function initDraw(ctx) {
|
|
|
121
121
|
dt, dir, wheelchair: ctx.isWheelchair(), held: ctx.held, rnd, tex: ctx.playerTextures(),
|
|
122
122
|
});
|
|
123
123
|
// solo/default; no MP o drawFrame troca a textura por viewport (applySharedTextures)
|
|
124
|
+
// ⚠️ `chaveDeTextura` E NÃO A ASSINATURA DE `playerVizTex`. Medido na etapa 0 da #104: aquela função só
|
|
125
|
+
// age quando existe `DIRECT_CFG[mode]`, e devolve a textura como veio para todo o resto. Então o que
|
|
126
|
+
// muda é o CHAMADOR — a metade do estado que interessa à textura — e a porta fica onde estava.
|
|
124
127
|
if (pl.sprite)
|
|
125
|
-
pl.sprite.texture = ctx.playerVizTex(tx, pl.
|
|
128
|
+
pl.sprite.texture = ctx.playerVizTex(tx, chaveDeTextura(pl.visual));
|
|
126
129
|
return tx;
|
|
127
130
|
}
|
|
128
131
|
/* ===================== o quadro ===================== */
|
|
@@ -187,12 +190,19 @@ export function initDraw(ctx) {
|
|
|
187
190
|
}
|
|
188
191
|
else {
|
|
189
192
|
// Otimização: se TODOS estão no mesmo modo (caso comum), troca as texturas UMA vez; senão, por viewport.
|
|
190
|
-
|
|
191
|
-
|
|
193
|
+
// ⚠️ A COMPARAÇÃO PASSOU A SER PELA CHAVE DE TEXTURA, e isso é output-preservador e mais barato ao
|
|
194
|
+
// mesmo tempo. `applySharedTextures` só usa o modo para escolher TEXTURA (`worldTexFor`,
|
|
195
|
+
// `parallaxTexFor`, `treeTexFor`, `spriteTexFor`, `pupTexFor`), e todas devolvem a base para o que não
|
|
196
|
+
// está em `DIRECT_CFG` — conferido em `high-contrast.worldTexFor`. Antes, dois jogadores em `normal` e
|
|
197
|
+
// `fix-deuter` contavam como modos DIFERENTES e disparavam uma re-aplicação de texturas que produzia
|
|
198
|
+
// exactamente as mesmas texturas. Agora contam como iguais, porque para a textura eles são.
|
|
199
|
+
const v0 = chaveDeTextura(PLS[0].visual);
|
|
200
|
+
const allSame = PLS.every((p) => chaveDeTextura(p.visual) === v0);
|
|
201
|
+
const anyOverlay = PLS.some((p) => ehBaixaVisao(p.visual));
|
|
192
202
|
if (allSame)
|
|
193
203
|
ctx.applySharedTextures(v0);
|
|
194
204
|
for (let i = 0, n = ctx.getNumPlayers(); i < n; i++) {
|
|
195
|
-
const viz = PLS[i].
|
|
205
|
+
const viz = chaveDeTextura(PLS[i].visual);
|
|
196
206
|
if (!allSame)
|
|
197
207
|
ctx.applySharedTextures(viz); // só troca por viewport quando os modos diferem
|
|
198
208
|
const itens2 = ctx.getItemSprites();
|
|
@@ -80,6 +80,23 @@ export declare function proximaCorrecao(v: VisualState): VisualState;
|
|
|
80
80
|
* uma cegueira simulada apaga a tela inteira, e nesse instante o tema não muda nada do que ela percebe.
|
|
81
81
|
*/
|
|
82
82
|
export declare function chaveDeTextura(v: VisualState): string;
|
|
83
|
+
/**
|
|
84
|
+
* A CHAVE ÚNICA que melhor descreve este estado no vocabulário ANTIGO — para quem só sabe ler uma.
|
|
85
|
+
*
|
|
86
|
+
* ⚠️ NÃO É A `chaveDeTextura`, e a diferença custou um gate vermelho para aparecer. A de textura devolve
|
|
87
|
+
* `normal` para uma correção de cor, porque correção não muda textura nenhuma — e usá-la como espelho faria
|
|
88
|
+
* uma criança em `fix-deuter` passar a gravar `'normal'` na chave legada. **Um leitor antigo perderia a
|
|
89
|
+
* correção dela**, que é exactamente o estrago que a migração inteira existe para não cometer.
|
|
90
|
+
*
|
|
91
|
+
* A ordem é simulação → tema → correção → padrão, e ela preserva TODO ajuste que já existia: nenhum estado
|
|
92
|
+
* antigo tinha dois eixos, então nenhum deles perde nada aqui.
|
|
93
|
+
*
|
|
94
|
+
* ⚠️ O ÚNICO CASO COM PERDA É O NOVO — `hc7 + fix-deuter` só cabe como uma das duas metades, e a escolhida é
|
|
95
|
+
* o tema. Não há regressão possível nisso: esse estado NÃO EXISTIA antes, e um leitor que só entende uma
|
|
96
|
+
* chave nunca soube exprimi-lo. Quem quiser as duas metades lê a chave nova, que existe precisamente para
|
|
97
|
+
* isso.
|
|
98
|
+
*/
|
|
99
|
+
export declare function chaveLegada(v: VisualState): string;
|
|
83
100
|
/**
|
|
84
101
|
* Traduz o valor salvo. Aceita o antigo (string) e o novo (objeto), e devolve sempre um estado válido.
|
|
85
102
|
*
|
|
@@ -122,6 +122,25 @@ export function proximaCorrecao(v) {
|
|
|
122
122
|
export function chaveDeTextura(v) {
|
|
123
123
|
return v.simulacao ?? temaDireto(v) ?? 'normal';
|
|
124
124
|
}
|
|
125
|
+
/**
|
|
126
|
+
* A CHAVE ÚNICA que melhor descreve este estado no vocabulário ANTIGO — para quem só sabe ler uma.
|
|
127
|
+
*
|
|
128
|
+
* ⚠️ NÃO É A `chaveDeTextura`, e a diferença custou um gate vermelho para aparecer. A de textura devolve
|
|
129
|
+
* `normal` para uma correção de cor, porque correção não muda textura nenhuma — e usá-la como espelho faria
|
|
130
|
+
* uma criança em `fix-deuter` passar a gravar `'normal'` na chave legada. **Um leitor antigo perderia a
|
|
131
|
+
* correção dela**, que é exactamente o estrago que a migração inteira existe para não cometer.
|
|
132
|
+
*
|
|
133
|
+
* A ordem é simulação → tema → correção → padrão, e ela preserva TODO ajuste que já existia: nenhum estado
|
|
134
|
+
* antigo tinha dois eixos, então nenhum deles perde nada aqui.
|
|
135
|
+
*
|
|
136
|
+
* ⚠️ O ÚNICO CASO COM PERDA É O NOVO — `hc7 + fix-deuter` só cabe como uma das duas metades, e a escolhida é
|
|
137
|
+
* o tema. Não há regressão possível nisso: esse estado NÃO EXISTIA antes, e um leitor que só entende uma
|
|
138
|
+
* chave nunca soube exprimi-lo. Quem quiser as duas metades lê a chave nova, que existe precisamente para
|
|
139
|
+
* isso.
|
|
140
|
+
*/
|
|
141
|
+
export function chaveLegada(v) {
|
|
142
|
+
return v.simulacao ?? temaDireto(v) ?? filtroChave(v) ?? 'normal';
|
|
143
|
+
}
|
|
125
144
|
/* ===================== A MIGRAÇÃO ===================== */
|
|
126
145
|
//
|
|
127
146
|
// ⚠️ ELA NÃO É OPCIONAL E VEM ANTES DA PRIMEIRA LEITURA DA FORMA NOVA. O ajuste salvo guarda o valor único
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
2
|
import { type VizMode } from './viz-modes.js';
|
|
3
|
+
import { type Tema, type Correcao, type VisualState } from './viz-axes.js';
|
|
3
4
|
import type { DomQuery } from '../core/dom-query.js';
|
|
4
5
|
import type { ComFiltro, ComTextura, Visivel, DesenhoComCirculo, AplicarFiltroCss, AlcanceDoFiltro, AplicarAltoContrasteNoDom } from './port.js';
|
|
5
6
|
/**
|
|
@@ -10,6 +11,24 @@ import type { ComFiltro, ComTextura, Visivel, DesenhoComCirculo, AplicarFiltroCs
|
|
|
10
11
|
export declare function alcanceDoModo(mode: string): AlcanceDoFiltro;
|
|
11
12
|
/** Modo por chave, com o fallback do original: chave desconhecida (ou nula) CAI em `normal`. */
|
|
12
13
|
export declare function resolveViz(key: string | null | undefined): VizMode;
|
|
14
|
+
/**
|
|
15
|
+
* O ESTADO VISUAL GUARDADO deste jogador, de qualquer das duas formas (issue #104).
|
|
16
|
+
*
|
|
17
|
+
* ⚠️ ESTA FUNÇÃO É A CAIXA «um ajuste salvo antes da divisão restaura o mesmo estado visível» da definition
|
|
18
|
+
* of done, e a ordem das duas leituras é a decisão inteira:
|
|
19
|
+
*
|
|
20
|
+
* 1. a chave NOVA (`visualP`), que é a única que sabe dizer dois eixos;
|
|
21
|
+
* 2. na falta dela, a chave VELHA (`vizP`), que guarda a string única — e é aqui que mora o ajuste de toda
|
|
22
|
+
* criança que já jogou este jogo antes de hoje;
|
|
23
|
+
* 3. na falta das duas, o padrão.
|
|
24
|
+
*
|
|
25
|
+
* ⚠️ O RECUO NÃO É ZELO: sem ele, a primeira sessão depois da actualização apagaria o modo visual que ela
|
|
26
|
+
* escolheu — e quem escolheu `fix-deuter` ou `hc-direto-7` escolheu-o porque enxerga assim. É a diferença
|
|
27
|
+
* entre migrar e recomeçar.
|
|
28
|
+
*
|
|
29
|
+
* `migrarVisual` aceita as duas formas e é idempotente, então isto pode correr quantas vezes for preciso.
|
|
30
|
+
*/
|
|
31
|
+
export declare function lerVisualGuardado(i: number): VisualState;
|
|
13
32
|
/** É um dos três níveis de Renderização Direta (alto contraste)? Mesmo teste do original: `!!DIRECT_CFG[mode]`. */
|
|
14
33
|
export declare function isDirectMode(mode: string): boolean;
|
|
15
34
|
/** Filtro CSS da canvas no SOLO: simulação/correção de visão + realce L→Q, compostos e sem vazios. */
|
|
@@ -44,6 +63,7 @@ interface ClassListHost {
|
|
|
44
63
|
}
|
|
45
64
|
interface Pl {
|
|
46
65
|
viz: string;
|
|
66
|
+
visual?: VisualState;
|
|
47
67
|
sprite?: Textured | null;
|
|
48
68
|
_tx?: unknown;
|
|
49
69
|
}
|
|
@@ -100,7 +120,7 @@ export interface VizSettersCtx {
|
|
|
100
120
|
setFrontDim: (on: boolean) => void;
|
|
101
121
|
rebuildExtras: () => void;
|
|
102
122
|
rebuildCoins: () => void;
|
|
103
|
-
setModoCego
|
|
123
|
+
setModoCego?: (on: boolean) => void;
|
|
104
124
|
hideTouchControls: (reason?: string) => void;
|
|
105
125
|
reflectVizButtons: () => void;
|
|
106
126
|
renderVisualPanel: () => void;
|
|
@@ -115,8 +135,18 @@ export interface VizSettersApi {
|
|
|
115
135
|
applyVpFilters(): void;
|
|
116
136
|
/** Troca o modo de UM jogador: persiste, invalida o render estático e reaplica pelo caminho certo. */
|
|
117
137
|
setPlayerViz(i: number, mode: string): void;
|
|
138
|
+
/**
|
|
139
|
+
* O ESTADO INTEIRO de um jogador, e os DOIS escritores por eixo (#104).
|
|
140
|
+
*
|
|
141
|
+
* ⚠️ Os dois de eixo existem separados porque é isso que um painel de dois controles precisa: mexer no
|
|
142
|
+
* TEMA sem tocar na correção, e vice-versa. Enquanto havia um campo só, «mexer num» significava
|
|
143
|
+
* inevitavelmente «apagar o outro» — e era o defeito, não a API.
|
|
144
|
+
*/
|
|
145
|
+
setVisualDoJogador(i: number, v: VisualState): void;
|
|
146
|
+
setTemaDoJogador(i: number, tema: Tema): void;
|
|
147
|
+
setCorrecaoDoJogador(i: number, correcao: Correcao): void;
|
|
118
148
|
/** Caminho SOLO: filtro CSS na canvas + texturas globais + overlay DOM + bolinha. */
|
|
119
|
-
applyVizGlobal(
|
|
149
|
+
applyVizGlobal(v: VisualState): void;
|
|
120
150
|
/** Reaplica tudo depois de uma mudança estrutural (cenário, nº de telas). */
|
|
121
151
|
reapplyVizAll(): void;
|
|
122
152
|
/** Bolinha global (#viz-indicator) para um `kind`. */
|
|
@@ -125,6 +155,8 @@ export interface VizSettersApi {
|
|
|
125
155
|
rebakeDirect(): void;
|
|
126
156
|
/** Grupo de rádios de modos visuais nos painéis (visual/empatia). */
|
|
127
157
|
renderVizGroup(listSel: string, tabsSel: string, modes: readonly VizMode[]): void;
|
|
158
|
+
/** Os DOIS eixos do painel visual (#104). Irmão do de cima — ver a nota na implementação. */
|
|
159
|
+
renderEixosVisuais(listSel: string, tabsSel: string): void;
|
|
128
160
|
}
|
|
129
161
|
export declare function initVizSetters(ctx: VizSettersCtx): VizSettersApi;
|
|
130
162
|
export {};
|