@the-inclusionist/engine 10.0.0 โ†’ 11.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (528) hide show
  1. package/README.md +32 -1
  2. package/app/css/style.css +216 -42
  3. package/app/public/vendor/fonts-licences/NOTICE.txt +166 -0
  4. package/app/public/vendor/fonts-licences/OFL-1.1.txt +86 -0
  5. package/app/public/vendor/fonts.css +11 -1175
  6. package/dist-pkg/boot/create-game.d.ts +84 -33
  7. package/dist-pkg/boot/create-game.js +708 -307
  8. package/dist-pkg/core/a11y-sr.d.ts +1 -1
  9. package/dist-pkg/core/a11y-sr.js +3 -5
  10. package/dist-pkg/core/accommodations.d.ts +26 -8
  11. package/dist-pkg/core/accommodations.js +27 -10
  12. package/dist-pkg/core/actions.d.ts +39 -10
  13. package/dist-pkg/core/actions.js +28 -6
  14. package/dist-pkg/core/contract.d.ts +0 -2
  15. package/dist-pkg/core/contract.js +0 -2
  16. package/dist-pkg/core/i18n.d.ts +36 -71
  17. package/dist-pkg/core/i18n.js +131 -198
  18. package/dist-pkg/core/session-clock.d.ts +23 -0
  19. package/dist-pkg/core/session-clock.js +37 -0
  20. package/dist-pkg/i18n/en.js +44 -49
  21. package/dist-pkg/i18n/es.js +44 -49
  22. package/dist-pkg/i18n/pt.js +49 -54
  23. package/dist-pkg/input/gamepad.d.ts +42 -11
  24. package/dist-pkg/input/gamepad.js +50 -15
  25. package/dist-pkg/input/key-default.d.ts +30 -0
  26. package/dist-pkg/input/key-default.js +60 -0
  27. package/dist-pkg/input/keyboard-runtime.d.ts +0 -4
  28. package/dist-pkg/input/keyboard.d.ts +0 -1
  29. package/dist-pkg/input/keydown.d.ts +5 -11
  30. package/dist-pkg/input/keydown.js +3 -3
  31. package/dist-pkg/input/latch-edge.d.ts +10 -2
  32. package/dist-pkg/input/latch-edge.js +4 -3
  33. package/dist-pkg/input/latch-scope.d.ts +27 -22
  34. package/dist-pkg/input/latch-scope.js +25 -37
  35. package/dist-pkg/input/latch-store.d.ts +9 -12
  36. package/dist-pkg/input/latch-store.js +13 -18
  37. package/dist-pkg/input/latch-sync.d.ts +5 -4
  38. package/dist-pkg/input/latch-sync.js +5 -5
  39. package/dist-pkg/input/pad-reading.js +7 -2
  40. package/dist-pkg/input/pad-wizard.d.ts +4 -1
  41. package/dist-pkg/input/pad-wizard.js +60 -9
  42. package/dist-pkg/input/switch-scan.d.ts +29 -12
  43. package/dist-pkg/input/switch-scan.js +15 -2
  44. package/dist-pkg/input/touch-bindings.d.ts +3 -6
  45. package/dist-pkg/input/touch.d.ts +17 -2
  46. package/dist-pkg/input/touch.js +55 -17
  47. package/dist-pkg/input/transport-in-use.d.ts +5 -5
  48. package/dist-pkg/input/transport-in-use.js +5 -5
  49. package/dist-pkg/input/transports.d.ts +0 -9
  50. package/dist-pkg/input/transports.js +1 -1
  51. package/dist-pkg/input/virtual-controller.d.ts +39 -3
  52. package/dist-pkg/input/virtual-controller.js +32 -4
  53. package/dist-pkg/input/voice-map.d.ts +27 -11
  54. package/dist-pkg/input/voice-map.js +41 -43
  55. package/dist-pkg/platform/audio-ambient.d.ts +0 -1
  56. package/dist-pkg/platform/audio-ambient.js +3 -1
  57. package/dist-pkg/platform/audio-mixer.js +4 -9
  58. package/dist-pkg/platform/audio-sonar.d.ts +8 -94
  59. package/dist-pkg/platform/audio-sonar.js +22 -207
  60. package/dist-pkg/platform/audio.d.ts +10 -1
  61. package/dist-pkg/platform/audio.js +35 -2
  62. package/dist-pkg/platform/choose-by-voice.d.ts +70 -0
  63. package/dist-pkg/platform/choose-by-voice.js +157 -0
  64. package/dist-pkg/platform/font-library.d.ts +77 -0
  65. package/dist-pkg/platform/font-library.js +98 -0
  66. package/dist-pkg/platform/font-library.json +721 -0
  67. package/dist-pkg/platform/heavy-catalogue.d.ts +46 -0
  68. package/dist-pkg/platform/heavy-catalogue.js +47 -14
  69. package/dist-pkg/platform/heavy-mirror.d.ts +3 -9
  70. package/dist-pkg/platform/heavy-mirror.js +14 -17
  71. package/dist-pkg/platform/heavy.d.ts +40 -8
  72. package/dist-pkg/platform/heavy.js +177 -22
  73. package/dist-pkg/platform/interruptible-speech.d.ts +15 -8
  74. package/dist-pkg/platform/interruptible-speech.js +37 -13
  75. package/dist-pkg/platform/listener-scope.js +22 -13
  76. package/dist-pkg/platform/locale-host.d.ts +7 -3
  77. package/dist-pkg/platform/locale-host.js +13 -2
  78. package/dist-pkg/platform/reading-in-worker.d.ts +24 -0
  79. package/dist-pkg/platform/reading-in-worker.js +38 -0
  80. package/dist-pkg/platform/reading-model.d.ts +21 -0
  81. package/dist-pkg/platform/reading-model.js +25 -0
  82. package/dist-pkg/platform/reading-runtime.js +13 -8
  83. package/dist-pkg/platform/reading.d.ts +12 -1
  84. package/dist-pkg/platform/reading.js +9 -1
  85. package/dist-pkg/platform/speech-recognition.d.ts +10 -1
  86. package/dist-pkg/platform/speech-recognition.js +29 -9
  87. package/dist-pkg/platform/speech.d.ts +10 -1
  88. package/dist-pkg/platform/speech.js +24 -13
  89. package/dist-pkg/platform/tts.d.ts +37 -4
  90. package/dist-pkg/platform/tts.js +279 -57
  91. package/dist-pkg/platform/voice-listener.d.ts +6 -2
  92. package/dist-pkg/platform/voice-listener.js +1 -1
  93. package/dist-pkg/platform/voice-plan.d.ts +13 -0
  94. package/dist-pkg/platform/voice-plan.js +57 -2
  95. package/dist-pkg/platform/vosk-runtime.d.ts +25 -0
  96. package/dist-pkg/platform/vosk-runtime.js +59 -1
  97. package/dist-pkg/platform/vosk-vocabulary.d.ts +6 -0
  98. package/dist-pkg/platform/vosk-vocabulary.js +178 -0
  99. package/dist-pkg/render/crt.d.ts +11 -9
  100. package/dist-pkg/render/crt.js +6 -10
  101. package/dist-pkg/render/cvd-matrices.d.ts +5 -1
  102. package/dist-pkg/render/cvd-matrices.js +5 -1
  103. package/dist-pkg/render/high-contrast.d.ts +1 -2
  104. package/dist-pkg/render/high-contrast.js +5 -1
  105. package/dist-pkg/render/viz-axes.d.ts +9 -0
  106. package/dist-pkg/render/viz-axes.js +15 -2
  107. package/dist-pkg/ui/aac-sets.d.ts +29 -24
  108. package/dist-pkg/ui/aac-sets.js +22 -30
  109. package/dist-pkg/ui/audio-choices.d.ts +10 -5
  110. package/dist-pkg/ui/audio-choices.js +14 -10
  111. package/dist-pkg/ui/camera-control.d.ts +47 -0
  112. package/dist-pkg/ui/camera-control.js +35 -1
  113. package/dist-pkg/ui/declared-words.d.ts +21 -0
  114. package/dist-pkg/ui/declared-words.js +66 -0
  115. package/dist-pkg/ui/drawing-problems.js +4 -3
  116. package/dist-pkg/ui/eye-control.js +17 -17
  117. package/dist-pkg/ui/face-control.js +15 -15
  118. package/dist-pkg/ui/fonts.d.ts +42 -8
  119. package/dist-pkg/ui/fonts.js +58 -24
  120. package/dist-pkg/ui/footer-scroll-driver.d.ts +14 -0
  121. package/dist-pkg/ui/footer-scroll-driver.js +201 -0
  122. package/dist-pkg/ui/footer-scroll.d.ts +38 -0
  123. package/dist-pkg/ui/footer-scroll.js +55 -0
  124. package/dist-pkg/ui/game-options.d.ts +14 -9
  125. package/dist-pkg/ui/game-options.js +41 -21
  126. package/dist-pkg/ui/hand-control.js +15 -15
  127. package/dist-pkg/ui/help-panel.d.ts +19 -8
  128. package/dist-pkg/ui/help-panel.js +15 -5
  129. package/dist-pkg/ui/hud-bands.d.ts +29 -12
  130. package/dist-pkg/ui/hud-bands.js +63 -25
  131. package/dist-pkg/ui/hud-row.d.ts +34 -0
  132. package/dist-pkg/ui/hud-row.js +80 -0
  133. package/dist-pkg/ui/hud.d.ts +0 -3
  134. package/dist-pkg/ui/layout.js +5 -2
  135. package/dist-pkg/ui/libras-avatar-clip.d.ts +30 -0
  136. package/dist-pkg/ui/libras-avatar-clip.js +86 -0
  137. package/dist-pkg/ui/libras-avatar-load.d.ts +52 -0
  138. package/dist-pkg/ui/libras-avatar-load.js +134 -0
  139. package/dist-pkg/ui/libras-avatar-plan.d.ts +102 -0
  140. package/dist-pkg/ui/libras-avatar-plan.js +211 -0
  141. package/dist-pkg/ui/libras-avatar-player.d.ts +28 -0
  142. package/dist-pkg/ui/libras-avatar-player.js +226 -0
  143. package/dist-pkg/ui/libras-avatar-stage.d.ts +37 -0
  144. package/dist-pkg/ui/libras-avatar-stage.js +208 -0
  145. package/dist-pkg/ui/libras-glosses.d.ts +28 -0
  146. package/dist-pkg/ui/libras-glosses.js +161 -0
  147. package/dist-pkg/ui/menu-intent.d.ts +29 -0
  148. package/dist-pkg/ui/menu-intent.js +35 -1
  149. package/dist-pkg/ui/menu-items.d.ts +10 -0
  150. package/dist-pkg/ui/menu-items.js +29 -0
  151. package/dist-pkg/ui/menu-nav.d.ts +40 -4
  152. package/dist-pkg/ui/menu-nav.js +132 -47
  153. package/dist-pkg/ui/panel-widgets.d.ts +34 -0
  154. package/dist-pkg/ui/panel-widgets.js +86 -0
  155. package/dist-pkg/ui/pause-icons.d.ts +30 -14
  156. package/dist-pkg/ui/pause-icons.js +106 -45
  157. package/dist-pkg/ui/scan-overlay.d.ts +3 -3
  158. package/dist-pkg/ui/scan-overlay.js +11 -3
  159. package/dist-pkg/ui/screen-text.d.ts +25 -0
  160. package/dist-pkg/ui/screen-text.js +152 -0
  161. package/dist-pkg/ui/session-clock.d.ts +32 -0
  162. package/dist-pkg/ui/session-clock.js +67 -0
  163. package/dist-pkg/ui/settings-aac.d.ts +3 -1
  164. package/dist-pkg/ui/settings-aac.js +32 -13
  165. package/dist-pkg/ui/settings-audio.d.ts +10 -5
  166. package/dist-pkg/ui/settings-audio.js +101 -35
  167. package/dist-pkg/ui/settings-controls.d.ts +0 -4
  168. package/dist-pkg/ui/settings-mobility.d.ts +3 -6
  169. package/dist-pkg/ui/settings-mobility.js +4 -46
  170. package/dist-pkg/ui/settings-motion.js +2 -2
  171. package/dist-pkg/ui/settings-panel.d.ts +10 -0
  172. package/dist-pkg/ui/settings-panel.js +25 -1
  173. package/dist-pkg/ui/settings-typo.d.ts +5 -6
  174. package/dist-pkg/ui/settings-typo.js +20 -11
  175. package/dist-pkg/ui/settings-visual.js +2 -2
  176. package/dist-pkg/ui/shell.d.ts +1 -3
  177. package/dist-pkg/ui/shell.js +8 -4
  178. package/dist-pkg/ui/title.d.ts +2 -11
  179. package/dist-pkg/ui/title.js +39 -6
  180. package/dist-pkg/ui/top-band.d.ts +5 -3
  181. package/dist-pkg/ui/top-band.js +21 -25
  182. package/dist-pkg/ui/typo-choices.d.ts +3 -3
  183. package/dist-pkg/ui/typo-choices.js +9 -8
  184. package/dist-pkg/ui/vlibras.d.ts +71 -27
  185. package/dist-pkg/ui/vlibras.js +116 -44
  186. package/dist-pkg/ui/voice-control.d.ts +23 -1
  187. package/dist-pkg/ui/voice-control.js +149 -16
  188. package/dist-pkg/ui/voice-settings.d.ts +2 -3
  189. package/dist-pkg/ui/voice-settings.js +53 -3
  190. package/dist-pkg/ui/where-the-child-is.js +2 -2
  191. package/docs/CREDITS.md +122 -10
  192. package/docs/LICENSES.md +36 -7
  193. package/package.json +34 -9
  194. package/scripts/check-cartridge.mjs +78 -0
  195. package/scripts/game-build.d.mts +25 -0
  196. package/scripts/game-build.mjs +144 -0
  197. package/scripts/heavy-into-the-delivery.mjs +284 -40
  198. package/scripts/libras-avatar.json +680 -0
  199. package/scripts/libras-avatar.mjs +246 -0
  200. package/scripts/libras-glosses/gloss.py +95 -0
  201. package/scripts/libras-glosses/pyproject.toml +32 -0
  202. package/scripts/libras-glosses/uv.lock +781 -0
  203. package/scripts/libras-glosses.mjs +351 -0
  204. package/scripts/licences/OFL-1.1.txt +86 -0
  205. package/scripts/licences/UFL-1.0.txt +96 -0
  206. package/scripts/licences/fonts.mjs +389 -0
  207. package/scripts/licences/third-party.mjs +35 -21
  208. package/app/public/vendor/fonts/bitter-var-ext.woff2 +0 -0
  209. package/app/public/vendor/fonts/bitter-var.woff2 +0 -0
  210. package/app/public/vendor/fonts/bodonimoda-var-ext.woff2 +0 -0
  211. package/app/public/vendor/fonts/bodonimoda-var.woff2 +0 -0
  212. package/app/public/vendor/fonts/cat-abril_fatface-400-ext.woff2 +0 -0
  213. package/app/public/vendor/fonts/cat-abril_fatface-400.woff2 +0 -0
  214. package/app/public/vendor/fonts/cat-adlam_display-400-ext.woff2 +0 -0
  215. package/app/public/vendor/fonts/cat-adlam_display-400.woff2 +0 -0
  216. package/app/public/vendor/fonts/cat-agu_display-400-ext.woff2 +0 -0
  217. package/app/public/vendor/fonts/cat-agu_display-400.woff2 +0 -0
  218. package/app/public/vendor/fonts/cat-alfa_slab_one-400-ext.woff2 +0 -0
  219. package/app/public/vendor/fonts/cat-alfa_slab_one-400.woff2 +0 -0
  220. package/app/public/vendor/fonts/cat-are_you_serious-400-ext.woff2 +0 -0
  221. package/app/public/vendor/fonts/cat-are_you_serious-400.woff2 +0 -0
  222. package/app/public/vendor/fonts/cat-asimovian-400-ext.woff2 +0 -0
  223. package/app/public/vendor/fonts/cat-asimovian-400.woff2 +0 -0
  224. package/app/public/vendor/fonts/cat-atkinson_hyperlegible_next-400-ext.woff2 +0 -0
  225. package/app/public/vendor/fonts/cat-atkinson_hyperlegible_next-400.woff2 +0 -0
  226. package/app/public/vendor/fonts/cat-bakbak_one-400-ext.woff2 +0 -0
  227. package/app/public/vendor/fonts/cat-bakbak_one-400.woff2 +0 -0
  228. package/app/public/vendor/fonts/cat-bangers-400-ext.woff2 +0 -0
  229. package/app/public/vendor/fonts/cat-bangers-400.woff2 +0 -0
  230. package/app/public/vendor/fonts/cat-barriecito-400-ext.woff2 +0 -0
  231. package/app/public/vendor/fonts/cat-barriecito-400.woff2 +0 -0
  232. package/app/public/vendor/fonts/cat-berkshire_swash-400-ext.woff2 +0 -0
  233. package/app/public/vendor/fonts/cat-berkshire_swash-400.woff2 +0 -0
  234. package/app/public/vendor/fonts/cat-bevan-400-ext.woff2 +0 -0
  235. package/app/public/vendor/fonts/cat-bevan-400.woff2 +0 -0
  236. package/app/public/vendor/fonts/cat-bitcount-400-ext.woff2 +0 -0
  237. package/app/public/vendor/fonts/cat-bitcount-400.woff2 +0 -0
  238. package/app/public/vendor/fonts/cat-bowlby_one_sc-400-ext.woff2 +0 -0
  239. package/app/public/vendor/fonts/cat-bowlby_one_sc-400.woff2 +0 -0
  240. package/app/public/vendor/fonts/cat-bungee_shade-400-ext.woff2 +0 -0
  241. package/app/public/vendor/fonts/cat-bungee_shade-400.woff2 +0 -0
  242. package/app/public/vendor/fonts/cat-charm-400-ext.woff2 +0 -0
  243. package/app/public/vendor/fonts/cat-charm-400.woff2 +0 -0
  244. package/app/public/vendor/fonts/cat-charm-700-ext.woff2 +0 -0
  245. package/app/public/vendor/fonts/cat-charm-700.woff2 +0 -0
  246. package/app/public/vendor/fonts/cat-cherry_bomb_one-400-ext.woff2 +0 -0
  247. package/app/public/vendor/fonts/cat-cherry_bomb_one-400.woff2 +0 -0
  248. package/app/public/vendor/fonts/cat-chokokutai-400-ext.woff2 +0 -0
  249. package/app/public/vendor/fonts/cat-chokokutai-400.woff2 +0 -0
  250. package/app/public/vendor/fonts/cat-chonburi-400-ext.woff2 +0 -0
  251. package/app/public/vendor/fonts/cat-chonburi-400.woff2 +0 -0
  252. package/app/public/vendor/fonts/cat-climate_crisis-400-ext.woff2 +0 -0
  253. package/app/public/vendor/fonts/cat-climate_crisis-400.woff2 +0 -0
  254. package/app/public/vendor/fonts/cat-cookie-400.woff2 +0 -0
  255. package/app/public/vendor/fonts/cat-dela_gothic_one-400-ext.woff2 +0 -0
  256. package/app/public/vendor/fonts/cat-dela_gothic_one-400.woff2 +0 -0
  257. package/app/public/vendor/fonts/cat-fascinate_inline-400-ext.woff2 +0 -0
  258. package/app/public/vendor/fonts/cat-fascinate_inline-400.woff2 +0 -0
  259. package/app/public/vendor/fonts/cat-ga_maamli-400-ext.woff2 +0 -0
  260. package/app/public/vendor/fonts/cat-ga_maamli-400.woff2 +0 -0
  261. package/app/public/vendor/fonts/cat-itim-400-ext.woff2 +0 -0
  262. package/app/public/vendor/fonts/cat-itim-400.woff2 +0 -0
  263. package/app/public/vendor/fonts/cat-jacquard_24-400-ext.woff2 +0 -0
  264. package/app/public/vendor/fonts/cat-jacquard_24-400.woff2 +0 -0
  265. package/app/public/vendor/fonts/cat-kablammo-400-ext.woff2 +0 -0
  266. package/app/public/vendor/fonts/cat-kablammo-400.woff2 +0 -0
  267. package/app/public/vendor/fonts/cat-kalam-400-ext.woff2 +0 -0
  268. package/app/public/vendor/fonts/cat-kalam-400.woff2 +0 -0
  269. package/app/public/vendor/fonts/cat-kalam-700-ext.woff2 +0 -0
  270. package/app/public/vendor/fonts/cat-kalam-700.woff2 +0 -0
  271. package/app/public/vendor/fonts/cat-katibeh-400-ext.woff2 +0 -0
  272. package/app/public/vendor/fonts/cat-katibeh-400.woff2 +0 -0
  273. package/app/public/vendor/fonts/cat-lemonada-400-ext.woff2 +0 -0
  274. package/app/public/vendor/fonts/cat-lemonada-400.woff2 +0 -0
  275. package/app/public/vendor/fonts/cat-lilita_one-400-ext.woff2 +0 -0
  276. package/app/public/vendor/fonts/cat-lilita_one-400.woff2 +0 -0
  277. package/app/public/vendor/fonts/cat-lobster-400-ext.woff2 +0 -0
  278. package/app/public/vendor/fonts/cat-lobster-400.woff2 +0 -0
  279. package/app/public/vendor/fonts/cat-luckiest_guy-400-ext.woff2 +0 -0
  280. package/app/public/vendor/fonts/cat-luckiest_guy-400.woff2 +0 -0
  281. package/app/public/vendor/fonts/cat-marhey-400-ext.woff2 +0 -0
  282. package/app/public/vendor/fonts/cat-marhey-400.woff2 +0 -0
  283. package/app/public/vendor/fonts/cat-modak-400-ext.woff2 +0 -0
  284. package/app/public/vendor/fonts/cat-modak-400.woff2 +0 -0
  285. package/app/public/vendor/fonts/cat-momo_signature-400-ext.woff2 +0 -0
  286. package/app/public/vendor/fonts/cat-momo_signature-400.woff2 +0 -0
  287. package/app/public/vendor/fonts/cat-monofett-400-ext.woff2 +0 -0
  288. package/app/public/vendor/fonts/cat-monofett-400.woff2 +0 -0
  289. package/app/public/vendor/fonts/cat-monomakh-400-ext.woff2 +0 -0
  290. package/app/public/vendor/fonts/cat-monomakh-400.woff2 +0 -0
  291. package/app/public/vendor/fonts/cat-monoton-400-ext.woff2 +0 -0
  292. package/app/public/vendor/fonts/cat-monoton-400.woff2 +0 -0
  293. package/app/public/vendor/fonts/cat-nerko_one-400-ext.woff2 +0 -0
  294. package/app/public/vendor/fonts/cat-nerko_one-400.woff2 +0 -0
  295. package/app/public/vendor/fonts/cat-noto_sans_adlam-400-ext.woff2 +0 -0
  296. package/app/public/vendor/fonts/cat-noto_sans_adlam-400.woff2 +0 -0
  297. package/app/public/vendor/fonts/cat-noto_sans_arabic-400-ext.woff2 +0 -0
  298. package/app/public/vendor/fonts/cat-noto_sans_arabic-400.woff2 +0 -0
  299. package/app/public/vendor/fonts/cat-noto_sans_devanagari-400-ext.woff2 +0 -0
  300. package/app/public/vendor/fonts/cat-noto_sans_devanagari-400.woff2 +0 -0
  301. package/app/public/vendor/fonts/cat-noto_sans_hebrew-400-ext.woff2 +0 -0
  302. package/app/public/vendor/fonts/cat-noto_sans_hebrew-400.woff2 +0 -0
  303. package/app/public/vendor/fonts/cat-noto_sans_jp-400-ext.woff2 +0 -0
  304. package/app/public/vendor/fonts/cat-noto_sans_jp-400.woff2 +0 -0
  305. package/app/public/vendor/fonts/cat-noto_sans_khmer-400-ext.woff2 +0 -0
  306. package/app/public/vendor/fonts/cat-noto_sans_khmer-400.woff2 +0 -0
  307. package/app/public/vendor/fonts/cat-noto_sans_kr-400-ext.woff2 +0 -0
  308. package/app/public/vendor/fonts/cat-noto_sans_kr-400.woff2 +0 -0
  309. package/app/public/vendor/fonts/cat-noto_sans_sc-400-ext.woff2 +0 -0
  310. package/app/public/vendor/fonts/cat-noto_sans_sc-400.woff2 +0 -0
  311. package/app/public/vendor/fonts/cat-noto_sans_thai-400-ext.woff2 +0 -0
  312. package/app/public/vendor/fonts/cat-noto_sans_thai-400.woff2 +0 -0
  313. package/app/public/vendor/fonts/cat-ole-400-ext.woff2 +0 -0
  314. package/app/public/vendor/fonts/cat-ole-400.woff2 +0 -0
  315. package/app/public/vendor/fonts/cat-pacifico-400-ext.woff2 +0 -0
  316. package/app/public/vendor/fonts/cat-pacifico-400.woff2 +0 -0
  317. package/app/public/vendor/fonts/cat-patrick_hand-400-ext.woff2 +0 -0
  318. package/app/public/vendor/fonts/cat-patrick_hand-400.woff2 +0 -0
  319. package/app/public/vendor/fonts/cat-petit_formal_script-400-ext.woff2 +0 -0
  320. package/app/public/vendor/fonts/cat-petit_formal_script-400.woff2 +0 -0
  321. package/app/public/vendor/fonts/cat-pixelify_sans-400-ext.woff2 +0 -0
  322. package/app/public/vendor/fonts/cat-pixelify_sans-400.woff2 +0 -0
  323. package/app/public/vendor/fonts/cat-plaster-400-ext.woff2 +0 -0
  324. package/app/public/vendor/fonts/cat-plaster-400.woff2 +0 -0
  325. package/app/public/vendor/fonts/cat-playwrite_ar_guides-400-0.woff2 +0 -0
  326. package/app/public/vendor/fonts/cat-playwrite_at-400-0.woff2 +0 -0
  327. package/app/public/vendor/fonts/cat-playwrite_at_guides-400-0.woff2 +0 -0
  328. package/app/public/vendor/fonts/cat-playwrite_au_nsw-400-0.woff2 +0 -0
  329. package/app/public/vendor/fonts/cat-playwrite_au_nsw_guides-400-0.woff2 +0 -0
  330. package/app/public/vendor/fonts/cat-playwrite_au_qld-400-0.woff2 +0 -0
  331. package/app/public/vendor/fonts/cat-playwrite_au_qld_guides-400-0.woff2 +0 -0
  332. package/app/public/vendor/fonts/cat-playwrite_au_sa-400-0.woff2 +0 -0
  333. package/app/public/vendor/fonts/cat-playwrite_au_sa_guides-400-0.woff2 +0 -0
  334. package/app/public/vendor/fonts/cat-playwrite_au_tas-400-0.woff2 +0 -0
  335. package/app/public/vendor/fonts/cat-playwrite_au_tas_guides-400-0.woff2 +0 -0
  336. package/app/public/vendor/fonts/cat-playwrite_au_vic-400-0.woff2 +0 -0
  337. package/app/public/vendor/fonts/cat-playwrite_au_vic_guides-400-0.woff2 +0 -0
  338. package/app/public/vendor/fonts/cat-playwrite_be_vlg-400-0.woff2 +0 -0
  339. package/app/public/vendor/fonts/cat-playwrite_be_vlg_guides-400-0.woff2 +0 -0
  340. package/app/public/vendor/fonts/cat-playwrite_be_wal-400-0.woff2 +0 -0
  341. package/app/public/vendor/fonts/cat-playwrite_be_wal_guides-400-0.woff2 +0 -0
  342. package/app/public/vendor/fonts/cat-playwrite_br_guides-400-0.woff2 +0 -0
  343. package/app/public/vendor/fonts/cat-playwrite_ca_guides-400-0.woff2 +0 -0
  344. package/app/public/vendor/fonts/cat-playwrite_cl_guides-400-0.woff2 +0 -0
  345. package/app/public/vendor/fonts/cat-playwrite_co_guides-400-0.woff2 +0 -0
  346. package/app/public/vendor/fonts/cat-playwrite_cu_guides-400-0.woff2 +0 -0
  347. package/app/public/vendor/fonts/cat-playwrite_cz-400-0.woff2 +0 -0
  348. package/app/public/vendor/fonts/cat-playwrite_cz_guides-400-0.woff2 +0 -0
  349. package/app/public/vendor/fonts/cat-playwrite_de_grund-400-0.woff2 +0 -0
  350. package/app/public/vendor/fonts/cat-playwrite_de_grund_guides-400-0.woff2 +0 -0
  351. package/app/public/vendor/fonts/cat-playwrite_de_la-400-0.woff2 +0 -0
  352. package/app/public/vendor/fonts/cat-playwrite_de_la_guides-400-0.woff2 +0 -0
  353. package/app/public/vendor/fonts/cat-playwrite_de_sas-400-0.woff2 +0 -0
  354. package/app/public/vendor/fonts/cat-playwrite_de_sas_guides-400-0.woff2 +0 -0
  355. package/app/public/vendor/fonts/cat-playwrite_de_va-400-0.woff2 +0 -0
  356. package/app/public/vendor/fonts/cat-playwrite_de_va_guides-400-0.woff2 +0 -0
  357. package/app/public/vendor/fonts/cat-playwrite_dk_loopet-400-0.woff2 +0 -0
  358. package/app/public/vendor/fonts/cat-playwrite_dk_loopet_guides-400-0.woff2 +0 -0
  359. package/app/public/vendor/fonts/cat-playwrite_dk_uloopet-400-0.woff2 +0 -0
  360. package/app/public/vendor/fonts/cat-playwrite_dk_uloopet_guides-400-0.woff2 +0 -0
  361. package/app/public/vendor/fonts/cat-playwrite_es_deco_guides-400-0.woff2 +0 -0
  362. package/app/public/vendor/fonts/cat-playwrite_es_guides-400-0.woff2 +0 -0
  363. package/app/public/vendor/fonts/cat-playwrite_fr_moderne-400-0.woff2 +0 -0
  364. package/app/public/vendor/fonts/cat-playwrite_fr_moderne_guides-400-0.woff2 +0 -0
  365. package/app/public/vendor/fonts/cat-playwrite_fr_trad-400-0.woff2 +0 -0
  366. package/app/public/vendor/fonts/cat-playwrite_fr_trad_guides-400-0.woff2 +0 -0
  367. package/app/public/vendor/fonts/cat-playwrite_gb_j_guides-400-0.woff2 +0 -0
  368. package/app/public/vendor/fonts/cat-playwrite_gb_s_guides-400-0.woff2 +0 -0
  369. package/app/public/vendor/fonts/cat-playwrite_hr-400-0.woff2 +0 -0
  370. package/app/public/vendor/fonts/cat-playwrite_hr_guides-400-0.woff2 +0 -0
  371. package/app/public/vendor/fonts/cat-playwrite_hr_lijeva-400-0.woff2 +0 -0
  372. package/app/public/vendor/fonts/cat-playwrite_hr_lijeva_guides-400-0.woff2 +0 -0
  373. package/app/public/vendor/fonts/cat-playwrite_hu-400-0.woff2 +0 -0
  374. package/app/public/vendor/fonts/cat-playwrite_hu_guides-400-0.woff2 +0 -0
  375. package/app/public/vendor/fonts/cat-playwrite_id-400-0.woff2 +0 -0
  376. package/app/public/vendor/fonts/cat-playwrite_id_guides-400-0.woff2 +0 -0
  377. package/app/public/vendor/fonts/cat-playwrite_ie-400-0.woff2 +0 -0
  378. package/app/public/vendor/fonts/cat-playwrite_ie_guides-400-0.woff2 +0 -0
  379. package/app/public/vendor/fonts/cat-playwrite_in-400-0.woff2 +0 -0
  380. package/app/public/vendor/fonts/cat-playwrite_in_guides-400-0.woff2 +0 -0
  381. package/app/public/vendor/fonts/cat-playwrite_is-400-0.woff2 +0 -0
  382. package/app/public/vendor/fonts/cat-playwrite_is_guides-400-0.woff2 +0 -0
  383. package/app/public/vendor/fonts/cat-playwrite_it_moderna-400-0.woff2 +0 -0
  384. package/app/public/vendor/fonts/cat-playwrite_it_moderna_guides-400-0.woff2 +0 -0
  385. package/app/public/vendor/fonts/cat-playwrite_it_trad-400-0.woff2 +0 -0
  386. package/app/public/vendor/fonts/cat-playwrite_it_trad_guides-400-0.woff2 +0 -0
  387. package/app/public/vendor/fonts/cat-playwrite_mx_guides-400-0.woff2 +0 -0
  388. package/app/public/vendor/fonts/cat-playwrite_ng_modern-400-0.woff2 +0 -0
  389. package/app/public/vendor/fonts/cat-playwrite_ng_modern_guides-400-0.woff2 +0 -0
  390. package/app/public/vendor/fonts/cat-playwrite_nl-400-0.woff2 +0 -0
  391. package/app/public/vendor/fonts/cat-playwrite_nl_guides-400-0.woff2 +0 -0
  392. package/app/public/vendor/fonts/cat-playwrite_no-400-0.woff2 +0 -0
  393. package/app/public/vendor/fonts/cat-playwrite_no_guides-400-0.woff2 +0 -0
  394. package/app/public/vendor/fonts/cat-playwrite_nz-400-0.woff2 +0 -0
  395. package/app/public/vendor/fonts/cat-playwrite_nz_basic-400-0.woff2 +0 -0
  396. package/app/public/vendor/fonts/cat-playwrite_nz_basic_guides-400-0.woff2 +0 -0
  397. package/app/public/vendor/fonts/cat-playwrite_nz_guides-400-0.woff2 +0 -0
  398. package/app/public/vendor/fonts/cat-playwrite_pe_guides-400-0.woff2 +0 -0
  399. package/app/public/vendor/fonts/cat-playwrite_pl-400-0.woff2 +0 -0
  400. package/app/public/vendor/fonts/cat-playwrite_pl_guides-400-0.woff2 +0 -0
  401. package/app/public/vendor/fonts/cat-playwrite_pt_guides-400-0.woff2 +0 -0
  402. package/app/public/vendor/fonts/cat-playwrite_ro-400-0.woff2 +0 -0
  403. package/app/public/vendor/fonts/cat-playwrite_ro_guides-400-0.woff2 +0 -0
  404. package/app/public/vendor/fonts/cat-playwrite_sk-400-0.woff2 +0 -0
  405. package/app/public/vendor/fonts/cat-playwrite_sk_guides-400-0.woff2 +0 -0
  406. package/app/public/vendor/fonts/cat-playwrite_tz-400-0.woff2 +0 -0
  407. package/app/public/vendor/fonts/cat-playwrite_tz_guides-400-0.woff2 +0 -0
  408. package/app/public/vendor/fonts/cat-playwrite_us_modern_guides-400-0.woff2 +0 -0
  409. package/app/public/vendor/fonts/cat-playwrite_us_trad_guides-400-0.woff2 +0 -0
  410. package/app/public/vendor/fonts/cat-playwrite_vn-400-0.woff2 +0 -0
  411. package/app/public/vendor/fonts/cat-playwrite_vn_guides-400-0.woff2 +0 -0
  412. package/app/public/vendor/fonts/cat-playwrite_za-400-0.woff2 +0 -0
  413. package/app/public/vendor/fonts/cat-playwrite_za_guides-400-0.woff2 +0 -0
  414. package/app/public/vendor/fonts/cat-potta_one-400-ext.woff2 +0 -0
  415. package/app/public/vendor/fonts/cat-potta_one-400.woff2 +0 -0
  416. package/app/public/vendor/fonts/cat-ranchers-400-ext.woff2 +0 -0
  417. package/app/public/vendor/fonts/cat-ranchers-400.woff2 +0 -0
  418. package/app/public/vendor/fonts/cat-ranga-400-ext.woff2 +0 -0
  419. package/app/public/vendor/fonts/cat-ranga-400.woff2 +0 -0
  420. package/app/public/vendor/fonts/cat-ranga-700-ext.woff2 +0 -0
  421. package/app/public/vendor/fonts/cat-ranga-700.woff2 +0 -0
  422. package/app/public/vendor/fonts/cat-risque-400-ext.woff2 +0 -0
  423. package/app/public/vendor/fonts/cat-risque-400.woff2 +0 -0
  424. package/app/public/vendor/fonts/cat-rowdies-400-ext.woff2 +0 -0
  425. package/app/public/vendor/fonts/cat-rowdies-400.woff2 +0 -0
  426. package/app/public/vendor/fonts/cat-rowdies-700-ext.woff2 +0 -0
  427. package/app/public/vendor/fonts/cat-rowdies-700.woff2 +0 -0
  428. package/app/public/vendor/fonts/cat-ruge_boogie-400-ext.woff2 +0 -0
  429. package/app/public/vendor/fonts/cat-ruge_boogie-400.woff2 +0 -0
  430. package/app/public/vendor/fonts/cat-ruslan_display-400-ext.woff2 +0 -0
  431. package/app/public/vendor/fonts/cat-ruslan_display-400.woff2 +0 -0
  432. package/app/public/vendor/fonts/cat-sacramento-400-ext.woff2 +0 -0
  433. package/app/public/vendor/fonts/cat-sacramento-400.woff2 +0 -0
  434. package/app/public/vendor/fonts/cat-sancreek-400-ext.woff2 +0 -0
  435. package/app/public/vendor/fonts/cat-sancreek-400.woff2 +0 -0
  436. package/app/public/vendor/fonts/cat-sankofa_display-400-ext.woff2 +0 -0
  437. package/app/public/vendor/fonts/cat-sankofa_display-400.woff2 +0 -0
  438. package/app/public/vendor/fonts/cat-sedgwick_ave_display-400-ext.woff2 +0 -0
  439. package/app/public/vendor/fonts/cat-sedgwick_ave_display-400.woff2 +0 -0
  440. package/app/public/vendor/fonts/cat-shantell_sans-400-ext.woff2 +0 -0
  441. package/app/public/vendor/fonts/cat-shantell_sans-400.woff2 +0 -0
  442. package/app/public/vendor/fonts/cat-smokum-400-ext.woff2 +0 -0
  443. package/app/public/vendor/fonts/cat-smokum-400.woff2 +0 -0
  444. package/app/public/vendor/fonts/cat-solitreo-400-ext.woff2 +0 -0
  445. package/app/public/vendor/fonts/cat-solitreo-400.woff2 +0 -0
  446. package/app/public/vendor/fonts/cat-spicy_rice-400-ext.woff2 +0 -0
  447. package/app/public/vendor/fonts/cat-spicy_rice-400.woff2 +0 -0
  448. package/app/public/vendor/fonts/cat-sriracha-400-ext.woff2 +0 -0
  449. package/app/public/vendor/fonts/cat-sriracha-400.woff2 +0 -0
  450. package/app/public/vendor/fonts/cat-srisakdi-400-ext.woff2 +0 -0
  451. package/app/public/vendor/fonts/cat-srisakdi-400.woff2 +0 -0
  452. package/app/public/vendor/fonts/cat-srisakdi-700-ext.woff2 +0 -0
  453. package/app/public/vendor/fonts/cat-srisakdi-700.woff2 +0 -0
  454. package/app/public/vendor/fonts/cat-stix_two_math-400-0.woff2 +0 -0
  455. package/app/public/vendor/fonts/cat-tillana-400-ext.woff2 +0 -0
  456. package/app/public/vendor/fonts/cat-tillana-400.woff2 +0 -0
  457. package/app/public/vendor/fonts/cat-tillana-700-ext.woff2 +0 -0
  458. package/app/public/vendor/fonts/cat-tillana-700.woff2 +0 -0
  459. package/app/public/vendor/fonts/cat-titan_one-400-ext.woff2 +0 -0
  460. package/app/public/vendor/fonts/cat-titan_one-400.woff2 +0 -0
  461. package/app/public/vendor/fonts/cat-yatra_one-400-ext.woff2 +0 -0
  462. package/app/public/vendor/fonts/cat-yatra_one-400.woff2 +0 -0
  463. package/app/public/vendor/fonts/cat-yomogi-400-ext.woff2 +0 -0
  464. package/app/public/vendor/fonts/cat-yomogi-400.woff2 +0 -0
  465. package/app/public/vendor/fonts/dmserifdisplay-400-ext.woff2 +0 -0
  466. package/app/public/vendor/fonts/dmserifdisplay-400.woff2 +0 -0
  467. package/app/public/vendor/fonts/domine-var-ext.woff2 +0 -0
  468. package/app/public/vendor/fonts/domine-var.woff2 +0 -0
  469. package/app/public/vendor/fonts/fondamento-400-ext.woff2 +0 -0
  470. package/app/public/vendor/fonts/fondamento-400.woff2 +0 -0
  471. package/app/public/vendor/fonts/fraunces-var-ext.woff2 +0 -0
  472. package/app/public/vendor/fonts/fraunces-var.woff2 +0 -0
  473. package/app/public/vendor/fonts/fredoka-var-ext.woff2 +0 -0
  474. package/app/public/vendor/fonts/fredoka-var.woff2 +0 -0
  475. package/app/public/vendor/fonts/inter-400.woff2 +0 -0
  476. package/app/public/vendor/fonts/inter-700.woff2 +0 -0
  477. package/app/public/vendor/fonts/jakarta-var-ext.woff2 +0 -0
  478. package/app/public/vendor/fonts/jakarta-var.woff2 +0 -0
  479. package/app/public/vendor/fonts/lato-400.woff2 +0 -0
  480. package/app/public/vendor/fonts/lato-700.woff2 +0 -0
  481. package/app/public/vendor/fonts/literata-400.woff2 +0 -0
  482. package/app/public/vendor/fonts/literata-700.woff2 +0 -0
  483. package/app/public/vendor/fonts/lora-var-ext.woff2 +0 -0
  484. package/app/public/vendor/fonts/lora-var.woff2 +0 -0
  485. package/app/public/vendor/fonts/merriweather-var-ext.woff2 +0 -0
  486. package/app/public/vendor/fonts/merriweather-var.woff2 +0 -0
  487. package/app/public/vendor/fonts/newsreader-400.woff2 +0 -0
  488. package/app/public/vendor/fonts/newsreader-700.woff2 +0 -0
  489. package/app/public/vendor/fonts/notosans-var-ext.woff2 +0 -0
  490. package/app/public/vendor/fonts/notosans-var.woff2 +0 -0
  491. package/app/public/vendor/fonts/nunito-var-ext.woff2 +0 -0
  492. package/app/public/vendor/fonts/nunito-var.woff2 +0 -0
  493. package/app/public/vendor/fonts/opendyslexic-400.woff2 +0 -0
  494. package/app/public/vendor/fonts/opensans-400.woff2 +0 -0
  495. package/app/public/vendor/fonts/opensans-700.woff2 +0 -0
  496. package/app/public/vendor/fonts/pinyon-400.woff2 +0 -0
  497. package/app/public/vendor/fonts/playfair-var-ext.woff2 +0 -0
  498. package/app/public/vendor/fonts/playfair-var.woff2 +0 -0
  499. package/app/public/vendor/fonts/pressstart-400.woff2 +0 -0
  500. package/app/public/vendor/fonts/quattro-400.woff2 +0 -0
  501. package/app/public/vendor/fonts/quattro-700.woff2 +0 -0
  502. package/app/public/vendor/fonts/quicksand-var-ext.woff2 +0 -0
  503. package/app/public/vendor/fonts/quicksand-var.woff2 +0 -0
  504. package/app/public/vendor/fonts/robotoflex-var-ext.woff2 +0 -0
  505. package/app/public/vendor/fonts/robotoflex-var.woff2 +0 -0
  506. package/app/public/vendor/fonts/sora-var-ext.woff2 +0 -0
  507. package/app/public/vendor/fonts/sora-var.woff2 +0 -0
  508. package/app/public/vendor/fonts/sourcesans-400.woff2 +0 -0
  509. package/app/public/vendor/fonts/sourcesans-700.woff2 +0 -0
  510. package/app/public/vendor/fonts/sourceserif-400.woff2 +0 -0
  511. package/app/public/vendor/fonts/sourceserif-700.woff2 +0 -0
  512. package/app/public/vendor/fonts/spacegrotesk-var-ext.woff2 +0 -0
  513. package/app/public/vendor/fonts/spacegrotesk-var.woff2 +0 -0
  514. package/app/public/vendor/fonts/spectral-400-ext.woff2 +0 -0
  515. package/app/public/vendor/fonts/spectral-400.woff2 +0 -0
  516. package/app/public/vendor/fonts/spectral-700-ext.woff2 +0 -0
  517. package/app/public/vendor/fonts/spectral-700.woff2 +0 -0
  518. package/app/public/vendor/fonts/teachers-var-ext.woff2 +0 -0
  519. package/app/public/vendor/fonts/teachers-var.woff2 +0 -0
  520. package/app/public/vendor/fonts/ubuntu-400-ext.woff2 +0 -0
  521. package/app/public/vendor/fonts/ubuntu-400.woff2 +0 -0
  522. package/app/public/vendor/fonts/ubuntu-700-ext.woff2 +0 -0
  523. package/app/public/vendor/fonts/ubuntu-700.woff2 +0 -0
  524. package/app/public/vendor/fonts/ufmag-400.woff2 +0 -0
  525. package/dist-pkg/platform/guide-intensity.d.ts +0 -39
  526. package/dist-pkg/platform/guide-intensity.js +0 -71
  527. package/dist-pkg/ui/latch-refusal.d.ts +0 -33
  528. package/dist-pkg/ui/latch-refusal.js +0 -60
@@ -0,0 +1,67 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ import { readSession, clockWords, clockDigits } from '../core/session-clock.js';
3
+ /** One tick a second: the digits move by the second, and the hour's pie by a tenth of a degree, which reads as continuous. */
4
+ const TICK_MS = 1000;
5
+ /** Writes `text` into `node` only when it changed โ€” a rewrite is a repaint, and on school hardware a repaint costs. */
6
+ function write(node, text) {
7
+ if (node.textContent !== text)
8
+ node.textContent = text;
9
+ }
10
+ /**
11
+ * Builds the clock's three parts: the label and the digits in a column, the pie to their right.
12
+ *
13
+ * ๐Ÿ”ด THE PARTS ARE `aria-hidden`: they are the picture, and the image's name is what a listener gets. ARIA calls an image's
14
+ * children presentational, but not every accessibility API prunes them โ€” Gecko keeps a graphic's children unless it has exactly
15
+ * one text child, and NVDA then read ยซTEMPO. 56:07.ยป instead of the words (the Dev, 2026-09-25). Hidden here, no API has any
16
+ * text under the image to hand out. Gate: `tests/quiz-accessibility-tree.browser.test.js`, on the browser's own tree.
17
+ */
18
+ function build(doc) {
19
+ const part = (cls) => { const n = doc.createElement('span'); n.className = cls; return n; };
20
+ const el = doc.createElement('div');
21
+ el.className = 'session-clock';
22
+ el.setAttribute('role', 'img');
23
+ const text = part('session-clock-text');
24
+ const label = part('session-clock-label');
25
+ const digits = part('session-clock-digits');
26
+ const pie = part('session-clock-pie');
27
+ text.setAttribute('aria-hidden', 'true');
28
+ pie.setAttribute('aria-hidden', 'true');
29
+ text.append(label, digits);
30
+ el.append(text, pie);
31
+ return { el, label, digits, pie };
32
+ }
33
+ /** Mounts the clock in its slot, draws it now, and redraws it every second until `remove()`; `null` where it cannot. */
34
+ export function mountSessionClock(ctx) {
35
+ const { slot, performance: clock, setInterval: every } = ctx;
36
+ if (!slot || typeof slot.appendChild !== 'function' || !clock || typeof every !== 'function')
37
+ return null;
38
+ const { el, label, digits, pie } = build(ctx.doc);
39
+ slot.appendChild(el);
40
+ const timer = clock; // the guard's answer, kept for the tick
41
+ const start = timer.now();
42
+ let look = null;
43
+ function refresh() {
44
+ const reading = readSession(timer.now() - start);
45
+ const next = reading.over ? 'red' : 'running';
46
+ pie.style.setProperty('--left', reading.left.toFixed(4));
47
+ write(label, ctx.t('clock.label'));
48
+ write(digits, clockDigits(reading.leftMs));
49
+ const words = clockWords(reading);
50
+ const name = ctx.t(words.key, { minutes: words.minutes });
51
+ if (el.getAttribute('aria-label') !== name)
52
+ el.setAttribute('aria-label', name);
53
+ if (next !== 'running' && look === 'running')
54
+ ctx.announce(name);
55
+ if (next !== look)
56
+ el.dataset.look = next;
57
+ look = next;
58
+ ctx.drawn();
59
+ }
60
+ refresh();
61
+ const handle = every(refresh, TICK_MS);
62
+ return {
63
+ el,
64
+ refresh,
65
+ remove: () => { ctx.clearInterval?.(handle); el.remove(); },
66
+ };
67
+ }
@@ -25,7 +25,9 @@ export declare function lettersRowSpec(t: Translate): ControlRowSpec;
25
25
  */
26
26
  export declare function aacRowSpec(t: Translate, s: AacSet): ControlRowSpec;
27
27
  /**
28
- * The menu's three sections, in decision order: the section says WHOSE turn it is to act.
28
+ * The menu's two sections: what works now (the letters switch), and the pictogram sets, each only with a licence
29
+ * (ADR-0233). Whether a set row is locked is the row's own business (its reason and its `disabled`), so a licence that
30
+ * arrives changes one row, not the sections.
29
31
  *
30
32
  * ๐Ÿ“Œ Each produces its own rows instead of filtering the catalogue, because the first does not come from the catalogue
31
33
  * โ€” the letters are a switch and not a set, the Dev's decision that took them out of the list. A table whose first row
@@ -1,6 +1,6 @@
1
1
  // SPDX-License-Identifier: AGPL-3.0-or-later
2
2
  import { DEFAULTS } from '../core/setting-defaults.js';
3
- import { AAC_SETS, AAC_BY_KEY, aacReason } from './aac-sets.js';
3
+ import { AAC_SETS, AAC_BY_KEY, aacMenuLocked, aacReason, aacSelectable } from './aac-sets.js';
4
4
  import { markChanged, markMenuChanged } from './changed-mark.js';
5
5
  import { controlRow, labelRow, sectionHeader } from './panel-widgets.js';
6
6
  import { toggleLabel } from './dom.js';
@@ -39,7 +39,9 @@ export function aacRowSpec(t, s) {
39
39
  };
40
40
  }
41
41
  /**
42
- * The menu's three sections, in decision order: the section says WHOSE turn it is to act.
42
+ * The menu's two sections: what works now (the letters switch), and the pictogram sets, each only with a licence
43
+ * (ADR-0233). Whether a set row is locked is the row's own business (its reason and its `disabled`), so a licence that
44
+ * arrives changes one row, not the sections.
43
45
  *
44
46
  * ๐Ÿ“Œ Each produces its own rows instead of filtering the catalogue, because the first does not come from the catalogue
45
47
  * โ€” the letters are a switch and not a set, the Dev's decision that took them out of the list. A table whose first row
@@ -48,14 +50,9 @@ export function aacRowSpec(t, s) {
48
50
  export const AAC_SECTIONS = [
49
51
  { title: 'aac.secao.agora', tag: 'aac.secao.agoraTag', rows: (t) => [lettersRowSpec(t)] },
50
52
  {
51
- title: 'aac.secao.preparo',
52
- tag: 'aac.secao.preparoTag',
53
- rows: (t) => AAC_SETS.filter((s) => s.tier !== 'negotiating').map((s) => aacRowSpec(t, s)),
54
- },
55
- {
56
- title: 'aac.secao.negociacao',
57
- tag: 'aac.secao.negociacaoTag',
58
- rows: (t) => AAC_SETS.filter((s) => s.tier === 'negotiating').map((s) => aacRowSpec(t, s)),
53
+ title: 'aac.section.pictograms',
54
+ tag: 'aac.section.pictogramsTag',
55
+ rows: (t) => AAC_SETS.map((s) => aacRowSpec(t, s)),
59
56
  },
60
57
  ];
61
58
  /**
@@ -85,8 +82,8 @@ export function mountAacInside(t, ctx, list) {
85
82
  /**
86
83
  * A new row, with what the kit does not know about this panel: the set's key and the lock.
87
84
  *
88
- * No set is available today. A button that does nothing is worse than absence, because it spends trust โ€” so it comes
89
- * locked, and the reason is already in `ariaLabel` for whoever navigates by keyboard.
85
+ * No set is licensed today (ADR-0233). A button that does nothing is worse than absence, because it spends trust โ€” so an
86
+ * unlicensed set comes locked, and the reason is already in `ariaLabel` for whoever navigates by keyboard.
90
87
  */
91
88
  function newRow(ctx, spec) {
92
89
  const { row: row, control: control } = controlRow(ctx, spec);
@@ -96,7 +93,8 @@ function newRow(ctx, spec) {
96
93
  }
97
94
  const key = spec.id.replace('caa-set-', '');
98
95
  control.setAttribute('data-caa', key);
99
- if (!AAC_BY_KEY[key]?.available)
96
+ const set = AAC_BY_KEY[key];
97
+ if (!set || !aacSelectable(set))
100
98
  control.setAttribute('disabled', '');
101
99
  return row;
102
100
  }
@@ -137,7 +135,27 @@ export function initSettingsAac(ctx) {
137
135
  markChanged(t, ctx.$('#caa-letras'), changed);
138
136
  markMenuChanged(t, ctx.$('[data-act="caa"]'), [changed]);
139
137
  }
138
+ /**
139
+ * THE MENU'S DOOR IS DISABLED while no pictogram set is licensed (ADR-0233 erratum), with the pause card's lock
140
+ * (ADR-0161): `aria-disabled` and the reason in `data-motivo`, which the menu navigation speaks on reach and writes in
141
+ * the footer. The reason is ONLY ยซmenu disabledยป: the licence story is not the child's to hear. No unlock branch: the
142
+ * catalogue is static, so a licensed set means the door is never locked in the first place.
143
+ */
144
+ function lockDoor() {
145
+ const door = ctx.$('[data-act="caa"]');
146
+ if (!door || !aacMenuLocked())
147
+ return;
148
+ door.setAttribute('aria-disabled', 'true');
149
+ door.dataset.motivo = t('pause.motivo.caa');
150
+ }
140
151
  function open() {
152
+ // A game that wires its own door to `open` meets the same lock: the reason is said and nothing opens, so no
153
+ // per-set licence row reaches the child.
154
+ if (aacMenuLocked()) {
155
+ lockDoor();
156
+ ctx.srSay(t('pause.motivo.caa'));
157
+ return;
158
+ }
141
159
  const ov = ctx.$('#caa');
142
160
  if (!ov)
143
161
  return;
@@ -174,6 +192,7 @@ export function initSettingsAac(ctx) {
174
192
  const closeBtn = ctx.$('#caa-close');
175
193
  if (closeBtn)
176
194
  closeBtn.addEventListener('click', close);
195
+ lockDoor();
177
196
  // ---- reset THIS menu's defaults (ADR-0028) ----
178
197
  // The menu has one choice, so the reset is one line โ€” but where it returns to is worth saying: the default case,
179
198
  // where Brazilian literacy usually starts. A child who switched the case and can no longer read the screen needs a way
@@ -5,7 +5,6 @@ import type { PlayerAudioOut } from '../platform/audio-sonar.js';
5
5
  import type { DomQuery } from '../core/dom-query.js';
6
6
  import type { PanelShellCtx } from './panel-shell.js';
7
7
  import { type TtsPanel, type VoiceSettingsStore } from './voice-settings.js';
8
- export type { DomQuery } from '../core/dom-query.js';
9
8
  /** Minimal platform/storage.ts shape this module needs (get/set only โ€” no direct localStorage access). */
10
9
  export interface AudioStore {
11
10
  get(key: string, fallback?: string | null): string | null;
@@ -115,9 +114,14 @@ export interface SettingsAudioApi {
115
114
  renderAudio: () => void;
116
115
  /** Refreshes the #opt-modocego button. Exported because the host's blind-mode setter calls it directly. */
117
116
  reflectBlindMode: () => void;
118
- /** Refreshes the #opt-tts button + #tts-engine selection. Exported because the pause-menu icon bar toggles
119
- * audioCat.tts.on itself and then calls this. */
117
+ /** Refreshes the #opt-tts button, the #tts-vol slider and the #tts-engine selection. */
120
118
  reflectTts: () => void;
119
+ /**
120
+ * Shows ONE mixer category as the mixer holds it now โ€” its switch and its volume wherever this panel draws them, and the
121
+ * left-the-default marks โ€” without redrawing anything else. The host calls it for every change the mixer tells
122
+ * (`Audio.onCatChange`, ADR-0247): a category written by a game, the bar or another panel.
123
+ */
124
+ reflectCategory: (cat: string) => void;
121
125
  }
122
126
  /**
123
127
  * MOUNTS THIS PANEL'S INSIDE โ€” the controls and containers it reaches.
@@ -125,8 +129,9 @@ export interface SettingsAudioApi {
125
129
  * ๐Ÿ”ด IT IS THE LARGEST INVISIBLE CONTRACT OF THE PANELS: this file looks for `#opt-modocego`, `#cane-div`,
126
130
  * `#opt-menuindex`, `#opt-tts`, `#tts-vol` and more, and nothing in the type says so โ€” which is why it builds them.
127
131
  *
128
- * โš ๏ธ AND EACH ONE'S TAG MATTERS, which makes this the worst place to guess: `#cane-div` and the voice selects have to be
129
- * `<select>` โ€” the panel writes `.value` into them โ€”, and the volumes have to be `<input type=range>`. On a `<button>`,
132
+ * โš ๏ธ AND EACH ONE'S TAG MATTERS, which makes this the worst place to guess: the voice selects have to be `<select>` โ€” the
133
+ * panel writes `.value` into them โ€”, the volumes `<input type=range>`, and `#cane-div` is the cycle row `caneRow` builds
134
+ * (a host's own `<select>` is still read). On a `<button>`,
130
135
  * writing `.value` gives no error at all: it creates a property nobody reads, and the child's choice vanishes silently.
131
136
  *
132
137
  * ๐Ÿ“Œ THE ORDER IS THE DECISION (ADR-0044 ยง2), from general to particular (see the composition below).
@@ -13,7 +13,7 @@ import { toggleLabel } from './dom.js';
13
13
  import { DEFAULTS } from '../core/setting-defaults.js';
14
14
  import { markChanged, markMenuChanged } from './changed-mark.js';
15
15
  import { defaultAudioCat } from '../platform/audio-mixer.js';
16
- import { controlRow, labelRow } from './panel-widgets.js';
16
+ import { controlRow, labelRow, mountChoice, updateChoice } from './panel-widgets.js';
17
17
  /*
18
18
  * ๐Ÿ”ด THE VOICE SECTION LIVES IN `ui/voice-settings` (ADR-0221, issue #203): speech โ€” the narration switch, the engine, the
19
19
  * voice, the rate, the spoken index and the test button โ€” and the sound categories, cane, blind mode and outputs never
@@ -27,14 +27,50 @@ import { createVoiceSettings } from './voice-settings.js';
27
27
  * and the browser test drove the rest.
28
28
  */
29
29
  import { NAV_CATS, GEN_CATS, volPercent, navMasterVolume, parseCaneDiv, caneDivMessage, sinksSupported, sinkOptionLabel, sinkSelectValue, } from './audio-choices.js';
30
+ /** The cane's two positions โ€” one tap per block, one every half block โ€” in the page's language. */
31
+ function canePositions(t) {
32
+ return [{ value: '1', label: t('audio.cane.block') }, { value: '2', label: t('audio.cane.halfBlock') }];
33
+ }
34
+ /**
35
+ * THE CANE ROW, a CYCLE ROW ยซโ—€ Batida da bengala: uma batida por bloco โ–ถยป (ADR-0130 rule 3: two positions).
36
+ *
37
+ * ๐Ÿ”ด It was the kit's `escolha`, a `<select>` nobody filled: offered to every game with a walker, it opened on nothing to
38
+ * choose. Two positions are a cycle, and the positions are the engine's words.
39
+ *
40
+ * ๐Ÿ“Œ THE `<strong>` STAYS, HIDDEN: it is where the game's word for the cane lands (`ui/audio-rows-that-apply`, ADR-0153),
41
+ * and `renderAudio` reads it into the cycle's own text. `hidden` keeps it out of the accessibility tree, so the name is
42
+ * heard once โ€” from the control.
43
+ */
44
+ function caneRow(t, ctx, spec) {
45
+ const row = ctx.create('div');
46
+ row.className = 'ctrl-row ctrl-row--passos';
47
+ const text = ctx.create('span');
48
+ const name = ctx.create('strong');
49
+ name.textContent = spec.label;
50
+ name.hidden = true;
51
+ text.appendChild(name);
52
+ if (spec.hint) {
53
+ const explanation = ctx.create('span');
54
+ explanation.className = 'opt-hint';
55
+ explanation.textContent = spec.hint;
56
+ text.appendChild(explanation);
57
+ }
58
+ row.appendChild(text);
59
+ // the pick is `initSettingsAudio`'s, which listens for the `change` every choice fires: this builder has no setter
60
+ const control = mountChoice(ctx, { label: spec.label, values: canePositions(t), current: '1' }, () => { });
61
+ control.id = spec.id;
62
+ row.appendChild(control);
63
+ return row;
64
+ }
30
65
  /**
31
66
  * MOUNTS THIS PANEL'S INSIDE โ€” the controls and containers it reaches.
32
67
  *
33
68
  * ๐Ÿ”ด IT IS THE LARGEST INVISIBLE CONTRACT OF THE PANELS: this file looks for `#opt-modocego`, `#cane-div`,
34
69
  * `#opt-menuindex`, `#opt-tts`, `#tts-vol` and more, and nothing in the type says so โ€” which is why it builds them.
35
70
  *
36
- * โš ๏ธ AND EACH ONE'S TAG MATTERS, which makes this the worst place to guess: `#cane-div` and the voice selects have to be
37
- * `<select>` โ€” the panel writes `.value` into them โ€”, and the volumes have to be `<input type=range>`. On a `<button>`,
71
+ * โš ๏ธ AND EACH ONE'S TAG MATTERS, which makes this the worst place to guess: the voice selects have to be `<select>` โ€” the
72
+ * panel writes `.value` into them โ€”, the volumes `<input type=range>`, and `#cane-div` is the cycle row `caneRow` builds
73
+ * (a host's own `<select>` is still read). On a `<button>`,
38
74
  * writing `.value` gives no error at all: it creates a property nobody reads, and the child's choice vanishes silently.
39
75
  *
40
76
  * ๐Ÿ“Œ THE ORDER IS THE DECISION (ADR-0044 ยง2), from general to particular (see the composition below).
@@ -61,7 +97,8 @@ export function mountAudioInside(t, ctx, card, list) {
61
97
  // โš ๏ธ Their own short keys, not the bar's `icon.blind`/`icon.tts`: those carry ยซ(navegaรงรฃo sonora)ยป and ยซ(TTS)ยป, and
62
98
  // a row keeps no explanation in parentheses (ADR-0158).
63
99
  { id: 'opt-modocego', label: t('audio.modocego') },
64
- { id: 'cane-div', label: t('audio.cane'), hint: t('audio.cane.dica'), shape: 'escolha' },
100
+ // two positions, so a cycle row (ADR-0130 rule 3): built by `caneRow`, not by the kit
101
+ { id: 'cane-div', label: t('audio.cane'), hint: t('audio.cane.dica') },
65
102
  { container: '@lista' }, // the shell's list: sonar, guard and guide
66
103
  { id: 'opt-tts', label: t('audio.narracao'), hint: t('audio.tts.dica') },
67
104
  { id: 'tts-vol', label: t('audio.ttsVol'), shape: 'cursor' },
@@ -115,7 +152,7 @@ export function mountAudioInside(t, ctx, card, list) {
115
152
  labelRow(rowNode, piece);
116
153
  continue;
117
154
  }
118
- card.insertBefore(controlRow(ctx, piece).row, actions);
155
+ card.insertBefore(piece.id === 'cane-div' ? caneRow(t, ctx, piece) : controlRow(ctx, piece).row, actions);
119
156
  }
120
157
  }
121
158
  /**
@@ -364,31 +401,21 @@ export function initSettingsAudio(ctx) {
364
401
  }
365
402
  const players = ctx.getPlayers();
366
403
  const n = Math.max(1, ctx.getNumPlayers());
404
+ /*
405
+ * ๐Ÿ”ด A PLAYER'S OUTPUT IS A CHOICE, AND ITS SIZE PICKS THE CONTROL (ADR-0130 rule 3): ยซsharedยป and up to four devices are
406
+ * a cycle row ยซโ—€ Jogador 1: Fone USB โ–ถยป, more a dropdown. The size is the room's today โ€” it is drawn again at every
407
+ * detection, so a fifth headset plugged in turns the rows into dropdowns.
408
+ */
409
+ const outputs = [{ value: '', label: t('audio.sinkShared') }, ...devices.map((d, k) => ({ value: d.deviceId, label: sinkOptionLabel(t, d, k) }))];
367
410
  for (let i = 0; i < n; i++) {
368
411
  const p = players[i];
412
+ const name = t('audio.playerN', { n: i + 1 });
369
413
  const row = make('div');
370
414
  row.className = 'ctrl-row';
371
- const lbl = make('label');
372
- lbl.textContent = t('audio.playerN', { n: i + 1 });
373
- lbl.htmlFor = 'sink-p' + i;
374
- const sel = make('select');
375
- sel.className = 'vol';
376
- sel.id = 'sink-p' + i;
377
- const o0 = make('option');
378
- o0.value = '';
379
- o0.textContent = t('audio.sinkShared');
380
- sel.appendChild(o0);
381
- devices.forEach((d, k) => {
382
- const o = make('option');
383
- o.value = d.deviceId;
384
- o.textContent = sinkOptionLabel(t, d, k);
385
- sel.appendChild(o);
386
- });
387
- sel.value = sinkSelectValue(p);
388
- sel.addEventListener('change', () => {
415
+ const choice = mountChoice(kitCtx(el), { label: name, values: outputs, current: sinkSelectValue(p) }, (value) => {
389
416
  if (!p)
390
417
  return;
391
- p.audioSink = sel.value || null;
418
+ p.audioSink = value || null;
392
419
  ctx.store.set('incl_sink_p' + i, p.audioSink || '');
393
420
  if (p._ac) {
394
421
  try {
@@ -400,8 +427,17 @@ export function initSettingsAudio(ctx) {
400
427
  }
401
428
  ctx.srSay(t(p.audioSink ? 'sr.audio.sinkChanged' : 'sr.audio.sinkDefault', { n: i + 1 }));
402
429
  });
403
- row.appendChild(lbl);
404
- row.appendChild(sel);
430
+ choice.id = 'sink-p' + i;
431
+ if (choice.tagName === 'SELECT') {
432
+ // a dropdown keeps its name beside it; a cycle row already carries it in its own text
433
+ const lbl = make('label');
434
+ lbl.textContent = name;
435
+ lbl.htmlFor = choice.id;
436
+ row.appendChild(lbl);
437
+ }
438
+ else
439
+ row.classList.add('ctrl-row--passos');
440
+ row.appendChild(choice);
405
441
  el.appendChild(row);
406
442
  }
407
443
  }
@@ -438,9 +474,7 @@ export function initSettingsAudio(ctx) {
438
474
  renderNavSound();
439
475
  drawBlindMode();
440
476
  voice.render();
441
- const cd = ctx.$('#cane-div');
442
- if (cd)
443
- cd.value = String(ctx.getCaneBlockDiv());
477
+ reflectCane();
444
478
  void enumerateSinks(); // not awaited: fire and carry on (the asynchronous list updates by itself)
445
479
  refreshMarks();
446
480
  }
@@ -528,11 +562,28 @@ export function initSettingsAudio(ctx) {
528
562
  ctx.srSay(t(ctx.getBlindMode() ? 'sr.blind.on' : 'sr.blind.off'));
529
563
  });
530
564
  }
531
- const caneDivSel = ctx.$('#cane-div');
532
- if (caneDivSel) {
533
- caneDivSel.value = String(ctx.getCaneBlockDiv());
534
- caneDivSel.addEventListener('change', () => {
535
- const div = parseCaneDiv(caneDivSel.value);
565
+ /**
566
+ * The cane's position in use. A host's own `<select>` gets its value; the engine's cycle row (`caneRow`) its words and
567
+ * value, with its name read from the row's hidden `<strong>` โ€” where the game's word for the cane lands (ADR-0153).
568
+ */
569
+ function reflectCane() {
570
+ const cd = ctx.$('#cane-div');
571
+ if (!cd)
572
+ return;
573
+ const current = String(ctx.getCaneBlockDiv());
574
+ if (cd.tagName === 'SELECT') {
575
+ cd.value = current;
576
+ return;
577
+ }
578
+ const label = cd.closest('.ctrl-row')?.querySelector('strong')?.textContent || t('audio.cane');
579
+ updateChoice(cd, { label, values: canePositions(t), current });
580
+ }
581
+ const caneDiv = ctx.$('#cane-div');
582
+ if (caneDiv) {
583
+ reflectCane();
584
+ caneDiv.addEventListener('change', () => {
585
+ // a host's `<select>` holds its value; the engine's cycle row carries it in `data-value` (`mountChoice`)
586
+ const div = parseCaneDiv(caneDiv.tagName === 'SELECT' ? caneDiv.value : (caneDiv.dataset.value ?? ''));
536
587
  ctx.setCaneBlockDiv(div);
537
588
  ctx.srSay(caneDivMessage(t, div));
538
589
  });
@@ -596,5 +647,20 @@ export function initSettingsAudio(ctx) {
596
647
  * announce: the bar's icon already speaks for itself.
597
648
  */
598
649
  ctx.on('blindMode', () => { drawBlindMode(); }); // through the root's door: released by its `dispose()`
599
- return { renderAudio, reflectBlindMode: drawBlindMode, reflectTts: voice.reflectTts };
650
+ /** One category, in place: the lists hold rows only once the panel has drawn them, and a row not there is not drawn here. */
651
+ function reflectCategory(k) {
652
+ if (k === 'tts')
653
+ voice.reflectTts();
654
+ for (const sel of ['#audio-list', '#navsound-list']) {
655
+ const list = ctx.$(sel);
656
+ if (list)
657
+ reflectCatRows(list, [k]);
658
+ }
659
+ const navMaster = ctx.$('#navsound-master');
660
+ const state = ctx.getAudioCat();
661
+ if (navMaster && state)
662
+ navMaster.value = String(navMasterVolume(state, NAV_CATS));
663
+ refreshMarks();
664
+ }
665
+ return { renderAudio, reflectBlindMode: drawBlindMode, reflectTts: voice.reflectTts, reflectCategory };
600
666
  }
@@ -5,10 +5,6 @@ import type { KeyScheme } from '../core/entity.js';
5
5
  import { type Action } from '../core/actions.js';
6
6
  import type { KBDefaults } from '../input/keyboard.js';
7
7
  import type { KeydownEventLike } from '../input/keydown.js';
8
- /** Minimal DOM-selector shape (matches ui/dom.ts's `$`). */
9
- export type { DomQuery } from '../core/dom-query.js';
10
- /** action -> list of physical key codes (KeyboardEvent.code), e.g. {jump:['KeyJ','Space']}. */
11
- export type { KeyScheme } from '../core/entity.js';
12
8
  /** Opaque keyboard config (input/keyboard.ts's KBDefaults shape: {solo,p2,p3,p4}) โ€” never indexed directly here;
13
9
  * all per-player reads go through the injected `kbFor`, so this module stays decoupled from its exact shape. */
14
10
  /** `input/keyboard`'s `KBDefaults`, which is the owner. This module still does NOT INDEX the value โ€” every per-player
@@ -4,8 +4,6 @@ import type { Translate } from '../core/i18n.js';
4
4
  import type { DomQuery } from '../core/dom-query.js';
5
5
  import { type LatchPlayer as JogadorDaAlternanciaDaAresta } from '../input/latch-sync.js';
6
6
  import type { PanelShellCtx } from './panel-shell.js';
7
- /** Minimal DOM-selector shape (matches ui/dom.ts's `$`). */
8
- export type { DomQuery } from '../core/dom-query.js';
9
7
  /** Minimal platform/storage.ts shape this module needs. */
10
8
  export interface MobilityStore {
11
9
  setBool(key: string, on: boolean): void;
@@ -35,8 +33,7 @@ export interface SettingsMobilityCtx {
35
33
  * panel.
36
34
  *
37
35
  * โš ๏ธ Not offering it means making the row absent for everyone, with `hidden`, which takes it off the screen AND out of
38
- * the accessibility tree. An `aria-disabled` would be the wrong answer โ€” that is clause 3 of ADR-0113, where the
39
- * control EXISTS and is locked with a reason.
36
+ * the accessibility tree โ€” not an `aria-disabled` row that stays in the tab order doing nothing.
40
37
  *
41
38
  * โš ๏ธ REQUIRED, for the same reason as in `PauseIconsCtx`: there is no safe default. `true` leaves the row in a game
42
39
  * where it does nothing; `false` hides it from a child who depends on it.
@@ -120,8 +117,8 @@ export declare function toggleMoveKey(i: number): string;
120
117
  * flags. A minimal slice lets both pass without either carrying the other's.
121
118
  *
122
119
  * ๐Ÿ“Œ THE DEFINITION LIVES IN `input/latch-sync` (issue #127), beside the rule that uses it, and the NAME stays published
123
- * here. โš ๏ธ **An alias and not `export ... from`**: the names snapshot leaves re-exports out and would read the change of
124
- * home as a removal (the lesson of ADR-0106 step 1a).
120
+ * here, as an alias (ADR-0106 step 1a). Since issue #204 the names snapshot reads a re-export too, so either form keeps
121
+ * the name in this module's snapshot.
125
122
  */
126
123
  export type LatchPlayer = JogadorDaAlternanciaDaAresta;
127
124
  /** What the write needs to know. Everything here already lives in `SettingsMobilityCtx` and `PauseIconsCtx`. */
@@ -16,7 +16,6 @@ import { DEFAULTS } from '../core/setting-defaults.js';
16
16
  import { KEYS } from '../platform/storage-keys.js';
17
17
  import { writeLatch } from '../input/latch-store.js';
18
18
  import { applyLatch, BASE_DA_MARCHA, } from '../input/latch-sync.js';
19
- import { latchRefusal } from './latch-refusal.js';
20
19
  import { clampSelPlayer, anyMobilityActive, onOffLabel, playerTabsHTML, easyAnnouncement, playerPrefix } from './mobility-choices.js';
21
20
  import { controlRow, labelRow } from './panel-widgets.js';
22
21
  // ---------------------------------------------------------------------------------------------
@@ -64,10 +63,8 @@ export function setMoveLatch(ctx, i, on) {
64
63
  // reason: a cartridge may still READ it through `KEYS.toggleMoveP(i)`. Stopping writing it now would make the child
65
64
  // lose the choice at the next boot โ€” the cost ADR-0113 names.
66
65
  ctx.store.setBool(toggleMoveKey(i), on);
67
- // ๐Ÿ“Œ And the NEW key, when the device is known. `writeLatch` refuses on the four assisted transports, where there is no
68
- // choice to store (ADR-0113 clause 3) โ€” and returns `false` so a caller can disable the control with the reason said.
69
- // Here the refusal changes nothing else: the in-memory value is still what the rule resolves, and the rule answers
70
- // `true` on those four.
66
+ // ๐Ÿ“Œ And the NEW key, when the device is known โ€” on every device (ADR-0249): on eyes, face, gestures and speech this is the
67
+ // child's choice overriding the game's `holdsKeys()` default for that transport.
71
68
  const transport = ctx.transportInUse ? ctx.transportInUse(i) : null;
72
69
  if (transport)
73
70
  writeLatch((key, isOn) => ctx.store.setBool(key, isOn), BASE_DA_MARCHA, i, transport, on);
@@ -164,32 +161,20 @@ export function initSettingsMobility(ctx) {
164
161
  let selMovPlayer = 0; // the player selected in the mobility panel
165
162
  const easyModeButton = ctx.$('#opt-facil');
166
163
  const altMoveBtn = ctx.$('#opt-altmove');
167
- /**
168
- * THE ROW'S ORIGINAL HINT, kept once.
169
- *
170
- * โš ๏ธ HERE THE REFUSAL COMES AND GOES: this button is persistent, and the child may put the webcam down and go back to
171
- * the keyboard โ€” without keeping the original text, the reason would pile up on the row with every device change.
172
- */
173
164
  const altMoveRow = altMoveBtn?.closest('.ctrl-row') ?? null;
174
- const altMoveHint = altMoveRow?.querySelector('.opt-hint') ?? null;
175
- const originalHint = altMoveHint?.textContent ?? '';
176
165
  /*
177
166
  * ADR-0115 ยท THE ROW DISAPPEARS IN A GAME THAT HOLDS NOTHING โ€” and disappears for EVERYONE.
178
167
  *
179
168
  * ๐Ÿ”ด `hidden` and not `aria-disabled`: the child who depends on the latch opens this panel to switch it on, and in a
180
169
  * quiz there is nothing to switch on. A control disabled with a reason is still a control that does nothing โ€” and it
181
170
  * still takes a place in keyboard navigation, between two that work.
182
- * ๐Ÿ“Œ Clause 3 of ADR-0113 is the opposite case and stays intact: there the device REQUIRES the latch, the control
183
- * exists, and it is `aria-disabled` WITH the reason, reachable so it can be read.
171
+ * ๐Ÿ“Œ Where the game holds keys the row is offered on EVERY device, eyes and voice included (ADR-0249): there the latch
172
+ * starts as the game's answer, and this row is how the child changes it.
184
173
  * โš ๏ธ `hidden` and not removal: the row may be the page's own markup, and `hidden` is reversible and idempotent where
185
174
  * removing someone else's markup is neither.
186
175
  */
187
176
  if (!ctx.holdsKeys && altMoveRow)
188
177
  altMoveRow.hidden = true;
189
- /** THIS player's refusal now, or `null`. Recomputed on every reflect: the device in use changes. */
190
- function refusalFor(i) {
191
- return ctx.transportInUse ? latchRefusal(ctx.transportInUse(i)) : null;
192
- }
193
178
  const toggleRunBtn = ctx.$('#opt-togglerun');
194
179
  // the bar lights up if ANY player uses Easy Mode or a latch
195
180
  function reflectMovementBtn() {
@@ -249,23 +234,6 @@ export function initSettingsMobility(ctx) {
249
234
  altMoveBtn.classList.toggle('is-on', on);
250
235
  altMoveBtn.setAttribute('aria-pressed', String(on));
251
236
  altMoveBtn.textContent = onOffLabel(t, on);
252
- /*
253
- * โš ๏ธ CLAUSE 3 OF ADR-0113 ON SCREEN: where the latch is required, the control does NOT disappear โ€” it is
254
- * `aria-disabled` and the reason goes into the hint, which the shell (`ui/settings-panel.fillExplain`) moves to the
255
- * footer. Disappearing would teach that the thing does not exist; leaving it active would make the child press it
256
- * and not understand why nothing changed.
257
- *
258
- * ๐Ÿ“Œ And `aria-disabled`, not `disabled`: a really disabled button LEAVES the tab order, and a keyboard user could no
259
- * longer reach it โ€” so could no longer READ the reason. The same choice #128 names as a defect when made the other
260
- * way (a CSS class only, no `aria`).
261
- */
262
- const refusal = refusalFor(selMovPlayer);
263
- if (refusal)
264
- altMoveBtn.setAttribute('aria-disabled', 'true');
265
- else
266
- altMoveBtn.removeAttribute('aria-disabled');
267
- if (altMoveHint)
268
- altMoveHint.textContent = refusal ? `${originalHint} ${t(refusal.key)}`.trim() : originalHint;
269
237
  }
270
238
  reflectMovementBtn();
271
239
  }
@@ -308,16 +276,6 @@ export function initSettingsMobility(ctx) {
308
276
  }
309
277
  if (altMoveBtn) {
310
278
  altMoveBtn.addEventListener('click', () => {
311
- /*
312
- * โš ๏ธ REFUSE BY SAYING, NOT IN SILENCE. The listener is wired once, and a mute `return` would accept the click and
313
- * ignore it โ€” the other half of what ADR-0076 forbids. So the refusal SPEAKS: whoever pressed learns why, even
314
- * without seeing the hint.
315
- */
316
- const refusal = refusalFor(selMovPlayer);
317
- if (refusal) {
318
- ctx.srSay(t(refusal.key));
319
- return;
320
- }
321
279
  setToggleMove(selMovPlayer, !ctx.players[selMovPlayer].toggleMove);
322
280
  reflectAltMove();
323
281
  });
@@ -16,8 +16,8 @@ import { SCENE_KEYS, CHARACTER_ANIMATIONS, readStoredScene, storeScene } from '.
16
16
  * ๐Ÿ“Œ THE PURE HALF LIVES IN `ui/motion-choices` (ADR-0221), and the SUITE pointed at the seam:
17
17
  * `tests/settings-motion.node.test.js` exercises exactly these names, and the node project mounts no document.
18
18
  *
19
- * โš ๏ธ NO ALIAS: a re-export would keep alive a path nothing in here uses and make the surface snapshot lie, because it
20
- * does not see re-exports (issue #204).
19
+ * โš ๏ธ NO ALIAS: a re-export would keep alive a published path nothing in here uses โ€” surface with no consumer
20
+ * (issue #204).
21
21
  */
22
22
  import { RM_LABEL, CRT_LBL, CRT_ROUND_LEVELS, clampSelectedPlayer, allMotionFrozen, motionMasterLabel, sceneMotionAnnouncement, crtToggleAnnouncement, crtLevelLabel, crtRoundAnnouncement, stopResumeAllAnnouncement, } from './motion-choices.js';
23
23
  /*
@@ -62,6 +62,16 @@ export interface SettingsPanelApi {
62
62
  escapeTarget: () => string | null;
63
63
  /** The highest VISIBLE overlay in the stack. A z tie: the last in the DOM wins. */
64
64
  topVisibleOverlay: () => HTMLElement | null;
65
+ /**
66
+ * Draws the MODAL BOUNDARY at the front card (ADR-0130 rule 1): every visible layer under it โ€” the other overlays and
67
+ * the pause cards โ€” is `inert`, and nothing is when no overlay is open. Opening, `restoreFocus` and `closeById` call it themselves
68
+ * (the last two BEFORE any focus goes back, since an inert element refuses focus); a host whose dialogs can also be hidden by paths that
69
+ * return no focus calls it when a `hidden` changes โ€” `createGame` does, from its observer.
70
+ *
71
+ * OPTIONAL in the type only: `initSettingsPanel` always returns it, and an API built elsewhere (a host's own double of
72
+ * this shell) keeps compiling without a boundary to draw.
73
+ */
74
+ syncLayers?: () => void;
65
75
  /** Registered ids, in order โ€” for tests/debugging only. */
66
76
  registeredIds: () => string[];
67
77
  }