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
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
const test = require('node:test');
|
|
2
|
+
const assert = require('node:assert/strict');
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
const vm = require('node:vm');
|
|
6
|
+
const { TerminalModeTracker, modeTransition } = require('../terminal-modes.js');
|
|
7
|
+
|
|
8
|
+
const source = fs.readFileSync(path.join(__dirname, '../static/js/gotty.js'), 'utf8');
|
|
9
|
+
const methods = source.slice(source.indexOf(' enqueueTerminal(task)'), source.indexOf(' setDisconnected(e)'));
|
|
10
|
+
|
|
11
|
+
function feed(tracker, ...chunks) {
|
|
12
|
+
for (const chunk of chunks) tracker.consume(Buffer.from(chunk, 'latin1'));
|
|
13
|
+
return tracker.snapshot();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function client({ alternate = false, dec = {}, mouse = 'NONE', encoding = 'DEFAULT', cursorHidden = false } = {}) {
|
|
17
|
+
const Wrapper = vm.runInNewContext(`(class {${methods}})`);
|
|
18
|
+
const w = new Wrapper();
|
|
19
|
+
const writes = [];
|
|
20
|
+
const defaults = { applicationCursorKeys: false, applicationKeypad: false, bracketedPasteMode: false,
|
|
21
|
+
origin: false, reverseWraparound: false, sendFocus: false, wraparound: true };
|
|
22
|
+
w.term = {
|
|
23
|
+
_core: { coreService: { decPrivateModes: { ...defaults, ...dec }, isCursorHidden: cursorHidden },
|
|
24
|
+
coreMouseService: { activeProtocol: mouse, activeEncoding: encoding } },
|
|
25
|
+
buffer: { active: { type: alternate ? 'alternate' : 'normal' } },
|
|
26
|
+
write: (s, callback) => { if (s) writes.push(s); callback?.(); },
|
|
27
|
+
};
|
|
28
|
+
w.kittyKeyboard = { states: { normal: { flags: 0, stack: [] }, alternate: { flags: 0, stack: [] } } };
|
|
29
|
+
return { w, writes };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
test('tracker follows DEC, mouse, alternate screen and kitty keyboard modes', () => {
|
|
33
|
+
const state = feed(new TerminalModeTracker(),
|
|
34
|
+
'hi\x1b[?1049h\x1b[?1;2004h\x1b=\x1b[?25l',
|
|
35
|
+
'\x1b[?1002h\x1b[?10', '06h', // split across chunks
|
|
36
|
+
'\x1b[>1u\x1b[>11u\x1b]0;title \x1b[?2004l\x07');
|
|
37
|
+
assert.equal(state.alternate, true);
|
|
38
|
+
assert.equal(state.dec.applicationCursorKeys, true);
|
|
39
|
+
assert.equal(state.dec.bracketedPasteMode, true, 'OSC payload is not parsed as CSI');
|
|
40
|
+
assert.equal(state.dec.applicationKeypad, true);
|
|
41
|
+
assert.equal(state.cursorHidden, true);
|
|
42
|
+
assert.equal(state.mouse, 'DRAG');
|
|
43
|
+
assert.equal(state.encoding, 'SGR');
|
|
44
|
+
assert.deepEqual(state.kitty.alternate, { flags: 11, stack: [0, 1] });
|
|
45
|
+
assert.deepEqual(state.kitty.normal, { flags: 0, stack: [] });
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test('tracker handles mode resets, kitty pop/set and RIS', () => {
|
|
49
|
+
const tracker = new TerminalModeTracker();
|
|
50
|
+
let state = feed(tracker, '\x1b[?1003h\x1b[?1006h\x1b[?1000l\x1b[?1006l\x1b[>1u\x1b[=8;2u\x1b[<u');
|
|
51
|
+
assert.equal(state.mouse, 'NONE');
|
|
52
|
+
assert.equal(state.encoding, 'DEFAULT');
|
|
53
|
+
assert.equal(state.kitty.normal.flags, 0);
|
|
54
|
+
state = feed(tracker, '\x1b[?1049h\x1b[?2004h\x1b[?1002h\x1b[!p');
|
|
55
|
+
assert.equal(state.dec.bracketedPasteMode, false, 'DECSTR resets DEC modes');
|
|
56
|
+
assert.equal(state.alternate, true, 'DECSTR keeps the active screen');
|
|
57
|
+
assert.equal(state.mouse, 'DRAG', 'DECSTR keeps mouse reporting');
|
|
58
|
+
state = feed(tracker, '\x1bc');
|
|
59
|
+
assert.deepEqual(state, new TerminalModeTracker().snapshot());
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test('fresh client gets the full state restored', () => {
|
|
63
|
+
const state = feed(new TerminalModeTracker(), '\x1b[?1049h\x1b[?2004h\x1b[?1002h\x1b[?1006h\x1b[?25l\x1b[>11u');
|
|
64
|
+
const { w, writes } = client();
|
|
65
|
+
w.applySessionModes(state);
|
|
66
|
+
assert.equal(writes.length, 1);
|
|
67
|
+
for (const seq of ['\x1b[?1049h', '\x1b[?2004h', '\x1b[?1002h', '\x1b[?1006h', '\x1b[?25l'])
|
|
68
|
+
assert.ok(writes[0].includes(seq), JSON.stringify(seq));
|
|
69
|
+
assert.equal(w.kittyKeyboard.states.alternate.flags, 11);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test('browser masks kitty flags it does not implement', () => {
|
|
73
|
+
const { w } = client();
|
|
74
|
+
w.applySessionModes(feed(new TerminalModeTracker(), '\x1b[>31u\x1b[>31u'));
|
|
75
|
+
assert.equal(JSON.stringify(w.kittyKeyboard.states.normal), JSON.stringify({ flags: 11, stack: [0, 11] }));
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test('socket-only reconnect with matching xterm state writes nothing', () => {
|
|
79
|
+
const state = feed(new TerminalModeTracker(), '\x1b[?1049h\x1b[?6h\x1b[?2004h\x1b[?1002h\x1b[?1006h\x1b[>1u\x1b[>11u');
|
|
80
|
+
const { w, writes } = client({ alternate: true, dec: { origin: true, bracketedPasteMode: true }, mouse: 'DRAG', encoding: 'SGR' });
|
|
81
|
+
w.applySessionModes(state);
|
|
82
|
+
assert.equal(writes.length, 0, 'no DECOM cursor homing or 1049 cursor save');
|
|
83
|
+
w.applySessionModes(state);
|
|
84
|
+
assert.deepEqual(JSON.parse(JSON.stringify(w.kittyKeyboard.states.alternate)), { flags: 11, stack: [0, 1] }, 'push stack does not grow');
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test('stale client modes are turned off when the program disabled them while disconnected', () => {
|
|
88
|
+
const state = new TerminalModeTracker().snapshot();
|
|
89
|
+
const { w, writes } = client({ alternate: true, dec: { bracketedPasteMode: true }, mouse: 'ANY', encoding: 'SGR' });
|
|
90
|
+
w.applySessionModes(state);
|
|
91
|
+
assert.equal(writes.length, 1);
|
|
92
|
+
for (const seq of ['\x1b[?1049l', '\x1b[?2004l', '\x1b[?1000l', '\x1b[?1006l'])
|
|
93
|
+
assert.ok(writes[0].includes(seq), JSON.stringify(seq));
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test('transport disconnect and reconnect clear held keys without resetting negotiation', () => {
|
|
97
|
+
const start = source.indexOf(' reset() {\n // Reconnecting');
|
|
98
|
+
const reset = source.slice(start, source.indexOf(' close() {', start));
|
|
99
|
+
assert.match(reset, /clearHeld\(false\)/);
|
|
100
|
+
assert.doesNotMatch(reset, /kittyKeyboard.reset/);
|
|
101
|
+
assert.match(source, /setDisconnected\(e\) \{\s*if \(e\) this.kittyKeyboard.clearHeld\(false\)/);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
const sessions = {
|
|
105
|
+
vim: '\x1b[?1049h\x1b[?1h\x1b=\x1b[?2004h\x1b[?1002h\x1b[?1006h\x1b[?25l',
|
|
106
|
+
kittyBothScreens: '\x1b[>1u\x1b[?1049h\x1b[>31u\x1b[>3u\x1b[?1003h\x1b[?1016h',
|
|
107
|
+
kittySetThenPush: '\x1b[=5;1u\x1b[>8u\x1b[?1004h\x1b[?6h',
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
for (const [name, output] of Object.entries(sessions)) {
|
|
111
|
+
test(`CLI resume and cleanup round-trip: ${name}`, () => {
|
|
112
|
+
const target = feed(new TerminalModeTracker(), output);
|
|
113
|
+
const local = new TerminalModeTracker();
|
|
114
|
+
assert.deepEqual(feed(local, modeTransition(local.snapshot(), target)), target, 'fresh terminal reaches PTY state');
|
|
115
|
+
assert.equal(modeTransition(local.snapshot(), target), '', 'nothing left to send once in sync');
|
|
116
|
+
assert.deepEqual(feed(local, modeTransition(local.snapshot())), new TerminalModeTracker().snapshot(), 'cleanup returns to defaults');
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
test('kitty flags are kept unmasked for terminals that support all of them', () => {
|
|
121
|
+
assert.equal(feed(new TerminalModeTracker(), '\x1b[>31u').kitty.normal.flags, 31);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test('incremental Kitty restoration preserves a non-default parent stack', () => {
|
|
125
|
+
const local = new TerminalModeTracker();
|
|
126
|
+
feed(local, '\x1b[=1u\x1b[>11u');
|
|
127
|
+
const parent = local.snapshot();
|
|
128
|
+
const remote = feed(new TerminalModeTracker(), '\x1b[>3u\x1b[>9u');
|
|
129
|
+
const attach = modeTransition(parent, remote);
|
|
130
|
+
assert.doesNotMatch(attach, /<100u/);
|
|
131
|
+
assert.deepEqual(feed(local, attach), remote);
|
|
132
|
+
assert.deepEqual(feed(local, modeTransition(local.snapshot(), parent)), parent);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test('unknown original modes are not replaced with guessed defaults on cleanup', () => {
|
|
136
|
+
const unknown = new TerminalModeTracker().snapshot();
|
|
137
|
+
for (const key of Object.keys(unknown.dec)) unknown.dec[key] = null;
|
|
138
|
+
for (const key of ['alternate','cursorHidden','mouse','encoding']) unknown[key] = null;
|
|
139
|
+
unknown.kitty.normal.flags = unknown.kitty.alternate.flags = null;
|
|
140
|
+
assert.equal(modeTransition(feed(new TerminalModeTracker(), '\x1b[?1049h\x1b[>11u'), unknown), '');
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
test('hidden Kitty restoration is negotiated and never toggles the screen',()=>{
|
|
144
|
+
for(const alternate of [false,true]){
|
|
145
|
+
const local=new TerminalModeTracker();if(alternate)feed(local,'\x1b[?1049h');
|
|
146
|
+
const target=local.snapshot(),hidden=alternate?'normal':'alternate';
|
|
147
|
+
target.kitty[hidden]={flags:9,stack:[0,1]};
|
|
148
|
+
assert.equal(modeTransition(local.snapshot(),target),'');
|
|
149
|
+
const sequence=modeTransition(local.snapshot(),target,{kittyRestore:true});
|
|
150
|
+
assert.doesNotMatch(sequence,/\[\?(?:47|1047|1049)[hl]/);
|
|
151
|
+
// Every-byte fragmentation must be handled by intermediate server trackers.
|
|
152
|
+
for(const byte of Buffer.from(sequence))local.consume(Buffer.from([byte]));
|
|
153
|
+
assert.deepEqual(local.snapshot(),target);
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
test('Kitty restore tracker rejects malformed or unrelated string commands',()=>{
|
|
157
|
+
const t=new TerminalModeTracker(),before=t.snapshot();
|
|
158
|
+
for(const payload of [
|
|
159
|
+
'\x1b]777;jsgotty-kitty-v1;normal;32;0\x07',
|
|
160
|
+
'\x1b]777;jsgotty-kitty-v1;normal;1;'+Array(33).fill('0').join(',')+'\x07',
|
|
161
|
+
'\x1bP777;jsgotty-kitty-v1;normal;1;0\x1b\\',
|
|
162
|
+
'\x1b]777;jsgotty-kitty-v1;normal;1;0\x18',
|
|
163
|
+
]){feed(t,payload);assert.deepEqual(t.snapshot(),before);}
|
|
164
|
+
feed(t,'\x1b]777;jsgotty-kitty-v1;normal;1;0\x1b\\');
|
|
165
|
+
assert.deepEqual(t.kitty.normal,{flags:1,stack:[0]});
|
|
166
|
+
});
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// Opt-in real bundled-xterm integration; no browser/dependency downloads.
|
|
2
|
+
// CHROMIUM_HEADLESS=/path/to/chromium-headless-shell node --test tests/touch-drag-browser.test.js
|
|
3
|
+
const test=require('node:test');
|
|
4
|
+
const assert=require('node:assert/strict');
|
|
5
|
+
const fs=require('node:fs');
|
|
6
|
+
const path=require('node:path');
|
|
7
|
+
const http=require('node:http');
|
|
8
|
+
const {spawn}=require('node:child_process');
|
|
9
|
+
const {once}=require('node:events');
|
|
10
|
+
const WebSocket=require('ws');
|
|
11
|
+
const root=path.join(__dirname,'../static');
|
|
12
|
+
test('touch double-tap drag and image-menu right click use bundled xterm', {skip:!process.env.CHROMIUM_HEADLESS,timeout:60000},async()=>{
|
|
13
|
+
const bundle=fs.readFileSync(path.join(root,'js/gotty.js'),'utf8').replace('(i.nc = void 0));','(i.nc = void 0)); window.TestGoTTYXterm = i(634).GoTTYXterm;');
|
|
14
|
+
const server=http.createServer((req,res)=>{
|
|
15
|
+
if(req.url==='/index.css'){res.setHeader('Content-Type','text/css');res.end(fs.readFileSync(path.join(root,'css/index.css')));}
|
|
16
|
+
else if(req.url==='/bundle.js'){res.setHeader('Content-Type','text/javascript');res.end(bundle);}
|
|
17
|
+
else if(req.url==='/gestures.js'){
|
|
18
|
+
const source=fs.readFileSync(path.join(root,'js/kitty.js'),'utf8');
|
|
19
|
+
res.setHeader('Content-Type','text/javascript');
|
|
20
|
+
res.end(source.slice(source.indexOf(' function reportTouchMouse('),source.indexOf(' function ensureContextMenu('))+source.slice(source.indexOf(' function ensureContextMenu('),source.indexOf(' function bindImageContextMenu('))+source.slice(source.indexOf(' function bindImageContextMenu('),source.indexOf(' function bindImageContextMenu(')+source.slice(source.indexOf(' function bindImageContextMenu(')).indexOf('\n function ',5))+'\nbindTouchScrolling();bindImageContextMenu();');
|
|
21
|
+
}
|
|
22
|
+
else if(req.url==='/keyboard.js'){res.setHeader('Content-Type','text/javascript');res.end(fs.readFileSync(path.join(root,'js/kitty-keyboard.js')));}
|
|
23
|
+
else res.end('<!doctype html><div id="terminal"><div id="fixture" style="width:800px;height:600px"></div></div><link rel="stylesheet" href="/index.css"><script src="/keyboard.js"></script><script src="/bundle.js"></script><script>window.wrapper=new TestGoTTYXterm(document.getElementById("fixture"));window.sent=[];wrapper.onInput(b=>sent.push(new TextDecoder().decode(b)));wrapper.kittyKeyboard.android=true;</script>');
|
|
24
|
+
});
|
|
25
|
+
server.listen(0,'127.0.0.1');await once(server,'listening');
|
|
26
|
+
const child=spawn(process.env.CHROMIUM_HEADLESS,['--no-sandbox','--disable-gpu','--remote-debugging-port=0','http://127.0.0.1:'+server.address().port],{detached:true,stdio:['ignore','ignore','pipe']});
|
|
27
|
+
let ws;
|
|
28
|
+
try{
|
|
29
|
+
const endpoint=await new Promise((resolve,reject)=>{let log='';const timer=setTimeout(()=>reject(Error('Browser startup timeout: '+log)),15000);child.on('error',reject);child.on('exit',code=>{clearTimeout(timer);reject(Error('Browser exited '+code+': '+log));});child.stderr.on('data',b=>{log+=b;const m=log.match(/DevTools listening on (ws:\/\/[^\s]+)/);if(m){clearTimeout(timer);resolve(m[1]);}});});
|
|
30
|
+
const u=new URL(endpoint);let tab;
|
|
31
|
+
for(let i=0;i<50;i++){tab=(await (await fetch('http://'+u.host+'/json/list')).json()).find(x=>x.type==='page');if(tab)break;await new Promise(r=>setTimeout(r,100));}
|
|
32
|
+
ws=new WebSocket(tab.webSocketDebuggerUrl);await once(ws,'open');let id=0;const pending=new Map();ws.on('message',data=>{const m=JSON.parse(data);if(pending.has(m.id)){const {resolve,reject}=pending.get(m.id);pending.delete(m.id);m.error?reject(m.error):resolve(m.result);}});
|
|
33
|
+
const call=(method,params)=>new Promise((resolve,reject)=>{pending.set(++id,{resolve,reject});ws.send(JSON.stringify({id,method,params}));});
|
|
34
|
+
for(let i=0;i<50;i++){const r=await call('Runtime.evaluate',{expression:'Boolean(window.wrapper)',returnByValue:true});if(r.result.value)break;await new Promise(r=>setTimeout(r,100));}
|
|
35
|
+
const result=await call('Runtime.evaluate',{awaitPromise:true,returnByValue:true,expression:`(async()=>{
|
|
36
|
+
const term=wrapper.term;
|
|
37
|
+
const write=s=>new Promise(r=>term.write(s,r));
|
|
38
|
+
const equal=(a,b)=>{if(JSON.stringify(a)!==JSON.stringify(b))throw Error(JSON.stringify({actual:a,expected:b}));};
|
|
39
|
+
await write('\\x1b[?1002h\\x1b[?1006h');
|
|
40
|
+
const terminal=document.getElementById('fixture');terminal.__gottyXterm=term;
|
|
41
|
+
const screen=term.element.querySelector('.xterm-screen'), rect=screen.getBoundingClientRect();
|
|
42
|
+
const state={placements:new Map()};
|
|
43
|
+
const xtermElements=()=>({screen});
|
|
44
|
+
const DEFAULT_TOUCH_SCROLL_THRESHOLD=24;
|
|
45
|
+
const removePlacementNode=node=>node.remove();
|
|
46
|
+
eval(await (await fetch('/gestures.js')).text());
|
|
47
|
+
const touch=(type,x=30,y=30,count=1)=>{
|
|
48
|
+
const point=new Touch({identifier:1,target:screen,clientX:rect.left+x,clientY:rect.top+y});
|
|
49
|
+
const touches=type==='touchend'||type==='touchcancel'?[]:count===1?[point]:[point,new Touch({identifier:2,target:screen,clientX:rect.left+x+30,clientY:rect.top+y})];
|
|
50
|
+
const event=new TouchEvent(type,{bubbles:true,cancelable:true,touches,changedTouches:[point]});
|
|
51
|
+
screen.dispatchEvent(event);return event.defaultPrevented;
|
|
52
|
+
};
|
|
53
|
+
const codes=()=>sent.map(s=>{const m=s.match(/<([0-9]+);[0-9]+;[0-9]+([Mm])/);return m?m[1]+m[2]:s;});
|
|
54
|
+
let focusCalls=0;const originalFocus=term._core.focus;term._core.focus=function(...args){focusCalls++;return originalFocus.apply(this,args);};
|
|
55
|
+
const firstTap=()=>{touch('touchstart');touch('touchend');
|
|
56
|
+
// Browser compatibility mouse sequence after an ordinary tap.
|
|
57
|
+
for(const type of ['mousedown','mouseup'])screen.dispatchEvent(new MouseEvent(type,{bubbles:true,clientX:rect.left+30,clientY:rect.top+30,button:0,buttons:type==='mousedown'?1:0}));};
|
|
58
|
+
sent.length=0;firstTap();equal(codes(),['0M','0m']);
|
|
59
|
+
const focusBeforeDrag=focusCalls;
|
|
60
|
+
if(!touch('touchstart'))throw Error('Second press must suppress compatibility click');
|
|
61
|
+
equal(codes(),['0M','0m','0M']); // still held, no premature release
|
|
62
|
+
touch('touchmove',80,70);touch('touchend',80,70);
|
|
63
|
+
equal(codes(),['0M','0m','0M','32M','0m']);
|
|
64
|
+
equal(focusCalls,focusBeforeDrag);
|
|
65
|
+
sent.length=0;firstTap();touch('touchstart');touch('touchend');
|
|
66
|
+
equal(codes(),['0M','0m','0M','0m']); // ordinary double click
|
|
67
|
+
for(const cancel of [()=>touch('touchcancel'),()=>touch('touchstart',30,30,2),()=>window.dispatchEvent(new Event('blur')),()=>state.cancelTouchGesture()]){
|
|
68
|
+
sent.length=0;firstTap();touch('touchstart');cancel();touch('touchend');equal(codes(),['0M','0m','0M','0m']);
|
|
69
|
+
}
|
|
70
|
+
sent.length=0;firstTap();touch('touchstart');
|
|
71
|
+
touch('touchmove',-200,-200);touch('touchend',-200,-200);
|
|
72
|
+
equal(codes(),['0M','0m','0M','32M','0m']); // release outside grid still reaches application
|
|
73
|
+
sent.length=0;touch('touchstart');touch('touchmove',30,90);touch('touchend',30,90);
|
|
74
|
+
if(!codes().length||codes().some(x=>!x.startsWith('64')))throw Error('Single touch must remain scrolling: '+codes());
|
|
75
|
+
sent.length=0;touch('touchstart');touch('touchmove',30,90);touch('touchend',30,90);
|
|
76
|
+
if(codes().some(x=>x==='0M'))throw Error('Scroll must not arm double tap');
|
|
77
|
+
// Use precisely the existing image context-menu path, with its original location.
|
|
78
|
+
const node=document.createElement('div');node.style.zIndex='0';node.getBoundingClientRect=()=>({left:0,top:0,right:1000,bottom:1000});state.placements.set('image',node);
|
|
79
|
+
screen.dispatchEvent(new MouseEvent('contextmenu',{bubbles:true,cancelable:true,clientX:rect.left+30,clientY:rect.top+30,button:2}));
|
|
80
|
+
if(state.contextMenu.root.hidden)throw Error('Menu missing');
|
|
81
|
+
const focusBeforeRight=focusCalls;
|
|
82
|
+
sent.length=0;state.contextMenu.rightButton.click();equal(focusCalls,focusBeforeRight);equal(codes(),['2M','2m']);
|
|
83
|
+
if(!state.contextMenu.root.hidden||!state.placements.has('image'))throw Error('Right click must hide menu and preserve image');
|
|
84
|
+
await write('\\x1b[?1002l');state.cancelTouchGesture();sent.length=0;
|
|
85
|
+
firstTap();touch('touchstart');touch('touchmove',80,70);touch('touchend');
|
|
86
|
+
if(codes().some(x=>/^[032][Mm]$/.test(x)))throw Error('No application mouse reporting outside mouse mode');
|
|
87
|
+
state.cancelTouchGesture();state.placements.clear();
|
|
88
|
+
term.focus();const textarea=term.textarea;
|
|
89
|
+
screen.dispatchEvent(new MouseEvent('contextmenu',{bubbles:true,cancelable:true,clientX:rect.left+30,clientY:rect.top+30,button:2}));
|
|
90
|
+
if(document.activeElement!==textarea)throw Error('Context menu must keep textarea focused for native paste');
|
|
91
|
+
const data=new DataTransfer();data.setData('text/plain','paste-check');sent.length=0;
|
|
92
|
+
textarea.dispatchEvent(new ClipboardEvent('paste',{bubbles:true,cancelable:true,clipboardData:data}));
|
|
93
|
+
if(!sent.join('').includes('paste-check'))throw Error('Paste stopped working');
|
|
94
|
+
equal(getComputedStyle(textarea).touchAction,'none');
|
|
95
|
+
equal(getComputedStyle(screen).touchAction,'manipulation');
|
|
96
|
+
touch('touchstart');
|
|
97
|
+
if(document.activeElement!==textarea)throw Error('Touch must not blur the input');
|
|
98
|
+
touch('touchend');
|
|
99
|
+
wrapper.close();return 'ok';
|
|
100
|
+
})()`});
|
|
101
|
+
assert.equal(result.exceptionDetails,undefined,JSON.stringify(result.exceptionDetails));assert.equal(result.result.value,'ok');
|
|
102
|
+
}finally{
|
|
103
|
+
ws?.close();if(child.pid){try{process.kill(-child.pid,'SIGTERM');}catch{}}
|
|
104
|
+
server.closeAllConnections();await new Promise(r=>server.close(r));
|
|
105
|
+
}
|
|
106
|
+
});
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// Opt-in real bundled-xterm integration; no browser/dependency downloads.
|
|
2
|
+
// CHROMIUM_HEADLESS=/path/to/chromium-headless-shell node --test tests/kitty-keyboard-browser.test.js
|
|
3
|
+
const test=require('node:test');
|
|
4
|
+
const assert=require('node:assert/strict');
|
|
5
|
+
const fs=require('node:fs');
|
|
6
|
+
const path=require('node:path');
|
|
7
|
+
const http=require('node:http');
|
|
8
|
+
const {spawn}=require('node:child_process');
|
|
9
|
+
const {once}=require('node:events');
|
|
10
|
+
const WebSocket=require('ws');
|
|
11
|
+
const root=path.join(__dirname,'../static');
|
|
12
|
+
test('shipped xterm reports horizontal, vertical and diagonal wheel events', {skip:!process.env.CHROMIUM_HEADLESS,timeout:60000},async()=>{
|
|
13
|
+
const bundle=fs.readFileSync(path.join(root,'js/gotty.js'),'utf8').replace('(i.nc = void 0));','(i.nc = void 0)); window.TestGoTTYXterm = i(634).GoTTYXterm;');
|
|
14
|
+
const server=http.createServer((req,res)=>{
|
|
15
|
+
if(req.url==='/bundle.js'){res.setHeader('Content-Type','text/javascript');res.end(bundle);}
|
|
16
|
+
else if(req.url==='/keyboard.js'){res.setHeader('Content-Type','text/javascript');res.end(fs.readFileSync(path.join(root,'js/kitty-keyboard.js')));}
|
|
17
|
+
else res.end('<!doctype html><div id="fixture" style="width:800px;height:600px"></div><script src="/keyboard.js"></script><script src="/bundle.js"></script><script>window.wrapper=new TestGoTTYXterm(document.getElementById("fixture"));window.sent=[];wrapper.onInput(b=>sent.push(new TextDecoder().decode(b)));wrapper.kittyKeyboard.android=true;</script>');
|
|
18
|
+
});
|
|
19
|
+
server.listen(0,'127.0.0.1');await once(server,'listening');
|
|
20
|
+
const child=spawn(process.env.CHROMIUM_HEADLESS,['--no-sandbox','--disable-gpu','--remote-debugging-port=0','http://127.0.0.1:'+server.address().port],{detached:true,stdio:['ignore','ignore','pipe']});
|
|
21
|
+
let ws;
|
|
22
|
+
try{
|
|
23
|
+
const endpoint=await new Promise((resolve,reject)=>{let log='';const timer=setTimeout(()=>reject(Error('Browser startup timeout: '+log)),15000);child.on('error',reject);child.on('exit',code=>{clearTimeout(timer);reject(Error('Browser exited '+code+': '+log));});child.stderr.on('data',b=>{log+=b;const m=log.match(/DevTools listening on (ws:\/\/[^\s]+)/);if(m){clearTimeout(timer);resolve(m[1]);}});});
|
|
24
|
+
const u=new URL(endpoint);let tab;
|
|
25
|
+
for(let i=0;i<50;i++){tab=(await (await fetch('http://'+u.host+'/json/list')).json()).find(x=>x.type==='page');if(tab)break;await new Promise(r=>setTimeout(r,100));}
|
|
26
|
+
ws=new WebSocket(tab.webSocketDebuggerUrl);await once(ws,'open');let id=0;const pending=new Map();ws.on('message',data=>{const m=JSON.parse(data);if(pending.has(m.id)){const {resolve,reject}=pending.get(m.id);pending.delete(m.id);m.error?reject(m.error):resolve(m.result);}});
|
|
27
|
+
const call=(method,params)=>new Promise((resolve,reject)=>{pending.set(++id,{resolve,reject});ws.send(JSON.stringify({id,method,params}));});
|
|
28
|
+
for(let i=0;i<50;i++){const r=await call('Runtime.evaluate',{expression:'Boolean(window.wrapper)',returnByValue:true});if(r.result.value)break;await new Promise(r=>setTimeout(r,100));}
|
|
29
|
+
const result=await call('Runtime.evaluate',{awaitPromise:true,returnByValue:true,expression:`(async()=>{
|
|
30
|
+
const term=wrapper.term;
|
|
31
|
+
const write=s=>new Promise(r=>term.write(s,r));
|
|
32
|
+
const equal=(a,b)=>{if(JSON.stringify(a)!==JSON.stringify(b))throw Error(JSON.stringify({actual:a,expected:b}));};
|
|
33
|
+
await write('\\x1b[?1002h\\x1b[?1006h');
|
|
34
|
+
const screen=term.element.querySelector('.xterm-screen'), rect=screen.getBoundingClientRect();
|
|
35
|
+
const wheel=(deltaX,deltaY,mods={})=>{
|
|
36
|
+
sent.length=0;
|
|
37
|
+
const event=new WheelEvent('wheel',{clientX:rect.left+20,clientY:rect.top+20,deltaX,deltaY,deltaMode:1,bubbles:true,cancelable:true,...mods});
|
|
38
|
+
screen.dispatchEvent(event);
|
|
39
|
+
if(term._core.coreMouseService.areMouseEventsActive&&!event.defaultPrevented)throw Error('Wheel default was not prevented');
|
|
40
|
+
return sent.map(s=>Number(s.match(/<([0-9]+);/)[1]));
|
|
41
|
+
};
|
|
42
|
+
equal(wheel(-1,0),[66]);equal(wheel(1,0),[67]);
|
|
43
|
+
equal(wheel(0,-1,{shiftKey:true}),[68]);equal(wheel(0,1,{shiftKey:true}),[69]);
|
|
44
|
+
equal(wheel(0,-1),[64]);equal(wheel(0,1),[65]);
|
|
45
|
+
equal(wheel(1,-1),[67,64]);
|
|
46
|
+
equal(wheel(-1,0,{shiftKey:true,altKey:true,ctrlKey:true}),[94]);
|
|
47
|
+
for(let bits=0;bits<8;bits++) {
|
|
48
|
+
const mods={shiftKey:!!(bits&1),altKey:!!(bits&2),ctrlKey:!!(bits&4)};
|
|
49
|
+
for(const [dx,dy,code] of [[0,-1,64],[0,1,65],[-1,0,66],[1,0,67]])
|
|
50
|
+
equal(wheel(dx,dy,mods),[code+(bits<<2)]);
|
|
51
|
+
}
|
|
52
|
+
await write('\\x1b[?1002l');equal(wheel(1,0),[]);
|
|
53
|
+
wrapper.close();return 'ok';
|
|
54
|
+
})()`});
|
|
55
|
+
assert.equal(result.exceptionDetails,undefined,JSON.stringify(result.exceptionDetails));assert.equal(result.result.value,'ok');
|
|
56
|
+
}finally{
|
|
57
|
+
ws?.close();if(child.pid){try{process.kill(-child.pid,'SIGTERM');}catch{}}
|
|
58
|
+
server.closeAllConnections();await new Promise(r=>server.close(r));
|
|
59
|
+
}
|
|
60
|
+
});
|
|
@@ -2,6 +2,50 @@
|
|
|
2
2
|
|
|
3
3
|
All notable user-visible changes to jsmdcui are documented here.
|
|
4
4
|
|
|
5
|
+
## [0.19.4] - 2026-10-26
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Catch failures during Go regexp conversion as well as compilation, and
|
|
10
|
+
bound the converter's nested-group recursion. An excessively nested rule
|
|
11
|
+
is disabled individually instead of throwing from `SyntaxDefinition` or
|
|
12
|
+
causing the loader to silently discard the entire syntax definition.
|
|
13
|
+
Other rules remain usable; this does not limit input line length.
|
|
14
|
+
|
|
15
|
+
## [0.19.3] - 2026-10-26
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- Restore Dockerfile highlighting and improve compatibility with Micro's
|
|
20
|
+
syntax definitions by translating Go regular-expression flags and POSIX
|
|
21
|
+
character classes into JavaScript equivalents. Scoped flags retain their
|
|
22
|
+
scope rather than changing the entire expression.
|
|
23
|
+
- Fix nested highlighting regions, especially HTML and XML attributes:
|
|
24
|
+
closing a quoted attribute now returns to the surrounding tag, and closing
|
|
25
|
+
the tag returns to normal text. Unfinished attributes still carry their
|
|
26
|
+
highlighting state across lines.
|
|
27
|
+
- Replace recursive region scanning with an iterative scanner so long,
|
|
28
|
+
minified lines can be highlighted through their final token without stack
|
|
29
|
+
exhaustion or truncation.
|
|
30
|
+
- Prevent zero-width region cycles and cyclic syntax/state trees from looping
|
|
31
|
+
indefinitely. Zero-width matches advance safely over Unicode characters,
|
|
32
|
+
and NUL characters or isolated surrogate units do not leak region state.
|
|
33
|
+
These guards do not impose a timeout on an individual JavaScript RegExp.
|
|
34
|
+
|
|
35
|
+
### Added
|
|
36
|
+
|
|
37
|
+
- Add syntax samples and a reproducible comparison against Micro's Go
|
|
38
|
+
highlighter, including JavaScript, Python and Markdown with upstream syntax
|
|
39
|
+
definitions. See [the comparison guide](tests/syntax-parity/README.md) for
|
|
40
|
+
generating the HTML comparison locally; generated HTML is not bundled.
|
|
41
|
+
|
|
42
|
+
### Changed
|
|
43
|
+
|
|
44
|
+
- Markdown heading tests detect Bun's actual raw-HTML parsing behavior instead
|
|
45
|
+
of relying on version checks, accommodating the slash-tag behavior change
|
|
46
|
+
while preserving fail-closed coverage. The heading suites were verified on
|
|
47
|
+
Bun 1.4.0 through 1.4.4.
|
|
48
|
+
|
|
5
49
|
## [0.19.2] - 2026-09-21
|
|
6
50
|
|
|
7
51
|
### Added
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// Adapt Go regexp syntax without changing the upstream syntax definitions.
|
|
2
|
+
// Scoped JS modifiers preserve flag changes across alternatives, but not out
|
|
3
|
+
// of their enclosing group. They add no capturing groups. Modifiers are only
|
|
4
|
+
// emitted where flags differ from those in effect, so flag-free patterns
|
|
5
|
+
// do not depend on engine support for them.
|
|
6
|
+
export function convertGoRegexp(pattern) {
|
|
7
|
+
const source = String(pattern);
|
|
8
|
+
if (!/\(\?[ims-]|\[\^?\]|\(\?P<|\[:[a-z]+:\]/.test(source)) return source;
|
|
9
|
+
let i = 0;
|
|
10
|
+
function expression(inherited, active, nested = false, depth = 0) {
|
|
11
|
+
// Bound syntax-rule recursion, not the length of text being highlighted.
|
|
12
|
+
if (depth > 256) throw new RangeError('Go regexp nesting exceeds 256 groups');
|
|
13
|
+
const flags = new Set(inherited);
|
|
14
|
+
let output = '', wrapped = false;
|
|
15
|
+
function open() {
|
|
16
|
+
const on = 'ims'.split('').filter(f => flags.has(f) && !active.has(f)).join('');
|
|
17
|
+
const off = 'ims'.split('').filter(f => !flags.has(f) && active.has(f)).join('');
|
|
18
|
+
if (!on && !off) return;
|
|
19
|
+
output += `(?${on}${off ? '-' + off : ''}:`;
|
|
20
|
+
wrapped = true;
|
|
21
|
+
}
|
|
22
|
+
function close() { if (wrapped) output += ')'; wrapped = false; }
|
|
23
|
+
while (i < source.length) {
|
|
24
|
+
const ch = source[i];
|
|
25
|
+
if (ch === ')' && nested) { i++; close(); return output; }
|
|
26
|
+
if (ch === '|') { close(); output += '|'; i++; continue; }
|
|
27
|
+
const directive = ch === '(' && source.slice(i).match(/^\(\?([ims]*)(?:-([ims]+))?\)/);
|
|
28
|
+
if (directive && (directive[1] || directive[2])) {
|
|
29
|
+
close();
|
|
30
|
+
for (const f of directive[1]) flags.add(f);
|
|
31
|
+
for (const f of directive[2] || '') flags.delete(f);
|
|
32
|
+
i += directive[0].length;
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
if (!wrapped) open();
|
|
36
|
+
if (ch === '\\') { output += source.slice(i, i + 2); i += 2; continue; }
|
|
37
|
+
if (ch === '[') {
|
|
38
|
+
output += source[i++];
|
|
39
|
+
if (source[i] === '^') output += source[i++];
|
|
40
|
+
// Go permits a literal ] as the first member of a character class.
|
|
41
|
+
if (source[i] === ']') { output += '\\]'; i++; }
|
|
42
|
+
while (i < source.length) {
|
|
43
|
+
const posix = source.slice(i).match(/^\[:([a-z]+):\]/);
|
|
44
|
+
if (posix && POSIX_CLASSES[posix[1]] !== undefined) {
|
|
45
|
+
output += POSIX_CLASSES[posix[1]]; i += posix[0].length; continue;
|
|
46
|
+
}
|
|
47
|
+
const c = source[i++]; output += c;
|
|
48
|
+
if (c === '\\' && i < source.length) output += source[i++];
|
|
49
|
+
else if (c === ']') break;
|
|
50
|
+
}
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (ch === '(') {
|
|
54
|
+
const scoped = source.slice(i).match(/^\(\?([ims]*)(?:-([ims]+))?:/);
|
|
55
|
+
const childFlags = new Set(flags);
|
|
56
|
+
if (scoped) {
|
|
57
|
+
for (const f of scoped[1]) childFlags.add(f);
|
|
58
|
+
for (const f of scoped[2] || '') childFlags.delete(f);
|
|
59
|
+
output += '(?:'; i += scoped[0].length;
|
|
60
|
+
} else {
|
|
61
|
+
const prefix = source.slice(i).match(/^\(\?(?:[:=!]|<[=!]|<[^>]+>|P<[^>]+>)/);
|
|
62
|
+
output += prefix ? prefix[0].replace('(?P<', '(?<') : '(';
|
|
63
|
+
i += prefix ? prefix[0].length : 1;
|
|
64
|
+
}
|
|
65
|
+
output += expression(childFlags, flags, true, depth + 1) + ')';
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
output += ch; i++;
|
|
69
|
+
}
|
|
70
|
+
close();
|
|
71
|
+
return output;
|
|
72
|
+
}
|
|
73
|
+
return expression(new Set(), new Set());
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Class contents, not nested [...] expressions: e.g. [[:alnum:]._-].
|
|
77
|
+
const POSIX_CLASSES = {
|
|
78
|
+
alnum: 'A-Za-z0-9', alpha: 'A-Za-z', ascii: '\\x00-\\x7F',
|
|
79
|
+
blank: ' \\t', cntrl: '\\x00-\\x1F\\x7F', digit: '0-9',
|
|
80
|
+
graph: '!-~', lower: 'a-z', print: ' -~',
|
|
81
|
+
punct: '!-/:-@\\[-`{-~', space: '\\t\\n\\v\\f\\r ',
|
|
82
|
+
upper: 'A-Z', word: 'A-Za-z0-9_', xdigit: 'A-Fa-f0-9',
|
|
83
|
+
};
|