diff options
| -rw-r--r-- | Cargo.lock | 1138 | ||||
| -rw-r--r-- | Cargo.toml | 12 | ||||
| -rw-r--r-- | README.md | 8 | ||||
| -rw-r--r-- | src/client.rs | 41 | ||||
| -rw-r--r-- | src/config.rs | 11 | ||||
| -rw-r--r-- | src/main.rs | 16 | ||||
| -rw-r--r-- | src/render.rs | 189 | ||||
| -rw-r--r-- | src/repl.rs | 126 |
8 files changed, 1454 insertions, 87 deletions
@@ -3,25 +3,61 @@ version = 3 [[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aho-corasick" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +dependencies = [ + "memchr", +] + +[[package]] name = "aichat" version = "0.1.0" dependencies = [ "anyhow", "bytes", "clap", + "crossbeam", + "crossterm 0.26.1", "dirs", "eventsource-stream", "futures-util", "inquire", + "is-terminal", + "mdcat", + "pulldown-cmark", "reedline", "reqwest", "serde", "serde_json", "serde_yaml", + "syntect", "tokio", ] [[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + +[[package]] name = "android_system_properties" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -31,18 +67,53 @@ dependencies = [ ] [[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] name = "anyhow" version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" [[package]] +name = "arrayref" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" + +[[package]] name = "arrayvec" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" [[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + +[[package]] +name = "async-compression" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "942c7cd7ae39e91bde4820d74132e9862e62c2f386c3aa90ccf55949f5bad63a" +dependencies = [ + "brotli", + "flate2", + "futures-core", + "memchr", + "pin-project-lite", + "tokio", +] + +[[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -55,18 +126,81 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" [[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bit_field" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" + +[[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] +name = "brotli" +version = "3.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a0b1dbcc8ae29329621f8d4f0d835787c1c38bb1401979b49d13b0b305ff68" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b6561fd3f895a11e8f72af2cb7d22e08366bebc2b6b57f7744c4bda27034744" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] name = "bumpalo" version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" [[package]] +name = "bytemuck" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] name = "bytes" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -115,6 +249,15 @@ dependencies = [ ] [[package]] +name = "clap_complete" +version = "4.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501ff0a401473ea1d4c3b125ff95506b62c5bc5768d818634195fbb7c4ad5ff4" +dependencies = [ + "clap", +] + +[[package]] name = "clap_derive" version = "4.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -147,12 +290,104 @@ dependencies = [ ] [[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] name = "core-foundation-sys" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" [[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2801af0d36612ae591caa9568261fddce32ce6e08a7275ea334a06a4ad021a2c" +dependencies = [ + "cfg-if", + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-epoch", + "crossbeam-queue", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" +dependencies = [ + "cfg-if", +] + +[[package]] name = "crossterm" version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -186,6 +421,22 @@ dependencies = [ ] [[package]] +name = "crossterm" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a84cda67535339806297f1b331d6dd6320470d2a0fe65381e79ee9e156dd3d13" +dependencies = [ + "bitflags", + "crossterm_winapi", + "libc", + "mio", + "parking_lot", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] name = "crossterm_winapi" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -195,6 +446,12 @@ dependencies = [ ] [[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] name = "cxx" version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -239,6 +496,12 @@ dependencies = [ ] [[package]] +name = "data-url" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d7439c3735f405729d52c3fbbe4de140eaf938a1fe47d227c27f8254d4302a5" + +[[package]] name = "dirs" version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -280,6 +543,16 @@ dependencies = [ ] [[package]] +name = "env_proxy" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a5019be18538406a43b5419a5501461f0c8b49ea7dfda0cfc32f4e51fc44be1" +dependencies = [ + "log", + "url", +] + +[[package]] name = "errno" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -312,12 +585,29 @@ dependencies = [ ] [[package]] -name = "fastrand" -version = "1.9.0" +name = "exr" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +checksum = "e8af5ef47e2ed89d23d0ecbc1b681b30390069de70260937877514377fc24feb" dependencies = [ - "instant", + "bit_field", + "flume", + "half", + "lebe", + "miniz_oxide", + "smallvec", + "threadpool", + "zune-inflate", +] + +[[package]] +name = "fancy-regex" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d6b8560a05112eb52f04b00e5d3790c0dd75d9d980eb8a122fb23b92a623ccf" +dependencies = [ + "bit-set", + "regex", ] [[package]] @@ -332,12 +622,62 @@ dependencies = [ ] [[package]] +name = "flate2" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "float-cmp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" + +[[package]] +name = "flume" +version = "0.10.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577" +dependencies = [ + "futures-core", + "futures-sink", + "nanorand", + "pin-project", + "spin 0.9.5", +] + +[[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] +name = "fontconfig-parser" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ab2e12762761366dcb876ab8b6e0cfa4797ddcd890575919f008b5ba655672a" +dependencies = [ + "roxmltree", +] + +[[package]] +name = "fontdb" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff20bef7942a72af07104346154a70a70b089c572e454b41bef6eb6cb10e9c06" +dependencies = [ + "fontconfig-parser", + "log", + "memmap2", + "ttf-parser", +] + +[[package]] name = "form_urlencoded" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -408,14 +748,46 @@ dependencies = [ ] [[package]] +name = "gethostname" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a329e22866dd78b35d2c639a4a23d7b950aeae300dfd79f4fb19f74055c2404" +dependencies = [ + "libc", + "windows", +] + +[[package]] name = "getrandom" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "gif" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3edd93c6756b4dfaf2709eafcc345ba2636565295c198a9cfbf75fa5e3e00b06" +dependencies = [ + "color_quant", + "weezl", +] + +[[package]] +name = "gif" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045" +dependencies = [ + "color_quant", + "weezl", ] [[package]] @@ -438,6 +810,15 @@ dependencies = [ ] [[package]] +name = "half" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b4af3693f1b705df946e9fe5631932443781d0aabb423b62fcd4d73f6d2fd0" +dependencies = [ + "crunchy", +] + +[[package]] name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -570,6 +951,31 @@ dependencies = [ ] [[package]] +name = "image" +version = "0.24.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69b7ea949b537b0fd0af141fff8c77690f2ce96f4f41f042ccb6c69c6c965945" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "exr", + "gif 0.11.4", + "jpeg-decoder", + "num-rational", + "num-traits", + "png", + "scoped_threadpool", + "tiff", +] + +[[package]] +name = "imagesize" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b72ad49b554c1728b1e83254a1b1565aea4161e28dabbfa171fc15fe62299caf" + +[[package]] name = "indexmap" version = "1.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -590,22 +996,12 @@ dependencies = [ "dyn-clone", "lazy_static", "newline-converter", - "tempfile", "thiserror", "unicode-segmentation", "unicode-width", ] [[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - -[[package]] name = "io-lifetimes" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -649,6 +1045,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" [[package]] +name = "jpeg-decoder" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e" +dependencies = [ + "rayon", +] + +[[package]] name = "js-sys" version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -658,12 +1063,36 @@ dependencies = [ ] [[package]] +name = "kurbo" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a53776d271cfb873b17c618af0298445c88afc52837f3e948fa3fafd131f449" +dependencies = [ + "arrayvec 0.7.2", +] + +[[package]] +name = "kurbo" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e119590a03caff1f7a582e8ee8c2164ddcc975791701188132fd1d1b518d3871" +dependencies = [ + "arrayvec 0.7.2", +] + +[[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] +name = "lebe" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" + +[[package]] name = "libc" version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -704,24 +1133,99 @@ dependencies = [ ] [[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "mdcat" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d33eab4d729c39b84648fffbdaafdd2d7ef9d3dfc5c3b423de7f329c5766b0a1" +dependencies = [ + "ansi_term", + "anyhow", + "base64", + "clap", + "clap_complete", + "env_proxy", + "gethostname", + "image", + "libc", + "mime", + "mime_guess", + "once_cell", + "pulldown-cmark", + "reqwest", + "resvg", + "shell-words", + "syntect", + "terminal_size", + "tracing", + "tracing-subscriber", + "url", +] + +[[package]] name = "memchr" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] +name = "memmap2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +dependencies = [ + "autocfg", +] + +[[package]] name = "mime" version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" [[package]] +name = "mime_guess" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +dependencies = [ + "mime", + "unicase", +] + +[[package]] name = "minimal-lexical" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] +name = "miniz_oxide" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +dependencies = [ + "adler", +] + +[[package]] name = "mio" version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -734,6 +1238,15 @@ dependencies = [ ] [[package]] +name = "nanorand" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" +dependencies = [ + "getrandom", +] + +[[package]] name = "newline-converter" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -773,6 +1286,17 @@ dependencies = [ ] [[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] name = "num-traits" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -798,6 +1322,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] name = "os_str_bytes" version = "6.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -839,6 +1369,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] +name = "pico-args" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" + +[[package]] +name = "pin-project" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] name = "pin-project-lite" version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -851,6 +1407,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] +name = "png" +version = "0.17.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d708eaf860a19b19ce538740d2b4bdeeb8337fa53f7738455e706623ad5c638" +dependencies = [ + "bitflags", + "crc32fast", + "flate2", + "miniz_oxide", +] + +[[package]] name = "proc-macro-error" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -884,6 +1452,17 @@ dependencies = [ ] [[package]] +name = "pulldown-cmark" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d9cc634bc78768157b5cbfe988ffcd1dcba95cd2b2f03a88316c08c6d00ed63" +dependencies = [ + "bitflags", + "memchr", + "unicase", +] + +[[package]] name = "quote" version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -893,6 +1472,34 @@ dependencies = [ ] [[package]] +name = "rayon" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "356a0625f1954f730c0201cdab48611198dc6ce21f4acff55089b5a78e6e835b" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] + +[[package]] +name = "rctree" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b42e27ef78c35d3998403c1d26f3efd9e135d3e5121b0a4845cc5cc27547f4f" + +[[package]] name = "redox_syscall" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -933,11 +1540,38 @@ dependencies = [ ] [[package]] +name = "regex" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" + +[[package]] name = "reqwest" version = "0.11.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9" dependencies = [ + "async-compression", "base64", "bytes", "encoding_rs", @@ -956,6 +1590,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "rustls", + "rustls-native-certs", "rustls-pemfile", "serde", "serde_json", @@ -975,6 +1610,34 @@ dependencies = [ ] [[package]] +name = "resvg" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76888219c0881e22b0ceab06fddcfe83163cd81642bd60c7842387f9c968a72e" +dependencies = [ + "gif 0.12.0", + "jpeg-decoder", + "log", + "pico-args", + "png", + "rgb", + "svgfilters", + "svgtypes 0.10.0", + "tiny-skia", + "usvg", + "usvg-text-layout", +] + +[[package]] +name = "rgb" +version = "0.8.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20ec2d3e3fc7a92ced357df9cebd5a10b6fb2aa1ee797bf7e9ce2f17dffc8f59" +dependencies = [ + "bytemuck", +] + +[[package]] name = "ring" version = "0.16.20" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -983,13 +1646,35 @@ dependencies = [ "cc", "libc", "once_cell", - "spin", + "spin 0.5.2", "untrusted", "web-sys", "winapi", ] [[package]] +name = "rosvgtree" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdc23d1ace03d6b8153c7d16f0708cd80b61ee8e80304954803354e67e40d150" +dependencies = [ + "log", + "roxmltree", + "simplecss", + "siphasher", + "svgtypes 0.9.0", +] + +[[package]] +name = "roxmltree" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8f595a457b6b8c6cda66a48503e92ee8d19342f905948f29c383200ec9eb1d8" +dependencies = [ + "xmlparser", +] + +[[package]] name = "rustix" version = "0.36.8" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1016,6 +1701,18 @@ dependencies = [ ] [[package]] +name = "rustls-native-certs" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0167bac7a9f490495f3c33013e7722b53cb087ecbe082fb0c6387c96f634ea50" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "schannel", + "security-framework", +] + +[[package]] name = "rustls-pemfile" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1031,12 +1728,52 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70" [[package]] +name = "rustybuzz" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162bdf42e261bee271b3957691018634488084ef577dddeb6420a9684cab2a6a" +dependencies = [ + "bitflags", + "bytemuck", + "smallvec", + "ttf-parser", + "unicode-bidi-mirroring", + "unicode-ccc", + "unicode-general-category", + "unicode-script", +] + +[[package]] name = "ryu" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" [[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 = "schannel" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +dependencies = [ + "windows-sys 0.42.0", +] + +[[package]] +name = "scoped_threadpool" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" + +[[package]] name = "scopeguard" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1059,6 +1796,29 @@ dependencies = [ ] [[package]] +name = "security-framework" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] name = "serde" version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1115,6 +1875,21 @@ dependencies = [ ] [[package]] +name = "sharded-slab" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shell-words" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" + +[[package]] name = "signal-hook" version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1145,6 +1920,27 @@ dependencies = [ ] [[package]] +name = "simd-adler32" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14a5df39617d7c8558154693a1bb8157a4aab8179209540cc0b10e5dc24e0b18" + +[[package]] +name = "simplecss" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a11be7c62927d9427e9f40f3444d5499d868648e2edbc4e2116de69e7ec0e89d" +dependencies = [ + "log", +] + +[[package]] +name = "siphasher" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" + +[[package]] name = "slab" version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1176,6 +1972,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] +name = "spin" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dccf47db1b41fa1573ed27ccf5e08e3ca771cb994f776668c5ebda893b248fc" +dependencies = [ + "lock_api", +] + +[[package]] +name = "strict-num" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9df65f20698aeed245efdde3628a6b559ea1239bbb871af1b6e3b58c413b2bd1" +dependencies = [ + "float-cmp", +] + +[[package]] name = "strip-ansi-escapes" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1210,6 +2024,36 @@ dependencies = [ ] [[package]] +name = "svgfilters" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "639abcebc15fdc2df179f37d6f5463d660c1c79cd552c12343a4600827a04bce" +dependencies = [ + "float-cmp", + "rgb", +] + +[[package]] +name = "svgtypes" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9ee29c1407a5b18ccfe5f6ac82ac11bab3b14407e09c209a6c1a32098b19734" +dependencies = [ + "kurbo 0.8.3", + "siphasher", +] + +[[package]] +name = "svgtypes" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98ffacedcdcf1da6579c907279b4f3c5492fbce99fbbf227f5ed270a589c2765" +dependencies = [ + "kurbo 0.9.0", + "siphasher", +] + +[[package]] name = "syn" version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1221,16 +2065,24 @@ dependencies = [ ] [[package]] -name = "tempfile" -version = "3.4.0" +name = "syntect" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95" +checksum = "c6c454c27d9d7d9a84c7803aaa3c50cd088d2906fe3c6e42da3209aa623576a8" dependencies = [ - "cfg-if", - "fastrand", - "redox_syscall", - "rustix", - "windows-sys 0.42.0", + "bincode", + "bitflags", + "fancy-regex", + "flate2", + "fnv", + "lazy_static", + "once_cell", + "regex-syntax", + "serde", + "serde_derive", + "serde_json", + "thiserror", + "walkdir", ] [[package]] @@ -1243,6 +2095,16 @@ dependencies = [ ] [[package]] +name = "terminal_size" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c9afddd2cec1c0909f06b00ef33f94ab2cc0578c4a610aa208ddfec8aa2b43a" +dependencies = [ + "rustix", + "windows-sys 0.45.0", +] + +[[package]] name = "thiserror" version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1263,6 +2125,36 @@ dependencies = [ ] [[package]] +name = "thread_local" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + +[[package]] +name = "tiff" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7449334f9ff2baf290d55d73983a7d6fa15e01198faef72af07e2a8db851e471" +dependencies = [ + "flate2", + "jpeg-decoder", + "weezl", +] + +[[package]] name = "time" version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1274,6 +2166,31 @@ dependencies = [ ] [[package]] +name = "tiny-skia" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfef3412c6975196fdfac41ef232f910be2bb37b9dd3313a49a1a6bc815a5bdb" +dependencies = [ + "arrayref", + "arrayvec 0.7.2", + "bytemuck", + "cfg-if", + "png", + "tiny-skia-path", +] + +[[package]] +name = "tiny-skia-path" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4b5edac058fc98f51c935daea4d805b695b38e2f151241cad125ade2a2ac20d" +dependencies = [ + "arrayref", + "bytemuck", + "strict-num", +] + +[[package]] name = "tinyvec" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1370,16 +2287,58 @@ checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if", "pin-project-lite", + "tracing-attributes", "tracing-core", ] [[package]] +name = "tracing-attributes" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] name = "tracing-core" version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" dependencies = [ "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +dependencies = [ + "lazy_static", + "log", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", ] [[package]] @@ -1389,12 +2348,45 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] +name = "ttf-parser" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0609f771ad9c6155384897e1df4d948e692667cc0588548b68eb44d052b27633" + +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check", +] + +[[package]] name = "unicode-bidi" version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" [[package]] +name = "unicode-bidi-mirroring" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56d12260fb92d52f9008be7e4bca09f584780eb2266dc8fecc6a192bec561694" + +[[package]] +name = "unicode-ccc" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc2520efa644f8268dce4dcd3050eaa7fc044fca03961e9998ac7e2e92b77cf1" + +[[package]] +name = "unicode-general-category" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2281c8c1d221438e373249e065ca4989c4c36952c211ff21a0ee91c44a3869e7" + +[[package]] name = "unicode-ident" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1410,12 +2402,24 @@ dependencies = [ ] [[package]] +name = "unicode-script" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d817255e1bed6dfd4ca47258685d14d2bdcfbc64fdc9e3819bd5848057b8ecc" + +[[package]] name = "unicode-segmentation" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" [[package]] +name = "unicode-vo" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" + +[[package]] name = "unicode-width" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1445,12 +2449,51 @@ dependencies = [ ] [[package]] +name = "usvg" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b6bb4e62619d9f68aa2d8a823fea2bff302340a1f2d45c264d5b0be170832e" +dependencies = [ + "base64", + "data-url", + "flate2", + "imagesize", + "kurbo 0.9.0", + "log", + "rctree", + "rosvgtree", + "strict-num", +] + +[[package]] +name = "usvg-text-layout" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "195386e01bc35f860db024de275a76e7a31afdf975d18beb6d0e44764118b4db" +dependencies = [ + "fontdb", + "kurbo 0.9.0", + "log", + "rustybuzz", + "unicode-bidi", + "unicode-script", + "unicode-vo", + "usvg", +] + +[[package]] name = "utf8parse" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372" [[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1462,7 +2505,7 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6cbce692ab4ca2f1f3047fcf732430249c0e971bfdd2b234cf2c47ad93af5983" dependencies = [ - "arrayvec", + "arrayvec 0.5.2", "utf8parse", "vte_generate_state_changes", ] @@ -1478,6 +2521,17 @@ dependencies = [ ] [[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + +[[package]] name = "want" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1608,6 +2662,12 @@ dependencies = [ ] [[package]] +name = "weezl" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" + +[[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1639,6 +2699,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] +name = "windows" +version = "0.43.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04662ed0e3e5630dfa9b26e4cb823b817f1a9addda855d973a9458c236556244" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] name = "windows-sys" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1727,3 +2802,18 @@ checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" dependencies = [ "winapi", ] + +[[package]] +name = "xmlparser" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d25c75bf9ea12c4040a97f829154768bbbce366287e2dc044af160cd79a13fd" + +[[package]] +name = "zune-inflate" +version = "0.2.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589245df6230839c305984dcc0a8385cc72af1fd223f360ffd5d65efa4216d40" +dependencies = [ + "simd-adler32", +] @@ -16,16 +16,26 @@ keywords = ["chatgpt", "ai"] anyhow = "1.0.69" bytes = "1.4.0" clap = { version = "4.1.8", features = ["derive", "string"] } +crossterm = "0.26.1" dirs = "4.0.0" eventsource-stream = "0.2.3" futures-util = "0.3.26" -inquire = { version = "0.5.3", features = ["editor"] } +inquire = "0.5.3" +is-terminal = "0.4.4" reedline = "0.16.0" reqwest = { version = "0.11.14", features = ["json", "stream", "socks", "rustls-tls"], default-features = false} serde = { version = "1.0.152", features = ["derive"] } serde_json = "1.0.93" serde_yaml = "0.9.17" tokio = { version = "1.26.0", features = ["full"] } +mdcat = { version = "1.1.0", default_features = false, features =["static"] } +pulldown-cmark = { version = "0.9.2", default-features = false, features = ['simd'] } +crossbeam = "0.8.2" + +[dependencies.syntect] +version = "5.0.0" +default-features = false +features = ["parsing", "regex-fancy", "default-themes", "default-syntaxes"] [profile.release] lto = true @@ -22,11 +22,12 @@ Download from [Github Releases](https://github.com/sigoden/aichat/releases), unz ## Features - Compared to the browser, the terminal starts faster and needs less resources. -- Support command mode and interactive mode. -- Support proxy. +- Support directive and interactive modes +- Support markdown highlighting. - Predefine prompts for role playing. - History query/completion. -- Persist chat messages +- Persist chat messages. +- Support proxy. - Written in rust, single executable file, cross-platform. ## Config @@ -47,6 +48,7 @@ After setting, it will automatically create the configuration file. Of course, y api_key: "<YOUR SECRET API KEY>" # Request via https://platform.openai.com/account/api-keys temperature: 1.0 # optional, see https://platform.openai.com/docs/api-reference/chat/create#chat/create-temperature save: true # optional, If set to true, aichat will save chat messages to message.md +highlight: true # optional, Whether to highlight reply message. proxy: "socks5://127.0.0.1:1080" # optional, set proxy server. e.g. http://127.0.0.1:8080 or socks5://127.0.0.1:1080 ``` diff --git a/src/client.rs b/src/client.rs index 3f42eb4..1f42346 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,4 +1,5 @@ use crate::config::Config; +use crate::repl::ReplyReceiver; use anyhow::{anyhow, Result}; use eventsource_stream::Eventsource; @@ -8,6 +9,7 @@ use serde_json::{json, Value}; use std::sync::atomic::{AtomicBool, Ordering}; use std::{sync::Arc, time::Duration}; use tokio::runtime::Runtime; +use tokio::time::sleep; const CONNECT_TIMEOUT: Duration = Duration::from_secs(10); const API_URL: &str = "https://api.openai.com/v1/chat/completions"; @@ -45,22 +47,31 @@ impl ChatGptClient { .block_on(async { self.acquire_inner(input, prompt).await }) } - pub fn acquire_stream<T>( + pub fn acquire_stream( &self, input: &str, prompt: Option<String>, - output: &mut String, - handler: T, + receiver: &mut ReplyReceiver, ctrlc: Arc<AtomicBool>, - ) -> Result<()> - where - T: FnOnce(&mut String, &str) + Copy, - { + ) -> Result<()> { + async fn watch_ctrlc(ctrlc: Arc<AtomicBool>) { + loop { + if ctrlc.load(Ordering::SeqCst) { + break; + } + sleep(Duration::from_millis(100)).await; + } + } self.runtime.block_on(async { tokio::select! { - ret = self.acquire_stream_inner(input, prompt, handler, output) => { + ret = self.acquire_stream_inner(input, prompt, receiver) => { + receiver.done(); ret } + _ = watch_ctrlc(ctrlc.clone()) => { + receiver.done(); + Ok(()) + }, _ = tokio::signal::ctrl_c() => { ctrlc.store(true, Ordering::SeqCst); Ok(()) @@ -85,19 +96,15 @@ impl ChatGptClient { Ok(output.to_string()) } - async fn acquire_stream_inner<T>( + async fn acquire_stream_inner( &self, content: &str, prompt: Option<String>, - handler: T, - output: &mut String, - ) -> Result<()> - where - T: FnOnce(&mut String, &str) + Copy, - { + receiver: &mut ReplyReceiver, + ) -> Result<()> { let content = combine(content, prompt); if self.config.dry_run { - handler(output, &content); + receiver.text(&content); return Ok(()); } let builder = self.request_builder(&content, true); @@ -121,7 +128,7 @@ impl ChatGptClient { continue; } } - handler(output, text); + receiver.text(text); } } diff --git a/src/config.rs b/src/config.rs index c3ebf3c..cce6b6a 100644 --- a/src/config.rs +++ b/src/config.rs @@ -24,6 +24,9 @@ pub struct Config { /// Whether to persistently save chat messages #[serde(default)] pub save: bool, + /// Whether to highlight reply message + #[serde(default)] + pub highlight: bool, /// Set proxy pub proxy: Option<String>, /// Used only for debugging @@ -172,6 +175,14 @@ fn create_config_file(config_path: &Path) -> Result<()> { raw_config.push_str("save: true\n"); } + let ans = Confirm::new("Whether to highlight reply message?") + .with_default(true) + .prompt() + .map_err(confirm_map_err)?; + if ans { + raw_config.push_str("highlight: true\n"); + } + std::fs::write(config_path, raw_config) .map_err(|err| anyhow!("Failed to write to config file, {err}"))?; Ok(()) diff --git a/src/main.rs b/src/main.rs index ddd856e..893850a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,17 +1,20 @@ mod cli; mod client; mod config; +mod render; mod repl; -use std::process::exit; use std::sync::Arc; +use std::{io::stdout, process::exit}; use cli::Cli; use client::ChatGptClient; use config::{Config, Role}; +use is_terminal::IsTerminal; use anyhow::{anyhow, Result}; use clap::Parser; +use render::MarkdownRender; use repl::{Repl, ReplCmdHandler}; fn main() { @@ -52,8 +55,15 @@ fn start_directive( ) -> Result<()> { let mut file = config.open_message_file()?; let output = client.acquire(input, role.map(|v| v.prompt))?; - println!("{}", output.trim()); - Config::save_message(file.as_mut(), input, &output); + let output = output.trim(); + if config.highlight && stdout().is_terminal() { + let markdown_render = MarkdownRender::init()?; + markdown_render.print(output)?; + } else { + println!("{output}"); + } + + Config::save_message(file.as_mut(), input, output); Ok(()) } diff --git a/src/render.rs b/src/render.rs new file mode 100644 index 0000000..6a052e2 --- /dev/null +++ b/src/render.rs @@ -0,0 +1,189 @@ +use anyhow::Result; +use crossbeam::sync::WaitGroup; +use crossterm::{ + cursor, + event::{self, Event, KeyCode, KeyEvent, KeyModifiers}, + execute, queue, style, + terminal::{ + self, disable_raw_mode, enable_raw_mode, size, ClearType, EnterAlternateScreen, + LeaveAlternateScreen, + }, +}; +use mdcat::{ + push_tty, + terminal::{TerminalProgram, TerminalSize}, + Environment, ResourceAccess, Settings, +}; +use pulldown_cmark::Parser; +use std::{ + io::{self, Write}, + sync::{ + atomic::{AtomicBool, Ordering}, + mpsc::Receiver, + Arc, + }, + thread, + time::Duration, +}; +use syntect::parsing::SyntaxSet; + +use crate::repl::{dump, ReplyEvent}; + +pub fn render_stream( + rx: Receiver<ReplyEvent>, + ctrlc: Arc<AtomicBool>, + markdown_render: Arc<MarkdownRender>, +) -> Result<()> { + let wg = WaitGroup::new(); + let ctrlc_clone = ctrlc.clone(); + let stream_done = Arc::new(AtomicBool::new(false)); + let stream_done_clone = stream_done.clone(); + let wg_clone = wg.clone(); + thread::spawn(move || { + let _ = detect_ctrlc(ctrlc_clone, stream_done_clone); + drop(wg_clone); + }); + let ret = render_stream_inner(rx, ctrlc, markdown_render); + stream_done.store(true, Ordering::SeqCst); + wg.wait(); + ret +} + +fn detect_ctrlc(ctrlc: Arc<AtomicBool>, stream_done: Arc<AtomicBool>) -> Result<()> { + loop { + if ctrlc.load(Ordering::SeqCst) || stream_done.load(Ordering::SeqCst) { + return Ok(()); + } + if event::poll(Duration::from_millis(100))? { + if let Event::Key(KeyEvent { + code: KeyCode::Char('c'), + modifiers: KeyModifiers::CONTROL, + .. + }) = event::read()? + { + ctrlc.store(true, Ordering::SeqCst); + break; + } + } + } + Ok(()) +} + +fn render_stream_inner( + rx: Receiver<ReplyEvent>, + ctrlc: Arc<AtomicBool>, + markdown_render: Arc<MarkdownRender>, +) -> Result<()> { + // setup terminal + enable_raw_mode()?; + let mut output = String::new(); + let mut stdout = io::stdout(); + execute!(stdout, EnterAlternateScreen)?; + + fn clear(stdout: &mut impl Write) -> io::Result<()> { + queue!( + stdout, + style::ResetColor, + terminal::Clear(ClearType::All), + cursor::Hide, + cursor::MoveTo(0, 0) + ) + } + + clear(&mut stdout)?; + + while let Ok(ev) = rx.recv() { + if ctrlc.load(Ordering::SeqCst) { + break; + } + match ev { + ReplyEvent::Text(text) => { + output.push_str(&text); + let rows = size()?.1 as usize; + let lines: Vec<&str> = output.split('\n').collect(); + let len = lines.len(); + let skip = if len > rows { len - rows } else { 0 }; + let mut selected_lines = vec![]; + let mut count_begin_code = 0; + let mut code = None; + for (index, line) in lines.iter().enumerate() { + if index < skip { + if line.starts_with("```") { + count_begin_code += 1; + code = Some(*line); + } + } else { + selected_lines.push(*line); + } + } + if count_begin_code % 2 == 1 { + if let Some(code) = code { + selected_lines[0] = code + } + }; + let content = selected_lines.join("\n"); + let markdown = markdown_render.render(&content)?; + if text.contains('\n') { + clear(&mut stdout)?; + for line in markdown.split('\n') { + queue!(stdout, style::Print(line), cursor::MoveToNextLine(1))?; + } + } else if let Some(line) = markdown.split('\n').last() { + queue!( + stdout, + style::ResetColor, + terminal::Clear(ClearType::CurrentLine), + cursor::MoveToColumn(0), + style::Print(line) + )?; + } + + stdout.flush()?; + } + ReplyEvent::Done => { + break; + } + } + } + + execute!(stdout, style::ResetColor, cursor::Show)?; + + // restore terminal + disable_raw_mode()?; + execute!(stdout, LeaveAlternateScreen)?; + + Ok(()) +} + +pub struct MarkdownRender { + env: Environment, + settings: Settings, +} + +impl MarkdownRender { + pub fn init() -> Result<Self> { + let terminal = TerminalProgram::detect(); + let env = + Environment::for_local_directory(&std::env::current_dir().expect("Working directory"))?; + let settings = Settings { + resource_access: ResourceAccess::LocalOnly, + syntax_set: SyntaxSet::load_defaults_newlines(), + terminal_capabilities: terminal.capabilities(), + terminal_size: TerminalSize::default(), + }; + Ok(Self { env, settings }) + } + + pub fn print(&self, input: &str) -> Result<()> { + let markdown = self.render(input)?; + dump(markdown, 0); + Ok(()) + } + + pub fn render(&self, input: &str) -> Result<String> { + let source = Parser::new(input); + let mut sink = Vec::new(); + push_tty(&self.settings, &self.env, &mut sink, source)?; + Ok(String::from_utf8_lossy(&sink).into()) + } +} diff --git a/src/repl.rs b/src/repl.rs index 2bb2aef..f8a10a3 100644 --- a/src/repl.rs +++ b/src/repl.rs @@ -1,7 +1,8 @@ use crate::client::ChatGptClient; use crate::config::{Config, Role}; +use crate::render::{self, MarkdownRender}; use anyhow::{anyhow, Result}; -use inquire::Editor; +use crossbeam::sync::WaitGroup; use reedline::{ default_emacs_keybindings, ColumnarMenu, DefaultCompleter, DefaultPrompt, DefaultPromptSegment, Emacs, FileBackedHistory, KeyCode, KeyModifiers, Keybindings, Reedline, ReedlineEvent, @@ -11,9 +12,12 @@ use std::cell::RefCell; use std::fs::File; use std::io::{stdout, Write}; use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::mpsc::channel; +use std::sync::mpsc::Sender; use std::sync::Arc; +use std::thread::spawn; -const REPL_COMMANDS: [(&str, &str); 8] = [ +const REPL_COMMANDS: [(&str, &str); 7] = [ (".clear", "Clear the screen"), (".clear-history", "Clear the history"), (".clear-role", "Clear the role status"), @@ -21,7 +25,6 @@ const REPL_COMMANDS: [(&str, &str); 8] = [ (".help", "Print this help message"), (".history", "Print the history"), (".role", "Specify the role that the AI will play"), - (".view", "Use an external editor to view the AI reply"), ]; const MENU_NAME: &str = "completion_menu"; @@ -86,13 +89,9 @@ impl Repl { Ok(Signal::CtrlD) => { break; } - Err(err) => { - dump(format!("{err:?}"), 1); - break; - } + _ => {} } } - // tx.send(ReplCmd::Quit).unwrap(); Ok(()) } @@ -103,7 +102,6 @@ impl Repl { None => (line.as_str(), None), }; match cmd { - ".view" => handler.handle(ReplCmd::View)?, ".exit" => { return Ok(true); } @@ -187,6 +185,7 @@ pub struct ReplCmdHandler { config: Arc<Config>, state: RefCell<ReplCmdHandlerState>, ctrlc: Arc<AtomicBool>, + render: Option<Arc<MarkdownRender>>, } struct ReplCmdHandlerState { @@ -197,6 +196,11 @@ struct ReplCmdHandlerState { impl ReplCmdHandler { pub fn init(client: ChatGptClient, config: Arc<Config>, role: Option<Role>) -> Result<Self> { + let render = if config.highlight { + Some(Arc::new(MarkdownRender::init()?)) + } else { + None + }; let prompt = role.map(|v| v.prompt).unwrap_or_default(); let save_file = config.open_message_file()?; let ctrlc = Arc::new(AtomicBool::new(false)); @@ -208,14 +212,14 @@ impl ReplCmdHandler { Ok(Self { client, config, - ctrlc, state, + ctrlc, + render, }) } fn handle(&self, cmd: ReplCmd) -> Result<()> { match cmd { ReplCmd::Input(input) => { - let mut output = String::new(); if input.is_empty() { self.state.borrow_mut().output.clear(); return Ok(()); @@ -226,27 +230,37 @@ impl ReplCmdHandler { } else { Some(prompt) }; - self.client.acquire_stream( + let wg = WaitGroup::new(); + let mut receiver = if let Some(markdown_render) = self.render.clone() { + let (tx, rx) = channel(); + let ctrlc = self.ctrlc.clone(); + let wg = wg.clone(); + spawn(move || { + let _ = render::render_stream(rx, ctrlc, markdown_render); + drop(wg); + }); + ReplyReceiver::new(Some(tx)) + } else { + ReplyReceiver::new(None) + }; + self.client + .acquire_stream(&input, prompt, &mut receiver, self.ctrlc.clone())?; + Config::save_message( + self.state.borrow_mut().save_file.as_mut(), &input, - prompt, - &mut output, - dump_and_collect, - self.ctrlc.clone(), - )?; - dump_and_collect(&mut output, "\n\n"); - Config::save_message(self.state.borrow_mut().save_file.as_mut(), &input, &output); - self.state.borrow_mut().output = output; - } - ReplCmd::View => { - let output = self.state.borrow().output.to_string(); - if output.is_empty() { - return Ok(()); + &receiver.output, + ); + wg.wait(); + match self.render.clone() { + Some(markdown_render) => { + markdown_render.print(&receiver.output)?; + dump("", 1); + } + None => { + dump(&receiver.output, 2); + } } - let _ = Editor::new("view ai reply with an external editor") - .with_file_extension(".md") - .with_predefined_text(&output) - .prompt()?; - dump("", 1); + self.state.borrow_mut().output = receiver.output; } ReplCmd::SetRole(name) => match self.config.find_role(&name) { Some(v) => { @@ -265,21 +279,55 @@ impl ReplCmdHandler { } } -pub enum ReplCmd { - View, - UnsetRole, - Input(String), - SetRole(String), +pub struct ReplyReceiver { + output: String, + sender: Option<Sender<ReplyEvent>>, +} + +impl ReplyReceiver { + pub fn new(sender: Option<Sender<ReplyEvent>>) -> Self { + Self { + output: String::new(), + sender, + } + } + pub fn text(&mut self, text: &str) { + match self.sender.as_ref() { + Some(tx) => { + let _ = tx.send(ReplyEvent::Text(text.to_string())); + } + None => { + dump(text, 0); + } + } + self.output.push_str(text); + } + pub fn done(&mut self) { + match self.sender.as_ref() { + Some(tx) => { + let _ = tx.send(ReplyEvent::Done); + } + None => { + dump("", 2); + } + } + } +} + +pub enum ReplyEvent { + Text(String), + Done, } pub fn dump<T: ToString>(text: T, newlines: usize) { print!("{}{}", text.to_string(), "\n".repeat(newlines)); - stdout().flush().unwrap(); + let _ = stdout().flush(); } -fn dump_and_collect(output: &mut String, reply: &str) { - output.push_str(reply); - dump(reply, 0); +enum ReplCmd { + UnsetRole, + Input(String), + SetRole(String), } fn dump_repl_help() { |
