@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
|
@@ -12,6 +12,8 @@ import { lqName as lqLabel } from '../render/lq-filter.js';
|
|
|
12
12
|
import { HC_ROLE_KEYS, HC_ROLE_DEF } from '../render/hc-role-data.js';
|
|
13
13
|
import { VIZ_CORRECTIONS, VIZ_MODES } from '../render/viz-modes.js';
|
|
14
14
|
import { DEFAULTS } from '../core/state.js';
|
|
15
|
+
// O padrao dos DOIS EIXOS (ADR-0076/#104). A marca pergunta ao modelo novo, nao ao espelho p.viz.
|
|
16
|
+
import { PADRAO as PADRAO_VISUAL } from '../render/viz-axes.js';
|
|
15
17
|
import { markChanged, markMenuChanged } from './changed-mark.js';
|
|
16
18
|
/** Contrast levels, in cycle order — mirrors game.js's HC_SEQ (also used there by the physical contrast-cycle button). */
|
|
17
19
|
export const CONTRAST_LEVELS = ['normal', 'hc-direto', 'hc-direto-45', 'hc-direto-7'];
|
|
@@ -77,6 +79,21 @@ export function rgbToHex(rgb) {
|
|
|
77
79
|
export function onOffLabel(on) {
|
|
78
80
|
return toggleLabel(on);
|
|
79
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* Lê `player[i].visual` defensivamente, sem importar o tipo do jogador — o mesmo molde do `playerViz` abaixo,
|
|
84
|
+
* e pela mesma razão: `getPlayers()` devolve `readonly unknown[]` porque o tipo real é do JOGO (ADR-0033), e
|
|
85
|
+
* cada consumidor estreita para a SUA fatia.
|
|
86
|
+
*
|
|
87
|
+
* ⚠️ Sem jogador ou sem o campo, devolve o PADRÃO — que é a resposta certa para «esta criança mexeu em
|
|
88
|
+
* alguma coisa?»: quem não existe não mexeu. Inventar `hc7` aqui marcaria um menu que ninguém tocou.
|
|
89
|
+
*/
|
|
90
|
+
function playerVisual(list, i) {
|
|
91
|
+
const v = list[i]?.visual;
|
|
92
|
+
return {
|
|
93
|
+
tema: typeof v?.tema === 'string' ? v.tema : PADRAO_VISUAL.tema,
|
|
94
|
+
correcao: typeof v?.correcao === 'string' ? v.correcao : PADRAO_VISUAL.correcao,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
80
97
|
/** Reads player[i].viz defensively (no player at that index -> 'normal'), without a Player type import. */
|
|
81
98
|
function playerViz(list, i) {
|
|
82
99
|
const p = list[i];
|
|
@@ -144,7 +161,11 @@ export function initSettingsVisual(ctx) {
|
|
|
144
161
|
ctx.setSelectedPlayer(selected);
|
|
145
162
|
const contrastValue = resolveVisualMode(playerViz(ctx.getPlayers(), selected));
|
|
146
163
|
const settings = ctx.getVisualSettings();
|
|
147
|
-
|
|
164
|
+
// ⚠️ `renderEixosVisuais` E NÃO `renderVizGroup` desde a #104: este painel passou a ter DOIS controles,
|
|
165
|
+
// e o `renderVizGroup` continua a servir o painel de EMPATIA, cuja lista de simulações é mesmo exclusiva.
|
|
166
|
+
// Trocar o corpo daquela função em vez de acrescentar esta teria posto os dois eixos na lista de
|
|
167
|
+
// simulações — foi o que quase aconteceu, e o que a separação impede.
|
|
168
|
+
ctx.renderEixosVisuais('#visual-modes', '#visual-players');
|
|
148
169
|
el.innerHTML = renderVisualPanelHtml(contrastValue, settings);
|
|
149
170
|
const lq = ctx.$('#opt-lq');
|
|
150
171
|
const lqv = ctx.$('#opt-lq-val');
|
|
@@ -182,6 +203,8 @@ export function initSettingsVisual(ctx) {
|
|
|
182
203
|
rr.addEventListener('click', () => { ctx.resetRoleColors(); render(); });
|
|
183
204
|
reflectOutlines();
|
|
184
205
|
refreshMarks();
|
|
206
|
+
// A prosa volta para o rodapé depois de as linhas serem reconstruídas (CLAUDE.md §4, #109).
|
|
207
|
+
ctx.fillExplain?.(ctx.$('#visual .overlay__card'));
|
|
185
208
|
}
|
|
186
209
|
/**
|
|
187
210
|
* A marca de "saiu do padrão" (ADR-0029). Cada linha contra o SEU padrão, e o botão do menu por cima.
|
|
@@ -193,7 +216,27 @@ export function initSettingsVisual(ctx) {
|
|
|
193
216
|
*/
|
|
194
217
|
function refreshMarks() {
|
|
195
218
|
const s = ctx.getVisualSettings();
|
|
196
|
-
|
|
219
|
+
/**
|
|
220
|
+
* 🔴 A MARCA É POR EIXO DESDE 2026-09-08, e a granularidade que ela recupera foi perdida por mim na #104
|
|
221
|
+
* etapa 4d. Enquanto o `#visual-modes` era UM rádio de sete opções, marcar o contentor era marcar a
|
|
222
|
+
* escolha. A etapa 4d pôs DOIS eixos lá dentro, e uma marca no contentor deixou de dizer QUAL saiu do
|
|
223
|
+
* padrão — que é o terceiro canal do ADR-0029 a perder informação: «a criança cega percorre o menu e OUVE,
|
|
224
|
+
* em ordem, o que saiu do padrão». Ouvir «mudado» sem saber de quê manda-a procurar em oito linhas.
|
|
225
|
+
*
|
|
226
|
+
* ⚠️ E VEM DO MODELO NOVO. Isto era `resolveVisualMode(playerViz(...))`, que lê o `p.viz` que a etapa 1a
|
|
227
|
+
* marcou `@deprecated` — um leitor que ficou para trás na migração.
|
|
228
|
+
*
|
|
229
|
+
* 📌 E A REGRA DA SIMULAÇÃO DEIXA DE SER DERIVADA E PASSA A SER ESTRUTURAL, que é o ganho de fundo. Ela
|
|
230
|
+
* está escrita logo acima: uma simulação de empatia não marca ESTE menu, porque a marca dela pertence ao
|
|
231
|
+
* menu de empatia e é lá que ela leva a criança. Antes isso dependia de o `resolveVisualMode` responder
|
|
232
|
+
* `normal`; agora a simulação vive noutro campo do `VisualState`, e perguntar pelo tema e pela correcção
|
|
233
|
+
* nunca a alcança.
|
|
234
|
+
*/
|
|
235
|
+
const visual = playerVisual(ctx.getPlayers(), ctx.getSelectedPlayer());
|
|
236
|
+
const tema = visual.tema !== PADRAO_VISUAL.tema;
|
|
237
|
+
const correcao = visual.correcao !== PADRAO_VISUAL.correcao;
|
|
238
|
+
const linhaDoEixo = (eixo) => ctx.$(`#visual-modes button[data-eixo="${eixo}"][aria-checked="true"]`)
|
|
239
|
+
?.closest('.ctrl-row') ?? null;
|
|
197
240
|
const lqOff = s.lq !== DEFAULTS.lq;
|
|
198
241
|
const owner = s.ownerColors !== DEFAULTS.ownerColors;
|
|
199
242
|
const cb = s.cbSafe !== DEFAULTS.cbSafe;
|
|
@@ -201,14 +244,15 @@ export function initSettingsVisual(ctx) {
|
|
|
201
244
|
const bg = s.outlineBg !== DEFAULTS.hcOutlineBg;
|
|
202
245
|
const papeis = ROLE_KEYS.some((k) => !sameRgb(s.roleColors[k], HC_ROLE_DEF[k]));
|
|
203
246
|
const linha = (sel) => ctx.$(sel)?.closest('.ctrl-row') ?? null;
|
|
204
|
-
markChanged(
|
|
247
|
+
markChanged(linhaDoEixo('tema'), tema);
|
|
248
|
+
markChanged(linhaDoEixo('correcao'), correcao);
|
|
205
249
|
markChanged(linha('#opt-lq'), lqOff);
|
|
206
250
|
markChanged(linha('#opt-ownercolors'), owner);
|
|
207
251
|
markChanged(linha('#opt-cbsafe'), cb);
|
|
208
252
|
markChanged(linha('#opt-outline-fg'), fg);
|
|
209
253
|
markChanged(linha('#opt-outline-bg'), bg);
|
|
210
254
|
markChanged(linha('#opt-role-reset'), papeis);
|
|
211
|
-
markMenuChanged(ctx.$('[data-act="visual"]'), [
|
|
255
|
+
markMenuChanged(ctx.$('[data-act="visual"]'), [tema, correcao, lqOff, owner, cb, fg, bg, papeis]);
|
|
212
256
|
}
|
|
213
257
|
// ---- restaurar os padrões DESTE menu (ADR-0028) ----
|
|
214
258
|
//
|
package/dist-pkg/ui/shell.d.ts
CHANGED
|
@@ -32,9 +32,19 @@ export type { FatosDaCena } from '../core/scenes.js';
|
|
|
32
32
|
export interface PhaseView {
|
|
33
33
|
/** `#title-overlay`.hidden — o splash só aparece no título. */
|
|
34
34
|
titleOverlayHidden: boolean;
|
|
35
|
-
/**
|
|
36
|
-
*
|
|
37
|
-
|
|
35
|
+
/**
|
|
36
|
+
* ⚠️ `pauseOverlayHidden` SAIU EM 2026-09-08, e a ausência é a notícia.
|
|
37
|
+
*
|
|
38
|
+
* Ele era `true` em toda fase, e existia porque a Etapa 2 aposentou a pausa GLOBAL sem apagar o elemento: a
|
|
39
|
+
* casca continuava a procurá-lo e a escondê-lo a cada troca de fase, com dois gates a afirmar que ele ficava
|
|
40
|
+
* escondido. Código a segurar um cadáver.
|
|
41
|
+
*
|
|
42
|
+
* E o cadáver custava mais do que as linhas: aquelas quarenta linhas de `#pause-overlay` no `index.html` do
|
|
43
|
+
* cartucho eram o menu de pausa com aspecto mais OFICIAL do repositório — o que o próximo autor de cartucho
|
|
44
|
+
* copia junto com o ficheiro —, e já tinham derivado do que a engine gera (dois itens «Comunicação», e um
|
|
45
|
+
* `#opt-letra` que hoje sai do `dynLabel`). Um campo que diz «está escondido» aceita que ele exista; não o
|
|
46
|
+
* ter diz que ele não existe.
|
|
47
|
+
*/
|
|
38
48
|
/** `.screen-pause`.hidden de CADA tela — os menus por tela só aparecem na pausa. */
|
|
39
49
|
screenPauseHidden: boolean;
|
|
40
50
|
/** `setMasterMuted(...)` — GAG: fora de 'playing' TODO o som cala (loops de ambiente/chuva inclusive). */
|
package/dist-pkg/ui/shell.js
CHANGED
|
@@ -99,7 +99,6 @@ import { PM_ITENS_VISIVEIS } from './pause-icons.js';
|
|
|
99
99
|
export function phaseView(f) {
|
|
100
100
|
return {
|
|
101
101
|
titleOverlayHidden: !f.telaDeTitulo,
|
|
102
|
-
pauseOverlayHidden: true,
|
|
103
102
|
screenPauseHidden: !f.menuDePausa,
|
|
104
103
|
masterMuted: !f.mundoRodando,
|
|
105
104
|
hideTouchControls: !f.mundoRodando,
|
|
@@ -308,11 +307,11 @@ export function initShell(ctx) {
|
|
|
308
307
|
/* ===================== a troca de fase ===================== */
|
|
309
308
|
/** A metade IMPURA: pega a projeção pronta e a carimba no documento. */
|
|
310
309
|
function applyPhaseView(v) {
|
|
311
|
-
|
|
310
|
+
// A pausa GLOBAL não é procurada: ela foi aposentada na Etapa 2, e a casca deixou de a segurar em
|
|
311
|
+
// 2026-09-08. Ver a nota no `PhaseView`, onde o campo estava.
|
|
312
|
+
const t = ctx.$('#title-overlay');
|
|
312
313
|
if (t)
|
|
313
314
|
t.hidden = v.titleOverlayHidden;
|
|
314
|
-
if (pa)
|
|
315
|
-
pa.hidden = v.pauseOverlayHidden; // pausa GLOBAL aposentada (Etapa 2): agora é uma por tela
|
|
316
315
|
ctx.getPauseScreens().forEach((sp) => { sp.hidden = v.screenPauseHidden; });
|
|
317
316
|
}
|
|
318
317
|
/** O estado do `#touch-controls` ANTES de qualquer coisa desta troca de fase mexer nele. */
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
import { type MotivoIndisponivel, type VisualState } from '../render/viz-axes.js';
|
|
3
|
+
/** Uma linha pronta a traduzir: a chave e o que ela precisa. `null` = não há nada a dizer. */
|
|
4
|
+
export interface Recusa {
|
|
5
|
+
readonly chave: string;
|
|
6
|
+
/** O que a criança tem de desfazer para a demonstração ser honesta. Entra na frase por `{eixo}`. */
|
|
7
|
+
readonly eixo: MotivoIndisponivel;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* A chave i18n do motivo, por eixo fora do padrão.
|
|
11
|
+
*
|
|
12
|
+
* ⚠️ TRÊS CHAVES E NÃO UMA COM PARÂMETRO, e a diferença é de tradução e não de estilo: em português «o tema»
|
|
13
|
+
* e «a correção de cor» levam artigos diferentes, e «os dois» não é o plural de nenhum dos dois. Uma frase
|
|
14
|
+
* com `{eixo}` obrigaria cada idioma a montar concordância a partir de um substantivo solto — que é
|
|
15
|
+
* exactamente o defeito que o `sr.nav.clockOne` já registou para «às 1 horas».
|
|
16
|
+
*/
|
|
17
|
+
export declare const CHAVE_DO_MOTIVO: Readonly<Record<MotivoIndisponivel, string>>;
|
|
18
|
+
/**
|
|
19
|
+
* Esta simulação pode ser oferecida? E se não, o que dizer.
|
|
20
|
+
*
|
|
21
|
+
* `null` = pode; a interface não mostra nada, porque um aviso que aparece sempre deixa de ser lido.
|
|
22
|
+
*/
|
|
23
|
+
export declare function recusaDaSimulacao(v: VisualState): Recusa | null;
|
|
24
|
+
/**
|
|
25
|
+
* A simulação deve aparecer DESABILITADA em vez de sumir?
|
|
26
|
+
*
|
|
27
|
+
* ⚠️ SEMPRE, E É A METADE «never silently removed» DO ADR-0076. Existe como função com nome próprio, e não
|
|
28
|
+
* como um `!recusa` no ponto de uso, porque ela responde a uma pergunta diferente: `recusaDaSimulacao` diz
|
|
29
|
+
* *por que* não dá; esta diz *que a linha continua na tela*. Quem desenha precisa das duas, e juntá-las numa
|
|
30
|
+
* só faria a resposta «não há motivo» parecer «não desenhe a linha».
|
|
31
|
+
*/
|
|
32
|
+
export declare function mostraMesmoIndisponivel(): boolean;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// ui/simulation-refusal — POR QUE A SIMULAÇÃO NÃO ESTÁ DISPONÍVEL, dito à criança (ADR-0076, issue #104).
|
|
3
|
+
//
|
|
4
|
+
// ========================= O QUE A RECUSA TEM DE FAZER, E O QUE ELA NÃO PODE FAZER =========================
|
|
5
|
+
// O ADR-0076 exige que uma simulação indisponível apareça **VISÍVEL e explicada** — «never silently removed,
|
|
6
|
+
// never accepted then ignored». As duas metades dessa frase descrevem dois defeitos diferentes:
|
|
7
|
+
//
|
|
8
|
+
// · REMOVER EM SILÊNCIO ensina que a coisa não existe. Um adulto que ontem mostrou a simulação a uma turma
|
|
9
|
+
// e hoje não a acha conclui que ela foi tirada, e não que ele próprio ligou o alto contraste.
|
|
10
|
+
// · ACEITAR E IGNORAR é pior: a demonstração PARECE estar a correr. Sobre uma tela já corrigida, ela não
|
|
11
|
+
// mostra nem a deficiência nem a correção; sobre um tema de alto contraste, mostra o que o TEMA faz e não
|
|
12
|
+
// o que a deuteranopia faz. ⚠️ Não é uma demonstração mais fraca — **ela ensina uma coisa falsa**.
|
|
13
|
+
//
|
|
14
|
+
// ⚠️ E O MOTIVO É UM FACTO SOBRE A DEMONSTRAÇÃO, NUNCA UMA REPREENSÃO A QUEM ESCOLHEU. Quem ligou o alto
|
|
15
|
+
// contraste ligou-o porque precisa; quem ligou a correção de cor vê melhor com ela. A frase diz o que a
|
|
16
|
+
// demonstração precisa para ser honesta, e diz como voltar — nunca «desligue isso».
|
|
17
|
+
//
|
|
18
|
+
// ========================= POR QUE ISTO É UM MÓDULO PURO =========================
|
|
19
|
+
// Mesma forma do `ui/reach-notice`: devolve DADO (chave i18n + parâmetros), não texto pronto. A frase é da
|
|
20
|
+
// interface e tem de passar por `t()`; devolver português daqui repetiria o defeito que o `PADWIZ_STEPS` já
|
|
21
|
+
// deixou de cometer. E, sendo puro, o desenho da recusa é conferível no project `node`, sem documento.
|
|
22
|
+
//
|
|
23
|
+
// Módulo-folha: importa só os tipos e o predicado do modelo de dois eixos.
|
|
24
|
+
import { simulacaoIndisponivel } from '../render/viz-axes.js';
|
|
25
|
+
/**
|
|
26
|
+
* A chave i18n do motivo, por eixo fora do padrão.
|
|
27
|
+
*
|
|
28
|
+
* ⚠️ TRÊS CHAVES E NÃO UMA COM PARÂMETRO, e a diferença é de tradução e não de estilo: em português «o tema»
|
|
29
|
+
* e «a correção de cor» levam artigos diferentes, e «os dois» não é o plural de nenhum dos dois. Uma frase
|
|
30
|
+
* com `{eixo}` obrigaria cada idioma a montar concordância a partir de um substantivo solto — que é
|
|
31
|
+
* exactamente o defeito que o `sr.nav.clockOne` já registou para «às 1 horas».
|
|
32
|
+
*/
|
|
33
|
+
export const CHAVE_DO_MOTIVO = Object.freeze({
|
|
34
|
+
tema: 'sim.indisponivel.tema',
|
|
35
|
+
correcao: 'sim.indisponivel.correcao',
|
|
36
|
+
ambos: 'sim.indisponivel.ambos',
|
|
37
|
+
});
|
|
38
|
+
/**
|
|
39
|
+
* Esta simulação pode ser oferecida? E se não, o que dizer.
|
|
40
|
+
*
|
|
41
|
+
* `null` = pode; a interface não mostra nada, porque um aviso que aparece sempre deixa de ser lido.
|
|
42
|
+
*/
|
|
43
|
+
export function recusaDaSimulacao(v) {
|
|
44
|
+
const motivo = simulacaoIndisponivel(v);
|
|
45
|
+
return motivo === null ? null : { chave: CHAVE_DO_MOTIVO[motivo], eixo: motivo };
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* A simulação deve aparecer DESABILITADA em vez de sumir?
|
|
49
|
+
*
|
|
50
|
+
* ⚠️ SEMPRE, E É A METADE «never silently removed» DO ADR-0076. Existe como função com nome próprio, e não
|
|
51
|
+
* como um `!recusa` no ponto de uso, porque ela responde a uma pergunta diferente: `recusaDaSimulacao` diz
|
|
52
|
+
* *por que* não dá; esta diz *que a linha continua na tela*. Quem desenha precisa das duas, e juntá-las numa
|
|
53
|
+
* só faria a resposta «não há motivo» parecer «não desenhe a linha».
|
|
54
|
+
*/
|
|
55
|
+
export function mostraMesmoIndisponivel() {
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
import { type Tema, type Correcao, type VisualState } from '../render/viz-axes.js';
|
|
3
|
+
/** O rótulo de cada tema. Chave i18n — quem exibe resolve, como todo o resto do menu. */
|
|
4
|
+
export declare const ROTULO_DO_TEMA: Readonly<Record<Tema, string>>;
|
|
5
|
+
/** O rótulo de cada correção de cor. */
|
|
6
|
+
export declare const ROTULO_DA_CORRECAO: Readonly<Record<Correcao, string>>;
|
|
7
|
+
/**
|
|
8
|
+
* Os rótulos CURTOS, para os ícones da barra rápida — «7:1», «deuteranopia».
|
|
9
|
+
*
|
|
10
|
+
* ⚠️ SÃO AS CHAVES QUE JÁ EXISTIAM (`contrast.*`, `cvd.*`), re-chaveadas por eixo. A barra rápida sempre
|
|
11
|
+
* falou curto porque anuncia UM ícone de cada vez, e o painel sempre falou por extenso porque a criança está
|
|
12
|
+
* a ler uma lista — a diferença é de contexto e sobrevive à divisão. Reaproveitar em vez de traduzir de novo
|
|
13
|
+
* é o que mantém a mesma palavra nos dois sítios.
|
|
14
|
+
*/
|
|
15
|
+
export declare const CURTO_DO_TEMA: Readonly<Record<Tema, string>>;
|
|
16
|
+
export declare const CURTO_DA_CORRECAO: Readonly<Record<Correcao, string>>;
|
|
17
|
+
/** Os dois eixos, como o painel os identifica no DOM. */
|
|
18
|
+
export type EixoVisual = 'tema' | 'correcao';
|
|
19
|
+
/** Um tradutor, igual ao que o resto da interface recebe. */
|
|
20
|
+
export type Tradutor = (chave: string, params?: Record<string, string | number>) => string;
|
|
21
|
+
/**
|
|
22
|
+
* As linhas de UM eixo, no formato de rádio que o painel já usa.
|
|
23
|
+
*
|
|
24
|
+
* ⚠️ RÁDIO E NÃO SETE BOTÕES, e o motivo sobrevive à divisão: DENTRO de um eixo os valores continuam
|
|
25
|
+
* exclusivos — um tema de cada vez, uma correção de cada vez. O que deixou de ser exclusivo é a relação
|
|
26
|
+
* ENTRE os eixos, e é por isso que eles viram dois rádios em vez de um.
|
|
27
|
+
*
|
|
28
|
+
* ⚠️ E MANTÉM-SE A FORMA DE LINHA VISÍVEL, não um `<select>`. O `settings-visual` regista o erro que o Dev
|
|
29
|
+
* apanhou na primeira tentativa: dentro de uma caixa fechada, um controle cuja razão de existir é ser ACHADO
|
|
30
|
+
* por quem enxerga mal fica «quase o mesmo que não ter movido».
|
|
31
|
+
*/
|
|
32
|
+
export declare function linhasDoEixo(eixo: EixoVisual, valores: readonly string[], rotulos: Readonly<Record<string, string>>, atual: string, t: Tradutor): string;
|
|
33
|
+
/**
|
|
34
|
+
* Os DOIS eixos, um a seguir ao outro, com um título cada.
|
|
35
|
+
*
|
|
36
|
+
* O título existe porque dois rádios seguidos sem nome são um rádio de oito para quem lê depressa — e essa
|
|
37
|
+
* leitura é exactamente o mal-entendido que a divisão existe para desfazer.
|
|
38
|
+
*/
|
|
39
|
+
export declare function eixosHtml(v: VisualState, t: Tradutor): string;
|
|
40
|
+
/** O que um clique num botão do painel quer dizer. `null` quando o botão não é de eixo nenhum. */
|
|
41
|
+
export interface EscolhaDeEixo {
|
|
42
|
+
readonly eixo: EixoVisual;
|
|
43
|
+
readonly valor: string;
|
|
44
|
+
}
|
|
45
|
+
export declare function escolhaDoBotao(dataset: {
|
|
46
|
+
eixo?: string;
|
|
47
|
+
valor?: string;
|
|
48
|
+
}): EscolhaDeEixo | null;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// ui/visual-axes-panel — O MENU VISUAL COM DOIS CONTROLES (ADR-0076, issue #104), na metade pura.
|
|
3
|
+
//
|
|
4
|
+
// ========================= O COMENTÁRIO QUE AUTORIZA ESTA MUDANÇA =========================
|
|
5
|
+
// O `ui/settings-visual` justifica o controle ÚNICO com estas palavras, e elas continuam lá, verdadeiras
|
|
6
|
+
// sobre o dia em que foram escritas:
|
|
7
|
+
//
|
|
8
|
+
// «Os sete vivem num CONTROLE SÓ, e isso não é economia de espaço: `p.viz` guarda UM valor. Dois
|
|
9
|
+
// controles separados se sobrescreveriam em silêncio — a criança escolheria a correção, depois o
|
|
10
|
+
// contraste, e perderia a correção sem nada dizer que perdeu.»
|
|
11
|
+
//
|
|
12
|
+
// ⚠️ A RAZÃO QUE ELE DÁ DEIXOU DE EXISTIR. `p.viz` já não guarda um valor: guarda dois eixos mais a
|
|
13
|
+
// simulação, e os escritores por eixo (`setTemaDoJogador`/`setCorrecaoDoJogador`) mexem num sem tocar no
|
|
14
|
+
// outro. O controle único era a forma honesta de contar uma exclusividade REAL; mantê-lo agora seria contar
|
|
15
|
+
// uma exclusividade que já não existe — e continuar a negar os dois a quem precisa dos dois.
|
|
16
|
+
//
|
|
17
|
+
// ========================= OS NOMES DOS PADRÕES SÃO DECISÃO, NÃO ROTINA =========================
|
|
18
|
+
// ⚠️ «Tema padrão» e «Visão tricromática», e o ADR-0076 fecha a *definition of done* com a regra que os
|
|
19
|
+
// escolheu: **nenhum rótulo de padrão diagnostica quem lê**. `modo sem deficiência visual` foi oferecido e
|
|
20
|
+
// recusado — ele diz à criança o que ela NÃO é, no menu que ela abriu para conseguir jogar. Um padrão
|
|
21
|
+
// chama-se pelo que ele É.
|
|
22
|
+
//
|
|
23
|
+
// E `Modo padrão` (o `viz.normal` de hoje) deixa de servir aqui: era o neutro PARTILHADO, de quando os dois
|
|
24
|
+
// eixos eram um só. Com dois controles, um «padrão» sem dizer padrão de QUÊ é ambíguo em ambos.
|
|
25
|
+
//
|
|
26
|
+
// Módulo puro: devolve HTML e chaves i18n, não toca documento nenhum. Quem monta é o `render/viz-setters`.
|
|
27
|
+
import { TEMAS, CORRECOES } from '../render/viz-axes.js';
|
|
28
|
+
/** O rótulo de cada tema. Chave i18n — quem exibe resolve, como todo o resto do menu. */
|
|
29
|
+
export const ROTULO_DO_TEMA = Object.freeze({
|
|
30
|
+
padrao: 'eixo.tema.padrao',
|
|
31
|
+
hc3: 'viz.hc-direto',
|
|
32
|
+
hc45: 'viz.hc-direto-45',
|
|
33
|
+
hc7: 'viz.hc-direto-7',
|
|
34
|
+
});
|
|
35
|
+
/** O rótulo de cada correção de cor. */
|
|
36
|
+
export const ROTULO_DA_CORRECAO = Object.freeze({
|
|
37
|
+
tricro: 'eixo.correcao.tricro',
|
|
38
|
+
protan: 'viz.fix-protan',
|
|
39
|
+
deuter: 'viz.fix-deuter',
|
|
40
|
+
tritan: 'viz.fix-tritan',
|
|
41
|
+
});
|
|
42
|
+
/**
|
|
43
|
+
* Os rótulos CURTOS, para os ícones da barra rápida — «7:1», «deuteranopia».
|
|
44
|
+
*
|
|
45
|
+
* ⚠️ SÃO AS CHAVES QUE JÁ EXISTIAM (`contrast.*`, `cvd.*`), re-chaveadas por eixo. A barra rápida sempre
|
|
46
|
+
* falou curto porque anuncia UM ícone de cada vez, e o painel sempre falou por extenso porque a criança está
|
|
47
|
+
* a ler uma lista — a diferença é de contexto e sobrevive à divisão. Reaproveitar em vez de traduzir de novo
|
|
48
|
+
* é o que mantém a mesma palavra nos dois sítios.
|
|
49
|
+
*/
|
|
50
|
+
export const CURTO_DO_TEMA = Object.freeze({
|
|
51
|
+
padrao: 'contrast.off', hc3: 'contrast.3', hc45: 'contrast.45', hc7: 'contrast.7',
|
|
52
|
+
});
|
|
53
|
+
export const CURTO_DA_CORRECAO = Object.freeze({
|
|
54
|
+
tricro: 'cvd.off', protan: 'cvd.protan', deuter: 'cvd.deuter', tritan: 'cvd.tritan',
|
|
55
|
+
});
|
|
56
|
+
/**
|
|
57
|
+
* As linhas de UM eixo, no formato de rádio que o painel já usa.
|
|
58
|
+
*
|
|
59
|
+
* ⚠️ RÁDIO E NÃO SETE BOTÕES, e o motivo sobrevive à divisão: DENTRO de um eixo os valores continuam
|
|
60
|
+
* exclusivos — um tema de cada vez, uma correção de cada vez. O que deixou de ser exclusivo é a relação
|
|
61
|
+
* ENTRE os eixos, e é por isso que eles viram dois rádios em vez de um.
|
|
62
|
+
*
|
|
63
|
+
* ⚠️ E MANTÉM-SE A FORMA DE LINHA VISÍVEL, não um `<select>`. O `settings-visual` regista o erro que o Dev
|
|
64
|
+
* apanhou na primeira tentativa: dentro de uma caixa fechada, um controle cuja razão de existir é ser ACHADO
|
|
65
|
+
* por quem enxerga mal fica «quase o mesmo que não ter movido».
|
|
66
|
+
*/
|
|
67
|
+
export function linhasDoEixo(eixo, valores, rotulos, atual, t) {
|
|
68
|
+
return valores.map((valor) => {
|
|
69
|
+
const sel = valor === atual;
|
|
70
|
+
return `<div class="ctrl-row"><span><strong>${t(rotulos[valor])}</strong></span>`
|
|
71
|
+
+ `<button class="mode-btn${sel ? ' is-on' : ''}" role="radio" aria-checked="${sel}"`
|
|
72
|
+
+ ` data-eixo="${eixo}" data-valor="${valor}" type="button">${sel ? t('viz.escolhido') : t('viz.escolher')}</button></div>`;
|
|
73
|
+
}).join('');
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Os DOIS eixos, um a seguir ao outro, com um título cada.
|
|
77
|
+
*
|
|
78
|
+
* O título existe porque dois rádios seguidos sem nome são um rádio de oito para quem lê depressa — e essa
|
|
79
|
+
* leitura é exactamente o mal-entendido que a divisão existe para desfazer.
|
|
80
|
+
*/
|
|
81
|
+
export function eixosHtml(v, t) {
|
|
82
|
+
return `<h3 class="opt-sub">${t('eixo.tema.titulo')}</h3>`
|
|
83
|
+
+ linhasDoEixo('tema', TEMAS, ROTULO_DO_TEMA, v.tema, t)
|
|
84
|
+
+ `<h3 class="opt-sub">${t('eixo.correcao.titulo')}</h3>`
|
|
85
|
+
+ linhasDoEixo('correcao', CORRECOES, ROTULO_DA_CORRECAO, v.correcao, t);
|
|
86
|
+
}
|
|
87
|
+
export function escolhaDoBotao(dataset) {
|
|
88
|
+
const { eixo, valor } = dataset;
|
|
89
|
+
if (eixo !== 'tema' && eixo !== 'correcao')
|
|
90
|
+
return null;
|
|
91
|
+
if (!valor)
|
|
92
|
+
return null;
|
|
93
|
+
const validos = eixo === 'tema' ? TEMAS : CORRECOES;
|
|
94
|
+
return validos.includes(valor) ? { eixo, valor } : null;
|
|
95
|
+
}
|
package/dist-pkg/ui/webcam.js
CHANGED
|
@@ -7,6 +7,7 @@ import { t } from '../core/i18n.js';
|
|
|
7
7
|
import { $ } from './dom.js';
|
|
8
8
|
import { srAlert } from '../core/a11y-sr.js';
|
|
9
9
|
import { emFracao } from '../input/pointer-space.js'; // #105: um lugar so converte um ponto de tela
|
|
10
|
+
import { carimbarOrigem } from '../input/origem-sintetica.js'; // ADR-0109: a tecla sintetica declara quem a produziu
|
|
10
11
|
const wg = () => window.webgazer;
|
|
11
12
|
export let eyeMode = false; // ligado pelo botão; onGaze só age com ele ligado
|
|
12
13
|
export function setEyeMode(on) { eyeMode = on; }
|
|
@@ -16,7 +17,11 @@ function eyeSet(k, on, code) {
|
|
|
16
17
|
if (_eyeKeys[k] === on)
|
|
17
18
|
return;
|
|
18
19
|
_eyeKeys[k] = on;
|
|
19
|
-
|
|
20
|
+
// ⚠️ CARIMBADO `'olhos'` ANTES DE DESPACHAR (ADR-0109). Sem esta linha o evento chega ao `input/keydown`
|
|
21
|
+
// indistinguível de uma tecla premida e é carimbado `teclado` — e o efeito não é cosmético: a regra 3 diz
|
|
22
|
+
// que apertar uma tecla devolve o teclado SEM alternância, logo o olhar da criança desligaria, sozinho e em
|
|
23
|
+
// silêncio, a alternância de que ela depende para jogar. É o defeito que o §C desta issue existe para fechar.
|
|
24
|
+
const ev = carimbarOrigem(new KeyboardEvent(on ? 'keydown' : 'keyup', { code, bubbles: true }), 'olhos');
|
|
20
25
|
window.dispatchEvent(ev);
|
|
21
26
|
document.dispatchEvent(ev);
|
|
22
27
|
}
|
package/docs/CREDITS.md
CHANGED
|
@@ -62,3 +62,21 @@ A narração por voz roda **inteiramente no navegador/offline** graças ao traba
|
|
|
62
62
|
|
|
63
63
|
> Os pesos das vozes são **baixados uma vez** (de um host público) e rodam **100% localmente** depois — nenhum áudio de
|
|
64
64
|
> criança sai do dispositivo. Ver `docs/2-Architecture/adr/ADR-0065-three-neural-voices-owned-by-the-engine-and-cached-on-first-use.yaml`.
|
|
65
|
+
|
|
66
|
+
## Arte — Liberated Pixel Cup — CC BY-SA 3.0
|
|
67
|
+
|
|
68
|
+
A atribuição do [Liberated Pixel Cup](https://github.com/OpenGameArt/LiberatedPixelCup) é **por recurso**, e por
|
|
69
|
+
isso não cabe nesta página em prosa: o repositório do LCP guarda ficheiros de autoria separados para originais e
|
|
70
|
+
derivados, e a cadeia lê-se recurso a recurso.
|
|
71
|
+
|
|
72
|
+
**Ela mora em [`../art/lcp/ATTRIBUTION.csv`](../art/lcp/ATTRIBUTION.csv)** — caminho, autor, fonte, licença e de que
|
|
73
|
+
recursos cada um derivou. É a mesma forma que o `LICENSES.md` §4 já usa para as 100 gerações do PixelLab: um
|
|
74
|
+
ficheiro tabular ao lado do apontador em prosa, porque um livro-razão que cresce com o catálogo não se mantém à mão.
|
|
75
|
+
|
|
76
|
+
⚠️ **Atribuição aqui é condição de uso, não linha de crédito.** Os dois braços da licença exigem-na, e um recurso
|
|
77
|
+
sem autor conhecido **não entra** — «não consegui descobrir» não é licença. O gate
|
|
78
|
+
`tests/lcp-quarantine.node.test.js` reprova a entrada vazia, a entrada órfã e o recurso sem entrada, e a árvore
|
|
79
|
+
está em **quarentena** por uma razão que o [`LICENSES.md`](LICENSES.md) §3 e o **ADR-0107** explicam: misturar o LCP
|
|
80
|
+
com a arte própria licenciaria em share-alike a obra de alguém que não está neste repositório para discordar.
|
|
81
|
+
|
|
82
|
+
**Hoje o livro está vazio** — o LCP foi decidido em 2026-09-08 e ainda não entrou.
|
package/docs/LICENSES.md
CHANGED
|
@@ -72,6 +72,18 @@ Nem tudo aqui é nosso, e o que não é **não muda de licença por estar neste
|
|
|
72
72
|
- **Tipografias** — roster e restrições no **ADR-0012**. ⚠️ Ronde e as alternativas OPTIFrench-Script e
|
|
73
73
|
Merveille são **gratuitas só para uso pessoal e NÃO podem ser empacotadas**: oferece-se download, e a
|
|
74
74
|
opção fica desabilitada quando nenhuma está presente.
|
|
75
|
+
- **Arte de terceiros — o Liberated Pixel Cup** (`github.com/OpenGameArt/LiberatedPixelCup`), sob
|
|
76
|
+
**CC BY-SA 3.0** (**ADR-0107**). O conjunto é duplamente licenciado — CC BY-SA 3.0 **ou** GPL-3.0 — e o
|
|
77
|
+
projeto toma o braço **CC BY-SA**, para que arte continue governada como arte e não seja puxada para a
|
|
78
|
+
licença do CÓDIGO; é a mesma separação de regimes que este ficheiro tem em três linhas.
|
|
79
|
+
⚠️ **E ele vive em QUARENTENA, o que é mecanismo e não arrumação.** Nenhum recurso pode conter ao mesmo
|
|
80
|
+
tempo material derivado do LCP e a **arte própria** da autora: o SA alcança o que nós alterarmos — como já
|
|
81
|
+
está escrito acima para os pictogramas — e misturar os dois licenciaria a arte DELA em share-alike, que é
|
|
82
|
+
precisamente a decisão que o **pilar 10** deixa com ela e não connosco. O CC BY-SA 3.0 também é
|
|
83
|
+
**incompatível com a família GPL**, então a mesma quarentena é o que mantém o regime do código limpo.
|
|
84
|
+
⚠️ **Atribuição é condição de uso, por recurso**: o repositório do LCP guarda ficheiros de autoria
|
|
85
|
+
separados para originais e derivados. Recurso sem autor conhecido **não entra** — «não consegui descobrir»
|
|
86
|
+
não é licença.
|
|
75
87
|
|
|
76
88
|
---
|
|
77
89
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@the-inclusionist/engine",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "8.0.0-rc.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Accessibility-first (WCAG 2.2 + GAG) engine for educational 2D pixel-art games, built with PixiJS. Screen reader, sonar, high contrast by role, colour-vision filters, remappable input as intent, i18n and offline PWA — the parts every game inherits (ADR-0035, ADR-0036).",
|
|
6
6
|
"comment:exports": [
|
|
@@ -17,10 +17,25 @@
|
|
|
17
17
|
"form and it is the one every module in this repository already writes internally, so a consumer's",
|
|
18
18
|
"import line looks exactly like ours. Extensionless is NOT offered: supporting both needs two patterns",
|
|
19
19
|
"per layer, and two spellings of one import is how two halves of a codebase drift.",
|
|
20
|
-
"
|
|
20
|
+
"⚠️ `./assets/*` IS GONE (2026-09-07, issue #119). It matched the whole of `app/public/` while `files`",
|
|
21
|
+
"ships only `app/public/vendor`, so the pattern promised what it did not deliver and a consumer found",
|
|
22
|
+
"out through a 404 at runtime. Measured before removing: `app/public/` holds three entries — `vendor/`,",
|
|
23
|
+
"`_headers`, `icon.svg` — and only `vendor` ships, so everything the wide door could resolve in a",
|
|
24
|
+
"published tarball is already covered by `./assets/vendor/*`, and the rest already 404'd. Nobody's import",
|
|
25
|
+
"line changes: `engine/assets/vendor/fonts.css` still matches.",
|
|
26
|
+
"`./style.css` and `./assets/vendor/*` exist because the borrowed panels REQUIRE them and nothing said so — a",
|
|
21
27
|
"consumer that installed this package got dialogs with no styling and a typography menu offering 18",
|
|
22
28
|
"fonts that 404. Copy `assets/vendor/` into your own `public/` at build time: `fonts.css` addresses the",
|
|
23
|
-
"faces by RELATIVE url, so the folder travels whole or not at all."
|
|
29
|
+
"faces by RELATIVE url, so the folder travels whole or not at all.",
|
|
30
|
+
"⚠️ AND COPYING IS NOT ENOUGH — THE CONSUMER MUST ALSO *LINK* THE SHEET, which nothing said either:",
|
|
31
|
+
" <link rel=\"stylesheet\" href=\"/vendor/fonts.css\">",
|
|
32
|
+
" <link rel=\"preload\" href=\"/vendor/fonts/atkinson-400.woff2\" as=\"font\" type=\"font/woff2\" crossorigin>",
|
|
33
|
+
"Measured on 2026-09-07: NOBODY in this repository referenced `vendor/fonts.css`. The `app/index.html`",
|
|
34
|
+
"that used to link it left with the cartridge (issue #111), and the typography panel went on offering",
|
|
35
|
+
"seventeen families of which none loaded — the child picks one, the menu marks it active, and the browser",
|
|
36
|
+
"draws the system font. No error anywhere. `app/quiz.html` links it now, and",
|
|
37
|
+
"`tests/fontes-carregam.node.test.js` holds both halves: no offerable font without a `@font-face`, and",
|
|
38
|
+
"the host actually loading the sheet."
|
|
24
39
|
],
|
|
25
40
|
"exports": {
|
|
26
41
|
".": {
|
|
@@ -51,8 +66,12 @@
|
|
|
51
66
|
"types": "./dist-pkg/educational/*.d.ts",
|
|
52
67
|
"default": "./dist-pkg/educational/*.js"
|
|
53
68
|
},
|
|
69
|
+
"./i18n/*.js": {
|
|
70
|
+
"types": "./dist-pkg/i18n/*.d.ts",
|
|
71
|
+
"default": "./dist-pkg/i18n/*.js"
|
|
72
|
+
},
|
|
54
73
|
"./style.css": "./app/css/style.css",
|
|
55
|
-
"./assets/*": "./app/public/*",
|
|
74
|
+
"./assets/vendor/*": "./app/public/vendor/*",
|
|
56
75
|
"./package.json": "./package.json"
|
|
57
76
|
},
|
|
58
77
|
"types": "./dist-pkg/boot/create-game.d.ts",
|
|
@@ -133,6 +152,7 @@
|
|
|
133
152
|
"test:node": "vitest run --project node",
|
|
134
153
|
"test:browser": "vitest run --project browser",
|
|
135
154
|
"check:precache": "node scripts/check-precache.mjs",
|
|
155
|
+
"check:annual-report": "node scripts/check-annual-report.mjs",
|
|
136
156
|
"test:a11y": "node scripts/axe-check.mjs",
|
|
137
157
|
"release": "release-it"
|
|
138
158
|
},
|
|
Binary file
|
|
Binary file
|