@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,246 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ //
3
+ // THE LIBRAS PLAYER'S FILES INTO A DELIVERY (ADR-0234, route B): `inclusionist-heavy <folder> --libras` puts the avatar and
4
+ // every sign clip of the B1 export (`scripts/libras-export.mjs`) into `<folder>/libras/avatar/`, each checked by
5
+ // sha256 against `libras-avatar.json`, and writes the manifest the player reads (`ui/libras-avatar-plan`), the GPL-3.0 text
6
+ // and the notice beside them. Nothing unchecked is written: a file whose bytes differ stops the delivery by name.
7
+ //
8
+ // WHERE FROM: the pins' `source`, the project's own mirror — the exports were made by this project, so there is no upstream
9
+ // (like the reading models, ADR-0203) — or, with `--base`, the folder `mirror` names under it. ⚠️ THE MIRROR DOES NOT HOLD THEM
10
+ // YET: nothing was uploaded, so a delivery without a base answers 404 for the avatar and stops, and a FOLDER on the build machine
11
+ // laid out as `<base>/<mirror>/avatar.glb` and `<base>/<mirror>/clips/<NAME>.json` (the B1 export's own layout) is what works.
12
+ //
13
+ // THE WINDOWS: one clip does not start where its sign starts. The export of FALA begins at a keyframe 1404 frames before the
14
+ // sign — its action spans frames −1404 to 60 in the .blend — so the clip holds 46.8 s of a slow drift toward the sign before
15
+ // the sign itself. It is not trimmed out of the file (its bytes are the export's, pinned); the pins say where the sign starts,
16
+ // with why, and the player plays from there (`ClipWindow.from`). `--pins-from` writes that into the pins it regenerates.
17
+ //
18
+ // WHERE A LETTER IS HELD UP (the Dev, 2026-09-26: a spelled word is signed with the hand held up between its letters): each
19
+ // letter's and digit's clip rises from the arms-down pose, holds its handshape and falls back. `--pins-from` MEASURES, on the
20
+ // export's own clips, where the hand is up (`held`, `[up, down]`; the rule and its numbers are `libras-export.mjs`
21
+ // `heldWindow`), and the manifest hands it to the player, which chains a word's letters through it (`ui/libras-avatar-plan`).
22
+ //
23
+ // OFFLINE (ADR-0234, phase B3; pillar 8): none of these files is in the precache — they are written after the build, with the
24
+ // glosses — and neither is the stage chunk that carries three.js, which the precache leaves out on purpose. `writeAvatarList` names them all in
25
+ // `libras/offline-avatar.json` with the sha256 of what is on the disk, and a device with deaf mode on keeps each file only if its
26
+ // bytes are the listed ones (`platform/heavy`), where the service worker answers the free player from offline.
27
+ //
28
+ // USAGE (regenerating the pins from an export; the repository never holds the export itself):
29
+ // node scripts/libras-avatar.mjs --pins-from <the export's --out folder>
30
+
31
+ import { createHash } from 'node:crypto';
32
+ import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
33
+ import { dirname, join, posix } from 'node:path';
34
+ import { fileURLToPath } from 'node:url';
35
+
36
+ export const AVATAR_PINS = fileURLToPath(new URL('./libras-avatar.json', import.meta.url));
37
+ const GPL_3 = new URL('./licences/GPL-3.0.txt', import.meta.url);
38
+ const sha256OfNode = (bytes) => createHash('sha256').update(bytes).digest('hex');
39
+
40
+ /** The avatar's file in the delivery's folder, as the manifest names it. */
41
+ export const AVATAR_FILE = 'avatar.glb';
42
+ /** The manifest the player reads in the delivery's folder (`ui/libras-avatar-plan.LIBRAS_AVATAR_MANIFEST`). */
43
+ const MANIFEST_FILE = 'manifest.json';
44
+ /** A clip's file in the delivery's folder: the sign's name as the export wrote it. */
45
+ export const clipFile = (name) => `clips/${name}.json`;
46
+
47
+ /**
48
+ * WHERE A SIGN STARTS, for the clips whose export does not start with it, and why — a clip missing here plays from its start.
49
+ * `frame` is the first frame of the sign counted from the export's first; the pins keep it in seconds, at the clip's rate.
50
+ */
51
+ export const WINDOWS = Object.freeze({
52
+ FALA: { frame: 1404, why: 'the .blend\'s action spans frames -1404 to 60: a stray keyframe 1404 frames before the sign, '
53
+ + 'which the export sampled from, so the sign starts 46.8 s into the clip' },
54
+ });
55
+
56
+ /**
57
+ * THE CLIPS A WORD IS SPELLED WITH: a letter of the manual alphabet or a digit, named by its one character — what
58
+ * `ui/libras-avatar-plan` `planSigns` fingerspells a word with no sign from. 📏 The export's ten digit clips are made as its
59
+ * letters are (the right wrist up from the arms-down pose in 0.45–0.48 s, a handshape held 0.65–0.70 s, down in 0.48–0.50 s),
60
+ * so a number is chained like a word.
61
+ */
62
+ export const isSpelledClip = (name) => /^[A-ZÇ0-9]$/u.test(name);
63
+
64
+ /**
65
+ * The pins, from a B1 export's `manifest.json`: the commit and tools that made it, the avatar and every clip with its bytes,
66
+ * sha256 and length, `from` for the clips `windows` names, and `held` — where the hand is held up, `[up, down]` in seconds —
67
+ * for the spelled clips `held` measures (`heldWindowsOf`, the export's own forward kinematics).
68
+ */
69
+ export function avatarPinsFromExport(exported, windows = WINDOWS, held = {}) {
70
+ const clips = {};
71
+ for (const name of Object.keys(exported.clips).sort()) {
72
+ const c = exported.clips[name];
73
+ if (c.file !== clipFile(name)) throw new Error(`the export names ${name}'s clip ${c.file}, not ${clipFile(name)}`);
74
+ const w = windows[name];
75
+ clips[name] = { bytes: c.bytes, sha256: c.sha256, duration: c.duration, ...(w ? { from: Math.round((w.frame / c.fps) * 1e4) / 1e4 } : {}),
76
+ ...(held[name] ? { held: held[name] } : {}) };
77
+ }
78
+ return {
79
+ comment: [
80
+ 'THE FREE LIBRAS PLAYER\'S FILES (ADR-0234, route B; scripts/libras-avatar.mjs): the avatar and one three.js clip per sign,',
81
+ 'exported by scripts/libras-export.mjs from LAViD-UFPB vlibras-dictionary-sources (GPL-3.0) at `commit`, each pinned by sha256',
82
+ 'and byte count. Their Corresponding Source is each sign\'s .blend (scripts/libras-export/sources.json) plus the two export',
83
+ 'scripts, run with the Blender named here. `from` is where a sign starts in a clip whose export does not start with it',
84
+ '(`windows`, and why). `held` is where a letter\'s or digit\'s hand is held up, [up, down] in seconds, measured on the clip',
85
+ '(libras-export.mjs `heldWindow`): the player chains a spelled word\'s letters through it.',
86
+ 'Written by `node scripts/libras-avatar.mjs --pins-from <export>`.',
87
+ ],
88
+ commit: exported.commit,
89
+ blender: exported.blender,
90
+ exporter: exported.exporter,
91
+ source: `https://lfs-oinclusionista.jrocha.dev.br/vlibras-avatar-${exported.commit.slice(0, 7)}/`,
92
+ mirror: `vlibras-avatar-${exported.commit.slice(0, 7)}/`,
93
+ windows: Object.fromEntries(Object.entries(windows).filter(([n]) => clips[n]).map(([n, w]) => [n, w.why])),
94
+ avatar: { bytes: exported.avatar.bytes, sha256: exported.avatar.sha256 },
95
+ clips,
96
+ };
97
+ }
98
+
99
+ /** The pins as the repository keeps them: one line per clip, so a changed sign is a one-line diff. */
100
+ export function avatarPinsText(pins) {
101
+ const { clips, ...head } = pins;
102
+ const lines = Object.keys(clips).map((k) => ` ${JSON.stringify(k)}: ${JSON.stringify(clips[k]).replace(/,"/g, ', "').replace(/":/g, '": ')}`);
103
+ return JSON.stringify(head, null, 2).replace(/\n}$/, `,\n "clips": {\n${lines.join(',\n')}\n }\n}\n`);
104
+ }
105
+
106
+ export function readAvatarPins(path = AVATAR_PINS) {
107
+ return JSON.parse(readFileSync(path, 'utf8'));
108
+ }
109
+
110
+ /** Where the files are read from: the pins' `source`, or — with a delivery's `--base` — the folder `mirror` names under it. */
111
+ export function avatarSourceOf(pins, base = '') {
112
+ if (!base) return pins.source;
113
+ return `${base.replace(/[/\\]+$/, '')}/${pins.mirror}`;
114
+ }
115
+
116
+ /**
117
+ * WHERE EACH SPELLED CLIP'S HAND IS HELD UP, measured on the export in `folder` (its `avatar.glb` and `clips/`): `{ NAME: [up,
118
+ * down] }` for every clip of `names` that `isSpelledClip`. The measure is the export's (`scripts/libras-export.mjs`), which the
119
+ * package does not ship — it is imported here only when the pins are regenerated.
120
+ */
121
+ export async function heldWindowsOf(folder, names, read = (p) => readFileSync(p)) {
122
+ const { heldWindow, parseGlb } = await import('./libras-export.mjs');
123
+ const gltf = parseGlb(read(join(folder, AVATAR_FILE))).json;
124
+ return Object.fromEntries(names.filter(isSpelledClip).sort()
125
+ .map((name) => [name, heldWindow(gltf, JSON.parse(read(join(folder, clipFile(name))).toString('utf8')))]));
126
+ }
127
+
128
+ /** The manifest the player reads (`ui/libras-avatar-plan` `avatarManifestOf`): the avatar's file and each clip with its window. */
129
+ export function deliveredManifest(pins) {
130
+ const clips = {};
131
+ for (const [name, c] of Object.entries(pins.clips)) {
132
+ clips[name] = { file: clipFile(name), duration: c.duration, ...(c.from ? { from: c.from } : {}), ...(c.held ? { held: c.held } : {}) };
133
+ }
134
+ return { format: 1, commit: pins.commit, avatar: AVATAR_FILE, clips };
135
+ }
136
+
137
+ /**
138
+ * Puts the avatar and every pinned clip into `<destino>/<folder>`, checked by sha256 — from `base` (see `avatarSourceOf`) when
139
+ * one is given, else from the pins' `source` — then the manifest, `LICENSE` (GPL-3.0) and `NOTICE`. Returns `{ files, bytes }`.
140
+ * THROWS on a file whose bytes are not the pinned ones, or that cannot be read: nothing unchecked reaches a delivery.
141
+ */
142
+ export async function deliverLibrasAvatar({ destino, folder, pins, base = '', fetch: fetchFile = fetch,
143
+ read = (p) => readFileSync(p), sha256 = sha256OfNode }) {
144
+ const from = avatarSourceOf(pins, base);
145
+ const remote = /^https?:\/\//i.test(from);
146
+ const bytesOf = async (rel) => {
147
+ if (!remote) return read(join(from, rel));
148
+ const address = `${from.replace(/\/?$/, '/')}${rel.split('/').map(encodeURIComponent).join('/')}`;
149
+ const resp = await fetchFile(address);
150
+ if (!resp.ok) throw new Error(`the Libras avatar's ${rel} could not be fetched: HTTP ${resp.status} — ${address}`);
151
+ return Buffer.from(await resp.arrayBuffer());
152
+ };
153
+ const files = [[AVATAR_FILE, pins.avatar], ...Object.entries(pins.clips).map(([name, c]) => [clipFile(name), c])];
154
+ let bytes = 0;
155
+ for (const [rel, pin] of files) {
156
+ const target = join(destino, folder, rel);
157
+ if (existsSync(target) && sha256(readFileSync(target)) === pin.sha256) { bytes += pin.bytes; continue; }
158
+ const body = await bytesOf(rel);
159
+ const got = sha256(body);
160
+ if (got !== pin.sha256) throw new Error(`REFUSED the Libras avatar's ${rel}: sha256 ${got}, pinned ${pin.sha256} — not written`);
161
+ mkdirSync(dirname(target), { recursive: true });
162
+ writeFileSync(target, body);
163
+ bytes += body.length;
164
+ }
165
+ writeFileSync(join(destino, folder, MANIFEST_FILE), `${JSON.stringify(deliveredManifest(pins), null, 1)}\n`);
166
+ writeFileSync(join(destino, folder, 'LICENSE'), readFileSync(GPL_3));
167
+ writeFileSync(join(destino, folder, 'NOTICE'), 'The avatar (avatar.glb) and the sign clips (clips/) in this folder are derived from '
168
+ + 'LAViD-UFPB\'s VLibras dictionary sources (vlibras-dictionary-sources, GPL-3.0, '
169
+ + `https://gitlab.lavid.ufpb.br/vlibras-public/vlibras-dictionary/vlibras-dictionary-sources, commit ${pins.commit}): each sign's `
170
+ + `.blend file, exported with Blender ${pins.blender} (${pins.exporter}) by The Inclusionist's scripts/libras-export.mjs and `
171
+ + 'scripts/libras-export/export.py. Their Corresponding Source is those .blend files (pinned by sha256 in '
172
+ + 'scripts/libras-export/sources.json) and the two scripts, in the engine\'s repository, '
173
+ + 'https://github.com/the-inclusionist/the-inclusionist-engine. Licensed under the GPL-3.0, whose text is LICENSE, beside this file.\n');
174
+ return { files: files.length, bytes };
175
+ }
176
+
177
+ /**
178
+ * THE STAGE CHUNK THE BUILD EMITTED, relative to the delivery: the one file under `start`'s folder whose name begins with `start`'s
179
+ * last segment and ends in `.js` (`platform/heavy-catalogue.LIBRAS_AVATAR_STAGE_CHUNK`; the bundler adds a content hash). THROWS
180
+ * when there is none — this build never emitted the free player, or put it elsewhere — or more than one, left by an earlier build:
181
+ * listing a stale chunk would keep bytes the page never asks for, and guessing would keep the wrong one.
182
+ */
183
+ export function stageChunkOf(destino, start, list = (dir) => readdirSync(dir)) {
184
+ const folder = posix.dirname(start);
185
+ const begins = posix.basename(start);
186
+ let names = [];
187
+ try { names = list(join(destino, folder)); } catch { /* no such folder: none found, said below */ }
188
+ const found = names.filter((name) => name.startsWith(begins) && name.endsWith('.js') && name.length > begins.length + 3);
189
+ if (found.length !== 1) {
190
+ throw new Error(`the free Libras player's stage chunk (${start}<hash>.js) was found ${found.length} times in ${join(destino, folder)}`
191
+ + `${found.length ? `: ${found.join(', ')}` : ''} — build the page again (with an emptied output folder) before \`--libras\`, `
192
+ + 'so the delivery can list the one chunk the page loads');
193
+ }
194
+ return `${folder}/${found[0]}`;
195
+ }
196
+
197
+ /**
198
+ * What the player's list names, in the order it opens them: the manifest, the avatar, the glosses (`glosses`, the path the
199
+ * glossing step wrote, relative to the delivery), every clip, then the stage chunk.
200
+ */
201
+ export function avatarListPaths({ folder, pins, stageChunk, glosses }) {
202
+ // without the glosses kept, offline the avatar would fingerspell every word: the list is not written without them
203
+ if (!glosses) throw new Error('the Libras list names no glosses: run the glossing step of `--libras` before the list is written');
204
+ return [`${folder}${MANIFEST_FILE}`, `${folder}${AVATAR_FILE}`, glosses,
205
+ ...Object.keys(pins.clips).map((name) => `${folder}${clipFile(name)}`), stageChunk];
206
+ }
207
+
208
+ /**
209
+ * WRITES A LIST A DEVICE KEEPS A DELIVERY'S OWN FILES OFFLINE BY (`platform/heavy-catalogue.DELIVERY_LISTS`): each of `paths`,
210
+ * relative to `destino`, with the sha256 and size of the bytes on the disk — what this delivery wrote, read back. Returns the
211
+ * list's path, the number of files and their bytes. THROWS, writing nothing, on a path outside the list's folders: the device
212
+ * would refuse it.
213
+ */
214
+ export function writeDeliveryList({ destino, list, paths, read = (p) => readFileSync(p), sha256 = sha256OfNode }) {
215
+ const files = paths.map((path) => {
216
+ if (!list.folders.some((folder) => path.startsWith(folder)) || path.split('/').includes('..')) {
217
+ throw new Error(`the list ${list.path} cannot name ${path}: only files under ${list.folders.join(', ')} are kept from it`);
218
+ }
219
+ const bytes = read(join(destino, path));
220
+ return { path, sha256: sha256(bytes), bytes: bytes.length };
221
+ });
222
+ mkdirSync(dirname(join(destino, list.path)), { recursive: true });
223
+ writeFileSync(join(destino, list.path), `${JSON.stringify({ format: 1, files }, null, 1)}\n`);
224
+ return { path: list.path, files: files.length, bytes: files.reduce((sum, f) => sum + f.bytes, 0) };
225
+ }
226
+
227
+ /**
228
+ * WRITES THE LIST A DEVICE KEEPS THE PLAYER OFFLINE BY (`list`, the catalogue's `libras:avatar:delivery`): each file of
229
+ * `avatarListPaths` with the sha256 and size of the bytes on the disk (`writeDeliveryList`). `stageChunk` is found first
230
+ * (`stageChunkOf`), so a build with no chunk stops the step before any list is written.
231
+ */
232
+ export function writeAvatarList({ destino, list, folder, pins, glosses, stageStart, stageChunk = stageChunkOf(destino, stageStart), read }) {
233
+ return writeDeliveryList({ destino, list, paths: avatarListPaths({ folder, pins, stageChunk, glosses }), ...(read ? { read } : {}) });
234
+ }
235
+
236
+ if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {
237
+ const at = process.argv.indexOf('--pins-from');
238
+ if (at < 0 || !process.argv[at + 1]) {
239
+ console.error('usage: node scripts/libras-avatar.mjs --pins-from <the export\'s --out folder>');
240
+ process.exit(2);
241
+ }
242
+ const exported = JSON.parse(readFileSync(join(process.argv[at + 1], 'manifest.json'), 'utf8'));
243
+ const held = await heldWindowsOf(process.argv[at + 1], Object.keys(exported.clips));
244
+ writeFileSync(AVATAR_PINS, avatarPinsText(avatarPinsFromExport(exported, WINDOWS, held)));
245
+ console.log(`${AVATAR_PINS}: the avatar and ${Object.keys(exported.clips).length} clips, ${Object.keys(held).length} of them spelled, with where each is held up`);
246
+ }
@@ -0,0 +1,95 @@
1
+ # SPDX-License-Identifier: AGPL-3.0-or-later
2
+ #
3
+ # THE GLOSSER (ADR-0234, route A — plan item 5b): Portuguese in, Libras glosses out, with LAViD's `vlibras-translator` in its
4
+ # RULE-BASED mode. Run by `scripts/libras-glosses.mjs` inside the environment `uv.lock` pins, never by hand in a delivery.
5
+ #
6
+ # stdin : {"texts": ["Jogador ZPARAMAZ entrou!", ...]} (UTF-8 JSON)
7
+ # stdout: {"translator": "1.3.3", "spacy": "3.8.16", "model": "pt_core_news_md", "modelVersion": "3.8.0",
8
+ # "mode": "rules", "glosses": ["JOGADOR ZPARAMAZ ENTRAR [EXCLAMAÇÃO]", ...], (one gloss per text, same order)
9
+ # "words": [[["Jogador", ["JOGADOR"]], ["ZPARAMAZ", ["ZPARAMAZ"]], ["entrou", ["ENTRAR", "ENTROU"]]], ...]}
10
+ #
11
+ # THE WORDS AS WRITTEN (ADR-0234, erratum «A WORD WITH NO SIGN IS SPELLED AS IT IS WRITTEN»): the translator returns lemmas, and a
12
+ # token with no sign is fingerspelled — so «entrou» would be spelled E-N-T-R-A-R. For each text, `words` lists the words the text
13
+ # WRITES (spaCy's tokens with a letter or digit, and the parts of a hyphenated one, since the translator splits at hyphens), each
14
+ # with the forms the translator's rules can turn it into, in capitals: the word itself, spaCy's lemma, the translator's own
15
+ # lookup lemma, with and without prepositions, and its singular. `scripts/libras-glosses.mjs` aligns each gloss token back to the
16
+ # word that produced it, and hands the player that written word where the delivery carries no sign. This script only reports
17
+ # what spaCy and the translator's lemmatizer know; the alignment, and what is done when it is ambiguous, is on the Node side,
18
+ # where the build's tests hold it without Python.
19
+ #
20
+ # 🔴 `neural=False`, ALWAYS: the neural mode fetches LAViD's `translator-models`, which declares no licence (ADR-0234).
21
+ # Exit codes the Node side turns into a sentence: 3 — spaCy's Portuguese model is not installed; 4 — the translator or spaCy
22
+ # is not installed; 2 — the input is not the JSON above.
23
+ import json
24
+ import sys
25
+
26
+ MODEL = "pt_core_news_md"
27
+
28
+
29
+ def written_words(doc, lemmatize, singular):
30
+ """The words a text writes, each `[as written, [the forms the translator can make of it, in capitals]]`, in order."""
31
+ words = []
32
+ for token in doc:
33
+ pieces = [token.text] + (token.text.split("-") if "-" in token.text else [])
34
+ for index, piece in enumerate(pieces):
35
+ if not any(ch.isalnum() for ch in piece):
36
+ continue
37
+ forms = {piece, lemmatize(piece), lemmatize(piece, exclude_prepositions=True), singular(piece)}
38
+ if index == 0: # the whole token: spaCy's own lemma and the lookup read in context
39
+ forms |= {token.lemma_, lemmatize(token), lemmatize(token, exclude_prepositions=True)}
40
+ words.append([piece, sorted({f.upper() for f in forms if f})])
41
+ return words
42
+
43
+
44
+ def main() -> int:
45
+ sys.stdout.reconfigure(encoding="utf-8")
46
+ try:
47
+ request = json.loads(sys.stdin.buffer.read().decode("utf-8"))
48
+ texts = request["texts"]
49
+ if not isinstance(texts, list) or not all(isinstance(t, str) for t in texts):
50
+ raise ValueError("texts must be a list of strings")
51
+ except (ValueError, KeyError, TypeError) as failure:
52
+ print(f"the glosser's input is not {{\"texts\": [...]}}: {failure}", file=sys.stderr)
53
+ return 2
54
+ try:
55
+ import spacy
56
+ from vlibras_translator import translate
57
+ from vlibras_translator.despluralize import PluralHandler
58
+ from vlibras_translator.lemmatization import Lemmatizer
59
+ except ImportError as failure:
60
+ print(f"the translator is not installed in this environment: {failure}", file=sys.stderr)
61
+ return 4
62
+ try:
63
+ from importlib.metadata import version
64
+
65
+ model_version = version(MODEL.replace("_", "-"))
66
+ translator = translate.Translator(model=MODEL)
67
+ # the text as WRITTEN is parsed apart from the translator, which parses its own rewritten copy of it
68
+ nlp = spacy.load(MODEL)
69
+ except Exception as failure: # the translator raises RuntimeError, importlib PackageNotFoundError, spaCy OSError
70
+ print(f"spaCy's model {MODEL} is not installed in this environment: {failure}", file=sys.stderr)
71
+ return 3
72
+ glosses = [translator.translate(text, neural=False) or "" for text in texts]
73
+ lemmatizer = Lemmatizer()
74
+ words = [
75
+ written_words(doc, lemmatizer.lemmatize, lambda w: PluralHandler.to_singular(w, lemmatizer))
76
+ for doc in nlp.pipe(texts)
77
+ ]
78
+ json.dump(
79
+ {
80
+ "translator": translator.version,
81
+ "spacy": spacy.__version__,
82
+ "model": MODEL,
83
+ "modelVersion": model_version,
84
+ "mode": "rules",
85
+ "glosses": glosses,
86
+ "words": words,
87
+ },
88
+ sys.stdout,
89
+ ensure_ascii=False,
90
+ )
91
+ return 0
92
+
93
+
94
+ if __name__ == "__main__":
95
+ sys.exit(main())
@@ -0,0 +1,32 @@
1
+ # SPDX-License-Identifier: AGPL-3.0-or-later
2
+ #
3
+ # THE BUILD-TIME GLOSSER'S ENVIRONMENT (ADR-0234, route A — plan item 5b). Not a package: a pinned set of tools the delivery's
4
+ # `--libras` runs once, on the build machine, to turn the Portuguese a child can be shown into Libras glosses. Nothing here is
5
+ # delivered to a device, and the environment is created OUTSIDE the repository (`scripts/libras-glosses.mjs --setup`).
6
+ #
7
+ # · `vlibras-translator` 1.3.3 (LAViD-UFPB, LGPL-3.0), published on test.pypi only — the RULE-BASED mode, never `neural`: the
8
+ # neural extra fetches a model that declares no licence, and `gloss.py` calls `translate(text, neural=False)`.
9
+ # · spaCy's `pt_core_news_md` 3.8.0 (Explosion, CC BY-SA 4.0), the Portuguese pipeline the rules read; from its GitHub release.
10
+ # `uv.lock` pins every one of them, with hashes. Regenerate it with `uv lock --project scripts/libras-glosses` after editing here.
11
+ [project]
12
+ name = "inclusionist-libras-glosses"
13
+ version = "0.0.0"
14
+ description = "Build-time glosses for the engine's Libras interpreter (ADR-0234)"
15
+ requires-python = "==3.12.*"
16
+ dependencies = [
17
+ "vlibras-translator==1.3.3",
18
+ "spacy>=3.8,<3.9",
19
+ "pt-core-news-md==3.8.0",
20
+ ]
21
+
22
+ [tool.uv]
23
+ package = false
24
+
25
+ [tool.uv.sources]
26
+ vlibras-translator = { index = "testpypi" }
27
+ pt-core-news-md = { url = "https://github.com/explosion/spacy-models/releases/download/pt_core_news_md-3.8.0/pt_core_news_md-3.8.0-py3-none-any.whl" }
28
+
29
+ [[tool.uv.index]]
30
+ name = "testpypi"
31
+ url = "https://test.pypi.org/simple/"
32
+ explicit = true