@the-inclusionist/engine 9.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 (532) hide show
  1. package/README.md +120 -78
  2. package/app/css/style.css +761 -377
  3. package/app/public/vendor/fonts/playwrite-cu.woff2 +0 -0
  4. package/app/public/vendor/fonts/playwrite-es-deco.woff2 +0 -0
  5. package/app/public/vendor/fonts/playwrite-es.woff2 +0 -0
  6. package/app/public/vendor/fonts/playwrite-gb-j.woff2 +0 -0
  7. package/app/public/vendor/fonts/playwrite-gb-s.woff2 +0 -0
  8. package/app/public/vendor/fonts/playwrite-pe.woff2 +0 -0
  9. package/app/public/vendor/fonts/playwrite-pt.woff2 +0 -0
  10. package/app/public/vendor/fonts-licences/NOTICE.txt +166 -0
  11. package/app/public/vendor/fonts-licences/OFL-1.1.txt +86 -0
  12. package/app/public/vendor/fonts.css +36 -84
  13. package/dist-pkg/boot/create-game.d.ts +499 -219
  14. package/dist-pkg/boot/create-game.js +3944 -562
  15. package/dist-pkg/core/a11y-sr.d.ts +25 -3
  16. package/dist-pkg/core/a11y-sr.js +38 -19
  17. package/dist-pkg/core/accessible-label.d.ts +17 -0
  18. package/dist-pkg/core/accessible-label.js +37 -0
  19. package/dist-pkg/core/accommodation-subjects.d.ts +26 -0
  20. package/dist-pkg/core/accommodation-subjects.js +32 -0
  21. package/dist-pkg/core/accommodations.d.ts +125 -0
  22. package/dist-pkg/core/accommodations.js +195 -0
  23. package/dist-pkg/core/actions.d.ts +108 -55
  24. package/dist-pkg/core/actions.js +122 -71
  25. package/dist-pkg/core/calm-mode.d.ts +31 -0
  26. package/dist-pkg/core/calm-mode.js +47 -0
  27. package/dist-pkg/core/camera-cycle.d.ts +15 -0
  28. package/dist-pkg/core/camera-cycle.js +7 -0
  29. package/dist-pkg/core/caption-duration.d.ts +15 -0
  30. package/dist-pkg/core/caption-duration.js +19 -0
  31. package/dist-pkg/core/cartridge-problems.d.ts +34 -0
  32. package/dist-pkg/core/cartridge-problems.js +53 -0
  33. package/dist-pkg/core/constants.d.ts +0 -54
  34. package/dist-pkg/core/constants.js +9 -76
  35. package/dist-pkg/core/contract.d.ts +142 -164
  36. package/dist-pkg/core/contract.js +222 -208
  37. package/dist-pkg/core/dom-query.d.ts +4 -4
  38. package/dist-pkg/core/dom-query.js +12 -20
  39. package/dist-pkg/core/entity.d.ts +62 -77
  40. package/dist-pkg/core/entity.js +18 -32
  41. package/dist-pkg/core/escape-html.d.ts +9 -10
  42. package/dist-pkg/core/escape-html.js +16 -20
  43. package/dist-pkg/core/flash-threshold.d.ts +17 -0
  44. package/dist-pkg/core/flash-threshold.js +113 -0
  45. package/dist-pkg/core/game-speed.d.ts +11 -0
  46. package/dist-pkg/core/game-speed.js +16 -0
  47. package/dist-pkg/core/genres.d.ts +20 -0
  48. package/dist-pkg/core/genres.js +44 -0
  49. package/dist-pkg/core/i18n.d.ts +71 -60
  50. package/dist-pkg/core/i18n.js +186 -200
  51. package/dist-pkg/core/loop.d.ts +17 -6
  52. package/dist-pkg/core/loop.js +26 -26
  53. package/dist-pkg/core/pause-icon-catalogue.d.ts +15 -0
  54. package/dist-pkg/core/pause-icon-catalogue.js +54 -0
  55. package/dist-pkg/core/ring.d.ts +17 -0
  56. package/dist-pkg/core/ring.js +29 -0
  57. package/dist-pkg/core/rng.d.ts +7 -12
  58. package/dist-pkg/core/rng.js +23 -53
  59. package/dist-pkg/core/route.d.ts +26 -26
  60. package/dist-pkg/core/route.js +126 -121
  61. package/dist-pkg/core/scenes.d.ts +37 -40
  62. package/dist-pkg/core/scenes.js +41 -43
  63. package/dist-pkg/core/screens.d.ts +4 -4
  64. package/dist-pkg/core/screens.js +11 -16
  65. package/dist-pkg/core/session-clock.d.ts +23 -0
  66. package/dist-pkg/core/session-clock.js +37 -0
  67. package/dist-pkg/core/setting-defaults.d.ts +61 -0
  68. package/dist-pkg/core/setting-defaults.js +66 -0
  69. package/dist-pkg/core/speech-rate.d.ts +29 -0
  70. package/dist-pkg/core/speech-rate.js +47 -0
  71. package/dist-pkg/core/state.d.ts +118 -118
  72. package/dist-pkg/core/state.js +322 -297
  73. package/dist-pkg/core/visual-cycles.d.ts +30 -0
  74. package/dist-pkg/core/visual-cycles.js +52 -0
  75. package/dist-pkg/core/visual-state.d.ts +13 -0
  76. package/dist-pkg/core/visual-state.js +6 -0
  77. package/dist-pkg/educational/activities-registry.d.ts +1 -1
  78. package/dist-pkg/educational/activities-registry.js +9 -8
  79. package/dist-pkg/educational/adaptive-engine.d.ts +11 -11
  80. package/dist-pkg/educational/adaptive-engine.js +17 -17
  81. package/dist-pkg/educational/segment-bar.d.ts +12 -12
  82. package/dist-pkg/educational/segment-bar.js +9 -9
  83. package/dist-pkg/i18n/en.js +324 -223
  84. package/dist-pkg/i18n/es.js +323 -223
  85. package/dist-pkg/i18n/pt.js +348 -226
  86. package/dist-pkg/input/default-bindings.d.ts +37 -38
  87. package/dist-pkg/input/default-bindings.js +127 -144
  88. package/dist-pkg/input/devices.d.ts +7 -7
  89. package/dist-pkg/input/devices.js +17 -16
  90. package/dist-pkg/input/edges.d.ts +23 -32
  91. package/dist-pkg/input/edges.js +23 -28
  92. package/dist-pkg/input/empathy-filter.d.ts +22 -0
  93. package/dist-pkg/input/empathy-filter.js +34 -0
  94. package/dist-pkg/input/face-map.d.ts +96 -0
  95. package/dist-pkg/input/face-map.js +255 -0
  96. package/dist-pkg/input/face-signals.d.ts +40 -0
  97. package/dist-pkg/input/face-signals.js +48 -0
  98. package/dist-pkg/input/gamepad.d.ts +195 -145
  99. package/dist-pkg/input/gamepad.js +383 -556
  100. package/dist-pkg/input/gaze-cycle.d.ts +64 -0
  101. package/dist-pkg/input/gaze-cycle.js +133 -0
  102. package/dist-pkg/input/gaze-relative.d.ts +68 -0
  103. package/dist-pkg/input/gaze-relative.js +142 -0
  104. package/dist-pkg/input/hand-map.d.ts +28 -0
  105. package/dist-pkg/input/hand-map.js +142 -0
  106. package/dist-pkg/input/input-cooldown.d.ts +17 -0
  107. package/dist-pkg/input/input-cooldown.js +39 -0
  108. package/dist-pkg/input/key-default.d.ts +30 -0
  109. package/dist-pkg/input/key-default.js +60 -0
  110. package/dist-pkg/input/keyboard-runtime.d.ts +15 -21
  111. package/dist-pkg/input/keyboard-runtime.js +23 -27
  112. package/dist-pkg/input/keyboard.d.ts +68 -63
  113. package/dist-pkg/input/keyboard.js +83 -105
  114. package/dist-pkg/input/keydown.d.ts +123 -137
  115. package/dist-pkg/input/keydown.js +284 -289
  116. package/dist-pkg/input/latch-edge.d.ts +33 -21
  117. package/dist-pkg/input/latch-edge.js +34 -35
  118. package/dist-pkg/input/latch-scope.d.ts +51 -50
  119. package/dist-pkg/input/latch-scope.js +61 -82
  120. package/dist-pkg/input/latch-store.d.ts +26 -30
  121. package/dist-pkg/input/latch-store.js +42 -50
  122. package/dist-pkg/input/latch-sync.d.ts +29 -29
  123. package/dist-pkg/input/latch-sync.js +25 -25
  124. package/dist-pkg/input/latch.d.ts +14 -17
  125. package/dist-pkg/input/latch.js +29 -37
  126. package/dist-pkg/input/pad-defaults.d.ts +13 -16
  127. package/dist-pkg/input/pad-defaults.js +21 -28
  128. package/dist-pkg/input/pad-reading.d.ts +76 -0
  129. package/dist-pkg/input/pad-reading.js +169 -0
  130. package/dist-pkg/input/pad-wizard.d.ts +99 -0
  131. package/dist-pkg/input/pad-wizard.js +256 -0
  132. package/dist-pkg/input/pointer-space.d.ts +19 -20
  133. package/dist-pkg/input/pointer-space.js +27 -32
  134. package/dist-pkg/input/pointer.d.ts +39 -40
  135. package/dist-pkg/input/pointer.js +42 -45
  136. package/dist-pkg/input/state.d.ts +101 -89
  137. package/dist-pkg/input/state.js +54 -134
  138. package/dist-pkg/input/switch-scan.d.ts +59 -0
  139. package/dist-pkg/input/switch-scan.js +67 -0
  140. package/dist-pkg/input/synthetic-source.d.ts +42 -0
  141. package/dist-pkg/input/synthetic-source.js +57 -0
  142. package/dist-pkg/input/touch-bindings.d.ts +127 -116
  143. package/dist-pkg/input/touch-bindings.js +158 -173
  144. package/dist-pkg/input/touch.d.ts +147 -72
  145. package/dist-pkg/input/touch.js +362 -126
  146. package/dist-pkg/input/transport-in-use.d.ts +107 -0
  147. package/dist-pkg/input/transport-in-use.js +130 -0
  148. package/dist-pkg/input/transports.d.ts +90 -117
  149. package/dist-pkg/input/transports.js +104 -106
  150. package/dist-pkg/input/virtual-controller.d.ts +82 -0
  151. package/dist-pkg/input/virtual-controller.js +69 -0
  152. package/dist-pkg/input/vocabulary-migration.d.ts +50 -49
  153. package/dist-pkg/input/vocabulary-migration.js +80 -79
  154. package/dist-pkg/input/voice-map.d.ts +59 -0
  155. package/dist-pkg/input/voice-map.js +123 -0
  156. package/dist-pkg/platform/audio-ambient.d.ts +13 -2
  157. package/dist-pkg/platform/audio-ambient.js +14 -14
  158. package/dist-pkg/platform/audio-earcons.d.ts +20 -19
  159. package/dist-pkg/platform/audio-earcons.js +51 -42
  160. package/dist-pkg/platform/audio-jingles.js +9 -10
  161. package/dist-pkg/platform/audio-mixer.d.ts +14 -2
  162. package/dist-pkg/platform/audio-mixer.js +31 -39
  163. package/dist-pkg/platform/audio-sonar.d.ts +49 -152
  164. package/dist-pkg/platform/audio-sonar.js +124 -302
  165. package/dist-pkg/platform/audio.d.ts +62 -19
  166. package/dist-pkg/platform/audio.js +193 -168
  167. package/dist-pkg/platform/choose-by-voice.d.ts +70 -0
  168. package/dist-pkg/platform/choose-by-voice.js +157 -0
  169. package/dist-pkg/platform/flash-sampler.d.ts +19 -0
  170. package/dist-pkg/platform/flash-sampler.js +79 -0
  171. package/dist-pkg/platform/font-library.d.ts +77 -0
  172. package/dist-pkg/platform/font-library.js +98 -0
  173. package/dist-pkg/platform/font-library.json +721 -0
  174. package/dist-pkg/platform/heavy-catalogue.d.ts +77 -0
  175. package/dist-pkg/platform/heavy-catalogue.js +256 -0
  176. package/dist-pkg/platform/heavy-mirror.d.ts +20 -0
  177. package/dist-pkg/platform/heavy-mirror.js +73 -0
  178. package/dist-pkg/platform/heavy.d.ts +136 -0
  179. package/dist-pkg/platform/heavy.js +324 -0
  180. package/dist-pkg/platform/interruptible-speech.d.ts +23 -16
  181. package/dist-pkg/platform/interruptible-speech.js +77 -54
  182. package/dist-pkg/platform/kokoro-port.d.ts +27 -0
  183. package/dist-pkg/platform/kokoro-port.js +50 -0
  184. package/dist-pkg/platform/kokoro-runtime.d.ts +20 -0
  185. package/dist-pkg/platform/kokoro-runtime.js +52 -0
  186. package/dist-pkg/platform/kokoro.d.ts +70 -0
  187. package/dist-pkg/platform/kokoro.js +133 -0
  188. package/dist-pkg/platform/listener-scope.d.ts +12 -0
  189. package/dist-pkg/platform/listener-scope.js +90 -0
  190. package/dist-pkg/platform/locale-host.d.ts +14 -0
  191. package/dist-pkg/platform/locale-host.js +45 -0
  192. package/dist-pkg/platform/microphone.d.ts +51 -0
  193. package/dist-pkg/platform/microphone.js +118 -0
  194. package/dist-pkg/platform/onnx-runtime.d.ts +34 -0
  195. package/dist-pkg/platform/onnx-runtime.js +42 -0
  196. package/dist-pkg/platform/reading-in-worker.d.ts +64 -0
  197. package/dist-pkg/platform/reading-in-worker.js +125 -0
  198. package/dist-pkg/platform/reading-model.d.ts +94 -0
  199. package/dist-pkg/platform/reading-model.js +272 -0
  200. package/dist-pkg/platform/reading-runtime.d.ts +24 -0
  201. package/dist-pkg/platform/reading-runtime.js +164 -0
  202. package/dist-pkg/platform/reading-worker.d.ts +41 -0
  203. package/dist-pkg/platform/reading-worker.js +58 -0
  204. package/dist-pkg/platform/reading.d.ts +80 -0
  205. package/dist-pkg/platform/reading.js +136 -0
  206. package/dist-pkg/platform/speech-recognition.d.ts +60 -0
  207. package/dist-pkg/platform/speech-recognition.js +104 -0
  208. package/dist-pkg/platform/speech.d.ts +23 -1
  209. package/dist-pkg/platform/speech.js +29 -18
  210. package/dist-pkg/platform/storage-keys.d.ts +96 -0
  211. package/dist-pkg/platform/storage-keys.js +106 -0
  212. package/dist-pkg/platform/storage.d.ts +46 -111
  213. package/dist-pkg/platform/storage.js +104 -169
  214. package/dist-pkg/platform/tts.d.ts +85 -39
  215. package/dist-pkg/platform/tts.js +447 -127
  216. package/dist-pkg/platform/vision-loop.d.ts +23 -0
  217. package/dist-pkg/platform/vision-loop.js +56 -0
  218. package/dist-pkg/platform/vision.d.ts +132 -0
  219. package/dist-pkg/platform/vision.js +117 -0
  220. package/dist-pkg/platform/voice-listener.d.ts +40 -0
  221. package/dist-pkg/platform/voice-listener.js +88 -0
  222. package/dist-pkg/platform/voice-plan.d.ts +15 -98
  223. package/dist-pkg/platform/voice-plan.js +56 -143
  224. package/dist-pkg/platform/vosk-runtime.d.ts +96 -0
  225. package/dist-pkg/platform/vosk-runtime.js +151 -0
  226. package/dist-pkg/platform/vosk-vocabulary.d.ts +6 -0
  227. package/dist-pkg/platform/vosk-vocabulary.js +178 -0
  228. package/dist-pkg/render/canvas.d.ts +13 -7
  229. package/dist-pkg/render/canvas.js +13 -13
  230. package/dist-pkg/render/crt.d.ts +48 -12
  231. package/dist-pkg/render/crt.js +78 -80
  232. package/dist-pkg/render/cvd-matrices.d.ts +22 -18
  233. package/dist-pkg/render/cvd-matrices.js +37 -38
  234. package/dist-pkg/render/hc-role-data.d.ts +7 -7
  235. package/dist-pkg/render/hc-role-data.js +14 -14
  236. package/dist-pkg/render/high-contrast.d.ts +67 -56
  237. package/dist-pkg/render/high-contrast.js +226 -217
  238. package/dist-pkg/render/low-vision-drawing.d.ts +5 -0
  239. package/dist-pkg/render/low-vision-drawing.js +46 -0
  240. package/dist-pkg/render/lq-filter.d.ts +32 -20
  241. package/dist-pkg/render/lq-filter.js +59 -65
  242. package/dist-pkg/render/port.d.ts +86 -88
  243. package/dist-pkg/render/port.js +17 -23
  244. package/dist-pkg/render/screen-pipeline.d.ts +42 -42
  245. package/dist-pkg/render/screen-pipeline.js +80 -87
  246. package/dist-pkg/render/sprite-fx.d.ts +2 -1
  247. package/dist-pkg/render/sprite-fx.js +16 -14
  248. package/dist-pkg/render/viewports.d.ts +20 -15
  249. package/dist-pkg/render/viewports.js +83 -114
  250. package/dist-pkg/render/viz-axes-labels.d.ts +47 -0
  251. package/dist-pkg/render/viz-axes-labels.js +88 -0
  252. package/dist-pkg/render/viz-axes.d.ts +89 -86
  253. package/dist-pkg/render/viz-axes.js +128 -116
  254. package/dist-pkg/render/viz-modes.d.ts +28 -29
  255. package/dist-pkg/render/viz-modes.js +40 -45
  256. package/dist-pkg/render/viz-refusal.d.ts +32 -0
  257. package/dist-pkg/render/viz-refusal.js +57 -0
  258. package/dist-pkg/render/viz-setters.d.ts +104 -66
  259. package/dist-pkg/render/viz-setters.js +182 -191
  260. package/dist-pkg/ui/aac-sets.d.ts +45 -0
  261. package/dist-pkg/ui/aac-sets.js +41 -0
  262. package/dist-pkg/ui/audio-choices.d.ts +68 -0
  263. package/dist-pkg/ui/audio-choices.js +98 -0
  264. package/dist-pkg/ui/audio-rows-that-apply.d.ts +12 -0
  265. package/dist-pkg/ui/audio-rows-that-apply.js +43 -0
  266. package/dist-pkg/ui/camera-control.d.ts +53 -0
  267. package/dist-pkg/ui/camera-control.js +48 -0
  268. package/dist-pkg/ui/changed-mark.d.ts +9 -8
  269. package/dist-pkg/ui/changed-mark.js +22 -53
  270. package/dist-pkg/ui/control-choices.d.ts +37 -0
  271. package/dist-pkg/ui/control-choices.js +84 -0
  272. package/dist-pkg/ui/debug-panel.d.ts +59 -43
  273. package/dist-pkg/ui/debug-panel.js +68 -69
  274. package/dist-pkg/ui/declared-words.d.ts +21 -0
  275. package/dist-pkg/ui/declared-words.js +66 -0
  276. package/dist-pkg/ui/dom.d.ts +10 -14
  277. package/dist-pkg/ui/dom.js +10 -47
  278. package/dist-pkg/ui/drawing-problems.d.ts +25 -0
  279. package/dist-pkg/ui/drawing-problems.js +94 -0
  280. package/dist-pkg/ui/eye-control.d.ts +36 -0
  281. package/dist-pkg/ui/eye-control.js +178 -0
  282. package/dist-pkg/ui/face-control.d.ts +25 -0
  283. package/dist-pkg/ui/face-control.js +183 -0
  284. package/dist-pkg/ui/focus-trap.d.ts +39 -49
  285. package/dist-pkg/ui/focus-trap.js +56 -65
  286. package/dist-pkg/ui/fonts.d.ts +157 -68
  287. package/dist-pkg/ui/fonts.js +305 -115
  288. package/dist-pkg/ui/footer-scroll-driver.d.ts +14 -0
  289. package/dist-pkg/ui/footer-scroll-driver.js +201 -0
  290. package/dist-pkg/ui/footer-scroll.d.ts +38 -0
  291. package/dist-pkg/ui/footer-scroll.js +55 -0
  292. package/dist-pkg/ui/game-options.d.ts +47 -0
  293. package/dist-pkg/ui/game-options.js +158 -0
  294. package/dist-pkg/ui/gaze-overlay.d.ts +64 -0
  295. package/dist-pkg/ui/gaze-overlay.js +165 -0
  296. package/dist-pkg/ui/hand-control.d.ts +25 -0
  297. package/dist-pkg/ui/hand-control.js +143 -0
  298. package/dist-pkg/ui/help-panel.d.ts +87 -0
  299. package/dist-pkg/ui/help-panel.js +242 -0
  300. package/dist-pkg/ui/hud-bands.d.ts +62 -0
  301. package/dist-pkg/ui/hud-bands.js +146 -0
  302. package/dist-pkg/ui/hud-row.d.ts +34 -0
  303. package/dist-pkg/ui/hud-row.js +80 -0
  304. package/dist-pkg/ui/hud.d.ts +73 -73
  305. package/dist-pkg/ui/hud.js +94 -98
  306. package/dist-pkg/ui/item-announcement.d.ts +13 -12
  307. package/dist-pkg/ui/item-announcement.js +25 -26
  308. package/dist-pkg/ui/layout.d.ts +103 -28
  309. package/dist-pkg/ui/layout.js +117 -99
  310. package/dist-pkg/ui/libras-avatar-clip.d.ts +30 -0
  311. package/dist-pkg/ui/libras-avatar-clip.js +86 -0
  312. package/dist-pkg/ui/libras-avatar-load.d.ts +52 -0
  313. package/dist-pkg/ui/libras-avatar-load.js +134 -0
  314. package/dist-pkg/ui/libras-avatar-plan.d.ts +102 -0
  315. package/dist-pkg/ui/libras-avatar-plan.js +211 -0
  316. package/dist-pkg/ui/libras-avatar-player.d.ts +28 -0
  317. package/dist-pkg/ui/libras-avatar-player.js +226 -0
  318. package/dist-pkg/ui/libras-avatar-stage.d.ts +37 -0
  319. package/dist-pkg/ui/libras-avatar-stage.js +208 -0
  320. package/dist-pkg/ui/libras-glosses.d.ts +28 -0
  321. package/dist-pkg/ui/libras-glosses.js +161 -0
  322. package/dist-pkg/ui/locale-flags.d.ts +16 -0
  323. package/dist-pkg/ui/locale-flags.js +30 -0
  324. package/dist-pkg/ui/loop-crash.d.ts +17 -17
  325. package/dist-pkg/ui/loop-crash.js +33 -62
  326. package/dist-pkg/ui/menu-intent.d.ts +61 -0
  327. package/dist-pkg/ui/menu-intent.js +88 -0
  328. package/dist-pkg/ui/menu-items.d.ts +20 -0
  329. package/dist-pkg/ui/menu-items.js +51 -0
  330. package/dist-pkg/ui/menu-nav.d.ts +98 -92
  331. package/dist-pkg/ui/menu-nav.js +398 -286
  332. package/dist-pkg/ui/mobility-choices.d.ts +26 -0
  333. package/dist-pkg/ui/mobility-choices.js +41 -0
  334. package/dist-pkg/ui/motion-choices.d.ts +31 -0
  335. package/dist-pkg/ui/motion-choices.js +68 -0
  336. package/dist-pkg/ui/motion-scene.d.ts +24 -19
  337. package/dist-pkg/ui/motion-scene.js +29 -61
  338. package/dist-pkg/ui/mount-panel.d.ts +81 -0
  339. package/dist-pkg/ui/mount-panel.js +61 -0
  340. package/dist-pkg/ui/panel-shell.d.ts +65 -31
  341. package/dist-pkg/ui/panel-shell.js +92 -66
  342. package/dist-pkg/ui/panel-widgets.d.ts +150 -0
  343. package/dist-pkg/ui/panel-widgets.js +294 -0
  344. package/dist-pkg/ui/pause-buttons.d.ts +35 -0
  345. package/dist-pkg/ui/pause-buttons.js +66 -0
  346. package/dist-pkg/ui/pause-icons.d.ts +261 -347
  347. package/dist-pkg/ui/pause-icons.js +671 -601
  348. package/dist-pkg/ui/pause-markup.d.ts +86 -0
  349. package/dist-pkg/ui/pause-markup.js +125 -0
  350. package/dist-pkg/ui/reach-notice.d.ts +22 -23
  351. package/dist-pkg/ui/reach-notice.js +55 -58
  352. package/dist-pkg/ui/scan-overlay.d.ts +23 -0
  353. package/dist-pkg/ui/scan-overlay.js +50 -0
  354. package/dist-pkg/ui/screen-text.d.ts +25 -0
  355. package/dist-pkg/ui/screen-text.js +152 -0
  356. package/dist-pkg/ui/session-clock.d.ts +32 -0
  357. package/dist-pkg/ui/session-clock.js +67 -0
  358. package/dist-pkg/ui/settings-aac.d.ts +70 -0
  359. package/dist-pkg/ui/settings-aac.js +208 -0
  360. package/dist-pkg/ui/settings-audio.d.ts +97 -114
  361. package/dist-pkg/ui/settings-audio.js +493 -425
  362. package/dist-pkg/ui/settings-controls.d.ts +53 -96
  363. package/dist-pkg/ui/settings-controls.js +143 -215
  364. package/dist-pkg/ui/settings-empathy.d.ts +39 -30
  365. package/dist-pkg/ui/settings-empathy.js +53 -60
  366. package/dist-pkg/ui/settings-mobility.d.ts +192 -0
  367. package/dist-pkg/ui/settings-mobility.js +332 -0
  368. package/dist-pkg/ui/settings-motion.d.ts +91 -70
  369. package/dist-pkg/ui/settings-motion.js +287 -198
  370. package/dist-pkg/ui/settings-panel.d.ts +58 -45
  371. package/dist-pkg/ui/settings-panel.js +189 -94
  372. package/dist-pkg/ui/settings-typo.d.ts +29 -78
  373. package/dist-pkg/ui/settings-typo.js +158 -129
  374. package/dist-pkg/ui/settings-visual.d.ts +29 -87
  375. package/dist-pkg/ui/settings-visual.js +240 -193
  376. package/dist-pkg/ui/shell.d.ts +112 -135
  377. package/dist-pkg/ui/shell.js +148 -217
  378. package/dist-pkg/ui/simulation-list.d.ts +27 -0
  379. package/dist-pkg/ui/simulation-list.js +76 -0
  380. package/dist-pkg/ui/simulation-over-the-world.d.ts +23 -0
  381. package/dist-pkg/ui/simulation-over-the-world.js +131 -0
  382. package/dist-pkg/ui/simulation-refusal.d.ts +5 -31
  383. package/dist-pkg/ui/simulation-refusal.js +8 -56
  384. package/dist-pkg/ui/switchable-control.d.ts +11 -0
  385. package/dist-pkg/ui/switchable-control.js +19 -0
  386. package/dist-pkg/ui/title.d.ts +3 -12
  387. package/dist-pkg/ui/title.js +49 -18
  388. package/dist-pkg/ui/top-band.d.ts +26 -0
  389. package/dist-pkg/ui/top-band.js +96 -0
  390. package/dist-pkg/ui/typo-choices.d.ts +71 -0
  391. package/dist-pkg/ui/typo-choices.js +85 -0
  392. package/dist-pkg/ui/visual-axes-panel.d.ts +11 -47
  393. package/dist-pkg/ui/visual-axes-panel.js +12 -94
  394. package/dist-pkg/ui/visual-choices.d.ts +77 -0
  395. package/dist-pkg/ui/visual-choices.js +107 -0
  396. package/dist-pkg/ui/vlibras.d.ts +77 -10
  397. package/dist-pkg/ui/vlibras.js +130 -81
  398. package/dist-pkg/ui/voice-control.d.ts +68 -0
  399. package/dist-pkg/ui/voice-control.js +276 -0
  400. package/dist-pkg/ui/voice-settings.d.ts +95 -0
  401. package/dist-pkg/ui/voice-settings.js +352 -0
  402. package/dist-pkg/ui/where-the-child-is.d.ts +31 -0
  403. package/dist-pkg/ui/where-the-child-is.js +70 -0
  404. package/docs/CREDITS.md +300 -44
  405. package/docs/LICENSES.md +189 -145
  406. package/package.json +51 -27
  407. package/scripts/check-cartridge.mjs +78 -0
  408. package/scripts/game-build.d.mts +25 -0
  409. package/scripts/game-build.mjs +144 -0
  410. package/scripts/heavy-into-the-delivery.mjs +388 -0
  411. package/scripts/libras-avatar.json +680 -0
  412. package/scripts/libras-avatar.mjs +246 -0
  413. package/scripts/libras-glosses/gloss.py +95 -0
  414. package/scripts/libras-glosses/pyproject.toml +32 -0
  415. package/scripts/libras-glosses/uv.lock +781 -0
  416. package/scripts/libras-glosses.mjs +351 -0
  417. package/scripts/licences/Apache-2.0.txt +176 -0
  418. package/scripts/licences/GPL-3.0.txt +674 -0
  419. package/scripts/licences/MIT.txt +17 -0
  420. package/scripts/licences/OFL-1.1.txt +86 -0
  421. package/scripts/licences/UFL-1.0.txt +96 -0
  422. package/scripts/licences/fonts.mjs +389 -0
  423. package/scripts/licences/third-party.mjs +301 -0
  424. package/scripts/licences/vosk-browser.NOTICE.txt +268 -0
  425. package/app/public/vendor/fonts/comicneue-400.woff2 +0 -0
  426. package/app/public/vendor/fonts/comicneue-700.woff2 +0 -0
  427. package/app/public/vendor/fonts/fondamento-400-ext.woff2 +0 -0
  428. package/app/public/vendor/fonts/fondamento-400.woff2 +0 -0
  429. package/app/public/vendor/fonts/inter-400.woff2 +0 -0
  430. package/app/public/vendor/fonts/inter-700.woff2 +0 -0
  431. package/app/public/vendor/fonts/lato-400.woff2 +0 -0
  432. package/app/public/vendor/fonts/lato-700.woff2 +0 -0
  433. package/app/public/vendor/fonts/literata-400.woff2 +0 -0
  434. package/app/public/vendor/fonts/literata-700.woff2 +0 -0
  435. package/app/public/vendor/fonts/newsreader-400.woff2 +0 -0
  436. package/app/public/vendor/fonts/newsreader-700.woff2 +0 -0
  437. package/app/public/vendor/fonts/opendyslexic-400.woff2 +0 -0
  438. package/app/public/vendor/fonts/opensans-400.woff2 +0 -0
  439. package/app/public/vendor/fonts/opensans-700.woff2 +0 -0
  440. package/app/public/vendor/fonts/pinyon-400.woff2 +0 -0
  441. package/app/public/vendor/fonts/pressstart-400.woff2 +0 -0
  442. package/app/public/vendor/fonts/quattro-400.woff2 +0 -0
  443. package/app/public/vendor/fonts/quattro-700.woff2 +0 -0
  444. package/app/public/vendor/fonts/sourcesans-400.woff2 +0 -0
  445. package/app/public/vendor/fonts/sourcesans-700.woff2 +0 -0
  446. package/app/public/vendor/fonts/sourceserif-400.woff2 +0 -0
  447. package/app/public/vendor/fonts/sourceserif-700.woff2 +0 -0
  448. package/app/public/vendor/fonts/ufmag-400.woff2 +0 -0
  449. package/dist-pkg/core/anel.d.ts +0 -17
  450. package/dist-pkg/core/anel.js +0 -31
  451. package/dist-pkg/core/collision.d.ts +0 -21
  452. package/dist-pkg/core/collision.js +0 -62
  453. package/dist-pkg/core/layers.d.ts +0 -63
  454. package/dist-pkg/core/layers.js +0 -95
  455. package/dist-pkg/core/letter-grid.d.ts +0 -55
  456. package/dist-pkg/core/letter-grid.js +0 -116
  457. package/dist-pkg/core/password.d.ts +0 -34
  458. package/dist-pkg/core/password.js +0 -205
  459. package/dist-pkg/core/rotulo-acessivel.d.ts +0 -17
  460. package/dist-pkg/core/rotulo-acessivel.js +0 -41
  461. package/dist-pkg/core/run-state.d.ts +0 -115
  462. package/dist-pkg/core/run-state.js +0 -40
  463. package/dist-pkg/core/tiles.d.ts +0 -12
  464. package/dist-pkg/core/tiles.js +0 -61
  465. package/dist-pkg/core/world.d.ts +0 -4
  466. package/dist-pkg/core/world.js +0 -58
  467. package/dist-pkg/input/origem-sintetica.d.ts +0 -44
  468. package/dist-pkg/input/origem-sintetica.js +0 -60
  469. package/dist-pkg/input/transporte-em-uso.d.ts +0 -101
  470. package/dist-pkg/input/transporte-em-uso.js +0 -130
  471. package/dist-pkg/platform/audio-nav.d.ts +0 -51
  472. package/dist-pkg/platform/audio-nav.js +0 -99
  473. package/dist-pkg/platform/guide-intensity.d.ts +0 -40
  474. package/dist-pkg/platform/guide-intensity.js +0 -73
  475. package/dist-pkg/platform/pesados-catalogo.d.ts +0 -14
  476. package/dist-pkg/platform/pesados-catalogo.js +0 -177
  477. package/dist-pkg/platform/pesados.d.ts +0 -31
  478. package/dist-pkg/platform/pesados.js +0 -75
  479. package/dist-pkg/platform/vozes-prontas.d.ts +0 -28
  480. package/dist-pkg/platform/vozes-prontas.js +0 -61
  481. package/dist-pkg/render/camera.d.ts +0 -58
  482. package/dist-pkg/render/camera.js +0 -105
  483. package/dist-pkg/render/cenario-data.d.ts +0 -127
  484. package/dist-pkg/render/cenario-data.js +0 -145
  485. package/dist-pkg/render/city-tex.d.ts +0 -63
  486. package/dist-pkg/render/city-tex.js +0 -228
  487. package/dist-pkg/render/city-tiles.d.ts +0 -11
  488. package/dist-pkg/render/city-tiles.js +0 -140
  489. package/dist-pkg/render/draw.d.ts +0 -149
  490. package/dist-pkg/render/draw.js +0 -231
  491. package/dist-pkg/render/fx.d.ts +0 -63
  492. package/dist-pkg/render/fx.js +0 -123
  493. package/dist-pkg/render/minimap.d.ts +0 -11
  494. package/dist-pkg/render/minimap.js +0 -94
  495. package/dist-pkg/render/parallax.d.ts +0 -85
  496. package/dist-pkg/render/parallax.js +0 -158
  497. package/dist-pkg/render/player-anim.d.ts +0 -71
  498. package/dist-pkg/render/player-anim.js +0 -152
  499. package/dist-pkg/render/recycling-tex.d.ts +0 -48
  500. package/dist-pkg/render/recycling-tex.js +0 -164
  501. package/dist-pkg/render/scene-city.d.ts +0 -77
  502. package/dist-pkg/render/scene-city.js +0 -181
  503. package/dist-pkg/render/scene-parallax.d.ts +0 -72
  504. package/dist-pkg/render/scene-parallax.js +0 -426
  505. package/dist-pkg/render/scene-sky.d.ts +0 -126
  506. package/dist-pkg/render/scene-sky.js +0 -295
  507. package/dist-pkg/render/set-cenario.d.ts +0 -54
  508. package/dist-pkg/render/set-cenario.js +0 -103
  509. package/dist-pkg/render/textures.d.ts +0 -75
  510. package/dist-pkg/render/textures.js +0 -240
  511. package/dist-pkg/render/title-scene.d.ts +0 -46
  512. package/dist-pkg/render/title-scene.js +0 -75
  513. package/dist-pkg/render/weather.d.ts +0 -96
  514. package/dist-pkg/render/weather.js +0 -171
  515. package/dist-pkg/render/wheelchair-sprites.d.ts +0 -32
  516. package/dist-pkg/render/wheelchair-sprites.js +0 -69
  517. package/dist-pkg/render/world-tex.d.ts +0 -18
  518. package/dist-pkg/render/world-tex.js +0 -87
  519. package/dist-pkg/ui/activities-menu.d.ts +0 -258
  520. package/dist-pkg/ui/activities-menu.js +0 -648
  521. package/dist-pkg/ui/caa-sets.d.ts +0 -39
  522. package/dist-pkg/ui/caa-sets.js +0 -65
  523. package/dist-pkg/ui/latch-refusal.d.ts +0 -32
  524. package/dist-pkg/ui/latch-refusal.js +0 -60
  525. package/dist-pkg/ui/map-hub.d.ts +0 -56
  526. package/dist-pkg/ui/map-hub.js +0 -138
  527. package/dist-pkg/ui/settings-caa.d.ts +0 -45
  528. package/dist-pkg/ui/settings-caa.js +0 -137
  529. package/dist-pkg/ui/settings-motor.d.ts +0 -175
  530. package/dist-pkg/ui/settings-motor.js +0 -342
  531. package/dist-pkg/ui/webcam.d.ts +0 -7
  532. package/dist-pkg/ui/webcam.js +0 -93
@@ -0,0 +1,102 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ /**
3
+ * Where the delivery puts the free player's avatar, clips, manifest and glosses, beside the game's page (`inclusionist-heavy --libras`):
4
+ * written in the catalogue, whose delivery list keeps these files offline (ADR-0234, phase B3), and re-exported here where the
5
+ * player and the delivery script already read it.
6
+ */
7
+ export { LIBRAS_AVATAR_FOLDER } from '../platform/heavy-catalogue.js';
8
+ /** The manifest the delivery writes in that folder: the avatar's file and every clip it carries, with its window. */
9
+ export declare const LIBRAS_AVATAR_MANIFEST = "manifest.json";
10
+ /**
11
+ * How long two clips overlap, fading one into the other, in seconds. Every clip is at least 1.2 s (📏 the 655 of the delivery:
12
+ * 1.2–7.1 s, median 2.47 s), and the overlap never exceeds half of either clip, so no clip is swallowed by its neighbours.
13
+ */
14
+ export declare const CROSS_FADE_S = 0.3;
15
+ /** One clip the delivery carries: its file under the folder, and the stretch of it that is played. */
16
+ export interface ClipWindow {
17
+ readonly file: string;
18
+ /** The clip's length in seconds, as exported. */
19
+ readonly duration: number;
20
+ /**
21
+ * Where the sign starts, in seconds — 0 for every clip but one: the export of FALA begins at a stray keyframe 1404 frames
22
+ * before the sign (46.8 s at 30 fps), which the delivery's pins name, so the player plays from the sign and not from the stray.
23
+ */
24
+ readonly from: number;
25
+ /**
26
+ * Where the hand is held up, `[up, down]` in seconds of the played window — only for the clips a word is spelled with (a letter
27
+ * or a digit): a chained letter starts at `up` and hands over at `down`. E's is its whole clip. Absent, the clip plays whole.
28
+ */
29
+ readonly held?: readonly [number, number];
30
+ }
31
+ /** The delivery's manifest, read: the avatar's file and the clips by name. */
32
+ export interface AvatarManifest {
33
+ readonly avatar: string;
34
+ readonly clips: ReadonlyMap<string, ClipWindow>;
35
+ }
36
+ /**
37
+ * The manifest the delivery wrote, or `null` for anything else — no file, a fallback page, another format: a delivery that did
38
+ * not ship the avatar. A clip entry of the wrong shape is left out, not trusted; a `held` of the wrong shape is dropped.
39
+ */
40
+ export declare function avatarManifestOf(data: unknown): AvatarManifest | null;
41
+ /** How long a clip plays: its window, from `from` to its end. */
42
+ export declare const playedLength: (w: ClipWindow) => number;
43
+ /**
44
+ * One clip to play: a sign, or a letter of a word being fingerspelled (`spells`, the word; `word`, its place in the gloss — two
45
+ * letters are of one word, and chained, only when both are the same).
46
+ */
47
+ export interface SignStep {
48
+ readonly clip: string;
49
+ readonly spells?: string;
50
+ readonly word?: number;
51
+ }
52
+ /** A word the avatar could not sign: no clip of its own, and letters it has no clip for. */
53
+ export interface UnsignedWord {
54
+ readonly word: string;
55
+ readonly missing: readonly string[];
56
+ }
57
+ export interface SignPlan {
58
+ readonly steps: readonly SignStep[];
59
+ readonly unsigned: readonly UnsignedWord[];
60
+ }
61
+ /**
62
+ * The clips a gloss is signed with, in order: a token with a clip is its clip; any other is fingerspelled from the letter and
63
+ * digit clips, as written (capitals, accents stripped, Ç kept); a word with a letter the avatar lacks is left out whole and
64
+ * listed.
65
+ */
66
+ export declare function planSigns(gloss: string, carried: (name: string) => boolean): SignPlan;
67
+ /**
68
+ * What the avatar left out of a text, said for `problems` (the `unsigned` of a `SignResult`): the words, and the letters it
69
+ * lacks clips for. `undefined` when it left nothing out.
70
+ */
71
+ export declare function unsignedText(unsigned: readonly UnsignedWord[]): string | undefined;
72
+ /**
73
+ * Everything a request left out: the words it could neither sign nor spell, and the clips the delivery could not give (`unread`).
74
+ * `undefined` when it left nothing out.
75
+ */
76
+ export declare function leftOutText(unsigned: readonly UnsignedWord[], unread: readonly string[]): string | undefined;
77
+ /** Why a request with nothing left to sign was not signed. */
78
+ export declare const nothingSigned: (leftOut: string | undefined) => string;
79
+ /** A clip to put on the stage now: fading in over `fade` seconds from what is there, starting `at` seconds into its window. */
80
+ export interface StartClip {
81
+ readonly clip: string;
82
+ readonly fade: number;
83
+ readonly at: number;
84
+ }
85
+ export interface SignSequencer {
86
+ /** Replaces whatever was being signed with `steps`; returns the clip to start now (none for no steps). */
87
+ readonly play: (steps: readonly SignStep[]) => StartClip[];
88
+ /** Moves the clock `dt` seconds: the clips to start, and whether the last one just reached its end («stopped»). */
89
+ readonly tick: (dt: number) => {
90
+ readonly starts: StartClip[];
91
+ readonly finished: boolean;
92
+ };
93
+ /** Drops the queue: nothing more starts, and nothing finishes. */
94
+ readonly stop: () => void;
95
+ }
96
+ /**
97
+ * The queue's clock. `lengthOf` is a clip's played length (`playedLength`), `heldOf` where a letter's hand is held up
98
+ * (`playedSpans`: a spelled word's letters are chained through it; none, and every clip plays whole). Two neighbours overlap by
99
+ * `fade`, never by more than half of the stretch either plays; the first clip of a queue fades in only when a pose is already on
100
+ * the stage (a queue that replaced another, or the one held since the last), and appears at once otherwise.
101
+ */
102
+ export declare function createSignSequencer(lengthOf: (clip: string) => number, fade?: number, heldOf?: (clip: string) => readonly [number, number] | undefined): SignSequencer;
@@ -0,0 +1,211 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // ui/libras-avatar-plan — WHAT THE FREE PLAYER SIGNS, IN WHICH ORDER, AND WHEN EACH CLIP STARTS (ADR-0234, route B, phase B2).
3
+ //
4
+ // Route B signs with LAViD-UFPB's own sign sources, exported once to ONE avatar and ONE three.js clip per sign
5
+ // (`scripts/libras-export.mjs`, phase B1). This module is the player's pure half — no three.js, no DOM — so the queue, the
6
+ // timing and the fingerspelling are held in node (`tests/libras-avatar-plan.node.test.js`); `ui/libras-avatar-stage` plays what
7
+ // it decides and `ui/libras-avatar-player` puts it behind the `Interpreter` port.
8
+ //
9
+ // THE GLOSS IS THE DELIVERY'S: its build-time glosses (`ui/libras-glosses`), where a word with no clip is already written as
10
+ // the child reads it on the screen. A token the delivery carries a clip for is signed; any other is FINGERSPELLED letter by
11
+ // letter from the clips named by a single letter or digit — the word as written, capitals, accents stripped but Ç kept, a letter
12
+ // of the manual alphabet with a clip of its own (`provisionalGloss`, the build's own spelling rule).
13
+ // 📌 A word whose letters the avatar does not all carry is LEFT OUT WHOLE, and said: spelling only the letters it has would
14
+ // show the child another word («ENTROU» with only its O is «O»).
15
+ //
16
+ // THE TIMING: each clip plays its window once, and the next one starts `CROSS_FADE_S` before it ends, fading in while the other
17
+ // fades out — almost every exported clip starts and ends in the same pose (arms down), so the overlap joins them without passing
18
+ // through the avatar's rest. 📏 13 of the 655 do not: the letter E holds its raised handshape from its first frame to its last,
19
+ // NÃO_OUVIR keeps the right arm at the avatar's rest, and eleven (CASA and SIM among them) rest their hands lower. The last one
20
+ // plays to its end and its final pose is held: that moment is the player's «stopped».
21
+ //
22
+ // 📌 A SPELLED WORD IS SIGNED WITH THE HAND HELD UP BETWEEN ITS LETTERS (the Dev, interface log 2026-09-26). Each letter's and
23
+ // digit's clip rises from the arms-down pose, holds its handshape and falls back; the delivery's manifest says where it is held
24
+ // (`ClipWindow.held`, measured on the clip: `scripts/libras-export.mjs` `heldWindow`). Inside one spelled word, a letter after the
25
+ // first starts where its hand is up, and a letter before the last hands over where its hand starts down — the cross-fade joins
26
+ // two held handshapes — so the hand rises at the first letter and comes down only after the last. E, still from its first frame
27
+ // to its last, is held for its whole clip and the chain passes through it. A sign keeps its whole clip, a word of one letter is
28
+ // unchanged, and a letter the manifest gives no window plays whole. 📏 On a served delivery «PÕE» went from 4.37 s to 2.86 s and
29
+ // «TRIÂNGULO» from 12.60 s to 5.35 s.
30
+ import { provisionalGloss } from './libras-glosses.js';
31
+ /**
32
+ * Where the delivery puts the free player's avatar, clips, manifest and glosses, beside the game's page (`inclusionist-heavy --libras`):
33
+ * written in the catalogue, whose delivery list keeps these files offline (ADR-0234, phase B3), and re-exported here where the
34
+ * player and the delivery script already read it.
35
+ */
36
+ export { LIBRAS_AVATAR_FOLDER } from '../platform/heavy-catalogue.js';
37
+ /** The manifest the delivery writes in that folder: the avatar's file and every clip it carries, with its window. */
38
+ export const LIBRAS_AVATAR_MANIFEST = 'manifest.json';
39
+ /**
40
+ * How long two clips overlap, fading one into the other, in seconds. Every clip is at least 1.2 s (📏 the 655 of the delivery:
41
+ * 1.2–7.1 s, median 2.47 s), and the overlap never exceeds half of either clip, so no clip is swallowed by its neighbours.
42
+ */
43
+ export const CROSS_FADE_S = 0.3;
44
+ const isWindow = (w) => {
45
+ const c = w;
46
+ return !!c && typeof c.file === 'string' && typeof c.duration === 'number' && c.duration > 0
47
+ && (c.from === undefined || (typeof c.from === 'number' && c.from >= 0 && c.from < c.duration));
48
+ };
49
+ /** A `held` inside the played window, up before down; anything else is no window, and the clip plays whole. */
50
+ const heldIn = (held, length) => {
51
+ if (!Array.isArray(held) || held.length !== 2)
52
+ return undefined;
53
+ const [up, down] = held;
54
+ return typeof up === 'number' && typeof down === 'number' && up >= 0 && down > up && down <= length ? [up, down] : undefined;
55
+ };
56
+ /**
57
+ * The manifest the delivery wrote, or `null` for anything else — no file, a fallback page, another format: a delivery that did
58
+ * not ship the avatar. A clip entry of the wrong shape is left out, not trusted; a `held` of the wrong shape is dropped.
59
+ */
60
+ export function avatarManifestOf(data) {
61
+ const m = data;
62
+ if (!m || m.format !== 1 || typeof m.avatar !== 'string' || !m.avatar || typeof m.clips !== 'object' || m.clips === null)
63
+ return null;
64
+ const clips = new Map();
65
+ for (const [name, w] of Object.entries(m.clips)) {
66
+ if (isWindow(w))
67
+ clips.set(name, windowRead(w));
68
+ }
69
+ return { avatar: m.avatar, clips };
70
+ }
71
+ /** A clip entry of the right shape, as the player keeps it: `from` defaulted, `held` only when it is inside the window. */
72
+ function windowRead(w) {
73
+ const from = w.from ?? 0;
74
+ const held = heldIn(w.held, w.duration - from);
75
+ return { file: w.file, duration: w.duration, from, ...(held ? { held } : {}) };
76
+ }
77
+ /** How long a clip plays: its window, from `from` to its end. */
78
+ export const playedLength = (w) => w.duration - w.from;
79
+ /** A gloss token that is not a sign: punctuation (`[PONTO]`) or a template hole the lookup left (`{n}`). */
80
+ const NOT_A_SIGN = /^\[.*\]$|^\{.*\}$/u;
81
+ /**
82
+ * The clips a gloss is signed with, in order: a token with a clip is its clip; any other is fingerspelled from the letter and
83
+ * digit clips, as written (capitals, accents stripped, Ç kept); a word with a letter the avatar lacks is left out whole and
84
+ * listed.
85
+ */
86
+ export function planSigns(gloss, carried) {
87
+ const steps = [];
88
+ const unsigned = [];
89
+ gloss.split(/\s+/u).forEach((token, word) => {
90
+ if (!token || NOT_A_SIGN.test(token))
91
+ return;
92
+ if (carried(token)) {
93
+ steps.push({ clip: token });
94
+ return;
95
+ }
96
+ const letters = [...provisionalGloss(token).replace(/\s+/gu, '')];
97
+ if (!letters.length)
98
+ return;
99
+ const missing = [...new Set(letters.filter((c) => !carried(c)))];
100
+ if (missing.length) {
101
+ unsigned.push({ word: token, missing });
102
+ return;
103
+ }
104
+ for (const c of letters)
105
+ steps.push({ clip: c, spells: token, word });
106
+ });
107
+ return { steps, unsigned };
108
+ }
109
+ /**
110
+ * What the avatar left out of a text, said for `problems` (the `unsigned` of a `SignResult`): the words, and the letters it
111
+ * lacks clips for. `undefined` when it left nothing out.
112
+ */
113
+ export function unsignedText(unsigned) {
114
+ if (!unsigned.length)
115
+ return undefined;
116
+ const words = [...new Set(unsigned.map((u) => `«${u.word}»`))];
117
+ const letters = [...new Set(unsigned.flatMap((u) => u.missing))].sort();
118
+ const one = words.length === 1;
119
+ return `${words.join(', ')} ${one ? 'lacks a sign of its' : 'lack a sign of their'} own and could not be fingerspelled: `
120
+ + `the avatar lacks the clips for ${letters.join(', ')}`;
121
+ }
122
+ /**
123
+ * Everything a request left out: the words it could neither sign nor spell, and the clips the delivery could not give (`unread`).
124
+ * `undefined` when it left nothing out.
125
+ */
126
+ export function leftOutText(unsigned, unread) {
127
+ const parts = [unsignedText(unsigned), unread.length ? `the clips of ${unread.map((n) => `«${n}»`).join(', ')} could not be read from the delivery` : ''];
128
+ return parts.filter(Boolean).join('; ') || undefined;
129
+ }
130
+ /** Why a request with nothing left to sign was not signed. */
131
+ export const nothingSigned = (leftOut) => `none of the text could be signed: ${leftOut ?? 'it holds not a single word'}`;
132
+ /**
133
+ * WHAT OF EACH CLIP IS PLAYED: all of it, except inside a spelled word, where two neighbouring letters are CHAINED — the first
134
+ * ends where its hand starts down, the next starts where its hand is up (`held`). Two steps are chained only when they spell the
135
+ * same word (`word`) and both have a window; so the first letter keeps its rise, the last its fall, a word of one letter and a
136
+ * sign are whole.
137
+ */
138
+ function playedSpans(steps, lengthOf, heldOf) {
139
+ const oneWord = (a, b) => a.spells !== undefined && a.word !== undefined
140
+ && b.word === a.word && b.spells === a.spells;
141
+ const chained = steps.map((s, i) => {
142
+ const next = steps[i + 1];
143
+ return !!next && oneWord(s, next) && !!heldOf(s.clip) && !!heldOf(next.clip);
144
+ });
145
+ return steps.map((s, i) => [
146
+ i > 0 && chained[i - 1] ? heldOf(s.clip)[0] : 0,
147
+ chained[i] ? heldOf(s.clip)[1] : lengthOf(s.clip),
148
+ ]);
149
+ }
150
+ /**
151
+ * The queue's clock. `lengthOf` is a clip's played length (`playedLength`), `heldOf` where a letter's hand is held up
152
+ * (`playedSpans`: a spelled word's letters are chained through it; none, and every clip plays whole). Two neighbours overlap by
153
+ * `fade`, never by more than half of the stretch either plays; the first clip of a queue fades in only when a pose is already on
154
+ * the stage (a queue that replaced another, or the one held since the last), and appears at once otherwise.
155
+ */
156
+ export function createSignSequencer(lengthOf, fade = CROSS_FADE_S, heldOf = () => undefined) {
157
+ let queue = [];
158
+ let spans = [];
159
+ /** The step on the stage now; -1 when none is playing. */
160
+ let index = -1;
161
+ /** Seconds into the current step's stretch. */
162
+ let elapsed = 0;
163
+ /** Whether a pose is on the stage: after the first clip, the last frame of the last one is held. */
164
+ let shown = false;
165
+ const lengthAt = (i) => spans[i][1] - spans[i][0];
166
+ const overlapAfter = (i) => (i + 1 < queue.length ? Math.min(fade, lengthAt(i) / 2, lengthAt(i + 1) / 2) : 0);
167
+ return {
168
+ play: (steps) => {
169
+ queue = steps;
170
+ spans = playedSpans(steps, lengthOf, heldOf);
171
+ elapsed = 0;
172
+ index = steps.length ? 0 : -1;
173
+ if (!steps.length)
174
+ return [];
175
+ const start = { clip: steps[0].clip, fade: shown ? Math.min(fade, lengthAt(0) / 2) : 0, at: spans[0][0] };
176
+ shown = true;
177
+ return [start];
178
+ },
179
+ tick: (dt) => {
180
+ const starts = [];
181
+ if (index < 0)
182
+ return { starts, finished: false };
183
+ elapsed += Math.max(0, dt);
184
+ for (;;) {
185
+ const length = lengthAt(index);
186
+ if (index + 1 < queue.length) {
187
+ const handOver = length - overlapAfter(index);
188
+ if (elapsed < handOver)
189
+ break;
190
+ elapsed -= handOver;
191
+ index += 1;
192
+ starts.push({ clip: queue[index].clip, fade: overlapAfter(index - 1), at: spans[index][0] + elapsed });
193
+ continue;
194
+ }
195
+ if (elapsed < length)
196
+ break;
197
+ index = -1;
198
+ queue = [];
199
+ spans = [];
200
+ return { starts, finished: true };
201
+ }
202
+ return { starts, finished: false };
203
+ },
204
+ stop: () => {
205
+ index = -1;
206
+ queue = [];
207
+ spans = [];
208
+ elapsed = 0;
209
+ },
210
+ };
211
+ }
@@ -0,0 +1,28 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ import type { Interpreter } from './vlibras.js';
3
+ import type { AvatarStage } from './libras-avatar-stage.js';
4
+ /** What the free interpreter is built over. Every port is the host's. */
5
+ export interface LibrasAvatarPorts {
6
+ /** The host's document: the avatar's canvas is created in it and put in front of the screen. */
7
+ readonly doc: Document;
8
+ /** The host's window: it keeps the timers and draws the frames. */
9
+ readonly win: Pick<Window, 'setTimeout' | 'clearTimeout' | 'requestAnimationFrame' | 'cancelAnimationFrame'> & {
10
+ readonly devicePixelRatio?: number;
11
+ };
12
+ /** The host's `fetch`, for the delivery's manifest, avatar, clips and glosses; `undefined` where it has none. */
13
+ readonly fetch: ((url: string) => Promise<Response>) | undefined;
14
+ /** The page's address the delivery's folders are resolved against (`document.baseURI`). */
15
+ readonly base: string;
16
+ /** The avatar's accessible name, read when its canvas is made — the page's language at that moment. */
17
+ readonly title: () => string;
18
+ /** Where the avatar, the clips, the manifest and the glosses are, under `base`. The delivery's folder unless a test points elsewhere. */
19
+ readonly avatarFolder?: string;
20
+ /** How long the avatar may take to load before the request is answered «unavailable». */
21
+ readonly loadTimeoutMs?: number;
22
+ /** How long the avatar stays after it stopped signing before it leaves. `LEAVE_AFTER_MS` unless a test says. */
23
+ readonly leaveAfterMs?: number;
24
+ /** Builds the stage on the canvas from the avatar's bytes. `ui/libras-avatar-stage`, imported when first needed, unless a test says. */
25
+ readonly loadStage?: (canvas: HTMLCanvasElement, avatar: ArrayBuffer) => Promise<AvatarStage>;
26
+ }
27
+ /** Builds the free interpreter over the avatar the delivery shipped — or, where it shipped none, «signing unavailable». */
28
+ export declare function createLibrasAvatarInterpreter(ports: LibrasAvatarPorts): Interpreter;
@@ -0,0 +1,226 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // ui/libras-avatar-player.ts — THE INTERPRETER THAT SIGNS: LAViD-UFPB's signs on a three.js avatar, behind the `Interpreter`
3
+ // port (ADR-0234, route B).
4
+ //
5
+ // 📌 THE ROOT'S OWN (phase B3): `createGame` builds this one when the host lends no interpreter (`EngineHost.interpreter`
6
+ // always wins), over the page's own address.
7
+ //
8
+ // THE RULES, as the Dev set them for the interpreter: the gloss is the delivery's (`glosses.json`, beside the avatar, where a
9
+ // word with no sign is written as the child reads it), and a text it does not cover is its words in capitals, accents stripped,
10
+ // Ç kept; the avatar appears at the bottom right when a request is played, in front of the screen, never taking the keyboard; a
11
+ // new request replaces what is being signed (the players' turns are deaf mode's); the avatar LEAVES the screen `leaveAfterMs` after it stopped signing — hidden, kept
12
+ // loaded for the next press — and a press in those seconds cancels the leaving; `hide()` takes it off at once and `dispose()`
13
+ // releases it. A delivery that did not ship it answers exactly as `NO_INTERPRETER` does («signing unavailable», with the flag
14
+ // that ships it), and a device that cannot draw it (no WebGL) or a load that never ends answers «unavailable» with its own
15
+ // reason (`ui/libras-avatar-load`).
16
+ //
17
+ // WHAT IS ITS OWN: the player is this engine's code, so «stopped» is not a message to wait for — it is the moment the last clip
18
+ // reaches its end (`ui/libras-avatar-plan`'s sequencer), its final pose held; a fingerspelled word is signed with the hand held
19
+ // up between its letters (the sequencer's chaining, the Dev's rule of 2026-09-26). And a word it can neither sign nor fingerspell is
20
+ // left out and SAID: the request still signs the rest, and what it left out comes back in `unsigned`, which deaf mode reports
21
+ // in `problems` (ADR-0169: what works in part is reported).
22
+ //
23
+ // 📌 three.js ARRIVES ONLY HERE, AND LATE: `ui/libras-avatar-stage` is imported dynamically when the avatar is first opened, so
24
+ // a page that never signs never downloads it (ADR-0234 errata; `tests/three-arrives-late.node.test.js`).
25
+ import { loadGlosser, provisionalGloss } from './libras-glosses.js';
26
+ import { createSignSequencer, leftOutText, nothingSigned, planSigns, playedLength, } from './libras-avatar-plan.js';
27
+ import { NOT_SHIPPED, RELEASED, avatarCanvas, avatarPlace, openAvatar, prepareClips, readManifest, } from './libras-avatar-load.js';
28
+ /** A school machine gets many times a fast one's load before it is given up on. */
29
+ const LOAD_TIMEOUT_MS = 60_000;
30
+ /** The Dev's five seconds (interface log, «The interpreter leaves 5 s after the player itself says it stopped»). */
31
+ const LEAVE_AFTER_MS = 5000;
32
+ /** The sharpest the avatar is drawn: above this, a school machine pays for pixels a child a metre away does not see. */
33
+ const MAX_PIXEL_RATIO = 1.5;
34
+ /** The longest step one frame may take: a tab that slept does not jump the signs ahead. */
35
+ const MAX_FRAME_S = 0.1;
36
+ /** The stage the delivery's player draws on, loaded — three.js with it — only when the avatar is first opened. */
37
+ async function stageOf(canvas, avatar) {
38
+ const { createAvatarStage } = await import('./libras-avatar-stage.js');
39
+ return createAvatarStage({ canvas, avatar });
40
+ }
41
+ /** Builds the free interpreter over the avatar the delivery shipped — or, where it shipped none, «signing unavailable». */
42
+ export function createLibrasAvatarInterpreter(ports) {
43
+ // ⚠️ `fetch` is taken OUT of the ports and called bare: the host lends `window.fetch` unbound, and called as `ports.fetch(…)`
44
+ // it would run with the ports object as `this` — an «Illegal invocation» in the browser, and no avatar.
45
+ const { doc, win, fetch: fetchFile } = ports;
46
+ const leaveAfterMs = ports.leaveAfterMs ?? LEAVE_AFTER_MS;
47
+ let place = null;
48
+ let shipped = null;
49
+ let manifest = null;
50
+ let glosser = null;
51
+ let canvas = null;
52
+ let stage = null;
53
+ let loading = null;
54
+ let cancelOpening = null;
55
+ /** Each clip is fetched and prepared once per stage. */
56
+ const clipLoads = new Map();
57
+ /** The request being signed now, answered when its last clip reached its end — with what it left out. */
58
+ let playing = null;
59
+ let playingLeftOut;
60
+ /** Moves on every `hide()` and at `dispose()`: a request that waited across one is not played. */
61
+ let generation = 0;
62
+ let disposed = false;
63
+ /** The seconds after the avatar stopped, at whose end it leaves the screen; `null` when none is running. */
64
+ let leaving = null;
65
+ let frameId = null;
66
+ let lastFrame = null;
67
+ // a spelled word's letters are chained through where each is held up, which the delivery measured (`ClipWindow.held`)
68
+ const sequencer = createSignSequencer((clip) => {
69
+ const w = manifest?.clips.get(clip);
70
+ return w ? playedLength(w) : 0;
71
+ }, undefined, (clip) => manifest?.clips.get(clip)?.held);
72
+ const signedWith = (leftOut) => (leftOut ? { signed: true, unsigned: leftOut } : { signed: true });
73
+ const answerPlaying = (r) => { const answer = playing; playing = null; answer?.(r); };
74
+ const released = () => ({ signed: false, reason: RELEASED });
75
+ /** Whether a request that started at `asked` was overtaken by a `hide()` or the root's end while it waited. */
76
+ const overtaken = (asked) => disposed || asked !== generation;
77
+ const stayOnScreen = () => {
78
+ if (leaving !== null)
79
+ win.clearTimeout(leaving);
80
+ leaving = null;
81
+ };
82
+ const stopDrawing = () => {
83
+ if (frameId !== null)
84
+ win.cancelAnimationFrame(frameId);
85
+ frameId = null;
86
+ lastFrame = null;
87
+ };
88
+ /** The avatar stopped: after `leaveAfterMs` at rest, it leaves — hidden, the stage kept loaded behind it. */
89
+ const leaveWhenAtRest = () => {
90
+ stayOnScreen();
91
+ leaving = win.setTimeout(() => {
92
+ leaving = null;
93
+ if (canvas)
94
+ canvas.hidden = true;
95
+ }, leaveAfterMs);
96
+ };
97
+ const frame = (now) => {
98
+ frameId = null;
99
+ if (!stage)
100
+ return;
101
+ const dt = lastFrame === null ? 0 : Math.min(MAX_FRAME_S, Math.max(0, (now - lastFrame) / 1000));
102
+ lastFrame = now;
103
+ const { starts, finished } = sequencer.tick(dt);
104
+ for (const s of starts)
105
+ stage.start(s.clip, s.fade, s.at);
106
+ stage.advance(dt);
107
+ if (!finished) {
108
+ frameId = win.requestAnimationFrame(frame);
109
+ return;
110
+ }
111
+ // «stopped»: the last clip reached its end and its final pose is held — nothing moves, so nothing more is drawn
112
+ lastFrame = null;
113
+ answerPlaying(signedWith(playingLeftOut));
114
+ leaveWhenAtRest();
115
+ };
116
+ /** Takes the canvas and the stage away; the next press opens them again. */
117
+ const release = () => {
118
+ stopDrawing();
119
+ cancelOpening?.();
120
+ cancelOpening = null;
121
+ stage?.dispose();
122
+ stage = null;
123
+ clipLoads.clear();
124
+ canvas?.remove();
125
+ canvas = null;
126
+ loading = null;
127
+ };
128
+ /** Opens the canvas and the stage once, and answers when the avatar is ready to sign — or why it cannot. */
129
+ const load = (at, m) => {
130
+ if (loading)
131
+ return loading;
132
+ const made = avatarCanvas(doc, ports.title());
133
+ canvas = made;
134
+ const opening = openAvatar(made, {
135
+ fetchFile, avatarUrl: new URL(m.avatar, at.folder).href, loadStage: ports.loadStage ?? stageOf,
136
+ timeoutMs: ports.loadTimeoutMs ?? LOAD_TIMEOUT_MS, win,
137
+ }, () => canvas === made);
138
+ cancelOpening = opening.cancel;
139
+ loading = opening.done.then((r) => {
140
+ if ('stage' in r) {
141
+ stage = r.stage;
142
+ return { signed: true };
143
+ }
144
+ if (canvas === made)
145
+ release(); // the next press tries again, from a fresh canvas
146
+ return { signed: false, reason: r.reason };
147
+ });
148
+ return loading;
149
+ };
150
+ /** The delivery's avatar, ready to sign: its place and manifest — or the answer the request gets instead. */
151
+ const reach = async (asked) => {
152
+ const at = (place ??= avatarPlace(ports.base, ports.avatarFolder));
153
+ const m = at ? await (shipped ??= readManifest(fetchFile, at)) : null;
154
+ if (!at || !m)
155
+ return { signed: false, reason: NOT_SHIPPED };
156
+ manifest = m;
157
+ if (overtaken(asked))
158
+ return released();
159
+ const loaded = await load(at, m);
160
+ return loaded.signed ? { at, m } : loaded;
161
+ };
162
+ /** The clips a text is signed with, all prepared, and what was left out — or the answer the request gets instead. */
163
+ const planFor = async (text, at, m, asked) => {
164
+ const glossOf = await (glosser ??= loadGlosser(fetchFile, at.glosses));
165
+ const plan = planSigns(glossOf(text) || provisionalGloss(text), (name) => m.clips.has(name));
166
+ const unread = stage ? await prepareClips(stage, [...new Set(plan.steps.map((s) => s.clip))], m, at, fetchFile, clipLoads) : [];
167
+ if (overtaken(asked) || !stage || !canvas)
168
+ return released();
169
+ const steps = plan.steps.filter((s) => !unread.includes(s.clip));
170
+ const leftOut = leftOutText(plan.unsigned, unread);
171
+ return steps.length ? { steps, leftOut } : { signed: false, reason: nothingSigned(leftOut) };
172
+ };
173
+ /** Puts the avatar in front of the screen and signs `steps`; answered when the last one reached its end. */
174
+ const perform = (on, shown, steps, leftOut) => {
175
+ // 📌 A new request replaces what is being signed. The turns among players are deaf mode's (`ui/vlibras`, ADR-0234 errata
176
+ // 2026-09-26): it asks again only for the same player, or for a request that names none. The one replaced is not a
177
+ // failure: the child asked again.
178
+ answerPlaying(signedWith(playingLeftOut));
179
+ shown.hidden = false;
180
+ const box = shown.getBoundingClientRect();
181
+ on.resize(box.width, box.height, Math.min(MAX_PIXEL_RATIO, win.devicePixelRatio || 1));
182
+ for (const s of sequencer.play(steps))
183
+ on.start(s.clip, s.fade, s.at);
184
+ lastFrame = null;
185
+ if (frameId === null)
186
+ frameId = win.requestAnimationFrame(frame);
187
+ return new Promise((resolve) => {
188
+ playing = resolve;
189
+ playingLeftOut = leftOut;
190
+ });
191
+ };
192
+ return {
193
+ sign: async (text) => {
194
+ // nothing to sign is not a failure, and nothing goes to `problems`
195
+ if (!provisionalGloss(text))
196
+ return { signed: true };
197
+ // a press in the seconds at rest keeps the avatar where it is: it is about to sign again
198
+ stayOnScreen();
199
+ const asked = generation;
200
+ const reached = await reach(asked);
201
+ if ('signed' in reached)
202
+ return reached;
203
+ const plan = await planFor(text, reached.at, reached.m, asked);
204
+ if ('signed' in plan || !stage || !canvas)
205
+ return 'signed' in plan ? plan : released();
206
+ return perform(stage, canvas, plan.steps, plan.leftOut);
207
+ },
208
+ hide: () => {
209
+ generation += 1;
210
+ stayOnScreen();
211
+ sequencer.stop();
212
+ stopDrawing();
213
+ if (canvas)
214
+ canvas.hidden = true;
215
+ answerPlaying(released());
216
+ },
217
+ dispose: () => {
218
+ disposed = true;
219
+ generation += 1;
220
+ stayOnScreen(); // no timer of this root outlives it
221
+ sequencer.stop();
222
+ answerPlaying(released());
223
+ release();
224
+ },
225
+ };
226
+ }
@@ -0,0 +1,37 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ import { type ClipJson } from './libras-avatar-clip.js';
3
+ /** What the player drives. */
4
+ export interface AvatarStage {
5
+ /** The avatar's three.js root, for a test or a measurement that reads the pose. Nothing in the engine reads it. */
6
+ readonly scene: object;
7
+ /** Makes a clip playable, from `from` seconds of it on. */
8
+ readonly prepare: (name: string, clip: ClipJson, from: number) => void;
9
+ readonly has: (name: string) => boolean;
10
+ /** Puts a prepared clip on the stage, fading in over `fade` seconds from what is there, `at` seconds into it. */
11
+ readonly start: (name: string, fade: number, at: number) => void;
12
+ /** Moves the clips `dt` seconds and draws. */
13
+ readonly advance: (dt: number) => void;
14
+ /** The canvas's size in CSS pixels and the pixel ratio it is drawn at. */
15
+ readonly resize: (width: number, height: number, pixelRatio: number) => void;
16
+ /** What the drawing costs, for a measurement: the renderer's own counters. */
17
+ readonly stats: () => {
18
+ readonly textures: number;
19
+ readonly geometries: number;
20
+ readonly calls: number;
21
+ readonly triangles: number;
22
+ };
23
+ /** Releases the GPU and everything the avatar holds. */
24
+ readonly dispose: () => void;
25
+ }
26
+ export interface AvatarStageOptions {
27
+ /** The canvas the avatar is drawn on: the player's, already in its document. */
28
+ readonly canvas: HTMLCanvasElement;
29
+ /** The delivery's `avatar.glb`, as bytes. */
30
+ readonly avatar: ArrayBuffer;
31
+ readonly textureSize?: number;
32
+ }
33
+ /**
34
+ * Builds the stage over `canvas`. THROWS where it cannot draw — no WebGL, an avatar three.js cannot read — and the player turns
35
+ * that into «signing unavailable», with the reason.
36
+ */
37
+ export declare function createAvatarStage({ canvas, avatar, textureSize }: AvatarStageOptions): Promise<AvatarStage>;