buninu-linux 0.14.57 → 0.16.59

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 (299) hide show
  1. package/LICENSES/linux-firmware/LICENCE.nvidia +131 -0
  2. package/LICENSES/linux-firmware/LICENSE.amdgpu +51 -0
  3. package/LICENSES/linux-firmware/LICENSE.amdisp +31 -0
  4. package/LICENSES/linux-firmware/LICENSE.i915 +39 -0
  5. package/LICENSES/linux-firmware/LICENSE.radeon +51 -0
  6. package/LICENSES/linux-firmware/LICENSE.xe +39 -0
  7. package/LICENSES/linux-firmware/WHENCE +10280 -0
  8. package/NOTICE.md +64 -46
  9. package/README.md +378 -87
  10. package/README.zh.md +2041 -0
  11. package/build-image.sh +10 -1
  12. package/build-uki.sh +3 -1
  13. package/fetch-alpine.sh +14 -0
  14. package/graphics.md +31 -0
  15. package/index.js +28 -11
  16. package/initramfs/bin/buninu-browser +4 -1
  17. package/initramfs/bin/buninu-land +1 -1
  18. package/initramfs/bin/bunterm +3 -3
  19. package/initramfs/bin/chroot +13 -0
  20. package/initramfs/bin/chvt +11 -0
  21. package/initramfs/bin/mount +4 -0
  22. package/initramfs/bin/umount +6 -0
  23. package/initramfs/bin/xdg-open +33 -0
  24. package/initramfs/buninu/CHANGELOG.md +66 -0
  25. package/initramfs/buninu/apps/bunmsh/CHANGELOG.md +4 -0
  26. package/initramfs/buninu/apps/bunmsh/package.json +1 -1
  27. package/initramfs/buninu/apps/bunmsh/src/main.js +1 -1
  28. package/initramfs/buninu/apps/jsgotty/LICENSES/MIT-jsmdcui.txt +22 -0
  29. package/initramfs/buninu/apps/jsgotty/NOTICE +5 -0
  30. package/initramfs/buninu/apps/jsgotty/README.en.md +61 -0
  31. package/initramfs/buninu/apps/jsgotty/README.md +61 -0
  32. package/initramfs/buninu/apps/jsgotty/client-modes.mjs +117 -0
  33. package/initramfs/buninu/apps/jsgotty/client.mjs +100 -10
  34. package/initramfs/buninu/apps/jsgotty/clipboard-powershell.js +18 -0
  35. package/initramfs/buninu/apps/jsgotty/clipboard.js +148 -0
  36. package/initramfs/buninu/apps/jsgotty/gotty.js +13 -0
  37. package/initramfs/buninu/apps/jsgotty/package-lock.json +2 -2
  38. package/initramfs/buninu/apps/jsgotty/package.json +1 -1
  39. package/initramfs/buninu/apps/jsgotty/static/css/index.css +14 -1
  40. package/initramfs/buninu/apps/jsgotty/static/index.html +1 -0
  41. package/initramfs/buninu/apps/jsgotty/static/js/gotty.js +187 -23
  42. package/initramfs/buninu/apps/jsgotty/static/js/kitty-keyboard.js +193 -0
  43. package/initramfs/buninu/apps/jsgotty/static/js/kitty.js +115 -7
  44. package/initramfs/buninu/apps/jsgotty/terminal-modes.js +332 -0
  45. package/initramfs/buninu/apps/jsgotty/tests/android-alt.test.js +115 -0
  46. package/initramfs/buninu/apps/jsgotty/tests/arrow-modifiers.test.js +80 -0
  47. package/initramfs/buninu/apps/jsgotty/tests/client-exit.test.mjs +45 -0
  48. package/initramfs/buninu/apps/jsgotty/tests/client-modes.test.mjs +105 -0
  49. package/initramfs/buninu/apps/jsgotty/tests/clipboard-browser.test.js +81 -0
  50. package/initramfs/buninu/apps/jsgotty/tests/clipboard.test.js +75 -0
  51. package/initramfs/buninu/apps/jsgotty/tests/kitty-keyboard-browser.test.js +53 -0
  52. package/initramfs/buninu/apps/jsgotty/tests/kitty-keyboard.test.js +102 -0
  53. package/initramfs/buninu/apps/jsgotty/tests/manual/README.md +20 -0
  54. package/initramfs/buninu/apps/jsgotty/tests/manual/keyboard-collector.cjs +55 -0
  55. package/initramfs/buninu/apps/jsgotty/tests/manual/keyboard-debug.js +74 -0
  56. package/initramfs/buninu/apps/jsgotty/tests/manual/kitty-keyboard.js +80 -0
  57. package/initramfs/buninu/apps/jsgotty/tests/manual/zoom-collector.cjs +55 -0
  58. package/initramfs/buninu/apps/jsgotty/tests/manual/zoom-debug.js +39 -0
  59. package/initramfs/buninu/apps/jsgotty/tests/manual-touch-diagnostics.js +33 -0
  60. package/initramfs/buninu/apps/jsgotty/tests/manual-touch-diagnostics.mjs +22 -0
  61. package/initramfs/buninu/apps/jsgotty/tests/nested-cli-pty.test.mjs +116 -0
  62. package/initramfs/buninu/apps/jsgotty/tests/reconnect-modes-browser.test.js +144 -0
  63. package/initramfs/buninu/apps/jsgotty/tests/reconnect-modes.md +44 -0
  64. package/initramfs/buninu/apps/jsgotty/tests/reconnect-modes.test.js +166 -0
  65. package/initramfs/buninu/apps/jsgotty/tests/touch-drag-browser.test.js +106 -0
  66. package/initramfs/buninu/apps/jsgotty/tests/wheel-browser.test.js +60 -0
  67. package/initramfs/buninu/apps/jsmdcui/CHANGELOG.md +44 -0
  68. package/initramfs/buninu/apps/jsmdcui/package.json +1 -1
  69. package/initramfs/buninu/apps/jsmdcui/src/highlight/go-regexp.js +83 -0
  70. package/initramfs/buninu/apps/jsmdcui/src/highlight/highlighter.js +49 -62
  71. package/initramfs/buninu/apps/jsmdcui/src/highlight/parser.js +8 -5
  72. package/initramfs/buninu/apps/jsmdcui/tests/go-regexp.test.js +98 -0
  73. package/initramfs/buninu/apps/jsmdcui/tests/heading-ids.test.js +21 -16
  74. package/initramfs/buninu/apps/jsmdcui/tests/highlight-regions.test.js +73 -0
  75. package/initramfs/buninu/apps/jsmdcui/tests/id-collision.test.js +32 -9
  76. package/initramfs/buninu/apps/jsmdcui/tests/markdown-behavior.mjs +9 -0
  77. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/NORMAL-HTML.md +46 -0
  78. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/README.md +85 -0
  79. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/REPORT.md +328 -0
  80. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/compare.mjs +75 -0
  81. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/go.mod +5 -0
  82. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/normal-html-report.json +619 -0
  83. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/normal-html.html +9 -0
  84. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/oracle.go +49 -0
  85. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/report.json +2174 -0
  86. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/PowerShell.txt +39 -0
  87. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/ada.txt +25 -0
  88. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/apacheconf.txt +49 -0
  89. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/arduino.txt +38 -0
  90. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/asciidoc.txt +33 -0
  91. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/asm.txt +77 -0
  92. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/ats.txt +48 -0
  93. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/awk.txt +28 -0
  94. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/b.txt +27 -0
  95. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/bat.txt +39 -0
  96. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/c.txt +34 -0
  97. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/caddyfile.txt +14 -0
  98. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/cake.txt +7 -0
  99. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/clojure.txt +18 -0
  100. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/cmake.txt +20 -0
  101. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/coffeescript.txt +32 -0
  102. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/colortest.txt +19 -0
  103. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/conky.txt +12 -0
  104. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/cpp.txt +46 -0
  105. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/crontab.txt +16 -0
  106. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/crystal.txt +28 -0
  107. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/csharp.txt +28 -0
  108. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/css.txt +19 -0
  109. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/csx.txt +7 -0
  110. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/cuda.txt +37 -0
  111. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/cython.txt +22 -0
  112. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/d.txt +52 -0
  113. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/dart.txt +27 -0
  114. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/default.txt +8 -0
  115. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/dockerfile.txt +10 -0
  116. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/dot.txt +15 -0
  117. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/elixir.txt +23 -0
  118. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/elm.txt +20 -0
  119. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/erb.txt +32 -0
  120. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/erlang.txt +23 -0
  121. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/fish.txt +20 -0
  122. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/forth.txt +17 -0
  123. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/fortran.txt +40 -0
  124. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/freebsd-kernel.txt +10 -0
  125. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/fsharp.txt +27 -0
  126. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/gdscript.txt +26 -0
  127. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/gemini.txt +11 -0
  128. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/gentoo-ebuild.txt +28 -0
  129. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/gentoo-etc-portage.txt +14 -0
  130. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/git-commit.txt +19 -0
  131. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/git-config.txt +11 -0
  132. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/git-rebase-todo.txt +10 -0
  133. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/gleam.txt +44 -0
  134. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/glsl.txt +18 -0
  135. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/gnuplot.txt +11 -0
  136. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/go.txt +33 -0
  137. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/godoc.txt +9 -0
  138. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/golo.txt +50 -0
  139. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/gomod.txt +16 -0
  140. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/graphql.txt +20 -0
  141. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/groff.txt +18 -0
  142. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/groovy.txt +42 -0
  143. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/haml.txt +16 -0
  144. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/hare.txt +31 -0
  145. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/haskell.txt +26 -0
  146. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/hc.txt +28 -0
  147. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/html.txt +26 -0
  148. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/html4.txt +20 -0
  149. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/html5.txt +20 -0
  150. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/ini.txt +16 -0
  151. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/inputrc.txt +14 -0
  152. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/java.txt +18 -0
  153. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/javascript.txt +41 -0
  154. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/jinja2.txt +13 -0
  155. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/json.txt +21 -0
  156. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/jsonnet.txt +28 -0
  157. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/julia.txt +24 -0
  158. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/justfile.txt +26 -0
  159. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/keymap.txt +15 -0
  160. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/kickstart.txt +16 -0
  161. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/kotlin.txt +30 -0
  162. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/kvlang.txt +37 -0
  163. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/ledger.txt +13 -0
  164. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/lfe.txt +17 -0
  165. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/lilypond.txt +15 -0
  166. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/lisp.txt +17 -0
  167. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/log.txt +45 -0
  168. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/lua.txt +48 -0
  169. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/mail.txt +17 -0
  170. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/makefile.txt +25 -0
  171. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/man.txt +12 -0
  172. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/markdown.txt +21 -0
  173. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/mc.txt +11 -0
  174. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/meson.txt +29 -0
  175. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/micro.txt +25 -0
  176. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/mpdconf.txt +13 -0
  177. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/msbuild.txt +7 -0
  178. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/nanorc.txt +16 -0
  179. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/nftables.txt +22 -0
  180. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/nginx.txt +17 -0
  181. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/nim.txt +23 -0
  182. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/nix.txt +15 -0
  183. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/nu.txt +92 -0
  184. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/objc.txt +36 -0
  185. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/ocaml.txt +25 -0
  186. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/octave.txt +35 -0
  187. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/odin.txt +35 -0
  188. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/pascal.txt +22 -0
  189. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/patch.txt +13 -0
  190. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/peg.txt +16 -0
  191. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/perl.txt +27 -0
  192. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/php.txt +44 -0
  193. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/pkg-config.txt +12 -0
  194. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/po.txt +12 -0
  195. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/pony.txt +29 -0
  196. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/pov.txt +17 -0
  197. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/privoxy-action.txt +14 -0
  198. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/privoxy-config.txt +10 -0
  199. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/privoxy-filter.txt +12 -0
  200. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/proto.txt +22 -0
  201. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/prql.txt +42 -0
  202. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/puppet.txt +22 -0
  203. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/python2.txt +24 -0
  204. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/python3.txt +28 -0
  205. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/r.txt +16 -0
  206. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/raku.txt +19 -0
  207. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/reST.txt +18 -0
  208. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/renpy.txt +11 -0
  209. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/rpmspec.txt +31 -0
  210. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/ruby.txt +34 -0
  211. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/rust.txt +27 -0
  212. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/sage.txt +24 -0
  213. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/scad.txt +23 -0
  214. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/scala.txt +16 -0
  215. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/sed.txt +12 -0
  216. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/sh.txt +20 -0
  217. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/sls.txt +15 -0
  218. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/smalltalk.txt +28 -0
  219. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/solidity.txt +26 -0
  220. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/sql.txt +35 -0
  221. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/stata.txt +32 -0
  222. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/svelte.txt +10 -0
  223. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/swift.txt +53 -0
  224. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/systemd.txt +15 -0
  225. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/tcl.txt +17 -0
  226. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/terraform.txt +28 -0
  227. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/tex.txt +15 -0
  228. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/toml.txt +31 -0
  229. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/twig.txt +29 -0
  230. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/typescript.txt +32 -0
  231. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/v.txt +47 -0
  232. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/vala.txt +22 -0
  233. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/verilog.txt +37 -0
  234. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/vhdl.txt +37 -0
  235. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/vi.txt +17 -0
  236. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/vue.txt +29 -0
  237. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/xml.txt +14 -0
  238. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/xresources.txt +14 -0
  239. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/yaml.txt +19 -0
  240. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/yum.txt +12 -0
  241. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/zig.txt +24 -0
  242. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/zscript.txt +31 -0
  243. package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/zsh.txt +21 -0
  244. package/initramfs/buninu/package.json +1 -1
  245. package/initramfs/init.js +19 -3
  246. package/initramfs/lib/bunterm/clipboard.js +14 -0
  247. package/initramfs/lib/bunterm/commands.js +37 -0
  248. package/initramfs/lib/bunterm/fps.js +38 -0
  249. package/initramfs/lib/bunterm/images.js +14 -6
  250. package/initramfs/lib/bunterm/input.js +58 -15
  251. package/initramfs/lib/bunterm/keyboard.js +62 -0
  252. package/initramfs/lib/bunterm/keycodes.js +104 -0
  253. package/initramfs/lib/bunterm/kitty-keyboard.cjs +195 -0
  254. package/initramfs/lib/bunterm/raw-protocol.js +111 -0
  255. package/initramfs/lib/bunterm/renderer.js +116 -28
  256. package/initramfs/lib/bunterm/selection.js +60 -0
  257. package/initramfs/lib/bunterm/terminal.js +157 -12
  258. package/initramfs/lib/bunterm/vt-menu.js +181 -0
  259. package/initramfs/lib/bunterm/vt.js +27 -1
  260. package/initramfs/lib/canvas.js +99 -0
  261. package/initramfs/lib/chvt.js +31 -0
  262. package/initramfs/lib/fbdev.js +32 -8
  263. package/initramfs/lib/gpu.js +77 -0
  264. package/initramfs/lib/mapped-frame.js +28 -0
  265. package/initramfs/lib/raw-image.js +71 -0
  266. package/initramfs/lib/xdg-open.js +42 -0
  267. package/initramfs/usr/share/doc/buninu-linux/bunterm.md +85 -11
  268. package/initramfs/usr/share/doc/buninu-linux/chvt.md +34 -0
  269. package/initramfs/usr/share/doc/buninu-linux/xdg-open.md +43 -0
  270. package/package.json +1 -1
  271. package/scripts/arch.js +7 -5
  272. package/scripts/arch.sh +5 -5
  273. package/scripts/build.js +16 -3
  274. package/scripts/fetch-gpu.js +105 -0
  275. package/scripts/fetch.js +11 -1
  276. package/scripts/gpu-firmware.json +22 -0
  277. package/scripts/modules.js +4 -1
  278. package/scripts/npm-packages.md +18 -17
  279. package/test/bunterm-fps.test.js +17 -0
  280. package/test/bunterm-image-bytes.test.js +15 -0
  281. package/test/bunterm-keyboard.test.js +116 -0
  282. package/test/bunterm-mapped-lifetime.test.js +147 -0
  283. package/test/bunterm-next-vt.test.js +9 -0
  284. package/test/bunterm-raw-protocol.test.js +39 -0
  285. package/test/bunterm-renderer.test.js +167 -0
  286. package/test/bunterm-selection.test.js +38 -0
  287. package/test/bunterm-vt-menu.test.js +178 -0
  288. package/test/chroot-bind.test.js +40 -2
  289. package/test/chvt.test.js +46 -0
  290. package/test/fbdev-sync.test.js +42 -0
  291. package/test/fetch-npm.test.js +2 -2
  292. package/test/gpu.test.js +155 -0
  293. package/test/image-size.test.js +17 -0
  294. package/test/manual-bunterm-image-paths.js +31 -0
  295. package/test/mount-libc-lifetime.test.js +50 -0
  296. package/test/no-gpu.test.js +43 -0
  297. package/test/readme-cli.test.js +29 -0
  298. package/test/stage-npm.test.js +11 -4
  299. package/test/xdg-open.test.js +55 -0
package/scripts/build.js CHANGED
@@ -28,6 +28,7 @@ import { format, openVolume } from "../initramfs/lib/fat.js";
28
28
  import { TYPES, randomGuid, writeGpt } from "../initramfs/lib/gpt.js";
29
29
  import { config, jsRuntime, rootDir, runIfMain } from "./arch.js";
30
30
  import { fetchAlpine } from "./fetch.js";
31
+ import { gpuReady } from "./fetch-gpu.js";
31
32
  import { pack } from "./pack.js";
32
33
 
33
34
  const nonEmpty = (path) => existsSync(path) && statSync(path).size > 0;
@@ -64,6 +65,7 @@ export const uki = async (cfg = config()) => {
64
65
  const missing = !nonEmpty(cfg.kernelImage)
65
66
  || !nonEmpty(join(cfg.kernelDir, cfg.efiStub))
66
67
  || !nonEmpty(join(cfg.modulesRoot, "modules.dep"))
68
+ || !gpuReady(cfg)
67
69
  || (cfg.real && (
68
70
  !nonEmpty(join(cfg.modulesRoot, "kernel/drivers/hid/usbhid/usbhid.ko"))
69
71
  || !nonEmpty(join(cfg.modulesRoot, "kernel/drivers/acpi/battery.ko"))
@@ -94,11 +96,22 @@ export const uki = async (cfg = config()) => {
94
96
  console.log(`Built ${cfg.arch} UKI with Alpine linux-${cfg.flavor} at vda/EFI/BOOT/${cfg.efiBootName}`);
95
97
  };
96
98
 
97
- /** build-image.sh: vda.img, 128 MiB, GPT with the ESP from sector 2048 to the last usable one. */
99
+ // Keep small images at 128 MiB. Reserve 1/16 payload size plus 4 MiB for
100
+ // FAT metadata, directories, GPT/alignment and free space, rounded to 64 MiB.
101
+ // Keep this calculation aligned with build-image.sh.
102
+ export const imageBytes = (payloadBytes) => {
103
+ if (!Number.isSafeInteger(payloadBytes) || payloadBytes < 0 || payloadBytes > 0xffffffff)
104
+ throw new Error("EFI payload exceeds the FAT32 single-file limit or has an invalid size");
105
+ const unit = 64 * 1024 * 1024;
106
+ return Math.max(128 * 1024 * 1024,
107
+ Math.ceil((payloadBytes + Math.ceil(payloadBytes / 16) + 4 * 1024 * 1024) / unit) * unit);
108
+ };
109
+
110
+ /** build-image.sh: dynamically sized GPT disk with one FAT32 ESP. */
98
111
  export const image = (cfg = config()) => {
99
112
  const efi = join(rootDir, "vda/EFI/BOOT", cfg.efiBootName);
100
113
  if (!nonEmpty(efi)) throw new Error(`no ${cfg.arch} EFI payload ${efi} yet; build the UKI first`);
101
- const diskSectors = (128 * 1024 * 1024) / 512;
114
+ const diskSectors = imageBytes(statSync(efi).size) / 512;
102
115
  const espStart = 2048;
103
116
  const espEnd = diskSectors - 34;
104
117
  const path = join(rootDir, "vda.img");
@@ -134,7 +147,7 @@ export const image = (cfg = config()) => {
134
147
  } finally {
135
148
  volume.close();
136
149
  }
137
- console.log(`Packed vda/EFI/BOOT/${cfg.efiBootName} into vda.img`);
150
+ console.log(`Packed vda/EFI/BOOT/${cfg.efiBootName} into vda.img (${diskSectors * 512 / 1024 / 1024} MiB)`);
138
151
  };
139
152
 
140
153
  export const buildAll = async (cfg = config()) => {
@@ -0,0 +1,105 @@
1
+ // Firmware is image data, not a running service. Only cfg.gpu loads GPU drivers.
2
+ import { createHash } from "node:crypto";
3
+ import { readFileSync, existsSync, mkdirSync, writeFileSync, rmSync, symlinkSync, statSync, mkdtempSync, renameSync } from "node:fs";
4
+ import { join, dirname, posix } from "node:path";
5
+ import pins from "./gpu-firmware.json" with { type: "json" };
6
+ import { config, runIfMain, withUmask022 } from "./arch.js";
7
+ import { download, readTar } from "./fetch.js";
8
+
9
+ const fingerprint = (cfg) => createHash("sha256").update(JSON.stringify({ revision: 2, arch: cfg.arch, release: cfg.kernelRelease, real: !!cfg.real, noGpu: !!cfg.noGpu, pins })).digest("hex");
10
+ const marker = (cfg) => join(cfg.modulesRoot, "gpu-support.json");
11
+
12
+ export const gpuReady = (cfg) => {
13
+ try {
14
+ const state = JSON.parse(readFileSync(marker(cfg), "utf8"));
15
+ if (cfg.noGpu && existsSync(join(cfg.nativeDir, "lib/firmware"))) return false;
16
+ return state.fingerprint === fingerprint(cfg) && state.files.every(path => statSync(join(cfg.nativeDir, path)).size > 0);
17
+ } catch { return false; }
18
+ };
19
+
20
+ // Validate paths before writing; firmware packages can link across GPU folders.
21
+ export const firmwarePath = (name) => {
22
+ if (!name.startsWith("lib/firmware/") || name.includes("\\") || posix.normalize(name) !== name || name.endsWith("/"))
23
+ throw Error(`Invalid firmware path: ${name}`);
24
+ return name;
25
+ };
26
+ export const firmwareLinkTarget = (name, { target, hard }) => {
27
+ firmwarePath(name);
28
+ if (target.startsWith("/") || target.includes("\\")) throw Error(`Invalid firmware link: ${name} -> ${target}`);
29
+ return firmwarePath(posix.normalize(hard ? target : posix.join(posix.dirname(name), target)));
30
+ };
31
+
32
+ export const extractFirmware = (archives, staging) => {
33
+ const files = new Map(), links = new Map();
34
+ for (const archive of archives) {
35
+ for (const [name, bytes] of archive.entries()) if (name.startsWith("lib/firmware/")) files.set(firmwarePath(name), bytes);
36
+ for (const [name, link] of archive.links()) if (name.startsWith("lib/firmware/")) {
37
+ firmwareLinkTarget(name, link);
38
+ links.set(name, link);
39
+ }
40
+ }
41
+ const directories = new Set();
42
+ for (const name of [...files.keys(), ...links.keys()])
43
+ for (let parent = posix.dirname(name); parent !== "."; parent = posix.dirname(parent)) directories.add(parent);
44
+ // NVIDIA also aliases entire GPU firmware directories. Resolve links in any
45
+ // path component, without following host filesystem links during validation.
46
+ const resolve = (name, seen = new Set()) => {
47
+ if (seen.has(name)) throw Error(`Firmware link cycle: ${name}`);
48
+ seen.add(name);
49
+ for (let prefix = name; prefix !== "."; prefix = posix.dirname(prefix)) {
50
+ const link = links.get(prefix);
51
+ if (link) {
52
+ if (seen.has("link:" + prefix)) throw Error(`Firmware link cycle: ${prefix}`);
53
+ seen.add("link:" + prefix);
54
+ return resolve(firmwareLinkTarget(prefix, link) + name.slice(prefix.length), seen);
55
+ }
56
+ }
57
+ if (files.has(name)) return files.get(name);
58
+ if (directories.has(name)) return null;
59
+ throw Error(`Missing firmware link target: ${name}`);
60
+ };
61
+ for (const name of [...files.keys(), ...links.keys()]) {
62
+ for (let parent = posix.dirname(name); parent !== "."; parent = posix.dirname(parent))
63
+ if (links.has(parent) || files.has(parent)) throw Error(`Firmware parent is not a directory: ${parent}`);
64
+ if (links.has(name)) {
65
+ if (files.has(name)) throw Error(`Duplicate firmware file/link: ${name}`);
66
+ if (resolve(name) === null && links.get(name).hard) throw Error(`Firmware hardlink to directory: ${name}`);
67
+ }
68
+ }
69
+ for (const [name, bytes] of files) {
70
+ const path = join(staging, name); mkdirSync(dirname(path), { recursive: true }); writeFileSync(path, bytes);
71
+ }
72
+ for (const [name, link] of links) {
73
+ const path = join(staging, name); mkdirSync(dirname(path), { recursive: true });
74
+ if (files.has(name)) throw Error(`Duplicate firmware file/link: ${name}`);
75
+ if (link.hard) writeFileSync(path, resolve(name));
76
+ else symlinkSync(link.target, path);
77
+ }
78
+ return [...files.keys(), ...links.keys()];
79
+ };
80
+
81
+ export const fetchGpuFirmware = (cfg = config()) => withUmask022(async () => {
82
+ const dep = readFileSync(join(cfg.modulesRoot, "modules.dep"), "utf8");
83
+ const builtin = readFileSync(join(cfg.modulesRoot, "modules.builtin"), "utf8");
84
+ const pc = !cfg.noGpu && cfg.arch === "x86_64" && /\/(?:i915|xe|amdgpu|radeon|nouveau)\.ko(?:[:\n]|$)/.test(dep + "\n" + builtin);
85
+ // Firmware bytes are architecture-independent; these pins refer to the x86_64 APKs.
86
+ const packages = pc ? pins : [];
87
+ const archives = [];
88
+ for (const pkg of packages) archives.push(readTar(await download(cfg, `${cfg.alpineBase}/${pkg.name}`, pkg.name, pkg.sha256)));
89
+ mkdirSync(join(cfg.nativeDir, "lib"), { recursive: true });
90
+ const staging = mkdtempSync(join(cfg.nativeDir, "lib", ".gpu-firmware-"));
91
+ try {
92
+ const files = extractFirmware(archives, staging);
93
+ const target = join(cfg.nativeDir, "lib/firmware");
94
+ // This generated directory is owned by the firmware fetch stage.
95
+ rmSync(target, { recursive: true, force: true });
96
+ if (files.length) renameSync(join(staging, "lib/firmware"), target);
97
+ writeFileSync(marker(cfg), JSON.stringify({ fingerprint: fingerprint(cfg), packages, files }, null, 2) + "\n");
98
+ console.log(`GPU support: ${packages.length} firmware packages, ${files.length} files/links; drivers stay opt-in via cfg.gpu`);
99
+ } finally { rmSync(staging, { recursive: true, force: true }); }
100
+ });
101
+
102
+ runIfMain(import.meta.url, async () => {
103
+ if (process.argv.includes("--check")) process.exitCode = gpuReady(config()) ? 0 : 1;
104
+ else await fetchGpuFirmware();
105
+ });
package/scripts/fetch.js CHANGED
@@ -73,7 +73,9 @@ export const download = async (cfg, url, name, sha256) => {
73
73
  export const readTar = (path) => {
74
74
  const data = gunzipSync(readFileSync(path));
75
75
  const files = new Map();
76
+ const links = new Map();
76
77
  let longName = null;
78
+ let longLink = null;
77
79
  for (let offset = 0; offset + 512 <= data.length;) {
78
80
  const header = data.subarray(offset, offset + 512);
79
81
  if (header.every((byte) => byte === 0)) { offset += 512; continue; }
@@ -87,19 +89,25 @@ export const readTar = (path) => {
87
89
  for (const record of body.toString("utf8").split("\n")) {
88
90
  const match = /^\d+ path=(.*)$/.exec(record);
89
91
  if (match) longName = match[1];
92
+ const link = /^\d+ linkpath=(.*)$/.exec(record);
93
+ if (link) longLink = link[1];
90
94
  }
91
95
  continue;
92
96
  }
93
97
  if (type === "L") { longName = body.toString("utf8").replace(/\0.*$/s, ""); continue; }
98
+ if (type === "K") { longLink = body.toString("utf8").replace(/\0.*$/s, ""); continue; }
94
99
  if (type === "g") continue;
95
100
  const prefix = field(345, 155);
96
101
  const name = longName ?? (prefix ? `${prefix}/${field(0, 100)}` : field(0, 100));
97
102
  longName = null;
98
103
  if (type === "0" || type === "\0") files.set(name.replace(/^\.\//, ""), body);
104
+ if (type === "1" || type === "2") links.set(name.replace(/^\.\//, ""), { target: longLink ?? field(157, 100), hard: type === "1" });
105
+ longLink = null;
99
106
  }
100
107
  return {
101
108
  // Enumerate regular files without exposing the Map's mutation methods.
102
109
  entries() { return files.entries(); },
110
+ links() { return links.entries(); },
103
111
  get(name) {
104
112
  const bytes = files.get(name);
105
113
  if (!bytes) throw new Error(`${path}: no ${name}`);
@@ -132,7 +140,7 @@ export const fetchAlpine = (cfg = config()) => withUmask022(async () => {
132
140
  const release = `lib/modules/${cfg.kernelRelease}`;
133
141
  const dep = linux.get(`${release}/modules.dep`).toString();
134
142
  const builtin = linux.get(`${release}/modules.builtin`);
135
- const { selected, notes } = select({ dep, builtin: builtin.toString(), packageName: cfg.linuxPackage, arch: cfg.arch, flavor: cfg.flavor, real: cfg.real });
143
+ const { selected, notes } = select({ dep, builtin: builtin.toString(), packageName: cfg.linuxPackage, arch: cfg.arch, flavor: cfg.flavor, real: cfg.real, noGpu: cfg.noGpu });
136
144
  for (const note of notes) console.error(note);
137
145
  rmSync(cfg.modulesRoot, { recursive: true, force: true });
138
146
  writeTo(join(cfg.modulesRoot, "modules.builtin"), builtin);
@@ -142,6 +150,8 @@ export const fetchAlpine = (cfg = config()) => withUmask022(async () => {
142
150
  writeTo(join(cfg.modulesRoot, "modules.dep"), trimDependencies(dep, selected));
143
151
  writeTo(join(cfg.modulesRoot, "modules.alias"), filterAliases(linux.get(`${release}/modules.alias`).toString(), selected));
144
152
 
153
+ await (await import("./fetch-gpu.js")).fetchGpuFirmware(cfg);
154
+
145
155
  chmodSync(join(cfg.nativeDir, "lib", cfg.muslLoader), 0o755);
146
156
  console.log(`Fetched Alpine ${cfg.arch} linux-${cfg.flavor} kernel, ${selected.length} modules, musl, and the ${cfg.efiStub} UKI stub.`);
147
157
  });
@@ -0,0 +1,22 @@
1
+ [
2
+ {
3
+ "name": "linux-firmware-amdgpu-20260519-r0.apk",
4
+ "sha256": "70bc5c972a2da62b84b53e06b6d4c5aec0a8e9d6d280802755c3c8643f3bb8c6"
5
+ },
6
+ {
7
+ "name": "linux-firmware-i915-20260519-r0.apk",
8
+ "sha256": "3946f8b0a8f0dd187bd71d9aa9b3085a10cb1e8cf4700aa876799e6368309fa4"
9
+ },
10
+ {
11
+ "name": "linux-firmware-nvidia-20260519-r0.apk",
12
+ "sha256": "9fd3fa7eff65792ef8558c01a27246862e2701a99ef97dba5111b9f6db2c80ee"
13
+ },
14
+ {
15
+ "name": "linux-firmware-radeon-20260519-r0.apk",
16
+ "sha256": "3b0d6d3982563d17df0c33c2d695a5a547487cb56f2fd9a004ed729fac8f0684"
17
+ },
18
+ {
19
+ "name": "linux-firmware-xe-20260519-r0.apk",
20
+ "sha256": "6ebc37da64ecb48973c1814d1499c2c2f8f38593e8cf7227811d840c8b17172f"
21
+ }
22
+ ]
@@ -16,7 +16,10 @@
16
16
  const required = (names) => names.map((name) => ({ name }));
17
17
  const optional = (names) => names.map((name) => ({ name, optional: true }));
18
18
 
19
- export const requests = ({ arch, flavor, real }) => [
19
+ export const requests = ({ arch, flavor, real, noGpu = false }) => [
20
+ // Pack, but never coldplug at boot or through cfg.all. cfg.gpu matches hardware.
21
+ ...(!noGpu ? optional(["virtio-gpu", "vmwgfx"]) : []),
22
+ ...(!noGpu && arch === "x86_64" ? optional(["i915", "xe", "amdgpu", "radeon", "nouveau"]) : []),
20
23
  ...required([
21
24
  "failover",
22
25
  "net_failover",
@@ -1,24 +1,25 @@
1
1
  # npm packages
2
2
  # Latest versions checked on 2026-10-11; SHA-256 pins verified against registry SHA-512 integrity.
3
- # buninu-browser 0.2.4 and buninu-land 0.1.4 verified on 2026-10-18.
4
- # casty 1.3.7 verified on 2026-10-16.
3
+ # buninu-browser 0.6.0, buninu-land 0.3.1, and casty 1.5.1 verified on 2026-10-29.
4
+ # jspulse 0.2.3 verified on 2026-10-22.
5
+ # jspinyin 0.2.0 verified on 2026-10-22.
5
6
 
6
- "buninu-browser@0.2.4":
7
- - https://registry.npmjs.org/buninu-browser/-/buninu-browser-0.2.4.tgz
8
- - sha256:247c1ae9644b5016d1dc3f74d1c6c18a986e3961b1d86d2497862e4007bc50aa
7
+ "buninu-browser@0.6.0":
8
+ - https://registry.npmjs.org/buninu-browser/-/buninu-browser-0.6.0.tgz
9
+ - sha256:81929d903239f615f6265e0b8bf52cc36e34c622486c517bc58d70078c28f72c
9
10
 
10
- "buninu-land@0.1.4":
11
- - https://registry.npmjs.org/buninu-land/-/buninu-land-0.1.4.tgz
12
- - sha256:5cda65c60a7af8c7a05ec519a254e0af3f9913ea94f6ae493e5ddc93a8d87407
11
+ "buninu-land@0.3.1":
12
+ - https://registry.npmjs.org/buninu-land/-/buninu-land-0.3.1.tgz
13
+ - sha256:6a2df4b52e12bd20972b195903b72a8a1fdefb62a60391c5f090764283731d22
13
14
 
14
- "@drxiaozhi/casty@1.3.7":
15
- - https://registry.npmjs.org/@drxiaozhi/casty/-/casty-1.3.7.tgz
16
- - sha256:acc0641fb68c00222ba1cd2b7520ba99c269b996e145e82f18466be4d126b811
15
+ "@drxiaozhi/casty@1.5.1":
16
+ - https://registry.npmjs.org/@drxiaozhi/casty/-/casty-1.5.1.tgz
17
+ - sha256:821b9ca5e726afa691c4761d9ec917f862bb1a14592a7e590713c5fc735f71fa
17
18
 
18
- "@drxiaozhi/jspulse@0.2.1":
19
- - https://registry.npmjs.org/@drxiaozhi/jspulse/-/jspulse-0.2.1.tgz
20
- - sha256:9a34a76b1edac6767fa90e33cbc4d3a511c082e9ab73fb196390f841aad37839
19
+ "@drxiaozhi/jspulse@0.2.3":
20
+ - https://registry.npmjs.org/@drxiaozhi/jspulse/-/jspulse-0.2.3.tgz
21
+ - sha256:c4a1a7adb559f94096243bebaac0890dc5096d518f7f83ed489c38d5cdfaa263
21
22
 
22
- "@drxiaozhi/jspinyin@0.1.1":
23
- - https://registry.npmjs.org/@drxiaozhi/jspinyin/-/jspinyin-0.1.1.tgz
24
- - sha256:c6d10a40b6c93a942d85dff69ed2e45dacf506b15caab6821688e8e29df810ed
23
+ "@drxiaozhi/jspinyin@0.2.0":
24
+ - https://registry.npmjs.org/@drxiaozhi/jspinyin/-/jspinyin-0.2.0.tgz
25
+ - sha256:b6dfb18f8a8602b9449b75823bce1790fd9682fac68fcbc57c55b4ebd98baa6e
@@ -0,0 +1,17 @@
1
+ import {test,expect} from 'bun:test';
2
+ import {createFpsMeter} from '../initramfs/lib/bunterm/fps.js';
3
+ test('FPS counts new image presentations, ignores redraws, idles at zero and resets on toggle',()=>{
4
+ let time=0;const meter=createFpsMeter(()=>time),old={},a={},b={};
5
+ meter.presented([{image:old}]);expect(meter.enabled).toBe(false);
6
+ expect(meter.toggle([{image:old}])).toBe(true);
7
+ meter.presented([{image:old}]);
8
+ meter.presented([{image:a}]);meter.presented([{image:a}]);
9
+ meter.presented([{image:a},{image:b}]);meter.presented([]);
10
+ time=1000;expect(meter.sample()).toBe(2);
11
+ time=2000;expect(meter.sample()).toBe(0);
12
+ expect(meter.toggle()).toBe(false);
13
+ meter.presented([{image:{}}]);
14
+ time=3000;expect(meter.sample()).toBe(0);
15
+ meter.toggle([{image:b}]);meter.presented([{image:b}]);
16
+ meter.presented([{image:{}}]);time=3500;expect(meter.sample()).toBe(2);
17
+ });
@@ -53,3 +53,18 @@ test('raw RGBA bytes pass directly to CanvasKit', async () => {
53
53
  expect(received).toBe(image.data);
54
54
  expect(received.equals(pixels)).toBe(true);
55
55
  });
56
+
57
+ import {rawImageOpaque} from '../initramfs/lib/raw-image.js';
58
+ test('raw opacity checks every pixel in aligned and unaligned byte views', () => {
59
+ for (const length of [0, 1, 7, 8, 9, 15, 16, 17, 33]) for (const offset of [0, 1, 4]) {
60
+ const storage = new Uint8Array(length * 4 + offset);
61
+ const pixels = storage.subarray(offset);
62
+ for (let i = 3; i < pixels.length; i += 4) pixels[i] = 255;
63
+ expect(rawImageOpaque({pixels})).toBe(true);
64
+ for (let i = 3; i < pixels.length; i += 4) {
65
+ pixels[i] = 254;
66
+ expect(rawImageOpaque({pixels})).toBe(false);
67
+ pixels[i] = 255;
68
+ }
69
+ }
70
+ });
@@ -0,0 +1,116 @@
1
+ import {test,expect} from "bun:test";
2
+ import {Terminal} from "../initramfs/lib/xterm/xterm-headless.mjs";
3
+ import {ConsoleKeys,MediumRawDecoder,legacyKey} from "../initramfs/lib/bunterm/keycodes.js";
4
+ import {attachKeyboard} from "../initramfs/lib/bunterm/keyboard.js";
5
+ const write = (term,text) => new Promise(resolve=>term.write(text,resolve));
6
+ const letters = {16:"q",17:"w",18:"e",19:"r",20:"t",21:"y",22:"u",23:"i",24:"o",25:"p",
7
+ 30:"a",31:"s",32:"d",33:"f",34:"g",35:"h",36:"j",37:"k",38:"l",44:"z",45:"x",46:"c",
8
+ 47:"v",48:"b",49:"n",50:"m"};
9
+ const keys = () => new ConsoleKeys({lookup:(table,n) => {
10
+ const letter = letters[n];
11
+ if (letter) return 0xb00 | (table & 1 ? letter.toUpperCase() : letter).charCodeAt(0);
12
+ return n === 57 ? 32 : n === 53 ? (table & 1 ? 63 : 47) : null;
13
+ }});
14
+ function fixture(extra = {}) {
15
+ const term = new Terminal({allowProposedApi:true}), output = [], modes = [], switches = [];
16
+ const keyboard = attachKeyboard({term,write:text=>output.push(text),
17
+ setEnhanced:v=>modes.push(v),switchVT:n=>switches.push(n),...extra});
18
+ return {term,output,modes,switches,keyboard,close(){keyboard.dispose();term.dispose();}};
19
+ }
20
+ test("mediumraw extended codes survive every possible read boundary", () => {
21
+ const bytes = Buffer.from([30,158,0,129,183,128,129,183]); // F13 = 183
22
+ for (let split=0;split<=bytes.length;split++) {
23
+ const decoder = new MediumRawDecoder(), events=[];
24
+ decoder.feed(bytes.subarray(0,split),(c,d)=>events.push([c,d]));
25
+ decoder.feed(bytes.subarray(split),(c,d)=>events.push([c,d]));
26
+ expect(events).toEqual([[30,true],[30,false],[183,true],[183,false]]);
27
+ }
28
+ });
29
+ test("all Ctrl/Alt/Shift/Super letter combinations stay distinct", async () => {
30
+ const f=fixture();
31
+ try {
32
+ await write(f.term,"\x1b[>9u");
33
+ for (let bits=0;bits<16;bits++) for (const [number,letter] of Object.entries(letters)) {
34
+ const state=keys();
35
+ for (const [mask,code] of [[1,42],[2,56],[4,29],[8,125]]) if(bits&mask) f.keyboard.event(state.event(code,true));
36
+ f.output.length=0;
37
+ f.keyboard.event(state.event(Number(number),true));
38
+ expect(f.output).toEqual([`\x1b[${letter.charCodeAt(0)}${bits?";"+(bits+1):""}u`]);
39
+ f.keyboard.blur();
40
+ }
41
+ } finally {f.close();}
42
+ });
43
+ test("push/pop, repeat/release, recovery keys, independent buffers and reset", async () => {
44
+ const f=fixture(), state=keys();
45
+ try {
46
+ await write(f.term,"\x1b[?u\x1b[>11u");
47
+ expect(f.output).toEqual(["\x1b[?0u"]);
48
+ f.output.length=0;
49
+ f.keyboard.event(state.event(125,true));
50
+ f.keyboard.event(state.event(50,true));
51
+ f.keyboard.event(state.event(50,true));
52
+ f.keyboard.event(state.event(50,false));
53
+ f.keyboard.event(state.event(125,false));
54
+ expect(f.output).toEqual(["\x1b[57444;9u","\x1b[109;9u","\x1b[109;9:2u","\x1b[109;9:3u","\x1b[57444;1:3u"]);
55
+ await write(f.term,"\x1b[>1u");
56
+ f.output.length=0;
57
+ f.keyboard.event(state.event(28,true));
58
+ f.keyboard.event(state.event(28,false));
59
+ f.keyboard.event(state.event(30,true));
60
+ expect(f.output).toEqual(["\r","a"]);
61
+ await write(f.term,"\x1b[<u");
62
+ expect(f.keyboard.flags).toBe(11);
63
+ await write(f.term,"\x1b[?1049h");
64
+ expect(f.keyboard.flags).toBe(0);
65
+ await write(f.term,"\x1b[?1049l");
66
+ expect(f.keyboard.flags).toBe(11);
67
+ await write(f.term,"\x1bc");
68
+ expect(f.keyboard.flags).toBe(0);
69
+ expect(f.modes).toEqual([true,false,true,false]);
70
+ } finally {f.close();}
71
+ });
72
+ test("local menu and reserved VT keys never reach the child", async () => {
73
+ let menu=false;
74
+ const f=fixture({localInput:()=>menu}), state=keys();
75
+ try {
76
+ await write(f.term,"\x1b[>11u");
77
+ f.keyboard.event(state.event(29,true));
78
+ f.keyboard.event(state.event(56,true));
79
+ f.output.length=0;
80
+ f.keyboard.event(state.event(60,true));
81
+ f.keyboard.event(state.event(60,false));
82
+ expect(f.switches).toEqual([2]);
83
+ expect(f.output).toEqual([]);
84
+ f.keyboard.blur(); f.output.length=0;
85
+ menu=true;
86
+ f.keyboard.event(state.event(30,true));
87
+ f.keyboard.event(state.event(30,false));
88
+ expect(f.output).toEqual([]);
89
+ } finally {f.close();}
90
+ });
91
+ test("layout translation, caps, AltGr, keypad and Unicode map", () => {
92
+ const state=keys();
93
+ state.event(58,true); state.event(58,false);
94
+ expect(state.event(30,true).key).toBe("A");
95
+ state.event(42,true);
96
+ expect(state.event(30,true).key).toBe("a");
97
+ expect(state.event(100,true).key).toBe("AltGraph");
98
+ expect(state.event(53,true).getModifierState("AltGraph")).toBe(true);
99
+ const unicode=new ConsoleKeys({lookup:()=>0xf000^0x4e2d});
100
+ expect(unicode.event(30,true).key).toBe("中");
101
+ const num=keys();
102
+ num.event(69,true);
103
+ expect(num.event(79,true).key).toBe("1");
104
+ expect(num.event(79,false).location).toBe(3);
105
+ expect(legacyKey({type:"keydown",key:"/",ctrlKey:true})).toBe("\x1f");
106
+ });
107
+ test("failed console transition resets flags and does not strand the protocol", async () => {
108
+ const errors=[];
109
+ const f=fixture({setEnhanced(){throw new Error("not a VT");},onError:e=>errors.push(e.message)});
110
+ try {
111
+ await write(f.term,"\x1b[>11u\x1b[?u");
112
+ expect(f.keyboard.flags).toBe(0);
113
+ expect(f.output).toEqual(["\x1b[?0u"]);
114
+ expect(errors).toEqual(["not a VT"]);
115
+ } finally {f.close();}
116
+ });
@@ -0,0 +1,147 @@
1
+ import {test, expect} from 'bun:test';
2
+ import fs from 'node:fs';
3
+ import {tmpdir} from 'node:os';
4
+ import {join} from 'node:path';
5
+ import {createRequire} from 'node:module';
6
+ import {bgraParser, BGRA_FORMAT} from '../initramfs/lib/bunterm/raw-protocol.js';
7
+ import {ownMapping, retainMapping} from '../initramfs/lib/mapped-frame.js';
8
+ import {createRawImage, rawImageData, skiaImage} from '../initramfs/lib/raw-image.js';
9
+ import {ImageStore} from '../initramfs/lib/bunterm/images.js';
10
+ const {KittyGraphicsParser} = createRequire(import.meta.url)('../initramfs/buninu/apps/jsgotty/gotty.js');
11
+ const CK = {ColorType:{RGBA_8888:1,BGRA_8888:2}};
12
+ function fixture() {
13
+ const dir=fs.mkdtempSync(join(tmpdir(),'tty-graphics-protocol-lifetime-'));
14
+ const parser=new (bgraParser(KittyGraphicsParser))({binaryData:true});
15
+ const term={buffer:{active:{type:'alternate',baseY:0,cursorY:0,cursorX:0,viewportY:0}}};
16
+ const store=new ImageStore({CanvasKit:CK,term,renderer:{cellWidth:1,cellHeight:1,offsetX:0,offsetY:0,rows:10,invalidateRows(){}}});
17
+ const parse=(control,payload='')=>parser.parsePacket(Buffer.from(control+';'+payload),{row:1,col:1});
18
+ let serial=0;
19
+ return {parser,store,parse,frame(id=1,format=BGRA_FORMAT){
20
+ const path=join(dir,'frame'+serial++);fs.writeFileSync(path,Buffer.from([1,2,3,255]));
21
+ return parse(`a=T,t=t,f=${format},s=1,v=1,i=${id},C=1`,Buffer.from(path).toString('base64'));
22
+ },close(){parser.dispose();store.delete();fs.rmSync(dir,{recursive:true,force:true});}};
23
+ }
24
+
25
+ test('replacement protects parser results and renderer until their independent owners release',async()=>{
26
+ const f=fixture();
27
+ try {
28
+ using first=f.frame();const bytes=first.image.data;
29
+ await f.store.handle(first);
30
+ using second=f.frame();
31
+ expect(bytes.byteLength).toBe(4); // parser replaced, renderer/result still own old frame
32
+ await f.store.handle(second);
33
+ expect(bytes.byteLength).toBe(4); // result is still borrowed
34
+ first[Symbol.dispose]();first[Symbol.dispose]();
35
+ expect(bytes.byteLength).toBe(0);
36
+ const next=second.image.data;
37
+ f.parser.dispose();second[Symbol.dispose]();
38
+ expect(next.byteLength).toBe(4); // renderer outlives parser
39
+ f.store.delete();expect(next.byteLength).toBe(0);
40
+ } finally {f.close();}
41
+ });
42
+
43
+ test('multiple placements and lowercase deletion retain pixels for re-placement',async()=>{
44
+ const f=fixture();
45
+ try {
46
+ using first=f.frame(1,32);const bytes=first.image.data;
47
+ await f.store.handle(first);first[Symbol.dispose]();
48
+ using second=f.parse('a=p,i=1,p=two,C=1');await f.store.handle(second);second[Symbol.dispose]();
49
+ expect(f.store.visible()).toHaveLength(2);
50
+ await f.store.handle(f.parse('a=d,d=i,i=1,p=two'));
51
+ expect(f.store.visible()).toHaveLength(1);expect(bytes.byteLength).toBe(4);
52
+ await f.store.handle(f.parse('a=d,d=a'));
53
+ expect(f.store.visible()).toHaveLength(0);expect(bytes.byteLength).toBe(4);
54
+ using again=f.parse('a=p,i=1,C=1');await f.store.handle(again);again[Symbol.dispose]();
55
+ expect(rawImageData(f.store.visible()[0].image).pixels[0]).toBe(1);
56
+ await f.store.handle(f.parse('a=d,d=A'));
57
+ expect(bytes.byteLength).toBe(0);
58
+ } finally {f.close();}
59
+ });
60
+
61
+ test('queued frames survive replacement and parser shutdown before consumer handles them',async()=>{
62
+ const f=fixture();
63
+ try {
64
+ using first=f.frame();using second=f.frame();f.parser.dispose();
65
+ await f.store.handle(first);first[Symbol.dispose]();
66
+ expect(first.image.data.byteLength).toBe(4);
67
+ await f.store.handle(second);expect(first.image.data.byteLength).toBe(0);
68
+ second[Symbol.dispose]();f.store.delete();expect(second.image.data.byteLength).toBe(0);
69
+ expect(f.frame()).toBeNull();
70
+ } finally {f.close();}
71
+ });
72
+
73
+ test('closing while decode awaits cannot repopulate the image store',async()=>{
74
+ const f=fixture();
75
+ try {
76
+ using first=f.frame();const handling=f.store.handle(first);
77
+ f.store.delete();f.parser.dispose();await handling;
78
+ expect(f.store.images.size).toBe(0);expect(f.store.placements.size).toBe(0);
79
+ first[Symbol.dispose]();expect(first.image.data.byteLength).toBe(0);
80
+ } finally {f.close();}
81
+ });
82
+
83
+ test('using releases a borrowed frame when processing throws; ordinary buffers never detach',()=>{
84
+ const f=fixture();let bytes;
85
+ try {
86
+ expect(()=>{using first=f.frame();bytes=first.image.data;f.parser.dispose();throw Error('test');}).toThrow('test');
87
+ expect(bytes.byteLength).toBe(0);
88
+ const ordinary=Buffer.alloc(4);const image=createRawImage(CK,ordinary,1,1,1);
89
+ image.delete();expect(ordinary.byteLength).toBe(4);
90
+ } finally {f.close();}
91
+ });
92
+
93
+ test('failed/unsupported transfer falls back to GC without breaking cleanup',()=>{
94
+ for(const transfer of [undefined,()=>{throw Error('not detachable');}]){
95
+ const bytes=new Uint8Array(4);Object.defineProperty(bytes.buffer,'transfer',{value:transfer});
96
+ const release=ownMapping(bytes),other=retainMapping(bytes);
97
+ release();expect(bytes.byteLength).toBe(4);
98
+ expect(()=>{other();other();release();}).not.toThrow();
99
+ expect(bytes.byteLength).toBe(4);
100
+ }
101
+ });
102
+
103
+ test('query/error mappings are released even though they never enter the image cache',()=>{
104
+ const f=fixture();const mapped=[];
105
+ const read=f.parser._readImageFile.bind(f.parser);
106
+ f.parser._readImageFile=(...args)=>{const bytes=read(...args);mapped.push(bytes);return bytes;};
107
+ try {
108
+ // A query validates its file then drops its data. Exercise the same packet
109
+ // cleanup on an exception after the read, before the cache can retain it.
110
+ for(const fail of [false,true]){
111
+ const path=join(tmpdir(),'tty-graphics-protocol-query-'+process.pid);fs.writeFileSync(path,Buffer.from([1,2,3,255]));
112
+ const resolve=f.parser._resolveImageData.bind(f.parser);
113
+ if(fail)f.parser._resolveImageData=(...args)=>{resolve(...args);throw Error('validation failed');};
114
+ const result=f.parse(`a=q,t=t,f=${BGRA_FORMAT},s=1,v=1,i=7`,Buffer.from(path).toString('base64'));
115
+ expect(result.responseMessage).toStartWith(fail?'ERROR:':'OK');
116
+ expect(mapped.at(-1).byteLength).toBe(0);
117
+ }
118
+ expect(f.parser.images.size).toBe(0);
119
+ } finally {f.close();}
120
+ });
121
+
122
+ test('materialized Skia images and mapped bytes both release even if Skia cleanup throws',()=>{
123
+ const bytes=new Uint8Array(4),owner=ownMapping(bytes);
124
+ const image=createRawImage({...CK,AlphaType:{Unpremul:1},ColorSpace:{SRGB:1},MakeImage(){return {delete(){throw Error('Skia cleanup');}};}},bytes,1,1,1);
125
+ owner();skiaImage(image);
126
+ expect(()=>image.delete()).toThrow('Skia cleanup');expect(bytes.byteLength).toBe(0);
127
+ expect(()=>image.delete()).not.toThrow();
128
+ });
129
+
130
+ test('repeated mapped replacements unmap promptly without waiting for a GC',async()=>{
131
+ const f=fixture();let previous;
132
+ try {
133
+ for(let i=0;i<200;i++){
134
+ using frame=f.frame();await f.store.handle(frame);
135
+ if(previous)expect(previous.byteLength).toBe(0);
136
+ previous=frame.image.data;
137
+ expect(previous[3]).toBe(255);
138
+ }
139
+ f.parser.dispose();f.store.delete();expect(previous.byteLength).toBe(0);
140
+ if(process.platform==='linux'){
141
+ const maps=fs.readFileSync('/proc/self/maps','utf8');
142
+ // Files were unlinked at read time; lingering mappings would still have
143
+ // their original name followed by '(deleted)' here.
144
+ expect(maps.split('\n').filter(line=>line.includes('tty-graphics-protocol-lifetime-'))).toHaveLength(0);
145
+ }
146
+ } finally {f.close();}
147
+ });
@@ -0,0 +1,9 @@
1
+ import {expect,test} from 'bun:test';
2
+ import {nextVirtualTerminal} from '../initramfs/lib/bunterm/vt.js';
3
+ test('new bunterm appends after known sessions and reserves the initial console',()=>{
4
+ expect(nextVirtualTerminal([])).toBe(2);
5
+ expect(nextVirtualTerminal([{number:1}])).toBe(2);
6
+ expect(nextVirtualTerminal([{number:1},{number:2},{number:6}])).toBe(7);
7
+ expect(nextVirtualTerminal([{number:4}])).toBe(5);
8
+ expect(()=>nextVirtualTerminal([{number:63}])).toThrow('No next tty');
9
+ });
@@ -0,0 +1,39 @@
1
+ import {test,expect} from 'bun:test';
2
+ import {createRequire} from 'node:module';
3
+ import {preferredPixelFormat,bgraParser,BGRA_FORMAT} from '../initramfs/lib/bunterm/raw-protocol.js';
4
+ const {KittyGraphicsParser}=createRequire(import.meta.url)('../initramfs/buninu/apps/jsgotty/gotty.js');
5
+ const field=offset=>({offset,length:8,msbRight:0});
6
+ const fb=(red=16,blue=0)=>({bitsPerPixel:32,fields:{red:field(red),green:field(8),blue:field(blue),transp:{length:0}}});
7
+ test('native preference follows framebuffer bitfields; browser parser remains unchanged',()=>{
8
+ for(const [display,preferred] of [[fb(),'bgra'],[fb(0,16),'rgba'],[{bitsPerPixel:16},'png'],[undefined,'png']]){
9
+ expect(preferredPixelFormat(display)).toBe(preferred);
10
+ const p=new (bgraParser(KittyGraphicsParser,display))({binaryData:true});
11
+ const packet=Buffer.from(`a=q,f=${BGRA_FORMAT},s=1,v=1,i=41;AAAA/w==`);
12
+ expect(p.parsePacket(packet,{}).responseMessage).toBe('OK Buninu1 '+preferred);
13
+ expect(new KittyGraphicsParser().parsePacket(packet,{}).responseMessage).toBe('OK');
14
+ expect(p.images.size).toBe(0);
15
+ expect(p.parsePacket(Buffer.from(`a=q,f=${BGRA_FORMAT},s=2,v=1,i=41;AAAA/w==`),{}).responseMessage).toStartWith('ERROR:');
16
+ }
17
+ const bad=fb();bad.fields.red.msbRight=1;
18
+ expect(preferredPixelFormat(bad)).toBe('png');expect(preferredPixelFormat(fb(),false)).toBe('png');
19
+ });
20
+
21
+ import fs from 'node:fs';
22
+ import {tmpdir} from 'node:os';
23
+ import {join} from 'node:path';
24
+ test('raw temporary mappings survive unlink; regular and partial files retain copying behavior',()=>{
25
+ const dir=fs.mkdtempSync(join(tmpdir(),'tty-graphics-protocol-mmap-'));
26
+ const parser=new (bgraParser(KittyGraphicsParser,fb()))({binaryData:true});
27
+ const bytes=Buffer.from([1,2,3,255,4,5,6,255]);
28
+ try{
29
+ for(const format of [32,BGRA_FORMAT]){
30
+ const path=join(dir,'frame');fs.writeFileSync(path,bytes);
31
+ const got=parser._readImageFile({f:String(format),s:'2',v:'1'},Buffer.from(path).toString('base64'),true);
32
+ expect(fs.existsSync(path)).toBe(false);expect(got).toEqual(bytes);
33
+ }
34
+ const path=join(dir,'regular');fs.writeFileSync(path,bytes);
35
+ const payload=Buffer.from(path).toString('base64'),control={f:String(BGRA_FORMAT),s:'2',v:'1'};
36
+ expect(parser._readImageFile(control,payload,false)).toEqual(bytes);expect(fs.existsSync(path)).toBe(true);
37
+ expect(parser._readImageFile({...control,O:'4',S:'4'},payload,true)).toEqual(bytes.subarray(4));
38
+ }finally{parser.dispose();fs.rmSync(dir,{recursive:true,force:true});}
39
+ });