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
|
@@ -561,6 +561,26 @@
|
|
|
561
561
|
state.renderBound = state.renderDisposables.length > 0;
|
|
562
562
|
}
|
|
563
563
|
|
|
564
|
+
// Touch gestures report mouse input without DOM mousedown: xterm's DOM
|
|
565
|
+
// handler focuses its hidden textarea, which can zoom Android WebView.
|
|
566
|
+
function reportTouchMouse(type, point, button = 0) {
|
|
567
|
+
const core = terminal?.__gottyXterm?._core;
|
|
568
|
+
if (!core?.coreMouseService?.areMouseEventsActive) return;
|
|
569
|
+
const screen = core.screenElement;
|
|
570
|
+
if (!screen) return;
|
|
571
|
+
const rect = screen.getBoundingClientRect();
|
|
572
|
+
const coords = core._mouseService.getMouseReportCoords({
|
|
573
|
+
clientX: Math.max(rect.left, Math.min(rect.right - 1, point.clientX)),
|
|
574
|
+
clientY: Math.max(rect.top, Math.min(rect.bottom - 1, point.clientY)),
|
|
575
|
+
}, screen);
|
|
576
|
+
if (!coords) return;
|
|
577
|
+
core.coreMouseService.triggerMouseEvent({
|
|
578
|
+
...coords, button,
|
|
579
|
+
action: type === "mousedown" ? 1 : type === "mousemove" ? 32 : 0,
|
|
580
|
+
ctrl: false, alt: false, shift: false,
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
|
|
564
584
|
function dispatchSyntheticWheel(target, touch, deltaX, deltaY) {
|
|
565
585
|
if (!target) {
|
|
566
586
|
return;
|
|
@@ -597,22 +617,69 @@
|
|
|
597
617
|
return screen || viewport || xterm || terminal;
|
|
598
618
|
};
|
|
599
619
|
|
|
620
|
+
let lastTap = null;
|
|
621
|
+
let drag = null;
|
|
622
|
+
const mouse = (type, point) => reportTouchMouse(type, point);
|
|
623
|
+
const releaseDrag = () => {
|
|
624
|
+
if (drag) {
|
|
625
|
+
const point = drag;
|
|
626
|
+
drag = null;
|
|
627
|
+
mouse("mouseup", point);
|
|
628
|
+
}
|
|
629
|
+
};
|
|
630
|
+
const cancelGesture = () => {
|
|
631
|
+
releaseDrag();
|
|
632
|
+
lastTap = null;
|
|
633
|
+
state.touchScroll = null;
|
|
634
|
+
};
|
|
635
|
+
state.cancelTouchGesture = cancelGesture;
|
|
636
|
+
bindTarget.addEventListener("contextmenu", (event) => {
|
|
637
|
+
if (drag) { event.preventDefault(); event.stopImmediatePropagation(); }
|
|
638
|
+
}, true);
|
|
639
|
+
window.addEventListener("blur", cancelGesture);
|
|
640
|
+
document.addEventListener("visibilitychange", () => {
|
|
641
|
+
if (document.hidden) cancelGesture();
|
|
642
|
+
});
|
|
643
|
+
|
|
600
644
|
bindTarget.addEventListener("touchstart", (event) => {
|
|
601
645
|
if (event.touches.length !== 1) {
|
|
602
|
-
|
|
646
|
+
cancelGesture();
|
|
603
647
|
return;
|
|
604
648
|
}
|
|
605
649
|
const touch = event.touches[0];
|
|
650
|
+
const reporting = terminal.__gottyXterm?._core?.coreMouseService?.areMouseEventsActive;
|
|
651
|
+
if (reporting && window.touchDoubleTapDrag !== false && lastTap &&
|
|
652
|
+
performance.now() - lastTap.time < 350 &&
|
|
653
|
+
Math.hypot(touch.clientX - lastTap.x, touch.clientY - lastTap.y) < 24) {
|
|
654
|
+
lastTap = null;
|
|
655
|
+
state.touchScroll = null;
|
|
656
|
+
drag = { identifier: touch.identifier, clientX: touch.clientX, clientY: touch.clientY };
|
|
657
|
+
// Cancel compatibility mouse events: this is the second press, not a third click.
|
|
658
|
+
event.preventDefault();
|
|
659
|
+
mouse("mousedown", drag);
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
lastTap = null;
|
|
606
663
|
state.touchScroll = {
|
|
664
|
+
startX: touch.clientX, startY: touch.clientY, startTime: performance.now(), moved: false,
|
|
607
665
|
identifier: touch.identifier,
|
|
608
666
|
lastX: touch.clientX,
|
|
609
667
|
lastY: touch.clientY,
|
|
610
668
|
residualX: 0,
|
|
611
669
|
residualY: 0
|
|
612
670
|
};
|
|
613
|
-
}, { passive:
|
|
671
|
+
}, { passive: false });
|
|
614
672
|
|
|
615
673
|
bindTarget.addEventListener("touchmove", (event) => {
|
|
674
|
+
if (drag) {
|
|
675
|
+
const touch = Array.from(event.touches).find(t => t.identifier === drag.identifier);
|
|
676
|
+
if (!touch || event.touches.length !== 1) { cancelGesture(); return; }
|
|
677
|
+
event.preventDefault();
|
|
678
|
+
drag.clientX = touch.clientX;
|
|
679
|
+
drag.clientY = touch.clientY;
|
|
680
|
+
mouse("mousemove", drag);
|
|
681
|
+
return;
|
|
682
|
+
}
|
|
616
683
|
if (!state.touchScroll) {
|
|
617
684
|
return;
|
|
618
685
|
}
|
|
@@ -621,6 +688,8 @@
|
|
|
621
688
|
return;
|
|
622
689
|
}
|
|
623
690
|
|
|
691
|
+
if (Math.hypot(touch.clientX - state.touchScroll.startX,
|
|
692
|
+
touch.clientY - state.touchScroll.startY) > 8) state.touchScroll.moved = true;
|
|
624
693
|
const deltaX = state.touchScroll.lastX - touch.clientX;
|
|
625
694
|
const deltaY = state.touchScroll.lastY - touch.clientY;
|
|
626
695
|
state.touchScroll.lastX = touch.clientX;
|
|
@@ -653,11 +722,20 @@
|
|
|
653
722
|
state.touchScroll.residualY -= ticksY * threshold * stepY;
|
|
654
723
|
}, { passive: false });
|
|
655
724
|
|
|
656
|
-
|
|
725
|
+
bindTarget.addEventListener("touchend", (event) => {
|
|
726
|
+
if (drag) {
|
|
727
|
+
event.preventDefault();
|
|
728
|
+
releaseDrag();
|
|
729
|
+
} else {
|
|
730
|
+
const scroll = state.touchScroll;
|
|
731
|
+
if (scroll && !scroll.moved && event.touches.length === 0 &&
|
|
732
|
+
performance.now() - scroll.startTime < 350) {
|
|
733
|
+
lastTap = { x: scroll.lastX, y: scroll.lastY, time: performance.now() };
|
|
734
|
+
}
|
|
735
|
+
}
|
|
657
736
|
state.touchScroll = null;
|
|
658
|
-
};
|
|
659
|
-
bindTarget.addEventListener("
|
|
660
|
-
bindTarget.addEventListener("touchcancel", clearTouchScroll, { passive: true });
|
|
737
|
+
}, { passive: false });
|
|
738
|
+
bindTarget.addEventListener("touchcancel", cancelGesture, { passive: true });
|
|
661
739
|
state.touchBound = true;
|
|
662
740
|
}
|
|
663
741
|
|
|
@@ -676,9 +754,25 @@
|
|
|
676
754
|
removeButton.textContent = "Remove image";
|
|
677
755
|
menu.appendChild(removeButton);
|
|
678
756
|
|
|
757
|
+
const separator = document.createElement("hr");
|
|
758
|
+
const rightButton = document.createElement("button");
|
|
759
|
+
rightButton.type = "button";
|
|
760
|
+
rightButton.className = "kitty-context-menu__item";
|
|
761
|
+
rightButton.textContent = "Right click";
|
|
762
|
+
menu.append(separator, rightButton);
|
|
763
|
+
rightButton.addEventListener("click", (event) => {
|
|
764
|
+
event.preventDefault();
|
|
765
|
+
event.stopPropagation();
|
|
766
|
+
const point = state.contextMenu?.point;
|
|
767
|
+
hideContextMenu();
|
|
768
|
+
if (!point || !terminal.__gottyXterm?._core?.coreMouseService?.areMouseEventsActive) return;
|
|
769
|
+
reportTouchMouse("mousedown", { clientX: point.x, clientY: point.y }, 2);
|
|
770
|
+
reportTouchMouse("mouseup", { clientX: point.x, clientY: point.y }, 2);
|
|
771
|
+
});
|
|
772
|
+
|
|
679
773
|
state.contextMenu = {
|
|
680
774
|
root: menu,
|
|
681
|
-
removeButton,
|
|
775
|
+
removeButton, rightButton, separator,
|
|
682
776
|
targetKey: null
|
|
683
777
|
};
|
|
684
778
|
|
|
@@ -722,6 +816,9 @@
|
|
|
722
816
|
function showContextMenu(x, y, key) {
|
|
723
817
|
const menu = ensureContextMenu();
|
|
724
818
|
menu.targetKey = key;
|
|
819
|
+
menu.point = { x, y };
|
|
820
|
+
const reporting = !!terminal.__gottyXterm?._core?.coreMouseService?.areMouseEventsActive;
|
|
821
|
+
menu.rightButton.hidden = menu.separator.hidden = !reporting;
|
|
725
822
|
menu.root.hidden = false;
|
|
726
823
|
|
|
727
824
|
const margin = 10;
|
|
@@ -1004,6 +1101,15 @@
|
|
|
1004
1101
|
return false;
|
|
1005
1102
|
}
|
|
1006
1103
|
|
|
1104
|
+
const watchedSockets = new WeakSet();
|
|
1105
|
+
function watchTouchSocket(socket) {
|
|
1106
|
+
if (watchedSockets.has(socket)) return;
|
|
1107
|
+
watchedSockets.add(socket);
|
|
1108
|
+
socket.addEventListener("close", () => {
|
|
1109
|
+
if (state.socket === socket) state.cancelTouchGesture?.();
|
|
1110
|
+
});
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1007
1113
|
const nativeSend = WebSocket.prototype.send;
|
|
1008
1114
|
const nativeOnOpen = WebSocket.prototype.addEventListener;
|
|
1009
1115
|
WebSocket.prototype.addEventListener = function (type, listener, options) {
|
|
@@ -1012,6 +1118,7 @@
|
|
|
1012
1118
|
}
|
|
1013
1119
|
const wrapped = function (event) {
|
|
1014
1120
|
state.socket = this;
|
|
1121
|
+
watchTouchSocket(this);
|
|
1015
1122
|
inspectTerminalOutput(event.data);
|
|
1016
1123
|
if (typeof event.data === "string" && event.data[0] === MSG_KITTY) {
|
|
1017
1124
|
if (handleKittyMessage(event.data.slice(1))) {
|
|
@@ -1058,6 +1165,7 @@
|
|
|
1058
1165
|
}
|
|
1059
1166
|
const wrapped = function (event) {
|
|
1060
1167
|
state.socket = this;
|
|
1168
|
+
watchTouchSocket(this);
|
|
1061
1169
|
inspectTerminalOutput(event.data);
|
|
1062
1170
|
if (typeof event.data === "string" && event.data[0] === MSG_KITTY) {
|
|
1063
1171
|
if (handleKittyMessage(event.data.slice(1))) {
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// All flags defined by the kitty keyboard protocol. Kept unmasked so a CLI
|
|
4
|
+
// client on a real kitty terminal can restore them; the browser masks its own.
|
|
5
|
+
const KITTY_KEYBOARD_FLAGS = 0x1f;
|
|
6
|
+
const TRACKED_DEC_FLAGS = {
|
|
7
|
+
1: "applicationCursorKeys",
|
|
8
|
+
6: "origin",
|
|
9
|
+
7: "wraparound",
|
|
10
|
+
45: "reverseWraparound",
|
|
11
|
+
66: "applicationKeypad",
|
|
12
|
+
1004: "sendFocus",
|
|
13
|
+
2004: "bracketedPasteMode",
|
|
14
|
+
};
|
|
15
|
+
const MOUSE_PROTOCOLS = { 9: "X10", 1000: "VT200", 1002: "DRAG", 1003: "ANY" };
|
|
16
|
+
const MOUSE_ENCODINGS = { 1006: "SGR", 1016: "SGR_PIXELS" };
|
|
17
|
+
|
|
18
|
+
// Mirrors the PTY's input/protocol modes so a resumed client can be brought
|
|
19
|
+
// back in sync. Mode semantics follow xterm.js, which renders the output.
|
|
20
|
+
class TerminalModeTracker {
|
|
21
|
+
constructor() {
|
|
22
|
+
this.reset();
|
|
23
|
+
this._state = "text";
|
|
24
|
+
this._seq = "";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
reset() {
|
|
28
|
+
this.dec = {
|
|
29
|
+
applicationCursorKeys: false,
|
|
30
|
+
applicationKeypad: false,
|
|
31
|
+
bracketedPasteMode: false,
|
|
32
|
+
origin: false,
|
|
33
|
+
reverseWraparound: false,
|
|
34
|
+
sendFocus: false,
|
|
35
|
+
wraparound: true,
|
|
36
|
+
};
|
|
37
|
+
this.cursorHidden = false;
|
|
38
|
+
this.mouse = "NONE";
|
|
39
|
+
this.encoding = "DEFAULT";
|
|
40
|
+
this.alternate = false;
|
|
41
|
+
this.kitty = { normal: { flags: 0, stack: [] }, alternate: { flags: 0, stack: [] } };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
snapshot() {
|
|
45
|
+
return {
|
|
46
|
+
dec: { ...this.dec },
|
|
47
|
+
cursorHidden: this.cursorHidden,
|
|
48
|
+
mouse: this.mouse,
|
|
49
|
+
encoding: this.encoding,
|
|
50
|
+
alternate: this.alternate,
|
|
51
|
+
kitty: {
|
|
52
|
+
normal: { flags: this.kitty.normal.flags, stack: [...this.kitty.normal.stack] },
|
|
53
|
+
alternate: { flags: this.kitty.alternate.flags, stack: [...this.kitty.alternate.stack] },
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
consume(buffer) {
|
|
59
|
+
// Control sequences are ASCII, so bytes can be scanned without UTF-8 decoding.
|
|
60
|
+
for (let i = 0; i < buffer.length; i += 1) {
|
|
61
|
+
const byte = buffer[i];
|
|
62
|
+
switch (this._state) {
|
|
63
|
+
case "text":
|
|
64
|
+
if (byte === 0x1b) {
|
|
65
|
+
this._state = "esc";
|
|
66
|
+
}
|
|
67
|
+
break;
|
|
68
|
+
case "esc":
|
|
69
|
+
if (byte === 0x5b) {
|
|
70
|
+
this._state = "csi";
|
|
71
|
+
this._seq = "";
|
|
72
|
+
} else if (byte === 0x5d || byte === 0x50 || byte === 0x5f || byte === 0x5e || byte === 0x58) {
|
|
73
|
+
this._state = "string";
|
|
74
|
+
this._osc = byte === 0x5d ? "" : null;
|
|
75
|
+
} else if (byte === 0x1b) {
|
|
76
|
+
this._state = "esc";
|
|
77
|
+
} else if (byte >= 0x20 && byte <= 0x2f) {
|
|
78
|
+
this._state = "esc-intermediate";
|
|
79
|
+
} else {
|
|
80
|
+
this._state = "text";
|
|
81
|
+
this.handleEsc(byte);
|
|
82
|
+
}
|
|
83
|
+
break;
|
|
84
|
+
case "esc-intermediate":
|
|
85
|
+
if (byte < 0x20 || byte > 0x2f) {
|
|
86
|
+
this._state = byte === 0x1b ? "esc" : "text";
|
|
87
|
+
}
|
|
88
|
+
break;
|
|
89
|
+
case "csi":
|
|
90
|
+
if (byte >= 0x40 && byte <= 0x7e) {
|
|
91
|
+
this._state = "text";
|
|
92
|
+
this.handleCsi(this._seq, String.fromCharCode(byte));
|
|
93
|
+
} else if (byte === 0x1b) {
|
|
94
|
+
this._state = "esc";
|
|
95
|
+
} else if (byte === 0x18 || byte === 0x1a || this._seq.length >= 64) {
|
|
96
|
+
this._state = "text";
|
|
97
|
+
} else if (byte >= 0x20) {
|
|
98
|
+
this._seq += String.fromCharCode(byte);
|
|
99
|
+
}
|
|
100
|
+
break;
|
|
101
|
+
case "string":
|
|
102
|
+
if (byte === 0x07 || byte === 0x18 || byte === 0x1a) {
|
|
103
|
+
if (byte === 0x07) this.handleOsc();
|
|
104
|
+
this._state = "text";
|
|
105
|
+
} else if (byte === 0x1b) {
|
|
106
|
+
this._state = "string-esc";
|
|
107
|
+
} else if (this._osc != null) {
|
|
108
|
+
this._osc = this._osc.length < 256 ? this._osc + String.fromCharCode(byte) : null;
|
|
109
|
+
}
|
|
110
|
+
break;
|
|
111
|
+
case "string-esc":
|
|
112
|
+
if (byte === 0x5c) this.handleOsc();
|
|
113
|
+
else this._osc = null;
|
|
114
|
+
this._state = byte === 0x1b ? "string-esc" : byte === 0x5c ? "text" : "string";
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
handleOsc() {
|
|
121
|
+
const match = /^777;jsgotty-kitty-v1;(normal|alternate);(\d{1,2});(\d{1,2}(?:,\d{1,2})*)?$/.exec(this._osc || '');
|
|
122
|
+
if (!match) return;
|
|
123
|
+
const flags = +match[2], stack = match[3] ? match[3].split(',').map(Number) : [];
|
|
124
|
+
if (flags > 31 || stack.length > 32 || stack.some(n=>n > 31)) return;
|
|
125
|
+
this.kitty[match[1]] = {flags, stack};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
handleEsc(byte) {
|
|
129
|
+
if (byte === 0x3d) {
|
|
130
|
+
this.dec.applicationKeypad = true;
|
|
131
|
+
} else if (byte === 0x3e) {
|
|
132
|
+
this.dec.applicationKeypad = false;
|
|
133
|
+
} else if (byte === 0x63) {
|
|
134
|
+
this.reset();
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
handleCsi(seq, final) {
|
|
139
|
+
const prefix = /^[<=>?]/.test(seq) ? seq[0] : "";
|
|
140
|
+
const body = prefix ? seq.slice(1) : seq;
|
|
141
|
+
if (!/^[0-9;:]*$/.test(body)) {
|
|
142
|
+
if (body === "!" && final === "p") {
|
|
143
|
+
this.softReset();
|
|
144
|
+
}
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
const params = body === "" ? [] : body.split(";").map((part) => {
|
|
148
|
+
const value = Number.parseInt(part, 10);
|
|
149
|
+
return Number.isFinite(value) ? value : 0;
|
|
150
|
+
});
|
|
151
|
+
if (prefix === "?" && (final === "h" || final === "l")) {
|
|
152
|
+
for (const mode of params) {
|
|
153
|
+
this.setPrivateMode(mode, final === "h");
|
|
154
|
+
}
|
|
155
|
+
} else if (final === "u" && prefix !== "?") {
|
|
156
|
+
this.handleKittyKeyboard(prefix, params);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
softReset() {
|
|
161
|
+
// DECSTR keeps the active screen and mouse reporting, like xterm.js.
|
|
162
|
+
const { alternate, mouse, encoding } = this;
|
|
163
|
+
this.reset();
|
|
164
|
+
Object.assign(this, { alternate, mouse, encoding });
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
setPrivateMode(mode, enabled) {
|
|
168
|
+
if (TRACKED_DEC_FLAGS[mode]) {
|
|
169
|
+
this.dec[TRACKED_DEC_FLAGS[mode]] = enabled;
|
|
170
|
+
} else if (MOUSE_PROTOCOLS[mode]) {
|
|
171
|
+
this.mouse = enabled ? MOUSE_PROTOCOLS[mode] : "NONE";
|
|
172
|
+
} else if (MOUSE_ENCODINGS[mode]) {
|
|
173
|
+
this.encoding = enabled ? MOUSE_ENCODINGS[mode] : "DEFAULT";
|
|
174
|
+
} else if (mode === 25) {
|
|
175
|
+
this.cursorHidden = !enabled;
|
|
176
|
+
} else if (mode === 47 || mode === 1047 || mode === 1049) {
|
|
177
|
+
this.alternate = enabled;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
handleKittyKeyboard(prefix, params) {
|
|
182
|
+
const state = this.kitty[this.alternate ? "alternate" : "normal"];
|
|
183
|
+
const value = (index, fallback = 0) => (Number.isInteger(params[index]) ? params[index] : fallback);
|
|
184
|
+
if (prefix === ">") {
|
|
185
|
+
state.stack.push(state.flags);
|
|
186
|
+
if (state.stack.length > 32) {
|
|
187
|
+
state.stack.shift();
|
|
188
|
+
}
|
|
189
|
+
state.flags = value(0) & KITTY_KEYBOARD_FLAGS;
|
|
190
|
+
} else if (prefix === "<") {
|
|
191
|
+
let count = Math.min(value(0) || 1, 33);
|
|
192
|
+
while (count--) {
|
|
193
|
+
state.flags = state.stack.pop() ?? 0;
|
|
194
|
+
}
|
|
195
|
+
} else if (prefix === "=") {
|
|
196
|
+
const flags = value(0) & KITTY_KEYBOARD_FLAGS;
|
|
197
|
+
const mode = value(1) || 1;
|
|
198
|
+
if (mode === 1) {
|
|
199
|
+
state.flags = flags;
|
|
200
|
+
} else if (mode === 2) {
|
|
201
|
+
state.flags |= flags;
|
|
202
|
+
} else if (mode === 3) {
|
|
203
|
+
state.flags &= ~flags;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const DEFAULT_MODES = new TerminalModeTracker().snapshot();
|
|
210
|
+
const MOUSE_PROTOCOL_MODES = { X10: 9, VT200: 1000, DRAG: 1002, ANY: 1003 };
|
|
211
|
+
const MOUSE_ENCODING_MODES = { SGR: 1006, SGR_PIXELS: 1016 };
|
|
212
|
+
|
|
213
|
+
function sameKitty(a, b) {
|
|
214
|
+
return a.flags === b.flags && a.stack.length === b.stack.length &&
|
|
215
|
+
a.stack.every((flags, index) => flags === b.stack[index]);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Work only on the observed stack; never pop an arbitrary number of entries.
|
|
219
|
+
// With a terminal that cannot report its stack, [] denotes the unseen baseline.
|
|
220
|
+
function kittyTransition(from, to) {
|
|
221
|
+
if (to.flags == null) return "";
|
|
222
|
+
let common = 0;
|
|
223
|
+
while (common < from.stack.length && common < to.stack.length &&
|
|
224
|
+
from.stack[common] === to.stack[common]) common++;
|
|
225
|
+
let sequence = "";
|
|
226
|
+
let flags = from.flags;
|
|
227
|
+
const pops = from.stack.length - common;
|
|
228
|
+
if (pops) {
|
|
229
|
+
sequence += `\x1b[<${pops}u`;
|
|
230
|
+
flags = from.stack[common];
|
|
231
|
+
}
|
|
232
|
+
const first = to.stack[common] ?? to.flags;
|
|
233
|
+
if (flags !== first) sequence += `\x1b[=${first};1u`;
|
|
234
|
+
for (let i = common; i < to.stack.length; i++)
|
|
235
|
+
sequence += `\x1b[>${to.stack[i + 1] ?? to.flags}u`;
|
|
236
|
+
return sequence;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Escape sequence that moves a terminal known to be in `from` to `to`, emitting
|
|
240
|
+
// only differences: DECOM homes the cursor and 1049 overwrites the saved cursor.
|
|
241
|
+
function modeTransition(from, to = DEFAULT_MODES, { normalizeMouse = false, kittyRestore = false } = {}) {
|
|
242
|
+
let sequence = "";
|
|
243
|
+
const fromScreen = from.alternate ? "alternate" : "normal";
|
|
244
|
+
const toScreen = to.alternate ? "alternate" : "normal";
|
|
245
|
+
// Kitty keyboard stacks are per screen, so each is fixed while it is active.
|
|
246
|
+
if (from.alternate != null && !sameKitty(from.kitty[fromScreen], to.kitty[fromScreen])) {
|
|
247
|
+
sequence += kittyTransition(from.kitty[fromScreen], to.kitty[fromScreen]);
|
|
248
|
+
}
|
|
249
|
+
if (to.alternate != null && from.alternate !== to.alternate) {
|
|
250
|
+
sequence += `\x1b[?1049${to.alternate ? "h" : "l"}`;
|
|
251
|
+
if (!sameKitty(from.kitty[toScreen], to.kitty[toScreen])) {
|
|
252
|
+
sequence += kittyTransition(from.kitty[toScreen], to.kitty[toScreen]);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
const inactive = toScreen === "normal" ? "alternate" : "normal";
|
|
256
|
+
// Switching screens just to edit a hidden stack destroys alternate contents.
|
|
257
|
+
// Only emit this extension after the actual terminal advertised support.
|
|
258
|
+
const hidden = to.kitty[inactive];
|
|
259
|
+
if (kittyRestore && from.alternate != null && to.alternate != null && fromScreen === toScreen &&
|
|
260
|
+
!sameKitty(from.kitty[inactive], hidden) && hidden.flags != null && hidden.stack.every(Number.isInteger)) {
|
|
261
|
+
sequence += `\x1b]777;jsgotty-kitty-v1;${inactive};${hidden.flags};${hidden.stack.join(',')}\x07`;
|
|
262
|
+
}
|
|
263
|
+
for (const [mode, name] of Object.entries(TRACKED_DEC_FLAGS)) {
|
|
264
|
+
if (to.dec[name] == null || from.dec[name] === to.dec[name]) {
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
if (name === "applicationKeypad") {
|
|
268
|
+
sequence += to.dec[name] ? "\x1b=" : "\x1b>"; // more widely supported than ?66
|
|
269
|
+
} else {
|
|
270
|
+
sequence += `\x1b[?${mode}${to.dec[name] ? "h" : "l"}`;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
if (to.cursorHidden != null && from.cursorHidden !== to.cursorHidden) {
|
|
274
|
+
sequence += `\x1b[?25${to.cursorHidden ? "l" : "h"}`;
|
|
275
|
+
}
|
|
276
|
+
// Raw query results preserve independent switches on native terminals. Reset
|
|
277
|
+
// first, then enable in priority order so exclusive terminals also end in the
|
|
278
|
+
// intended effective mode. Unanswered switches are never guessed.
|
|
279
|
+
for (const [name, bitsName, table] of [
|
|
280
|
+
['mouse', 'mouseModeBits', MOUSE_PROTOCOL_MODES],
|
|
281
|
+
['encoding', 'mouseEncodingBits', MOUSE_ENCODING_MODES],
|
|
282
|
+
]) {
|
|
283
|
+
const bits = to[bitsName];
|
|
284
|
+
if (bits) {
|
|
285
|
+
for (const mode of Object.values(table))
|
|
286
|
+
if (bits[mode] === false) sequence += `\x1b[?${mode}l`;
|
|
287
|
+
for (const mode of Object.values(table))
|
|
288
|
+
if (bits[mode] === true) sequence += `\x1b[?${mode}h`;
|
|
289
|
+
} else if (to[name] != null && (normalizeMouse || from[name] !== to[name])) {
|
|
290
|
+
for (const mode of Object.values(table)) sequence += `\x1b[?${mode}l`;
|
|
291
|
+
if (table[to[name]]) sequence += `\x1b[?${table[to[name]]}h`;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return sequence;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function validateSnapshot(value) {
|
|
298
|
+
if (!value || !value.dec || !value.kitty) throw Error("Invalid terminal modes");
|
|
299
|
+
const result = new TerminalModeTracker().snapshot();
|
|
300
|
+
for (const key of Object.values(TRACKED_DEC_FLAGS)) {
|
|
301
|
+
if (typeof value.dec[key] !== 'boolean' && value.dec[key] !== null) throw Error("Invalid DEC mode");
|
|
302
|
+
result.dec[key] = value.dec[key];
|
|
303
|
+
}
|
|
304
|
+
for (const key of ['alternate', 'cursorHidden']) {
|
|
305
|
+
if (typeof value[key] !== 'boolean' && value[key] !== null) throw Error("Invalid screen mode");
|
|
306
|
+
result[key] = value[key];
|
|
307
|
+
}
|
|
308
|
+
for (const [key, choices] of [['mouse', ['NONE', ...Object.values(MOUSE_PROTOCOLS)]], ['encoding', ['DEFAULT', ...Object.values(MOUSE_ENCODINGS)]]]) {
|
|
309
|
+
if (value[key] !== null && !choices.includes(value[key])) throw Error("Invalid mouse mode");
|
|
310
|
+
result[key] = value[key];
|
|
311
|
+
}
|
|
312
|
+
for (const [key, table] of [['mouseModeBits', MOUSE_PROTOCOLS], ['mouseEncodingBits', MOUSE_ENCODINGS]]) {
|
|
313
|
+
if (value[key] === undefined) continue;
|
|
314
|
+
if (!value[key] || typeof value[key] !== 'object') throw Error('Invalid mouse bits');
|
|
315
|
+
result[key] = {};
|
|
316
|
+
for (const mode of Object.keys(table)) {
|
|
317
|
+
const bit = value[key][mode];
|
|
318
|
+
if (bit !== null && typeof bit !== 'boolean') throw Error('Invalid mouse bit');
|
|
319
|
+
result[key][mode] = bit;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
for (const key of ['normal', 'alternate']) {
|
|
323
|
+
const state = value.kitty[key];
|
|
324
|
+
const flag = n => Number.isInteger(n) && n >= 0 && n <= KITTY_KEYBOARD_FLAGS;
|
|
325
|
+
if (!state || (state.flags !== null && !flag(state.flags)) || !Array.isArray(state.stack) ||
|
|
326
|
+
state.stack.length > 32 || !state.stack.every(n => n === null || flag(n))) throw Error("Invalid Kitty state");
|
|
327
|
+
result.kitty[key] = {flags: state.flags, stack: [...state.stack]};
|
|
328
|
+
}
|
|
329
|
+
return result;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
module.exports = { TerminalModeTracker, modeTransition, validateSnapshot, TRACKED_DEC_FLAGS, MOUSE_PROTOCOLS, MOUSE_ENCODINGS };
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
const assert = require('node:assert/strict');
|
|
2
|
+
const test = require('node:test');
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const vm = require('node:vm');
|
|
5
|
+
const path = require('node:path');
|
|
6
|
+
|
|
7
|
+
// Exercise the shipped bundle's wrapper method, without a DOM or new dependencies.
|
|
8
|
+
const bundle = fs.readFileSync(path.join(__dirname, '../static/js/gotty.js'), 'utf8');
|
|
9
|
+
const start = bundle.indexOf(' handleAndroidAltKey(event) {');
|
|
10
|
+
assert.ok(start >= 0);
|
|
11
|
+
const end = bundle.indexOf(' info() {', start);
|
|
12
|
+
assert.ok(end > start);
|
|
13
|
+
const method = bundle.slice(start, end);
|
|
14
|
+
function setup(userAgent = 'Mozilla/5.0 (Linux; Android 15) AppleWebKit/537.36 wv') {
|
|
15
|
+
const handler = vm.runInNewContext(`({${method}}).handleAndroidAltKey`, {
|
|
16
|
+
navigator: { userAgent },
|
|
17
|
+
});
|
|
18
|
+
const sent = [];
|
|
19
|
+
const target = { term: { input: (text, userInput) => sent.push([text, userInput]) } };
|
|
20
|
+
function dispatch(overrides = {}) {
|
|
21
|
+
const event = {
|
|
22
|
+
type: 'keydown', code: 'KeyE', key: 'Dead', keyCode: 69,
|
|
23
|
+
altKey: true, ctrlKey: false, metaKey: false, shiftKey: false,
|
|
24
|
+
isComposing: false, getModifierState: () => false,
|
|
25
|
+
prevented: false, stopped: false,
|
|
26
|
+
preventDefault() { this.prevented = true; },
|
|
27
|
+
stopPropagation() { this.stopped = true; },
|
|
28
|
+
...overrides,
|
|
29
|
+
};
|
|
30
|
+
return { result: handler.call(target, event), event };
|
|
31
|
+
}
|
|
32
|
+
return { sent, dispatch };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
test('Android accent shortcuts emit one ESC sequence; keypress/keyup do not duplicate it', () => {
|
|
36
|
+
for (const letter of ['E', 'I', 'N', 'U']) {
|
|
37
|
+
for (const keyCode of [letter.charCodeAt(0), 229]) {
|
|
38
|
+
const { sent, dispatch } = setup();
|
|
39
|
+
const { event, result } = dispatch({ code: `Key${letter}`, keyCode });
|
|
40
|
+
assert.equal(result, false);
|
|
41
|
+
assert.equal(event.prevented, true);
|
|
42
|
+
assert.equal(event.stopped, true);
|
|
43
|
+
dispatch({ type: 'keypress', code: `Key${letter}`, keyCode });
|
|
44
|
+
dispatch({ type: 'keyup', code: `Key${letter}`, keyCode });
|
|
45
|
+
assert.deepEqual(sent, [[`\x1b${letter.toLowerCase()}`, true]]);
|
|
46
|
+
// No dead-key state is retained in this handler for the next ordinary key.
|
|
47
|
+
assert.equal(dispatch({ altKey: false, key: 'e' }).result, true);
|
|
48
|
+
assert.equal(sent.length, 1);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('all printable physical Alt keys bypass dead-key handling, including Shift and repeat', () => {
|
|
54
|
+
const { sent, dispatch } = setup();
|
|
55
|
+
for (const [code, shiftKey, expected] of [
|
|
56
|
+
['KeyA', false, 'a'], ['KeyE', true, 'E'], ['Digit0', false, '0'],
|
|
57
|
+
['Digit1', true, '!'], ['Digit0', true, ')'], ['Quote', false, "'"],
|
|
58
|
+
['Backquote', true, '~'], ['Backslash', false, '\\'], ['Space', false, ' '],
|
|
59
|
+
]) {
|
|
60
|
+
dispatch({ code, shiftKey, key: 'Dead' });
|
|
61
|
+
assert.deepEqual(sent.at(-1), [`\x1b${expected}`, true]);
|
|
62
|
+
}
|
|
63
|
+
dispatch({ code: 'KeyE', key: 'é', repeat: true });
|
|
64
|
+
assert.deepEqual(sent.at(-1), ['\x1be', true]);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test('composition, AltGr, other modifiers, and unmapped keys retain existing handling', () => {
|
|
68
|
+
for (const overrides of [
|
|
69
|
+
{ isComposing: true, keyCode: 229 }, { getModifierState: key => key === 'AltGraph' },
|
|
70
|
+
{ altKey: false }, { ctrlKey: true }, { metaKey: true },
|
|
71
|
+
{ code: '', keyCode: 229 }, { code: 'ArrowLeft' }, { code: 'Enter' },
|
|
72
|
+
{ code: 'Unidentified', keyCode: 229 }, { code: 'toString' },
|
|
73
|
+
{ type: 'keyup', key: 'e' },
|
|
74
|
+
]) {
|
|
75
|
+
const { sent, dispatch } = setup();
|
|
76
|
+
const { result, event } = dispatch(overrides);
|
|
77
|
+
assert.equal(result, true);
|
|
78
|
+
assert.equal(event.prevented, false);
|
|
79
|
+
assert.deepEqual(sent, []);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test('non-Android browsers retain existing Alt/dead-key handling', () => {
|
|
84
|
+
for (const ua of ['Mozilla/5.0 (X11; Linux x86_64)', 'Mozilla/5.0 (Macintosh)', 'Windows']) {
|
|
85
|
+
const { sent, dispatch } = setup(ua);
|
|
86
|
+
assert.equal(dispatch().result, true);
|
|
87
|
+
assert.deepEqual(sent, []);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// Replay the actual Android 16 WebView trace: no letter keydown and empty code.
|
|
92
|
+
test('Android keyup-only dead keys use legacy keyCode, and ordinary IME input stays untouched', () => {
|
|
93
|
+
const { sent, dispatch } = setup();
|
|
94
|
+
for (const keyCode of [69, 78, 85, 73]) {
|
|
95
|
+
dispatch({ type: 'keydown', code: '', key: 'Alt', keyCode: 18 });
|
|
96
|
+
assert.equal(dispatch({ type: 'keyup', code: '', key: 'Dead', keyCode }).result, false);
|
|
97
|
+
dispatch({ type: 'keyup', code: '', key: 'Alt', keyCode: 18 });
|
|
98
|
+
}
|
|
99
|
+
assert.deepEqual(sent, ['e', 'n', 'u', 'i'].map(c => ['\x1b' + c, true]));
|
|
100
|
+
assert.equal(dispatch({ code: '', key: 'Unidentified', keyCode: 229, altKey: false }).result, true);
|
|
101
|
+
assert.equal(sent.length, 4);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test('legacy keydown plus dead keyup emits once, even with different code availability', () => {
|
|
105
|
+
for (const [downCode, upCode] of [['', ''], ['KeyE', ''], ['', 'KeyE']]) {
|
|
106
|
+
const { sent, dispatch } = setup();
|
|
107
|
+
dispatch({ code: downCode, keyCode: 69 });
|
|
108
|
+
dispatch({ type: 'keypress', code: downCode, keyCode: 69 });
|
|
109
|
+
dispatch({ type: 'keyup', code: upCode, keyCode: 69 });
|
|
110
|
+
assert.deepEqual(sent, [['\x1be', true]]);
|
|
111
|
+
// The next keyup-only shortcut must not be suppressed by the preceding press.
|
|
112
|
+
dispatch({ type: 'keyup', code: '', keyCode: 69 });
|
|
113
|
+
assert.equal(sent.length, 2);
|
|
114
|
+
}
|
|
115
|
+
});
|