diff --git a/.gitignore b/.gitignore index aec67d3..ea3989d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ target node_modules +Cargo.lock \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index cddde83..0000000 --- a/Cargo.lock +++ /dev/null @@ -1,941 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "aho-corasick" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" -dependencies = [ - "memchr", -] - -[[package]] -name = "ansi2" -version = "0.2.11" -dependencies = [ - "base64", - "clap", - "html-escape", - "insta", - "nom", - "osvg", - "wasm-bindgen", -] - -[[package]] -name = "ansi2-wasm" -version = "0.2.11" -dependencies = [ - "ansi2", - "wasm-bindgen", - "wasm-bindgen-test", -] - -[[package]] -name = "anstream" -version = "0.6.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" - -[[package]] -name = "anstyle-parse" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" -dependencies = [ - "windows-sys 0.52.0", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" -dependencies = [ - "anstyle", - "windows-sys 0.52.0", -] - -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - -[[package]] -name = "bindgen" -version = "0.69.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" -dependencies = [ - "bitflags", - "cexpr", - "clang-sys", - "itertools", - "lazy_static", - "lazycell", - "log", - "prettyplease", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex", - "syn 2.0.79", - "which", -] - -[[package]] -name = "bitflags" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" - -[[package]] -name = "bumpalo" -version = "3.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" - -[[package]] -name = "cc" -version = "1.1.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16803a61b81d9eabb7eae2588776c4c1e584b738ede45fdbb4c972cec1e9945" -dependencies = [ - "shlex", -] - -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "clang-sys" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" -dependencies = [ - "glob", - "libc", - "libloading", -] - -[[package]] -name = "clap" -version = "4.5.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" -dependencies = [ - "clap_builder", - "clap_derive", -] - -[[package]] -name = "clap_builder" -version = "4.5.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim", -] - -[[package]] -name = "clap_derive" -version = "4.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.79", -] - -[[package]] -name = "clap_lex" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" - -[[package]] -name = "colorchoice" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" - -[[package]] -name = "console" -version = "0.15.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" -dependencies = [ - "encode_unicode", - "lazy_static", - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "console_error_panic_hook" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" -dependencies = [ - "cfg-if", - "wasm-bindgen", -] - -[[package]] -name = "convert_case" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "either" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" - -[[package]] -name = "encode_unicode" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" - -[[package]] -name = "equivalent" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" - -[[package]] -name = "errno" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "glob" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" - -[[package]] -name = "hashbrown" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "home" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" -dependencies = [ - "windows-sys 0.52.0", -] - -[[package]] -name = "html-escape" -version = "0.2.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d1ad449764d627e22bfd7cd5e8868264fc9236e07c752972b4080cd351cb476" -dependencies = [ - "utf8-width", -] - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "indexmap" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" -dependencies = [ - "equivalent", - "hashbrown", -] - -[[package]] -name = "insta" -version = "1.40.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6593a41c7a73841868772495db7dc1e8ecab43bb5c0b6da2059246c4b506ab60" -dependencies = [ - "console", - "lazy_static", - "linked-hash-map", - "similar", -] - -[[package]] -name = "is_terminal_polyfill" -version = "1.70.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" - -[[package]] -name = "itertools" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" -dependencies = [ - "either", -] - -[[package]] -name = "js-sys" -version = "0.3.72" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" - -[[package]] -name = "libc" -version = "0.2.159" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" - -[[package]] -name = "libloading" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" -dependencies = [ - "cfg-if", - "windows-targets", -] - -[[package]] -name = "linked-hash-map" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" - -[[package]] -name = "linux-raw-sys" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" - -[[package]] -name = "log" -version = "0.4.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" - -[[package]] -name = "memchr" -version = "2.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" - -[[package]] -name = "minicov" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c71e683cd655513b99affab7d317deb690528255a0d5f717f1024093c12b169" -dependencies = [ - "cc", - "walkdir", -] - -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - -[[package]] -name = "once_cell" -version = "1.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" - -[[package]] -name = "osvg" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d64287d0fbacc1f8154c5673a41d5c7f100794bc84b9a0da14bbb0a34911fbbc" -dependencies = [ - "rquickjs", -] - -[[package]] -name = "prettyplease" -version = "0.2.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba" -dependencies = [ - "proc-macro2", - "syn 2.0.79", -] - -[[package]] -name = "proc-macro-crate" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" -dependencies = [ - "once_cell", - "toml_edit", -] - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro2" -version = "1.0.88" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "regex" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" - -[[package]] -name = "relative-path" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" - -[[package]] -name = "rquickjs" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cbd33e0b668aea0ab238b9164523aca929096f9f40834700d71d91dd4888882" -dependencies = [ - "rquickjs-core", - "rquickjs-macro", -] - -[[package]] -name = "rquickjs-core" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e9129d69b7b8f7ee8ad1da5b12c7f4a8a8acd45f2e6dd9cb2ee1bc5a1f2fa3d" -dependencies = [ - "relative-path", - "rquickjs-sys", -] - -[[package]] -name = "rquickjs-macro" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7d2ecaf7c9eda262e02a91e9541989a9dd18984d17d0d97f99f33b464318057" -dependencies = [ - "convert_case", - "fnv", - "ident_case", - "indexmap", - "proc-macro-crate", - "proc-macro-error", - "proc-macro2", - "quote", - "rquickjs-core", - "syn 2.0.79", -] - -[[package]] -name = "rquickjs-sys" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf6f2288d8e7fbb5130f62cf720451641e99d55f6fde9db86aa2914ecb553fd2" -dependencies = [ - "bindgen", - "cc", -] - -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - -[[package]] -name = "rustix" -version = "0.38.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" -dependencies = [ - "bitflags", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.52.0", -] - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "scoped-tls" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "similar" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e" - -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "toml_datetime" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" - -[[package]] -name = "toml_edit" -version = "0.19.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" -dependencies = [ - "indexmap", - "toml_datetime", - "winnow", -] - -[[package]] -name = "unicode-ident" -version = "1.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" - -[[package]] -name = "unicode-segmentation" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" - -[[package]] -name = "utf8-width" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3" - -[[package]] -name = "utf8parse" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" - -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "wasm-bindgen" -version = "0.2.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" -dependencies = [ - "cfg-if", - "once_cell", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn 2.0.79", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.79", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" - -[[package]] -name = "wasm-bindgen-test" -version = "0.3.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d381749acb0943d357dcbd8f0b100640679883fcdeeef04def49daf8d33a5426" -dependencies = [ - "console_error_panic_hook", - "js-sys", - "minicov", - "scoped-tls", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-bindgen-test-macro", -] - -[[package]] -name = "wasm-bindgen-test-macro" -version = "0.3.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c97b2ef2c8d627381e51c071c2ab328eac606d3f69dd82bcbca20a9e389d95f0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.79", -] - -[[package]] -name = "web-sys" -version = "0.3.72" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "which" -version = "4.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" -dependencies = [ - "either", - "home", - "once_cell", - "rustix", -] - -[[package]] -name = "winapi-util" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" -dependencies = [ - "windows-sys 0.59.0", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "winnow" -version = "0.5.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" -dependencies = [ - "memchr", -] diff --git a/Cargo.toml b/Cargo.toml index 04d1da1..e1bec87 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,8 @@ wasm-bindgen-test = "0.3.45" ansi2 = { path = "./ansi2", features = ["wasm"] } osvg = { version = "0.1.3", features = ["bindgen"] } insta = { version = "1.40.0" } +infer = "0.16.0" +image = "0.25.5" [profile.release] debug = false diff --git a/ansi2/Cargo.toml b/ansi2/Cargo.toml index ef3c143..7ffc0e2 100644 --- a/ansi2/Cargo.toml +++ b/ansi2/Cargo.toml @@ -14,6 +14,9 @@ nom = { workspace = true } html-escape = { workspace = true } base64 = { workspace = true } osvg = { workspace = true, optional = true } +infer = { workspace = true } +image = { workspace = true } + [dev-dependencies] insta = { workspace = true } @@ -28,4 +31,4 @@ required-features = ["cli"] name = "ansi2" [lints.rust] -unused_assignments = "allow" \ No newline at end of file +unused_assignments = "allow" diff --git a/ansi2/src/ans.rs b/ansi2/src/ans.rs index 656a36e..7705545 100644 --- a/ansi2/src/ans.rs +++ b/ansi2/src/ans.rs @@ -1,6 +1,10 @@ use std::collections::BinaryHeap; -use crate::{canvas::Canvas, color::AnsiColor, node::Node}; +use crate::{ + canvas::{pixels_to_ans, Canvas}, + color::AnsiColor, + node::Node, +}; #[derive(Debug, Clone, PartialEq, Eq)] struct Item { state: Node, @@ -227,7 +231,7 @@ fn bfs(from: &Node, to: &Node) -> Vec { Vec::new() } -fn min_distance(from: &Node, to: &Node) -> String { +pub fn min_distance(from: &Node, to: &Node) -> String { if from.same_style(to) { return String::new(); } @@ -249,25 +253,12 @@ pub fn to_ans>(str: S, width: Option, compress: bool) -> St let s = str.as_ref(); let canvas = Canvas::new(s, width); - let iter = if compress { - canvas.minify().into_iter() + let pixels = if compress { + canvas.minify() } else { - canvas.pixels.into_iter() + canvas.pixels }; - - let mut text: Vec = Vec::new(); - let mut last_node = Node::default(); - - for row in iter { - let mut row_str = Vec::new(); - for c in row.iter() { - row_str.push(min_distance(&last_node, c)); - row_str.push(c.text.clone()); - last_node = c.clone(); - } - text.push(row_str.into_iter().collect()); - } - text.join("\n") + pixels_to_ans(pixels) } #[cfg(test)] diff --git a/ansi2/src/canvas.rs b/ansi2/src/canvas.rs index 40b8ef9..8de7c33 100644 --- a/ansi2/src/canvas.rs +++ b/ansi2/src/canvas.rs @@ -1,452 +1,468 @@ -use std::collections::VecDeque; - -use crate::{ - color::{AnsiColor, Color8}, - lex::{parse_ansi, Sgr, Token}, - node::Node, -}; - -#[derive(Debug, Clone, Default, PartialEq, Eq)] -pub struct Canvas { - pub pixels: Vec>, - pub w: usize, - pub h: usize, -} - -fn set_node(v: &mut Vec>, node: Node, x: usize, y: usize) { - ensure_height(v, y); - - let row = &mut v[y]; - while x >= row.len() { - let empty = Node { - bg_color: AnsiColor::Default, - color: AnsiColor::Default, - bold: false, - text: ' '.into(), - blink: false, - dim: false, - italic: false, - underline: false, - hide: false, - strike: false, - - bg_color_r: (0, 0), - color_r: (0, 0), - bold_r: (0, 0), - blink_r: (0, 0), - text_r: (0, 0), - dim_r: (0, 0), - italic_r: (0, 0), - underline_r: (0, 0), - hide_r: (0, 0), - strike_r: (0, 0), - }; - row.push(empty); - } - - row[x] = node; -} - -fn ensure_height(v: &mut Vec>, h: usize) { - while v.len() <= h { - v.push(Vec::new()); - } -} - -fn merge_range(a: (usize, usize), b: (usize, usize)) -> (usize, usize) { - let (a1, a2) = a; - let (b1, b2) = b; - - (a1.min(b1), a2.max(b2)) -} - -fn offset_range(range: (usize, usize), offset: (usize, usize)) -> (usize, usize) { - let (a1, _) = range; - let (b1, b2) = offset; - - (a1 + b1, a1 + b2) -} -impl Canvas { - pub fn new>(str: S, max_width: Option) -> Self { - let s = str.as_ref(); - let (_, lex) = parse_ansi(s).unwrap(); - let mut cur_x = 0; - let mut cur_y = 0; - let mut cur_c = AnsiColor::Default; - let mut cur_bg_c = AnsiColor::Default; - let mut bold = false; - let mut dim = false; - let mut italic = false; - let mut reverse = false; - let mut underline = false; - let mut blink = false; - let mut strike = false; - - let mut bg_color_r = (0, 0); - let mut color_r = (0, 0); - let mut bold_r = (0, 0); - let mut blink_r = (0, 0); - let mut text_r = (0, 0); - let mut dim_r = (0, 0); - let mut italic_r = (0, 0); - let mut underline_r = (0, 0); - let mut hide_r = (0, 0); - let mut strike_r = (0, 0); - - let mut w = 0; - let mut h = 0; - let mut pixels = Vec::new(); - let mut hide = false; - let max_width = max_width.unwrap_or(usize::MAX); - - let mut q = VecDeque::from(lex); - - while let Some(token) = q.pop_front() { - let Token { sgr: i, range } = token; - // eprintln!("{:?} {:?}", i, range); - - macro_rules! set_bg_color { - ($color:expr) => { - if reverse && $color == AnsiColor::Default { - cur_bg_c = AnsiColor::Color8(Color8::Black); - } else { - cur_bg_c = $color; - } - - bg_color_r = range; - }; - } - - macro_rules! set_color { - ($color:expr) => { - if reverse && $color == AnsiColor::Default { - cur_c = AnsiColor::Color8(Color8::White); - } else { - cur_c = $color; - } - color_r = range; - }; - } - - match i { - Sgr::LineFeed => { - cur_y += 1; - cur_x = 0; - ensure_height(&mut pixels, cur_y); - } - - Sgr::Char(c) => { - text_r = range; - let node = Node { - text: c.into(), - bg_color: cur_bg_c, - color: cur_c, - bold, - blink, - dim, - italic, - underline, - hide, - strike, - - bg_color_r, - color_r, - bold_r, - blink_r, - text_r, - dim_r, - italic_r, - underline_r, - hide_r, - strike_r, - }; - if cur_x >= max_width { - cur_x = 0; - cur_y += 1; - } - set_node(&mut pixels, node, cur_x, cur_y); - cur_x += 1; - } - Sgr::ColorBackground(c) => { - if reverse { - set_color!(c); - } else { - set_bg_color!(c); - } - } - Sgr::ColorForeground(c) => { - if reverse { - set_bg_color!(c); - } else { - set_color!(c); - } - } - Sgr::ColorFgBg(fg, bg) => { - if reverse { - set_color!(bg); - set_bg_color!(fg); - } else { - set_color!(fg); - set_bg_color!(bg); - } - } - Sgr::Bold => { - bold = true; - bold_r = range; - } - Sgr::Italic => { - italic = true; - italic_r = range; - } - Sgr::UnItalic => { - italic = false; - italic_r = range; - } - Sgr::Underline => { - underline = true; - underline_r = range; - } - Sgr::UnUnderlined => { - underline = false; - underline_r = range; - } - Sgr::Dim => { - dim = true; - dim_r = range; - } - Sgr::ColorReset => { - bold = false; - dim = false; - italic = false; - underline = false; - reverse = false; - cur_bg_c = AnsiColor::Default; - cur_c = AnsiColor::Default; - blink = false; - hide = false; - strike = false; - - bold_r = range; - dim_r = range; - italic_r = range; - underline_r = range; - bg_color_r = range; - color_r = range; - blink_r = range; - hide_r = range; - strike_r = range; - } - Sgr::CursorUp(c) => cur_y = cur_y.saturating_sub(c as usize), - Sgr::CursorDown(c) => { - cur_y += c as usize; - ensure_height(&mut pixels, cur_y); - } - Sgr::CursorBack(c) => cur_x = cur_x.saturating_sub(c as usize), - Sgr::CursorForward(c) => { - cur_x += c as usize; - if cur_x >= max_width { - cur_x %= max_width; - cur_y += 1; - } - ensure_height(&mut pixels, cur_y); - } - Sgr::Backspace => cur_x = cur_x.saturating_sub(1), - Sgr::Tab => { - let tail = cur_x & 7; - if tail == 0 { - cur_x += 8 - } else { - cur_x += 8 - tail; - } - - if cur_x >= max_width { - cur_x %= max_width; - cur_y += 1; - } - ensure_height(&mut pixels, cur_y); - } - - Sgr::CarriageReturn => cur_x = 0, - - Sgr::CursorNextLine(n) => { - cur_y += n as usize; - cur_x = 0; - ensure_height(&mut pixels, cur_y); - } - Sgr::CursorPreviousLine(n) => { - cur_y = cur_y.saturating_sub(n as usize); - cur_x = 0; - ensure_height(&mut pixels, cur_y); - } - Sgr::CursorHorizontalAbsolute(n) => cur_x = (n - 1).max(0) as usize, - Sgr::CursorPosition(x, y) => { - cur_x = x as usize; - cur_y = y as usize; - ensure_height(&mut pixels, cur_y); - } - Sgr::SlowBlink | Sgr::RapidBlink => blink = true, - Sgr::UnBlink => blink = false, - Sgr::Reverse => { - reverse = true; - let tmp_c = cur_c; - let tmp_bg_c = cur_bg_c; - set_color!(tmp_bg_c); - set_bg_color!(tmp_c); - } - Sgr::NormalIntensity => { - dim = false; - bold = false; - - dim_r = range; - bold_r = range; - } - Sgr::UnReversed => { - reverse = false; - set_bg_color!(AnsiColor::Default); - set_color!(AnsiColor::Default); - } - Sgr::Strike => { - strike = true; - strike_r = range; - } - Sgr::UnStrike => { - strike = false; - strike_r = range; - } - Sgr::ColorDefaultForeground => { - if reverse { - set_bg_color!(AnsiColor::Default); - } else { - set_color!(AnsiColor::Default); - } - } - Sgr::ColorDefaultBackground => { - if reverse { - set_color!(AnsiColor::Default); - } else { - set_bg_color!(AnsiColor::Default); - } - } - - Sgr::Link(_, title) => { - if title.contains("\x1b") { - if let Ok((_, tokens)) = parse_ansi(&title) { - // FIXME: Avoid the influence of styles in link on subsequent characters - q.push_front(Token { - range, - sgr: Sgr::ColorReset, - }); - for mut i in tokens.into_iter().rev() { - i.range = offset_range(i.range, range); - q.push_front(i); - } - q.push_front(Token { - range, - sgr: Sgr::Underline, - }); - } - } else { - for (k, i) in title.chars().enumerate() { - if i == '\n' { - cur_x = 0; - cur_y += 1; - ensure_height(&mut pixels, cur_y); - continue; - } - - let node = Node { - text: i.into(), - bg_color: cur_bg_c, - color: cur_c, - bold, - blink, - dim, - italic, - underline: true, - hide, - strike, - - bg_color_r, - color_r, - bold_r, - blink_r, - text_r: (range.0 + k, range.1 + k), - dim_r, - italic_r, - underline_r, - hide_r, - strike_r, - }; - - if cur_x >= max_width { - cur_x = 0; - cur_y += 1; - } - set_node(&mut pixels, node, cur_x, cur_y); - cur_x += 1; - } - } - } - Sgr::CursorHide => { - hide = true; - hide_r = range; - } - Sgr::UnHide => { - hide = false; - hide_r = range; - } - Sgr::DoublyUnderlined => { - bold = false; - underline = true; - - bold_r = range; - underline_r = range; - } - Sgr::List(v) => { - for i in v.into_iter().rev() { - q.push_front(Token { - range: (0, 0), - sgr: i, - }); - } - } - _ => {} - } - - w = w.max(cur_x + 1); - h = h.max(cur_y + 1); - } - - Canvas { pixels, w, h } - } - - pub fn minify(&self) -> Vec> { - let mut v = vec![]; - for row in &self.pixels { - let row_len = row.len(); - - if row_len == 0 { - v.push(vec![]); - continue; - } - - let mut block = row[0].clone(); - - let mut list = vec![]; - for c in row.iter().take(row_len).skip(1) { - if c.same_style(&block) { - block.text.push_str(&c.text); - block.text_r = merge_range(block.text_r, c.text_r); - } else { - list.push(block.clone()); - block = c.clone(); - } - } - - list.push(block); - v.push(list); - } - - v - } -} +use std::collections::VecDeque; + +use crate::{ + ans::min_distance, + color::{AnsiColor, Color8}, + lex::{parse_ansi, Sgr, Token}, + node::Node, +}; + +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub struct Canvas { + pub pixels: Vec>, + pub w: usize, + pub h: usize, +} + +fn set_node(v: &mut Vec>, node: Node, x: usize, y: usize) { + ensure_height(v, y); + + let row = &mut v[y]; + while x >= row.len() { + let empty = Node { + bg_color: AnsiColor::Default, + color: AnsiColor::Default, + bold: false, + text: ' '.into(), + blink: false, + dim: false, + italic: false, + underline: false, + hide: false, + strike: false, + + bg_color_r: (0, 0), + color_r: (0, 0), + bold_r: (0, 0), + blink_r: (0, 0), + text_r: (0, 0), + dim_r: (0, 0), + italic_r: (0, 0), + underline_r: (0, 0), + hide_r: (0, 0), + strike_r: (0, 0), + }; + row.push(empty); + } + + row[x] = node; +} + +fn ensure_height(v: &mut Vec>, h: usize) { + while v.len() <= h { + v.push(Vec::new()); + } +} + +fn merge_range(a: (usize, usize), b: (usize, usize)) -> (usize, usize) { + let (a1, a2) = a; + let (b1, b2) = b; + + (a1.min(b1), a2.max(b2)) +} + +fn offset_range(range: (usize, usize), offset: (usize, usize)) -> (usize, usize) { + let (a1, _) = range; + let (b1, b2) = offset; + + (a1 + b1, a1 + b2) +} +impl Canvas { + pub fn new>(str: S, max_width: Option) -> Self { + let s = str.as_ref(); + let (_, lex) = parse_ansi(s).unwrap(); + let mut cur_x = 0; + let mut cur_y = 0; + let mut cur_c = AnsiColor::Default; + let mut cur_bg_c = AnsiColor::Default; + let mut bold = false; + let mut dim = false; + let mut italic = false; + let mut reverse = false; + let mut underline = false; + let mut blink = false; + let mut strike = false; + + let mut bg_color_r = (0, 0); + let mut color_r = (0, 0); + let mut bold_r = (0, 0); + let mut blink_r = (0, 0); + let mut text_r = (0, 0); + let mut dim_r = (0, 0); + let mut italic_r = (0, 0); + let mut underline_r = (0, 0); + let mut hide_r = (0, 0); + let mut strike_r = (0, 0); + + let mut w = 0; + let mut h = 0; + let mut pixels = Vec::new(); + let mut hide = false; + let max_width = max_width.unwrap_or(usize::MAX); + + let mut q = VecDeque::from(lex); + + while let Some(token) = q.pop_front() { + let Token { sgr: i, range } = token; + // eprintln!("{:?} {:?}", i, range); + + macro_rules! set_bg_color { + ($color:expr) => { + if reverse && $color == AnsiColor::Default { + cur_bg_c = AnsiColor::Color8(Color8::Black); + } else { + cur_bg_c = $color; + } + + bg_color_r = range; + }; + } + + macro_rules! set_color { + ($color:expr) => { + if reverse && $color == AnsiColor::Default { + cur_c = AnsiColor::Color8(Color8::White); + } else { + cur_c = $color; + } + color_r = range; + }; + } + + match i { + Sgr::LineFeed => { + cur_y += 1; + cur_x = 0; + ensure_height(&mut pixels, cur_y); + } + + Sgr::Char(c) => { + text_r = range; + let node = Node { + text: c.into(), + bg_color: cur_bg_c, + color: cur_c, + bold, + blink, + dim, + italic, + underline, + hide, + strike, + + bg_color_r, + color_r, + bold_r, + blink_r, + text_r, + dim_r, + italic_r, + underline_r, + hide_r, + strike_r, + }; + if cur_x >= max_width { + cur_x = 0; + cur_y += 1; + } + set_node(&mut pixels, node, cur_x, cur_y); + cur_x += 1; + } + Sgr::ColorBackground(c) => { + if reverse { + set_color!(c); + } else { + set_bg_color!(c); + } + } + Sgr::ColorForeground(c) => { + if reverse { + set_bg_color!(c); + } else { + set_color!(c); + } + } + Sgr::ColorFgBg(fg, bg) => { + if reverse { + set_color!(bg); + set_bg_color!(fg); + } else { + set_color!(fg); + set_bg_color!(bg); + } + } + Sgr::Bold => { + bold = true; + bold_r = range; + } + Sgr::Italic => { + italic = true; + italic_r = range; + } + Sgr::UnItalic => { + italic = false; + italic_r = range; + } + Sgr::Underline => { + underline = true; + underline_r = range; + } + Sgr::UnUnderlined => { + underline = false; + underline_r = range; + } + Sgr::Dim => { + dim = true; + dim_r = range; + } + Sgr::ColorReset => { + bold = false; + dim = false; + italic = false; + underline = false; + reverse = false; + cur_bg_c = AnsiColor::Default; + cur_c = AnsiColor::Default; + blink = false; + hide = false; + strike = false; + + bold_r = range; + dim_r = range; + italic_r = range; + underline_r = range; + bg_color_r = range; + color_r = range; + blink_r = range; + hide_r = range; + strike_r = range; + } + Sgr::CursorUp(c) => cur_y = cur_y.saturating_sub(c as usize), + Sgr::CursorDown(c) => { + cur_y += c as usize; + ensure_height(&mut pixels, cur_y); + } + Sgr::CursorBack(c) => cur_x = cur_x.saturating_sub(c as usize), + Sgr::CursorForward(c) => { + cur_x += c as usize; + if cur_x >= max_width { + cur_x %= max_width; + cur_y += 1; + } + ensure_height(&mut pixels, cur_y); + } + Sgr::Backspace => cur_x = cur_x.saturating_sub(1), + Sgr::Tab => { + let tail = cur_x & 7; + if tail == 0 { + cur_x += 8 + } else { + cur_x += 8 - tail; + } + + if cur_x >= max_width { + cur_x %= max_width; + cur_y += 1; + } + ensure_height(&mut pixels, cur_y); + } + + Sgr::CarriageReturn => cur_x = 0, + + Sgr::CursorNextLine(n) => { + cur_y += n as usize; + cur_x = 0; + ensure_height(&mut pixels, cur_y); + } + Sgr::CursorPreviousLine(n) => { + cur_y = cur_y.saturating_sub(n as usize); + cur_x = 0; + ensure_height(&mut pixels, cur_y); + } + Sgr::CursorHorizontalAbsolute(n) => cur_x = (n - 1).max(0) as usize, + Sgr::CursorPosition(x, y) => { + cur_x = x as usize; + cur_y = y as usize; + ensure_height(&mut pixels, cur_y); + } + Sgr::SlowBlink | Sgr::RapidBlink => blink = true, + Sgr::UnBlink => blink = false, + Sgr::Reverse => { + reverse = true; + let tmp_c = cur_c; + let tmp_bg_c = cur_bg_c; + set_color!(tmp_bg_c); + set_bg_color!(tmp_c); + } + Sgr::NormalIntensity => { + dim = false; + bold = false; + + dim_r = range; + bold_r = range; + } + Sgr::UnReversed => { + reverse = false; + set_bg_color!(AnsiColor::Default); + set_color!(AnsiColor::Default); + } + Sgr::Strike => { + strike = true; + strike_r = range; + } + Sgr::UnStrike => { + strike = false; + strike_r = range; + } + Sgr::ColorDefaultForeground => { + if reverse { + set_bg_color!(AnsiColor::Default); + } else { + set_color!(AnsiColor::Default); + } + } + Sgr::ColorDefaultBackground => { + if reverse { + set_color!(AnsiColor::Default); + } else { + set_bg_color!(AnsiColor::Default); + } + } + + Sgr::Link(_, title) => { + if title.contains("\x1b") { + if let Ok((_, tokens)) = parse_ansi(&title) { + // FIXME: Avoid the influence of styles in link on subsequent characters + q.push_front(Token { + range, + sgr: Sgr::ColorReset, + }); + for mut i in tokens.into_iter().rev() { + i.range = offset_range(i.range, range); + q.push_front(i); + } + q.push_front(Token { + range, + sgr: Sgr::Underline, + }); + } + } else { + for (k, i) in title.chars().enumerate() { + if i == '\n' { + cur_x = 0; + cur_y += 1; + ensure_height(&mut pixels, cur_y); + continue; + } + + let node = Node { + text: i.into(), + bg_color: cur_bg_c, + color: cur_c, + bold, + blink, + dim, + italic, + underline: true, + hide, + strike, + + bg_color_r, + color_r, + bold_r, + blink_r, + text_r: (range.0 + k, range.1 + k), + dim_r, + italic_r, + underline_r, + hide_r, + strike_r, + }; + + if cur_x >= max_width { + cur_x = 0; + cur_y += 1; + } + set_node(&mut pixels, node, cur_x, cur_y); + cur_x += 1; + } + } + } + Sgr::CursorHide => { + hide = true; + hide_r = range; + } + Sgr::UnHide => { + hide = false; + hide_r = range; + } + Sgr::DoublyUnderlined => { + bold = false; + underline = true; + + bold_r = range; + underline_r = range; + } + Sgr::List(v) => { + for i in v.into_iter().rev() { + q.push_front(Token { + range: (0, 0), + sgr: i, + }); + } + } + _ => {} + } + + w = w.max(cur_x + 1); + h = h.max(cur_y + 1); + } + + Canvas { pixels, w, h } + } + + pub fn minify(&self) -> Vec> { + let mut v = vec![]; + for row in &self.pixels { + let row_len = row.len(); + + if row_len == 0 { + v.push(vec![]); + continue; + } + + let mut block = row[0].clone(); + + let mut list = vec![]; + for c in row.iter().take(row_len).skip(1) { + if c.same_style(&block) { + block.text.push_str(&c.text); + block.text_r = merge_range(block.text_r, c.text_r); + } else { + list.push(block.clone()); + block = c.clone(); + } + } + + list.push(block); + v.push(list); + } + + v + } +} + +pub fn pixels_to_ans(pixels: Vec>) -> String { + let mut text: Vec = Vec::new(); + let mut last_node = Node::default(); + for row in pixels.iter() { + let mut row_str = Vec::new(); + for c in row.iter() { + row_str.push(min_distance(&last_node, c)); + row_str.push(c.text.clone()); + last_node = c.clone(); + } + text.push(row_str.into_iter().collect()); + } + text.join("\n") +} diff --git a/ansi2/src/image.rs b/ansi2/src/image.rs new file mode 100644 index 0000000..264f38a --- /dev/null +++ b/ansi2/src/image.rs @@ -0,0 +1,22 @@ +use image::{ImageReader, Pixel}; + +use crate::{canvas::pixels_to_ans, color::AnsiColor, node::Node}; + +pub fn image_to_ans(buf: &[u8]) -> Option { + let img = ImageReader::new(std::io::Cursor::new(buf)) + .with_guessed_format() + .ok()? + .decode() + .ok()?; + + let rgb_image = img.to_rgb8(); + let (width, height) = rgb_image.dimensions(); + let mut pixels = vec![vec![Node::default(); width as usize]; height as usize]; + for (y, x, pixel) in rgb_image.enumerate_pixels() { + let node = &mut pixels[x as usize][y as usize]; + let rgb = pixel.to_rgb(); + node.color = AnsiColor::Rgb(rgb[0], rgb[1], rgb[2]); + node.text = "█".to_string(); + } + Some(pixels_to_ans(pixels)) +} diff --git a/ansi2/src/lib.rs b/ansi2/src/lib.rs index a9cc009..bd1a6bc 100644 --- a/ansi2/src/lib.rs +++ b/ansi2/src/lib.rs @@ -3,6 +3,7 @@ pub mod canvas; pub mod color; pub mod css; pub mod html; +pub mod image; pub mod lex; pub mod node; pub mod svg; diff --git a/ansi2/src/main.rs b/ansi2/src/main.rs index 5379eba..1372216 100644 --- a/ansi2/src/main.rs +++ b/ansi2/src/main.rs @@ -1,4 +1,5 @@ use ansi2::ans::to_ans; +use ansi2::image::image_to_ans; use ansi2::{css::Mode, theme::Theme}; use ansi2::{html::to_html, svg::to_svg, text::to_text}; use base64::prelude::BASE64_STANDARD; @@ -51,6 +52,17 @@ struct Args { sourcemap: bool, } +fn process_input(buf: Vec) -> String { + if let Some(ty) = infer::get(&buf) { + if ty.matcher_type() == infer::MatcherType::Image { + if let Some(s) = image_to_ans(&buf) { + return s; + } + } + } + + return String::from_utf8_lossy(&buf).to_string(); +} fn main() { let args: Args = Args::parse(); let Args { @@ -73,6 +85,8 @@ fn main() { std::io::stdin() .read_to_end(&mut buf) .expect("can't read string from stdin"); + + let s = process_input(buf); let base64 = font.map(|font_url| { if font_url.starts_with("http") { return font_url; @@ -87,7 +101,6 @@ fn main() { return format!("data:font;base64,{base64}"); }); - let s = String::from_utf8_lossy(&buf); let output = match format { Format::Svg => { let mut svg = to_svg( diff --git a/assets/ghostty.png b/assets/ghostty.png new file mode 100644 index 0000000..62a0037 Binary files /dev/null and b/assets/ghostty.png differ diff --git a/readme.md b/readme.md index b0b78c5..6a15754 100644 --- a/readme.md +++ b/readme.md @@ -16,6 +16,7 @@ neofetch | ansi2 --format=svg --theme=vscode > neofetch.svg vitest bench --run | ansi2 --format=html --mode=light > bench.html vitest bench --run | ansi2 --format=text > bench.txt vitest bench --run | ansi2 --format=svg --mode=dark | resvg - -c > bench.png +cat ./assets/ghostty.png | ansi2 -f=ans ``` ## [ansi2](./ansi2)