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.
- package/LICENSES/linux-firmware/LICENCE.nvidia +131 -0
- package/LICENSES/linux-firmware/LICENSE.amdgpu +51 -0
- package/LICENSES/linux-firmware/LICENSE.amdisp +31 -0
- package/LICENSES/linux-firmware/LICENSE.i915 +39 -0
- package/LICENSES/linux-firmware/LICENSE.radeon +51 -0
- package/LICENSES/linux-firmware/LICENSE.xe +39 -0
- package/LICENSES/linux-firmware/WHENCE +10280 -0
- package/NOTICE.md +64 -46
- package/README.md +378 -87
- package/README.zh.md +2041 -0
- package/build-image.sh +10 -1
- package/build-uki.sh +3 -1
- package/fetch-alpine.sh +14 -0
- package/graphics.md +31 -0
- package/index.js +28 -11
- package/initramfs/bin/buninu-browser +4 -1
- package/initramfs/bin/buninu-land +1 -1
- package/initramfs/bin/bunterm +3 -3
- package/initramfs/bin/chroot +13 -0
- package/initramfs/bin/chvt +11 -0
- package/initramfs/bin/mount +4 -0
- package/initramfs/bin/umount +6 -0
- package/initramfs/bin/xdg-open +33 -0
- package/initramfs/buninu/CHANGELOG.md +66 -0
- package/initramfs/buninu/apps/bunmsh/CHANGELOG.md +4 -0
- package/initramfs/buninu/apps/bunmsh/package.json +1 -1
- package/initramfs/buninu/apps/bunmsh/src/main.js +1 -1
- package/initramfs/buninu/apps/jsgotty/LICENSES/MIT-jsmdcui.txt +22 -0
- package/initramfs/buninu/apps/jsgotty/NOTICE +5 -0
- package/initramfs/buninu/apps/jsgotty/README.en.md +61 -0
- package/initramfs/buninu/apps/jsgotty/README.md +61 -0
- package/initramfs/buninu/apps/jsgotty/client-modes.mjs +117 -0
- package/initramfs/buninu/apps/jsgotty/client.mjs +100 -10
- package/initramfs/buninu/apps/jsgotty/clipboard-powershell.js +18 -0
- package/initramfs/buninu/apps/jsgotty/clipboard.js +148 -0
- package/initramfs/buninu/apps/jsgotty/gotty.js +13 -0
- package/initramfs/buninu/apps/jsgotty/package-lock.json +2 -2
- package/initramfs/buninu/apps/jsgotty/package.json +1 -1
- package/initramfs/buninu/apps/jsgotty/static/css/index.css +14 -1
- package/initramfs/buninu/apps/jsgotty/static/index.html +1 -0
- package/initramfs/buninu/apps/jsgotty/static/js/gotty.js +187 -23
- package/initramfs/buninu/apps/jsgotty/static/js/kitty-keyboard.js +193 -0
- package/initramfs/buninu/apps/jsgotty/static/js/kitty.js +115 -7
- package/initramfs/buninu/apps/jsgotty/terminal-modes.js +332 -0
- package/initramfs/buninu/apps/jsgotty/tests/android-alt.test.js +115 -0
- package/initramfs/buninu/apps/jsgotty/tests/arrow-modifiers.test.js +80 -0
- package/initramfs/buninu/apps/jsgotty/tests/client-exit.test.mjs +45 -0
- package/initramfs/buninu/apps/jsgotty/tests/client-modes.test.mjs +105 -0
- package/initramfs/buninu/apps/jsgotty/tests/clipboard-browser.test.js +81 -0
- package/initramfs/buninu/apps/jsgotty/tests/clipboard.test.js +75 -0
- package/initramfs/buninu/apps/jsgotty/tests/kitty-keyboard-browser.test.js +53 -0
- package/initramfs/buninu/apps/jsgotty/tests/kitty-keyboard.test.js +102 -0
- package/initramfs/buninu/apps/jsgotty/tests/manual/README.md +20 -0
- package/initramfs/buninu/apps/jsgotty/tests/manual/keyboard-collector.cjs +55 -0
- package/initramfs/buninu/apps/jsgotty/tests/manual/keyboard-debug.js +74 -0
- package/initramfs/buninu/apps/jsgotty/tests/manual/kitty-keyboard.js +80 -0
- package/initramfs/buninu/apps/jsgotty/tests/manual/zoom-collector.cjs +55 -0
- package/initramfs/buninu/apps/jsgotty/tests/manual/zoom-debug.js +39 -0
- package/initramfs/buninu/apps/jsgotty/tests/manual-touch-diagnostics.js +33 -0
- package/initramfs/buninu/apps/jsgotty/tests/manual-touch-diagnostics.mjs +22 -0
- package/initramfs/buninu/apps/jsgotty/tests/nested-cli-pty.test.mjs +116 -0
- package/initramfs/buninu/apps/jsgotty/tests/reconnect-modes-browser.test.js +144 -0
- package/initramfs/buninu/apps/jsgotty/tests/reconnect-modes.md +44 -0
- package/initramfs/buninu/apps/jsgotty/tests/reconnect-modes.test.js +166 -0
- package/initramfs/buninu/apps/jsgotty/tests/touch-drag-browser.test.js +106 -0
- package/initramfs/buninu/apps/jsgotty/tests/wheel-browser.test.js +60 -0
- package/initramfs/buninu/apps/jsmdcui/CHANGELOG.md +44 -0
- package/initramfs/buninu/apps/jsmdcui/package.json +1 -1
- package/initramfs/buninu/apps/jsmdcui/src/highlight/go-regexp.js +83 -0
- package/initramfs/buninu/apps/jsmdcui/src/highlight/highlighter.js +49 -62
- package/initramfs/buninu/apps/jsmdcui/src/highlight/parser.js +8 -5
- package/initramfs/buninu/apps/jsmdcui/tests/go-regexp.test.js +98 -0
- package/initramfs/buninu/apps/jsmdcui/tests/heading-ids.test.js +21 -16
- package/initramfs/buninu/apps/jsmdcui/tests/highlight-regions.test.js +73 -0
- package/initramfs/buninu/apps/jsmdcui/tests/id-collision.test.js +32 -9
- package/initramfs/buninu/apps/jsmdcui/tests/markdown-behavior.mjs +9 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/NORMAL-HTML.md +46 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/README.md +85 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/REPORT.md +328 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/compare.mjs +75 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/go.mod +5 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/normal-html-report.json +619 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/normal-html.html +9 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/oracle.go +49 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/report.json +2174 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/PowerShell.txt +39 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/ada.txt +25 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/apacheconf.txt +49 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/arduino.txt +38 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/asciidoc.txt +33 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/asm.txt +77 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/ats.txt +48 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/awk.txt +28 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/b.txt +27 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/bat.txt +39 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/c.txt +34 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/caddyfile.txt +14 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/cake.txt +7 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/clojure.txt +18 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/cmake.txt +20 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/coffeescript.txt +32 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/colortest.txt +19 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/conky.txt +12 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/cpp.txt +46 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/crontab.txt +16 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/crystal.txt +28 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/csharp.txt +28 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/css.txt +19 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/csx.txt +7 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/cuda.txt +37 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/cython.txt +22 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/d.txt +52 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/dart.txt +27 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/default.txt +8 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/dockerfile.txt +10 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/dot.txt +15 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/elixir.txt +23 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/elm.txt +20 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/erb.txt +32 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/erlang.txt +23 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/fish.txt +20 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/forth.txt +17 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/fortran.txt +40 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/freebsd-kernel.txt +10 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/fsharp.txt +27 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/gdscript.txt +26 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/gemini.txt +11 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/gentoo-ebuild.txt +28 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/gentoo-etc-portage.txt +14 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/git-commit.txt +19 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/git-config.txt +11 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/git-rebase-todo.txt +10 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/gleam.txt +44 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/glsl.txt +18 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/gnuplot.txt +11 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/go.txt +33 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/godoc.txt +9 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/golo.txt +50 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/gomod.txt +16 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/graphql.txt +20 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/groff.txt +18 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/groovy.txt +42 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/haml.txt +16 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/hare.txt +31 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/haskell.txt +26 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/hc.txt +28 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/html.txt +26 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/html4.txt +20 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/html5.txt +20 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/ini.txt +16 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/inputrc.txt +14 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/java.txt +18 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/javascript.txt +41 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/jinja2.txt +13 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/json.txt +21 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/jsonnet.txt +28 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/julia.txt +24 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/justfile.txt +26 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/keymap.txt +15 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/kickstart.txt +16 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/kotlin.txt +30 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/kvlang.txt +37 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/ledger.txt +13 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/lfe.txt +17 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/lilypond.txt +15 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/lisp.txt +17 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/log.txt +45 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/lua.txt +48 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/mail.txt +17 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/makefile.txt +25 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/man.txt +12 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/markdown.txt +21 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/mc.txt +11 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/meson.txt +29 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/micro.txt +25 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/mpdconf.txt +13 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/msbuild.txt +7 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/nanorc.txt +16 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/nftables.txt +22 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/nginx.txt +17 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/nim.txt +23 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/nix.txt +15 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/nu.txt +92 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/objc.txt +36 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/ocaml.txt +25 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/octave.txt +35 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/odin.txt +35 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/pascal.txt +22 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/patch.txt +13 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/peg.txt +16 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/perl.txt +27 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/php.txt +44 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/pkg-config.txt +12 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/po.txt +12 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/pony.txt +29 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/pov.txt +17 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/privoxy-action.txt +14 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/privoxy-config.txt +10 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/privoxy-filter.txt +12 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/proto.txt +22 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/prql.txt +42 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/puppet.txt +22 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/python2.txt +24 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/python3.txt +28 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/r.txt +16 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/raku.txt +19 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/reST.txt +18 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/renpy.txt +11 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/rpmspec.txt +31 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/ruby.txt +34 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/rust.txt +27 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/sage.txt +24 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/scad.txt +23 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/scala.txt +16 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/sed.txt +12 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/sh.txt +20 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/sls.txt +15 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/smalltalk.txt +28 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/solidity.txt +26 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/sql.txt +35 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/stata.txt +32 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/svelte.txt +10 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/swift.txt +53 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/systemd.txt +15 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/tcl.txt +17 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/terraform.txt +28 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/tex.txt +15 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/toml.txt +31 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/twig.txt +29 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/typescript.txt +32 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/v.txt +47 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/vala.txt +22 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/verilog.txt +37 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/vhdl.txt +37 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/vi.txt +17 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/vue.txt +29 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/xml.txt +14 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/xresources.txt +14 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/yaml.txt +19 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/yum.txt +12 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/zig.txt +24 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/zscript.txt +31 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/zsh.txt +21 -0
- package/initramfs/buninu/package.json +1 -1
- package/initramfs/init.js +19 -3
- package/initramfs/lib/bunterm/clipboard.js +14 -0
- package/initramfs/lib/bunterm/commands.js +37 -0
- package/initramfs/lib/bunterm/fps.js +38 -0
- package/initramfs/lib/bunterm/images.js +14 -6
- package/initramfs/lib/bunterm/input.js +58 -15
- package/initramfs/lib/bunterm/keyboard.js +62 -0
- package/initramfs/lib/bunterm/keycodes.js +104 -0
- package/initramfs/lib/bunterm/kitty-keyboard.cjs +195 -0
- package/initramfs/lib/bunterm/raw-protocol.js +111 -0
- package/initramfs/lib/bunterm/renderer.js +116 -28
- package/initramfs/lib/bunterm/selection.js +60 -0
- package/initramfs/lib/bunterm/terminal.js +157 -12
- package/initramfs/lib/bunterm/vt-menu.js +181 -0
- package/initramfs/lib/bunterm/vt.js +27 -1
- package/initramfs/lib/canvas.js +99 -0
- package/initramfs/lib/chvt.js +31 -0
- package/initramfs/lib/fbdev.js +32 -8
- package/initramfs/lib/gpu.js +77 -0
- package/initramfs/lib/mapped-frame.js +28 -0
- package/initramfs/lib/raw-image.js +71 -0
- package/initramfs/lib/xdg-open.js +42 -0
- package/initramfs/usr/share/doc/buninu-linux/bunterm.md +85 -11
- package/initramfs/usr/share/doc/buninu-linux/chvt.md +34 -0
- package/initramfs/usr/share/doc/buninu-linux/xdg-open.md +43 -0
- package/package.json +1 -1
- package/scripts/arch.js +7 -5
- package/scripts/arch.sh +5 -5
- package/scripts/build.js +16 -3
- package/scripts/fetch-gpu.js +105 -0
- package/scripts/fetch.js +11 -1
- package/scripts/gpu-firmware.json +22 -0
- package/scripts/modules.js +4 -1
- package/scripts/npm-packages.md +18 -17
- package/test/bunterm-fps.test.js +17 -0
- package/test/bunterm-image-bytes.test.js +15 -0
- package/test/bunterm-keyboard.test.js +116 -0
- package/test/bunterm-mapped-lifetime.test.js +147 -0
- package/test/bunterm-next-vt.test.js +9 -0
- package/test/bunterm-raw-protocol.test.js +39 -0
- package/test/bunterm-renderer.test.js +167 -0
- package/test/bunterm-selection.test.js +38 -0
- package/test/bunterm-vt-menu.test.js +178 -0
- package/test/chroot-bind.test.js +40 -2
- package/test/chvt.test.js +46 -0
- package/test/fbdev-sync.test.js +42 -0
- package/test/fetch-npm.test.js +2 -2
- package/test/gpu.test.js +155 -0
- package/test/image-size.test.js +17 -0
- package/test/manual-bunterm-image-paths.js +31 -0
- package/test/mount-libc-lifetime.test.js +50 -0
- package/test/no-gpu.test.js +43 -0
- package/test/readme-cli.test.js +29 -0
- package/test/stage-npm.test.js +11 -4
- 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
|
-
|
|
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 = (
|
|
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
|
+
]
|
package/scripts/modules.js
CHANGED
|
@@ -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",
|
package/scripts/npm-packages.md
CHANGED
|
@@ -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.
|
|
4
|
-
#
|
|
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.
|
|
7
|
-
- https://registry.npmjs.org/buninu-browser/-/buninu-browser-0.
|
|
8
|
-
- sha256:
|
|
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
|
|
11
|
-
- https://registry.npmjs.org/buninu-land/-/buninu-land-0.1.
|
|
12
|
-
- sha256:
|
|
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.
|
|
15
|
-
- https://registry.npmjs.org/@drxiaozhi/casty/-/casty-1.
|
|
16
|
-
- sha256:
|
|
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.
|
|
19
|
-
- https://registry.npmjs.org/@drxiaozhi/jspulse/-/jspulse-0.2.
|
|
20
|
-
- sha256:
|
|
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.
|
|
23
|
-
- https://registry.npmjs.org/@drxiaozhi/jspinyin/-/jspinyin-0.
|
|
24
|
-
- sha256:
|
|
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
|
+
});
|