diff --git a/.github/demo_01.webp b/.github/demo_01.webp index 8a7f085..a159342 100644 Binary files a/.github/demo_01.webp and b/.github/demo_01.webp differ diff --git a/.github/release-body.md b/.github/release-body.md index fe9b3e0..4bf4712 100644 --- a/.github/release-body.md +++ b/.github/release-body.md @@ -1,18 +1,25 @@ -### 2025-09-28 +### 2026-02-23 + +*BREAKING CHANGES* ++ `log_scroll_forward`, `log_scroll_back` renamed to `scroll_forward`, `scroll_back` ++ Additional KeyMap entry, `inspect` defaults to `i`, enables Inspect mode ++ Docker Host priorities reordered, *should* now be, from high to low order, `--host` cli argument, `config.toml` `host` value, `DOCKER_HOST` env, [Docker library](https://github.com/fussybeaver/bollard) default setting. ++ `config.toml` `host` value is now commented out by default - this should help with invalid Docker connection errors and enable easy Podman support ### Chores -+ create_release.sh updated, [d4af754ad245540db60177f7b202b3c64519c961] -+ dependencies updated, [03599b46657d38d0c9f25c2ccfd9510f2b98dd84], [aef0c9503e7045a256856aa887d8c8d7722b9936], [f0771eab5d07d141fe7a8997db650f0f65ffe0a7], [1596de8681ad6c0a7832eb922dd2dc36ab30eb41] -+ GitHub workflow updated, [66dae5e61ea294ac8ce134a6c32b27c04166b6eb] - -### Docs -+ fix numerous typos, [618a43b501914fdf2659e171172ad180364cf87a] ++ dependencies updated, [4658a8de264698b0c8092e1227f0683527219a0b], [8b5899ca238bcbff32519b376b920cd7b7509809], [bebb687c59f3b408e69b23d2e68fa69f006a3231] ++ GitHub workflow updated, [a0aa7918241ee8f702d6472c620287aa4be7d56c] ### Features -+ *BREAKING CHANGE* - `scroll_down_many` & `scroll_up_many` removed, `scroll_down_one` `scroll_up_one` renamed `scroll_down`, `scroll_up`, see [example_config](https://github.com/mrjackwills/oxker/tree/main/example_config), [52a04ec1d0b9e4877e304f60a857ebc00f88b4fd] -+ log search feature, closes #72. Use `#` button, remappable via `log_search_mode`, to enter log search mode. Case-sensitive by default, editable in `config.toml` with `log_search_case_sensitive` entry. Customise colours via `[colors.log_search]` entries, again see see [example_config](https://github.com/mrjackwills/oxker/tree/main/example_config), [96d9469623a7c90b79aa8d82abf587290343ad37], [a2316a9cac270790920a1ebd1be6532d51aba77c] -+ `term` renamed `filter term`, tests updated, [487c3faf96f4c197c8b82644c02466ea40626a5e] ++ Network chart, closes #79, [99fcb8fedf01599ec346b65d435d4c301a7a8851] ++ Inspect mode & help panel redesign, [ae7f3f4a9472b451c37c0ab97b1756b41a3529f5] ++ set rust-version in Cargo.toml, closes #77, [0763a1024f44d98b8d9d65f57995da538e40963c] -My 32-bit armhf armv6 hardware no longer seems to be able to run Docker. Future `oxker` releases won't be tested on real hardware but will continue to be built and published for armv6. +### Fixes ++ Enable quit on Docker connect error screen, [5f942eb2e963660bd7fe9d80fa7ba8a83754803a] + +### Refactors ++ dead code removed, [3e31a2a6bc02d6ef75bd6cbc18568e82e60e1ee3] ++ docker data spawns, [cd943f67e465fff9726b40570a089301a4a8f534] see CHANGELOG.md for more details diff --git a/.github/screenshot_01.png b/.github/screenshot_01.png index f39df48..27b91c9 100644 Binary files a/.github/screenshot_01.png and b/.github/screenshot_01.png differ diff --git a/.github/workflows/create_release_and_build.yml b/.github/workflows/create_release_and_build.yml index c737331..f8d0b56 100644 --- a/.github/workflows/create_release_and_build.yml +++ b/.github/workflows/create_release_and_build.yml @@ -47,11 +47,19 @@ jobs: - name: install cross run: cargo install cross --git https://github.com/cross-rs/cross - # Build binary for arm MacOS using Docker Zigbuild - name: build if: matrix.target == 'aarch64-apple-darwin' run: | - docker run --rm -v $(pwd):/io -w /io ghcr.io/rust-cross/cargo-zigbuild cargo zigbuild --release --target aarch64-apple-darwin + docker run --rm \ + -v "$(pwd):/io" \ + -w /io \ + ghcr.io/rust-cross/cargo-zigbuild \ + bash -ec ' + rustup update stable + rustup default stable + rustup target add aarch64-apple-darwin + cargo zigbuild --release --target aarch64-apple-darwin + ' # Build all other targets using Cross - name: build @@ -72,7 +80,7 @@ jobs: # Upload output for release page - name: Upload Artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: if-no-files-found: error name: ${{ matrix.target }} @@ -91,7 +99,7 @@ jobs: uses: actions/checkout@v5 - name: Setup | Artifacts - uses: actions/download-artifact@v5 + uses: actions/download-artifact@v7 - name: Update Release uses: ncipollo/release-action@v1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 633a832..f6c05f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,28 @@ +# v0.13.0 +### 2026-02-23 + +*BREAKING CHANGES* ++ `log_scroll_forward`, `log_scroll_back` renamed to `scroll_forward`, `scroll_back` ++ Additional KeyMap entry, `inspect` defaults to `i`, enables Inspect mode ++ Docker Host priorities reordered, *should* now be, from high to low order, `--host` cli argument, `config.toml` `host` value, `DOCKER_HOST` env, [Docker library](https://github.com/fussybeaver/bollard) default setting. ++ `config.toml` `host` value is now commented out by default - this should help with invalid Docker connection errors and enable easy Podman support + +### Chores ++ dependencies updated, [4658a8de](https://github.com/mrjackwills/oxker/commit/4658a8de264698b0c8092e1227f0683527219a0b), [8b5899ca](https://github.com/mrjackwills/oxker/commit/8b5899ca238bcbff32519b376b920cd7b7509809), [bebb687c](https://github.com/mrjackwills/oxker/commit/bebb687c59f3b408e69b23d2e68fa69f006a3231) ++ GitHub workflow updated, [a0aa7918](https://github.com/mrjackwills/oxker/commit/a0aa7918241ee8f702d6472c620287aa4be7d56c) + +### Features ++ Network chart, closes [#79](https://github.com/mrjackwills/oxker/issues/79), [99fcb8fe](https://github.com/mrjackwills/oxker/commit/99fcb8fedf01599ec346b65d435d4c301a7a8851) ++ Inspect mode & help panel redesign, [ae7f3f4a](https://github.com/mrjackwills/oxker/commit/ae7f3f4a9472b451c37c0ab97b1756b41a3529f5) ++ set rust-version in Cargo.toml, closes [#77](https://github.com/mrjackwills/oxker/issues/77), [0763a102](https://github.com/mrjackwills/oxker/commit/0763a1024f44d98b8d9d65f57995da538e40963c) + +### Fixes ++ Enable quit on Docker connect error screen, [5f942eb2](https://github.com/mrjackwills/oxker/commit/5f942eb2e963660bd7fe9d80fa7ba8a83754803a) + +### Refactors ++ dead code removed, [3e31a2a6](https://github.com/mrjackwills/oxker/commit/3e31a2a6bc02d6ef75bd6cbc18568e82e60e1ee3) ++ docker data spawns, [cd943f67](https://github.com/mrjackwills/oxker/commit/cd943f67e465fff9726b40570a089301a4a8f534) + # v0.12.0 ### 2025-09-28 diff --git a/Cargo.lock b/Cargo.lock index f89e920..3912ed1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,40 +3,25 @@ version = 4 [[package]] -name = "addr2line" -version = "0.25.1" +name = "aho-corasick" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ - "gimli", + "memchr", ] -[[package]] -name = "adler2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" - [[package]] name = "allocator-api2" version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - [[package]] name = "anstream" -version = "0.6.20" +version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" dependencies = [ "anstyle", "anstyle-parse", @@ -49,9 +34,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.11" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" [[package]] name = "anstyle-parse" @@ -64,29 +49,38 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.10" +version = "3.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "anyhow" -version = "1.0.100" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "atomic" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89cbf775b137e9b968e67227ef7f775587cde3fd31b0d8599dbd0f598a48340" +dependencies = [ + "bytemuck", +] [[package]] name = "atomic-waker" @@ -100,21 +94,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" -[[package]] -name = "backtrace" -version = "0.3.76" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" -dependencies = [ - "addr2line", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", - "windows-link", -] - [[package]] name = "base64" version = "0.22.1" @@ -122,16 +101,46 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] -name = "bitflags" -version = "2.9.4" +name = "bit-set" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" +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 = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] [[package]] name = "bollard" -version = "0.19.2" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8796b390a5b4c86f9f2e8173a68c2791f4fa6b038b84e96dbc01c016d1e6722c" +checksum = "227aa051deec8d16bd9c34605e7aaf153f240e35483dd42f6f78903847934738" dependencies = [ "base64", "bollard-stubs", @@ -150,9 +159,8 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "serde_repr", "serde_urlencoded", - "thiserror", + "thiserror 2.0.18", "tokio", "tokio-util", "tower-service", @@ -162,27 +170,32 @@ dependencies = [ [[package]] name = "bollard-stubs" -version = "1.49.0-rc.28.3.3" +version = "1.52.1-rc.29.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e7814991259013d5a5bee4ae28657dae0747d843cf06c40f7fc0c2894d6fa38" +checksum = "0f0a8ca8799131c1837d1282c3f81f31e76ceb0ce426e04a7fe1ccee3287c066" dependencies = [ "serde", "serde_json", "serde_repr", - "serde_with", ] [[package]] name = "bumpalo" -version = "3.19.0" +version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" + +[[package]] +name = "bytemuck" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" [[package]] name = "bytes" -version = "1.10.1" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" [[package]] name = "cansi" @@ -190,12 +203,6 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4bdcae87153686017415ce77e48c53e6818a0a058f0e21b56640d1e944967ef8" -[[package]] -name = "cassowary" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" - [[package]] name = "castaway" version = "0.2.4" @@ -205,39 +212,23 @@ dependencies = [ "rustversion", ] -[[package]] -name = "cc" -version = "1.2.39" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1354349954c6fc9cb0deab020f27f783cf0b604e8bb754dc4658ecf0d29c35f" -dependencies = [ - "find-msvc-tools", - "shlex", -] - [[package]] name = "cfg-if" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] -name = "chrono" -version = "0.4.42" +name = "cfg_aliases" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" -dependencies = [ - "iana-time-zone", - "num-traits", - "serde", - "windows-link", -] +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "clap" -version = "4.5.48" +version = "4.5.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2134bb3ea021b78629caa971416385309e0131b351b25e01dc16fb54e1b5fae" +checksum = "2797f34da339ce31042b27d23607e051786132987f595b02ba4f6a6dffb7030a" dependencies = [ "clap_builder", "clap_derive", @@ -245,35 +236,35 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.48" +version = "4.5.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2ba64afa3c0a6df7fa517765e31314e983f51dda798ffba27b988194fb65dc9" +checksum = "24a241312cea5059b13574bb9b3861cabf758b879c15190b37b6d6fd63ab6876" dependencies = [ "anstream", "anstyle", "clap_lex", "strsim", "unicase", - "unicode-width 0.2.0", + "unicode-width", ] [[package]] name = "clap_derive" -version = "4.5.47" +version = "4.5.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c" +checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5" dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] name = "clap_lex" -version = "0.7.5" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" +checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831" [[package]] name = "colorchoice" @@ -283,9 +274,9 @@ checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] name = "compact_str" -version = "0.8.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b79c4069c6cad78e2e0cdfcbd26275770669fb39fd308a752dc110e83b9af32" +checksum = "3fdb1325a1cece981e8a296ab8f0f9b63ae357bd0784a9faaf548cc7b480707a" dependencies = [ "castaway", "cfg-if", @@ -309,33 +300,20 @@ dependencies = [ [[package]] name = "convert_case" -version = "0.7.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" dependencies = [ "unicode-segmentation", ] [[package]] -name = "core-foundation-sys" -version = "0.8.7" +name = "cpufeatures" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - -[[package]] -name = "crossterm" -version = "0.28.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" dependencies = [ - "bitflags", - "crossterm_winapi", - "mio", - "parking_lot", - "rustix 0.38.44", - "signal-hook", - "signal-hook-mio", - "winapi", + "libc", ] [[package]] @@ -344,13 +322,13 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" dependencies = [ - "bitflags", + "bitflags 2.11.0", "crossterm_winapi", "derive_more", "document-features", "mio", "parking_lot", - "rustix 1.1.2", + "rustix", "signal-hook", "signal-hook-mio", "winapi", @@ -366,10 +344,30 @@ dependencies = [ ] [[package]] -name = "darling" -version = "0.20.11" +name = "crypto-common" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "csscolorparser" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb2a7d3066da2de787b7f032c736763eb7ae5d355f81a68bab2675a96008b0bf" +dependencies = [ + "lab", + "phf", +] + +[[package]] +name = "darling" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" dependencies = [ "darling_core", "darling_macro", @@ -377,58 +375,73 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.11" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" dependencies = [ - "fnv", "ident_case", "proc-macro2", "quote", "strsim", - "syn", + "syn 2.0.117", ] [[package]] name = "darling_macro" -version = "0.20.11" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" dependencies = [ "darling_core", "quote", - "syn", + "syn 2.0.117", ] [[package]] -name = "deranged" -version = "0.5.4" +name = "deltae" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a41953f86f8a05768a6cda24def994fd2f424b04ec5c719cf89989779f199071" +checksum = "5729f5117e208430e437df2f4843f5e5952997175992d1414f94c57d61e270b4" + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" dependencies = [ "powerfmt", - "serde_core", ] [[package]] name = "derive_more" -version = "2.0.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" dependencies = [ "derive_more-impl", ] [[package]] name = "derive_more-impl" -version = "2.0.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" dependencies = [ "convert_case", "proc-macro2", "quote", - "syn", + "rustc_version", + "syn 2.0.117", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", ] [[package]] @@ -449,7 +462,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.61.1", + "windows-sys 0.61.2", ] [[package]] @@ -460,24 +473,18 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] name = "document-features" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" dependencies = [ "litrs", ] -[[package]] -name = "dyn-clone" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" - [[package]] name = "either" version = "1.15.0" @@ -503,14 +510,56 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.1", + "windows-sys 0.61.2", ] [[package]] -name = "find-msvc-tools" -version = "0.1.2" +name = "euclid" +version = "0.22.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ced73b1dacfc750a6db6c0a0c3a3853c8b41997e2e2c563dc90804ae6867959" +checksum = "df61bf483e837f88d5c2291dcf55c67be7e676b3a51acc48db3a7b163b91ed63" +dependencies = [ + "num-traits", +] + +[[package]] +name = "fancy-regex" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b95f7c0680e4142284cf8b22c14a476e87d61b004a3a0861872b32ef7ead40a2" +dependencies = [ + "bit-set", + "regex", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "filedescriptor" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e40758ed24c9b2eeb76c35fb0aebc66c626084edd827e07e1552279814c6682d" +dependencies = [ + "libc", + "thiserror 1.0.69", + "winapi", +] + +[[package]] +name = "finl_unicode" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9844ddc3a6e533d62bba727eb6c28b5d360921d5175e9ff0f1e621a5c590a4d5" + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "fnv" @@ -524,6 +573,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "form_urlencoded" version = "1.2.2" @@ -535,90 +590,100 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" dependencies = [ "futures-core", ] [[package]] name = "futures-core" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" [[package]] name = "futures-macro" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] name = "futures-sink" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" [[package]] name = "futures-task" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" [[package]] name = "futures-util" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ "futures-core", "futures-macro", "futures-task", "pin-project-lite", - "pin-utils", "slab", ] [[package]] -name = "getrandom" -version = "0.2.16" +name = "generic-array" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ - "cfg-if", - "libc", - "wasi 0.11.1+wasi-snapshot-preview1", + "typenum", + "version_check", ] [[package]] name = "getrandom" -version = "0.3.3" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "libc", "r-efi", - "wasi 0.14.7+wasi-0.2.4", + "wasip2", ] [[package]] -name = "gimli" -version = "0.32.3" +name = "getrandom" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", + "wasip3", +] [[package]] name = "hashbrown" @@ -626,16 +691,19 @@ version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ - "allocator-api2", - "equivalent", - "foldhash", + "foldhash 0.1.5", ] [[package]] name = "hashbrown" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash 0.2.0", +] [[package]] name = "heck" @@ -651,12 +719,11 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "http" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" dependencies = [ "bytes", - "fnv", "itoa", ] @@ -697,9 +764,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" dependencies = [ "atomic-waker", "bytes", @@ -734,13 +801,12 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.17" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" dependencies = [ "bytes", "futures-channel", - "futures-core", "futures-util", "http", "http-body", @@ -768,35 +834,11 @@ dependencies = [ "tower-service", ] -[[package]] -name = "iana-time-zone" -version = "0.1.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "log", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - [[package]] name = "icu_collections" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" dependencies = [ "displaydoc", "potential_utf", @@ -807,9 +849,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" dependencies = [ "displaydoc", "litemap", @@ -820,11 +862,10 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" dependencies = [ - "displaydoc", "icu_collections", "icu_normalizer_data", "icu_properties", @@ -835,42 +876,38 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" [[package]] name = "icu_properties" -version = "2.0.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" dependencies = [ - "displaydoc", "icu_collections", "icu_locale_core", "icu_properties_data", "icu_provider", - "potential_utf", "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "2.0.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" [[package]] name = "icu_provider" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" dependencies = [ "displaydoc", "icu_locale_core", - "stable_deref_trait", - "tinystr", "writeable", "yoke", "zerofrom", @@ -878,6 +915,12 @@ dependencies = [ "zerovec", ] +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + [[package]] name = "ident_case" version = "1.0.1" @@ -907,94 +950,76 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.3" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", - "serde", -] - -[[package]] -name = "indexmap" -version = "2.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", - "hashbrown 0.16.0", + "hashbrown 0.16.1", "serde", "serde_core", ] [[package]] name = "indoc" -version = "2.0.6" +version = "2.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd" +checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" +dependencies = [ + "rustversion", +] [[package]] name = "insta" -version = "1.43.2" +version = "1.46.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46fdb647ebde000f43b5b53f773c30cf9b0cb4300453208713fa38b2c70935a0" +checksum = "e82db8c87c7f1ccecb34ce0c24399b8a73081427f3c7c50a5d597925356115e4" dependencies = [ "console", "once_cell", "similar", + "tempfile", ] [[package]] name = "instability" -version = "0.3.9" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435d80800b936787d62688c927b6490e887c7ef5ff9ce922c6c6050fca75eb9a" +checksum = "357b7205c6cd18dd2c86ed312d1e70add149aea98e7ef72b9fdf0270e555c11d" dependencies = [ "darling", "indoc", "proc-macro2", "quote", - "syn", -] - -[[package]] -name = "io-uring" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" -dependencies = [ - "bitflags", - "cfg-if", - "libc", + "syn 2.0.117", ] [[package]] name = "is_terminal_polyfill" -version = "1.70.1" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" [[package]] name = "itertools" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" dependencies = [ "either", ] [[package]] name = "itoa" -version = "1.0.15" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" [[package]] name = "jiff" -version = "0.2.15" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be1f93b8b1eb69c77f24bbb0afdf66f54b632ee39af40ca21c4365a1d7347e49" +checksum = "b3e3d65f018c6ae946ab16e80944b97096ed73c35b221d1c478a6c81d8f57940" dependencies = [ "jiff-static", "jiff-tzdb", @@ -1002,26 +1027,26 @@ dependencies = [ "log", "portable-atomic", "portable-atomic-util", - "serde", - "windows-sys 0.59.0", + "serde_core", + "windows-sys 0.61.2", ] [[package]] name = "jiff-static" -version = "0.2.15" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4" +checksum = "a17c2b211d863c7fde02cbea8a3c1a439b98e109286554f2860bdded7ff83818" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] name = "jiff-tzdb" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1283705eb0a21404d2bfd6eef2a7593d240bc42a0bdb39db0ad6fa2ec026524" +checksum = "68971ebff725b9e2ca27a601c5eb38a4c5d64422c4cbab0c535f248087eda5c2" [[package]] name = "jiff-tzdb-platform" @@ -1034,14 +1059,31 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.81" +version = "0.3.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305" +checksum = "c7e709f3e3d22866f9c25b3aff01af289b18422cc8b4262fb19103ee80fe513d" dependencies = [ "once_cell", "wasm-bindgen", ] +[[package]] +name = "kasuari" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fe90c1150662e858c7d5f945089b7517b0a80d8bf7ba4b1b5ffc984e7230a5b" +dependencies = [ + "hashbrown 0.16.1", + "portable-atomic", + "thiserror 2.0.18", +] + +[[package]] +name = "lab" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf36173d4167ed999940f804952e6b08197cae5ad5d572eb4db150ce8ad5d58f" + [[package]] name = "lazy_static" version = "1.5.0" @@ -1049,111 +1091,175 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] -name = "libc" -version = "0.2.176" +name = "leb128fmt" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "libc" +version = "0.2.182" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" [[package]] name = "libredox" -version = "0.1.10" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" +checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" dependencies = [ - "bitflags", + "bitflags 2.11.0", "libc", ] [[package]] -name = "linux-raw-sys" -version = "0.4.15" +name = "line-clipping" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" +checksum = "5f4de44e98ddbf09375cbf4d17714d18f39195f4f4894e8524501726fd9a8a4a" +dependencies = [ + "bitflags 2.11.0", +] [[package]] name = "linux-raw-sys" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "litemap" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" [[package]] name = "litrs" -version = "0.4.2" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5e54036fe321fd421e10d732f155734c4e4afd610dd556d9a82833ab3ee0bed" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" [[package]] name = "lock_api" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" dependencies = [ - "autocfg", "scopeguard", ] [[package]] name = "log" -version = "0.4.28" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "lru" -version = "0.12.5" +version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" +checksum = "a1dc47f592c06f33f8e3aea9591776ec7c9f9e4124778ff8a3c3b87159f7e593" dependencies = [ - "hashbrown 0.15.5", + "hashbrown 0.16.1", +] + +[[package]] +name = "mac_address" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0aeb26bf5e836cc1c341c8106051b573f1766dfa05aa87f0b98be5e51b02303" +dependencies = [ + "nix", + "winapi", ] [[package]] name = "memchr" -version = "2.7.6" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] -name = "miniz_oxide" -version = "0.8.9" +name = "memmem" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +checksum = "a64a92489e2744ce060c349162be1c5f33c6969234104dbd99ddb5feb08b8c15" + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" dependencies = [ - "adler2", + "autocfg", ] [[package]] -name = "mio" -version = "1.0.4" +name = "minimal-lexical" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "mio" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" dependencies = [ "libc", "log", - "wasi 0.11.1+wasi-snapshot-preview1", - "windows-sys 0.59.0", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags 2.11.0", + "cfg-if", + "cfg_aliases", + "libc", + "memoffset", +] + +[[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 = "nu-ansi-term" -version = "0.50.1" +version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] name = "num-conv" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] [[package]] name = "num-traits" @@ -1165,12 +1271,12 @@ dependencies = [ ] [[package]] -name = "object" -version = "0.37.3" +name = "num_threads" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" dependencies = [ - "memchr", + "libc", ] [[package]] @@ -1181,9 +1287,9 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "once_cell_polyfill" -version = "1.70.1" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" [[package]] name = "option-ext" @@ -1191,15 +1297,24 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "ordered-float" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bb71e1b3fa6ca1c61f383464aaf2bb0e2f8e772a1f01d486832464de363b951" +dependencies = [ + "num-traits", +] + [[package]] name = "oxker" -version = "0.12.0" +version = "0.13.0" dependencies = [ "anyhow", "bollard", "cansi", "clap", - "crossterm 0.29.0", + "crossterm", "directories", "futures-util", "insta", @@ -1219,9 +1334,9 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.12.4" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" dependencies = [ "lock_api", "parking_lot_core", @@ -1229,29 +1344,118 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.11" +version = "0.9.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-targets 0.52.6", + "windows-link", ] -[[package]] -name = "paste" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" - [[package]] name = "percent-encoding" version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "pest" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0848c601009d37dfa3430c4666e147e49cdcf1b92ecd3e63657d8a5f19da662" +dependencies = [ + "memchr", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11f486f1ea21e6c10ed15d5a7c77165d0ee443402f0780849d1768e7d9d6fe77" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8040c4647b13b210a963c1ed407c1ff4fdfa01c31d6d2a098218702e6664f94f" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "pest_meta" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89815c69d36021a140146f26659a81d6c2afa33d216d736dd4be5381a7362220" +dependencies = [ + "pest", + "sha2", +] + +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_macros", + "phf_shared", +] + +[[package]] +name = "phf_codegen" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" +dependencies = [ + "phf_generator", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +dependencies = [ + "phf_shared", + "rand 0.8.5", +] + +[[package]] +name = "phf_macros" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher", +] + [[package]] name = "pin-project-lite" version = "0.2.16" @@ -1266,24 +1470,24 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "portable-atomic" -version = "1.11.1" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" [[package]] name = "portable-atomic-util" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +checksum = "7a9db96d7fa8782dd8c15ce32ffe8680bbd1e978a43bf51a34d39483540495f5" dependencies = [ "portable-atomic", ] [[package]] name = "potential_utf" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" dependencies = [ "zerovec", ] @@ -1304,19 +1508,29 @@ dependencies = [ ] [[package]] -name = "proc-macro2" -version = "1.0.101" +name = "prettyplease" +version = "0.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.117", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.40" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" dependencies = [ "proc-macro2", ] @@ -1327,6 +1541,15 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_core 0.6.4", +] + [[package]] name = "rand" version = "0.9.2" @@ -1334,7 +1557,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha", - "rand_core", + "rand_core 0.9.5", ] [[package]] @@ -1344,46 +1567,116 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.9.5", ] [[package]] name = "rand_core" -version = "0.9.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" dependencies = [ - "getrandom 0.3.3", + "getrandom 0.3.4", ] [[package]] name = "ratatui" -version = "0.29.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b" +checksum = "d1ce67fb8ba4446454d1c8dbaeda0557ff5e94d39d5e5ed7f10a65eb4c8266bc" dependencies = [ - "bitflags", - "cassowary", + "instability", + "ratatui-core", + "ratatui-crossterm", + "ratatui-macros", + "ratatui-termwiz", + "ratatui-widgets", +] + +[[package]] +name = "ratatui-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ef8dea09a92caaf73bff7adb70b76162e5937524058a7e5bff37869cbbec293" +dependencies = [ + "bitflags 2.11.0", "compact_str", - "crossterm 0.28.1", + "hashbrown 0.16.1", + "indoc", + "itertools", + "kasuari", + "lru", + "strum", + "thiserror 2.0.18", + "unicode-segmentation", + "unicode-truncate", + "unicode-width", +] + +[[package]] +name = "ratatui-crossterm" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "577c9b9f652b4c121fb25c6a391dd06406d3b092ba68827e6d2f09550edc54b3" +dependencies = [ + "cfg-if", + "crossterm", + "instability", + "ratatui-core", +] + +[[package]] +name = "ratatui-macros" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7f1342a13e83e4bb9d0b793d0ea762be633f9582048c892ae9041ef39c936f4" +dependencies = [ + "ratatui-core", + "ratatui-widgets", +] + +[[package]] +name = "ratatui-termwiz" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f76fe0bd0ed4295f0321b1676732e2454024c15a35d01904ddb315afd3d545c" +dependencies = [ + "ratatui-core", + "termwiz", +] + +[[package]] +name = "ratatui-widgets" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7dbfa023cd4e604c2553483820c5fe8aa9d71a42eea5aa77c6e7f35756612db" +dependencies = [ + "bitflags 2.11.0", + "hashbrown 0.16.1", "indoc", "instability", "itertools", - "lru", - "paste", + "line-clipping", + "ratatui-core", "strum", + "time", "unicode-segmentation", - "unicode-truncate", - "unicode-width 0.2.0", + "unicode-width", ] [[package]] name = "redox_syscall" -version = "0.5.17" +version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags", + "bitflags 2.11.0", ] [[package]] @@ -1392,61 +1685,60 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", "libredox", - "thiserror", + "thiserror 2.0.18", ] [[package]] -name = "ref-cast" -version = "1.0.24" +name = "regex" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" dependencies = [ - "ref-cast-impl", + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", ] [[package]] -name = "ref-cast-impl" -version = "1.0.24" +name = "regex-automata" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ - "proc-macro2", - "quote", - "syn", + "aho-corasick", + "memchr", + "regex-syntax", ] [[package]] -name = "rustc-demangle" -version = "0.1.26" +name = "regex-syntax" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" +checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" [[package]] -name = "rustix" -version = "0.38.44" +name = "rustc_version" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ - "bitflags", - "errno", - "libc", - "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", + "semver", ] [[package]] name = "rustix" -version = "1.1.2" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags", + "bitflags 2.11.0", "errno", "libc", - "linux-raw-sys 0.11.0", - "windows-sys 0.61.1", + "linux-raw-sys", + "windows-sys 0.61.2", ] [[package]] @@ -1457,33 +1749,9 @@ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" -version = "1.0.20" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" - -[[package]] -name = "schemars" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" -dependencies = [ - "dyn-clone", - "ref-cast", - "serde", - "serde_json", -] - -[[package]] -name = "schemars" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0" -dependencies = [ - "dyn-clone", - "ref-cast", - "serde", - "serde_json", -] +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" [[package]] name = "scopeguard" @@ -1491,6 +1759,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + [[package]] name = "serde" version = "1.0.228" @@ -1518,20 +1792,21 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] name = "serde_json" -version = "1.0.145" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ + "indexmap", "itoa", "memchr", - "ryu", "serde", "serde_core", + "zmij", ] [[package]] @@ -1553,14 +1828,14 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] name = "serde_spanned" -version = "1.0.2" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5417783452c2be558477e104686f7de5dae53dba813c28435e0e70f82d9b04ee" +checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" dependencies = [ "serde_core", ] @@ -1578,22 +1853,14 @@ dependencies = [ ] [[package]] -name = "serde_with" -version = "3.14.1" +name = "sha2" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c522100790450cf78eeac1507263d0a350d4d5b30df0c8e1fe051a10c22b376e" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ - "base64", - "chrono", - "hex", - "indexmap 1.9.3", - "indexmap 2.11.4", - "schemars 0.9.0", - "schemars 1.0.4", - "serde", - "serde_derive", - "serde_json", - "time", + "cfg-if", + "cpufeatures", + "digest", ] [[package]] @@ -1605,12 +1872,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - [[package]] name = "signal-hook" version = "0.3.18" @@ -1623,9 +1884,9 @@ dependencies = [ [[package]] name = "signal-hook-mio" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" +checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc" dependencies = [ "libc", "mio", @@ -1634,10 +1895,11 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.6" +version = "1.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" dependencies = [ + "errno", "libc", ] @@ -1648,10 +1910,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" [[package]] -name = "slab" -version = "0.4.11" +name = "siphasher" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" +checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "smallvec" @@ -1661,19 +1929,19 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "socket2" -version = "0.6.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "stable_deref_trait" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "static_assertions" @@ -1689,31 +1957,41 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "strum" -version = "0.26.3" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" dependencies = [ "strum_macros", ] [[package]] name = "strum_macros" -version = "0.26.4" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" dependencies = [ "heck", "proc-macro2", "quote", - "rustversion", - "syn", + "syn 2.0.117", ] [[package]] name = "syn" -version = "2.0.106" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", @@ -1728,27 +2006,123 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", +] + +[[package]] +name = "tempfile" +version = "3.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0136791f7c95b1f6dd99f9cc786b91bb81c3800b639b3478e561ddb7be95e5f1" +dependencies = [ + "fastrand", + "getrandom 0.4.1", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + +[[package]] +name = "terminfo" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4ea810f0692f9f51b382fff5893887bb4580f5fa246fde546e0b13e7fcee662" +dependencies = [ + "fnv", + "nom", + "phf", + "phf_codegen", +] + +[[package]] +name = "termios" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "411c5bf740737c7918b8b1fe232dca4dc9f8e754b8ad5e20966814001ed0ac6b" +dependencies = [ + "libc", +] + +[[package]] +name = "termwiz" +version = "0.23.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4676b37242ccbd1aabf56edb093a4827dc49086c0ffd764a5705899e0f35f8f7" +dependencies = [ + "anyhow", + "base64", + "bitflags 2.11.0", + "fancy-regex", + "filedescriptor", + "finl_unicode", + "fixedbitset", + "hex", + "lazy_static", + "libc", + "log", + "memmem", + "nix", + "num-derive", + "num-traits", + "ordered-float", + "pest", + "pest_derive", + "phf", + "sha2", + "signal-hook", + "siphasher", + "terminfo", + "termios", + "thiserror 1.0.69", + "ucd-trie", + "unicode-segmentation", + "vtparse", + "wezterm-bidi", + "wezterm-blob-leases", + "wezterm-color-types", + "wezterm-dynamic", + "wezterm-input-types", + "winapi", ] [[package]] name = "thiserror" -version = "2.0.16" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl 2.0.18", ] [[package]] name = "thiserror-impl" -version = "2.0.16" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", ] [[package]] @@ -1762,40 +2136,30 @@ dependencies = [ [[package]] name = "time" -version = "0.3.44" +version = "0.3.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" dependencies = [ "deranged", - "itoa", + "libc", "num-conv", + "num_threads", "powerfmt", - "serde", + "serde_core", "time-core", - "time-macros", ] [[package]] name = "time-core" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" - -[[package]] -name = "time-macros" -version = "0.2.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" -dependencies = [ - "num-conv", - "time-core", -] +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" [[package]] name = "tinystr" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" dependencies = [ "displaydoc", "zerovec", @@ -1803,40 +2167,37 @@ dependencies = [ [[package]] name = "tokio" -version = "1.47.1" +version = "1.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" +checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" dependencies = [ - "backtrace", "bytes", - "io-uring", "libc", "mio", "parking_lot", "pin-project-lite", "signal-hook-registry", - "slab", "socket2", "tokio-macros", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "tokio-macros" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] name = "tokio-util" -version = "0.7.16" +version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" dependencies = [ "bytes", "futures-core", @@ -1847,9 +2208,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.9.7" +version = "1.0.3+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00e5e5d9bf2475ac9d4f0d9edab68cc573dc2fd644b0dba36b0c30a92dd9eaa0" +checksum = "c7614eaf19ad818347db24addfa201729cf2a9b6fdfd9eb0ab870fcacc606c0c" dependencies = [ "serde_core", "serde_spanned", @@ -1860,18 +2221,18 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.7.2" +version = "1.0.0+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f1085dec27c2b6632b04c80b3bb1b4300d6495d1e129693bdda7d91e72eec1" +checksum = "32c2555c699578a4f59f0cc68e5116c8d7cabbd45e1409b989d4be085b53f13e" dependencies = [ "serde_core", ] [[package]] name = "toml_parser" -version = "1.0.3" +version = "1.0.9+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cf893c33be71572e0e9aa6dd15e6677937abd686b066eac3f8cd3531688a627" +checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4" dependencies = [ "winnow", ] @@ -1884,9 +2245,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.41" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ "pin-project-lite", "tracing-attributes", @@ -1895,20 +2256,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] name = "tracing-core" -version = "0.1.34" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", "valuable", @@ -1927,9 +2288,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.20" +version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" +checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" dependencies = [ "nu-ansi-term", "sharded-slab", @@ -1946,16 +2307,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] -name = "unicase" -version = "2.8.1" +name = "typenum" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + +[[package]] +name = "unicase" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" [[package]] name = "unicode-ident" -version = "1.0.19" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "unicode-segmentation" @@ -1965,32 +2338,32 @@ checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "unicode-truncate" -version = "1.1.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf" +checksum = "16b380a1238663e5f8a691f9039c73e1cdae598a30e9855f541d29b08b53e9a5" dependencies = [ "itertools", "unicode-segmentation", - "unicode-width 0.1.14", + "unicode-width", ] [[package]] name = "unicode-width" -version = "0.1.14" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" [[package]] -name = "unicode-width" -version = "0.2.0" +name = "unicode-xid" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "url" -version = "2.5.7" +version = "2.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" dependencies = [ "form_urlencoded", "idna", @@ -2012,13 +2385,14 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.18.1" +version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" +checksum = "b672338555252d43fd2240c714dc444b8c6fb0a5c5335e65a07bba7742735ddb" dependencies = [ - "getrandom 0.3.3", + "atomic", + "getrandom 0.4.1", "js-sys", - "rand", + "rand 0.9.2", "wasm-bindgen", ] @@ -2028,6 +2402,21 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "vtparse" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d9b2acfb050df409c972a37d3b8e08cdea3bddb0c09db9d53137e504cfabed0" +dependencies = [ + "utf8parse", +] + [[package]] name = "want" version = "0.3.1" @@ -2044,28 +2433,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] -name = "wasi" -version = "0.14.7+wasi-0.2.4" +name = "wasip2" +version = "1.0.2+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" dependencies = [ - "wasip2", + "wit-bindgen", ] [[package]] -name = "wasip2" -version = "1.0.1+wasi-0.2.4" +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" dependencies = [ "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.104" +version = "0.2.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1da10c01ae9f1ae40cbfac0bac3b1e724b320abfcf52229f80b547c0d250e2d" +checksum = "ec1adf1535672f5b7824f817792b1afd731d7e843d2d04ec8f27e8cb51edd8ac" dependencies = [ "cfg-if", "once_cell", @@ -2074,25 +2463,11 @@ dependencies = [ "wasm-bindgen-shared", ] -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.104" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "671c9a5a66f49d8a47345ab942e2cb93c7d1d0339065d4f8139c486121b43b19" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - [[package]] name = "wasm-bindgen-macro" -version = "0.2.104" +version = "0.2.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ca60477e4c59f5f2986c50191cd972e3a50d8a95603bc9434501cf156a9a119" +checksum = "19e638317c08b21663aed4d2b9a2091450548954695ff4efa75bff5fa546b3b1" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2100,26 +2475,132 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.104" +version = "0.2.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7" +checksum = "2c64760850114d03d5f65457e96fc988f11f01d38fbaa51b254e4ab5809102af" dependencies = [ + "bumpalo", "proc-macro2", "quote", - "syn", - "wasm-bindgen-backend", + "syn 2.0.117", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.104" +version = "0.2.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1" +checksum = "60eecd4fe26177cfa3339eb00b4a36445889ba3ad37080c2429879718e20ca41" dependencies = [ "unicode-ident", ] +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags 2.11.0", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "wezterm-bidi" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0a6e355560527dd2d1cf7890652f4f09bb3433b6aadade4c9b5ed76de5f3ec" +dependencies = [ + "log", + "wezterm-dynamic", +] + +[[package]] +name = "wezterm-blob-leases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692daff6d93d94e29e4114544ef6d5c942a7ed998b37abdc19b17136ea428eb7" +dependencies = [ + "getrandom 0.3.4", + "mac_address", + "sha2", + "thiserror 1.0.69", + "uuid", +] + +[[package]] +name = "wezterm-color-types" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7de81ef35c9010270d63772bebef2f2d6d1f2d20a983d27505ac850b8c4b4296" +dependencies = [ + "csscolorparser", + "deltae", + "lazy_static", + "wezterm-dynamic", +] + +[[package]] +name = "wezterm-dynamic" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f2ab60e120fd6eaa68d9567f3226e876684639d22a4219b313ff69ec0ccd5ac" +dependencies = [ + "log", + "ordered-float", + "strsim", + "thiserror 1.0.69", + "wezterm-dynamic-derive", +] + +[[package]] +name = "wezterm-dynamic-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c0cf2d539c645b448eaffec9ec494b8b19bd5077d9e58cb1ae7efece8d575b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "wezterm-input-types" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7012add459f951456ec9d6c7e6fc340b1ce15d6fc9629f8c42853412c029e57e" +dependencies = [ + "bitflags 1.3.2", + "euclid", + "lazy_static", + "serde", + "wezterm-dynamic", +] + [[package]] name = "winapi" version = "0.3.9" @@ -2142,73 +2623,11 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-core" -version = "0.62.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6844ee5416b285084d3d3fffd743b925a6c9385455f64f6d4fa3031c4c2749a9" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-link", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-implement" -version = "0.60.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edb307e42a74fb6de9bf3a02d9712678b22399c87e6fa869d6dfcd8c1b7754e0" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "windows-interface" -version = "0.59.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0abd1ddbc6964ac14db11c7213d6532ef34bd9aa042c2e5935f59d7908b46a5" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "windows-link" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" - -[[package]] -name = "windows-result" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7084dcc306f89883455a206237404d3eaf961e5bd7e0f312f7c91f57eb44167f" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7218c655a553b0bed4426cf54b20d7ba363ef543b52d515b3e48d7fd55318dda" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets 0.52.6", -] +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-sys" @@ -2225,14 +2644,14 @@ version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.53.4", + "windows-targets 0.53.5", ] [[package]] name = "windows-sys" -version = "0.61.1" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f109e41dd4a3c848907eb83d5a42ea98b3769495597450cf6d153507b166f0f" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ "windows-link", ] @@ -2255,19 +2674,19 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.4" +version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d42b7b7f66d2a06854650af09cfdf8713e427a439c97ad65a6375318033ac4b" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ "windows-link", - "windows_aarch64_gnullvm 0.53.0", - "windows_aarch64_msvc 0.53.0", - "windows_i686_gnu 0.53.0", - "windows_i686_gnullvm 0.53.0", - "windows_i686_msvc 0.53.0", - "windows_x86_64_gnu 0.53.0", - "windows_x86_64_gnullvm 0.53.0", - "windows_x86_64_msvc 0.53.0", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", ] [[package]] @@ -2278,9 +2697,9 @@ checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" [[package]] name = "windows_aarch64_msvc" @@ -2290,9 +2709,9 @@ checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_aarch64_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" [[package]] name = "windows_i686_gnu" @@ -2302,9 +2721,9 @@ checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnu" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" [[package]] name = "windows_i686_gnullvm" @@ -2314,9 +2733,9 @@ checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" [[package]] name = "windows_i686_msvc" @@ -2326,9 +2745,9 @@ checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_i686_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" [[package]] name = "windows_x86_64_gnu" @@ -2338,9 +2757,9 @@ checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnu" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" [[package]] name = "windows_x86_64_gnullvm" @@ -2350,9 +2769,9 @@ checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" [[package]] name = "windows_x86_64_msvc" @@ -2362,35 +2781,116 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "windows_x86_64_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "winnow" -version = "0.7.13" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" [[package]] name = "wit-bindgen" -version = "0.46.0" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn 2.0.117", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn 2.0.117", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags 2.11.0", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] [[package]] name = "writeable" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" [[package]] name = "yoke" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" dependencies = [ - "serde", "stable_deref_trait", "yoke-derive", "zerofrom", @@ -2398,34 +2898,34 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", "synstructure", ] [[package]] name = "zerocopy" -version = "0.8.27" +version = "0.8.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +checksum = "db6d35d663eadb6c932438e763b262fe1a70987f9ae936e60158176d710cae4a" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.27" +version = "0.8.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -2445,15 +2945,15 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", "synstructure", ] [[package]] name = "zerotrie" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" dependencies = [ "displaydoc", "yoke", @@ -2462,9 +2962,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.4" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" dependencies = [ "yoke", "zerofrom", @@ -2473,11 +2973,17 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/Cargo.toml b/Cargo.toml index ed71efa..1125a77 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "oxker" -version = "0.12.0" +version = "0.13.0" edition = "2024" authors = ["Jack Wills "] description = "A simple tui to view & control docker containers" repository = "https://github.com/mrjackwills/oxker" homepage = "https://github.com/mrjackwills/oxker" license = "MIT" +rust-version = "1.90.0" readme = "README.md" keywords = ["docker", "tui", "tokio", "terminal", "podman"] categories = ["command-line-utilities"] @@ -15,8 +16,6 @@ categories = ["command-line-utilities"] unsafe_code = "forbid" [lints.clippy] -nursery = { level = "warn", priority = -1 } -pedantic = { level = "warn", priority = -1 } expect_used = "warn" todo = "warn" unused_async = "warn" @@ -27,7 +26,7 @@ similar_names = "allow" [dependencies] anyhow = "1.0" -bollard = "0.19" +bollard = "0.20" cansi = "2.2" clap = { version = "4.5", features = ["color", "derive", "unicode"] } crossterm = "0.29" @@ -35,16 +34,20 @@ directories = "6.0" futures-util = "0.3" jiff = { version = "0.2", features = ["tzdb-bundle-always"] } parking_lot = { version = "0.12" } -ratatui = "0.29" +ratatui = "0.30" serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" +serde_json = { version = "1.0"} serde_jsonc = "1.0" -tokio = { version = "1.47", features = ["full"] } +tokio = { version = "1.49", features = ["full"] } tokio-util = "0.7" -toml = { version = "0.9", default-features = false, features = ["parse", "serde"] } +toml = { version = "1.0", default-features = false, features = ["parse", "serde"] } tracing = "0.1" tracing-subscriber = "0.3" -uuid = { version = "1.18", features = ["fast-rng", "v4"] } +uuid = { version = "1.21", features = ["fast-rng", "v4"] } + +[dev-dependencies] +insta = "1.46" +serde_json = { version = "1.0", features = ["preserve_order"]} [profile.release] lto = true @@ -53,5 +56,4 @@ panic = 'abort' strip = true debug = false -[dev-dependencies] -insta = "1.42.2" + diff --git a/README.md b/README.md index 7aaaa01..394121f 100644 --- a/README.md +++ b/README.md @@ -10,10 +10,12 @@

+ An animated demo of oxker in operation + link to alternative screenshot @@ -111,7 +113,7 @@ In application controls, these, amongst many other settings, can be customized w |--|--| | ```( tab )``` or ```( shift+tab )``` | Change panel, clicking on a panel also changes the selected panel.| | ```( โ†‘ โ†“ )``` or ```( j k )``` or ```( Home End )```| Scroll line in selected panel - mouse wheel will also scroll.| -| ```( โ† โ†’ )``` | When logs panel selected, scroll horizontally across the text of the logs.| +| ```( โ† โ†’ )``` | Scroll horizontally across text.| | ```( ctrl )``` | Increase scroll speed, used in conjunction with scroll keys.| | ```( enter )```| Run selected docker command.| | ```( 1-9 )``` | Sort containers by heading, clicking on headings also sorts the selected column. | @@ -121,6 +123,7 @@ In application controls, these, amongst many other settings, can be customized w | ```( - ) ``` or ```(=)``` | Reduce or increase the height of the logs panel.| | ```( \ )``` | Toggle the visibility of the logs panel.| | ```( e )``` | Exec into the selected container - not available on Windows.| +| ```( i )``` | Enter container inspect mode. | | ```( f )``` | Force clear the screen & redraw the gui.| | ```( h )``` | Toggle help menu.| | ```( m )``` | Toggle mouse capture - if disabled, text on screen can be selected.| diff --git a/containerised/DOCKERHUB_README.md b/containerised/DOCKERHUB_README.md index d073b02..4a0b238 100644 --- a/containerised/DOCKERHUB_README.md +++ b/containerised/DOCKERHUB_README.md @@ -8,9 +8,15 @@

- - + + +
+ + + link to alternative screenshot + +

## Run diff --git a/containerised/Dockerfile b/containerised/Dockerfile index e05760a..0f93266 100644 --- a/containerised/Dockerfile +++ b/containerised/Dockerfile @@ -6,7 +6,7 @@ FROM --platform=$BUILDPLATFORM rust:slim AS builder ARG TARGETARCH -# These are build platform depandant, but will be ignored if not needed +# These are build platform dependant, but will be ignored if not needed ENV CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER="aarch64-linux-gnu-gcc" ENV CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_RUSTFLAGS="-C target-feature=+crt-static -C link-arg=-lgcc" ENV CARGO_TARGET_ARM_UNKNOWN_LINUX_MUSLEABIHF_LINKER="arm-linux-gnueabihf-ld" diff --git a/create_release.sh b/create_release.sh index 8b50e64..393ccbf 100755 --- a/create_release.sh +++ b/create_release.sh @@ -232,8 +232,12 @@ cross_build_x86_windows() { # Build, using zig-build, for Apple silicon zig_build_aarch64_apple() { # mkdir /workspace/oxker/target - echo -e "${YELLOW}docker run --rm -v $(pwd):/io -w /io ghcr.io/rust-cross/cargo-zigbuild cargo zigbuild --release --target aarch64-apple-darwin${RESET}" - docker run --rm -v "$(pwd):/io" -w /io ghcr.io/rust-cross/cargo-zigbuild cargo zigbuild --release --target aarch64-apple-darwin + echo -e "${YELLOW}docker run --rm -v $(pwd):/io -w /io ghcr.io/rust-cross/cargo-zigbuild bash -e -c 'rustup update stable && rustup default stable && rustup target add aarch64-apple-darwin && cargo zigbuild --release --target aarch64-apple-darwin${RESET}" + + docker run --rm -v "$(pwd):/io" -w /io \ + ghcr.io/rust-cross/cargo-zigbuild \ + bash -ec 'rustup update stable && rustup default stable && rustup target add aarch64-apple-darwin && cargo zigbuild --release --target aarch64-apple-darwin' + if ask_yn "sudo chown $(pwd)/target"; then echo -e "${YELLOW}sudo chown -R vscode:vscode $(pwd)/target${RESET}" sudo chown -R vscode:vscode "$(pwd)/target" diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 7c58aac..2ef1418 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -4,7 +4,7 @@ networks: name: oxker-examaple-net services: postgres: - image: postgres:17-alpine + image: postgres:18-alpine container_name: postgres environment: - POSTGRES_PASSWORD=never_use_this_password_in_production diff --git a/example_config/example.config.jsonc b/example_config/example.config.jsonc index 3b1f8fd..db1251f 100644 --- a/example_config/example.config.jsonc +++ b/example_config/example.config.jsonc @@ -18,8 +18,8 @@ "show_timestamp": true, // Don't draw gui - for debugging - mostly pointless "gui": true, - // Docker host location - "host": "/var/run/docker.sock", + // Docker host location. Will take priority over a DOCKER_HOST env. + // "host": "/var/run/docker.sock", // Display the timestamp in a custom format, if given option is invalid, it will default to %Y-%m-%dT%H:%M:%S.%8f -> 2025-02-18T12:34:56.01234567 // *Should* accept any valid strftime string up to 32 chars, see https://strftime.org/ "timestamp_format": "%Y-%m-%dT%H:%M:%S.%8f", @@ -104,10 +104,10 @@ "k" ], // Horizontal scroll of the logs - "log_scroll_forward": [ + "scroll_forward": [ "right" ], - "log_scroll_back": [ + "scroll_back": [ "left" ], // Select next panel @@ -170,6 +170,10 @@ "log_section_toggle": [ "\\" ], + // Toggle to inspect container screen + "inspect": [ + "i" + ], // Force a complete clear & redraw of the screen "force_redraw": [ "f" @@ -280,6 +284,27 @@ // Ports & IP listing text "text": "white" }, + // The bandwidth chart + "chart_bandwidth": { + //Background color of panel + "background": "reset", + // Border color + "border": "white", + // Maximum RX value - again paused & stopped colors not yet customizable + "max_rx": "#FFE9C1", + // # Maximum TX value - again paused & stopped colors not yet customizable + "max_tx": "#CD8C8C", + // RX points on the chart - again paused & stopped colors not yet customizable + "points_rx": "#FFE9C1", + // TX points on the chart - again paused & stopped colors not yet customizable + "points_tx": "#CD8C8C", + // RX title color + "title_rx": "#FFE9C1", + // TX title color + "title_tx": "#CD8C8C", + // The charts y-axis + "y_axis": "white" + }, // The filter panel "filter": { // Background color of panel diff --git a/example_config/example.config.toml b/example_config/example.config.toml index 8d5f1db..1b1a0c1 100644 --- a/example_config/example.config.toml +++ b/example_config/example.config.toml @@ -24,8 +24,8 @@ show_timestamp = true # Don't draw gui - for debugging - mostly pointless gui = true -# Docker host location -host = "/var/run/docker.sock" +# Docker host location. Will take priority over a DOCKER_HOST env. +# host = "/var/run/docker.sock" # Display the container logs timestamp with a given timezone, if timezone is unknown, defaults to UTC timezone = "Etc/UTC" @@ -95,8 +95,8 @@ scroll_start = ["home"] # scroll up a list by one item scroll_up = ["up", "k"] # Horizontal scroll of the logs -log_scroll_forward = ["right"] -log_scroll_back = ["left"] +scroll_forward = ["right"] +scroll_back = ["left"] # Select next panel select_next_panel = ["tab"] # Select previous panel @@ -122,6 +122,8 @@ log_section_height_decrease = ["-"] log_section_height_increase = ["+"] # Toggle visibility of the log section log_section_toggle = ["\\"] +# Toggle to inspect container screen +inspect = ["i"] @@ -254,6 +256,27 @@ points = "cyan" # The charts y-axis y_axis = "white" +# The bandwidth chart +[colors.chart_bandwidth] +# Background color of panel +background = "reset" +# Border color +border = "white" +# Maximum RX value - again paused & stopped colors not yet customizable +max_rx = "#FFE9C1" +# Maximum TX value - again paused & stopped colors not yet customizable +max_tx = "#CD8C8C" +# RX points on the chart - again paused & stopped colors not yet customizable +points_rx = "#FFE9C1" +# TX points on the chart - again paused & stopped colors not yet customizable +points_tx = "#CD8C8C" +# TX title color +title_tx = "#FFE9C1" +# RX title color +title_rx = "#CD8C8C" +# The charts y-axis +y_axis = "white" + # The ports chart [colors.chart_ports] # Background color of panel diff --git a/src/app_data/container_state.rs b/src/app_data/container_state.rs index 11e39b3..252d982 100644 --- a/src/app_data/container_state.rs +++ b/src/app_data/container_state.rs @@ -5,7 +5,7 @@ use std::{ net::IpAddr, }; -use bollard::service::Port; +use bollard::secret::{ContainerSummaryHealthStatusEnum, PortSummary}; use jiff::{Timestamp, tz::TimeZone}; use ratatui::{ layout::Size, @@ -24,8 +24,12 @@ const ONE_GB: f64 = ONE_MB * 1000.0; #[derive(Debug, Clone, Eq, Hash, PartialEq)] pub enum ScrollDirection { - Next, - Previous, + // Next, + // Previous, + Up, + Down, + Left, + Right, } #[derive(Debug, Clone, Eq, Hash, PartialEq)] @@ -121,8 +125,8 @@ pub struct ContainerPorts { pub public: Option, } -impl From for ContainerPorts { - fn from(value: Port) -> Self { +impl From for ContainerPorts { + fn from(value: PortSummary) -> Self { Self { ip: value.ip.and_then(|i| i.parse::().ok()), private: value.private_port, @@ -185,8 +189,10 @@ impl StatefulList { pub fn scroll(&mut self, scroll: &ScrollDirection) { match scroll { - ScrollDirection::Next => self.next(), - ScrollDirection::Previous => self.previous(), + ScrollDirection::Down => self.next(), + ScrollDirection::Up => self.previous(), + // TODO set offset + _ => (), } } @@ -414,7 +420,7 @@ impl fmt::Display for State { } /// Items for the container control list -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum DockerCommand { Pause, Restart, @@ -570,8 +576,116 @@ impl fmt::Display for ByteStats { } } -pub type MemTuple = (Vec<(f64, f64)>, ByteStats, State); -pub type CpuTuple = (Vec<(f64, f64)>, CpuStats, State); +#[derive(Debug, Default, Clone, Copy, Eq)] +pub struct BandwidthStat(u64); + +#[cfg(test)] +impl BandwidthStat { + pub fn new(x: u64) -> Self { + Self(x) + } +} + +impl PartialEq for BandwidthStat { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} + +impl PartialOrd for BandwidthStat { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for BandwidthStat { + fn cmp(&self, other: &Self) -> Ordering { + self.0.cmp(&other.0) + } +} + +#[allow(clippy::cast_precision_loss)] +impl Stats for BandwidthStat { + fn get_value(&self) -> f64 { + self.0 as f64 + } +} + +/// convert from bytes to per second, using 1000 instead of 1024 +impl fmt::Display for BandwidthStat { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let as_f64 = self.get_value(); + let p = match as_f64 { + x if x >= ONE_GB => format!("{y:.2} GB/s", y = as_f64 / ONE_GB), + x if x >= ONE_MB => format!("{y:.2} Mb/s", y = as_f64 / ONE_MB), + _ => format!("{y:.2} kb/s", y = as_f64 / ONE_KB), + }; + write!(f, "{p:>x$}", x = f.width().unwrap_or(1)) + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct NetworkBandwidth(VecDeque); + +impl NetworkBandwidth { + pub fn new() -> Self { + Self(VecDeque::with_capacity(60)) + } + + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + /// Find the highest speed recorded in the vecque + pub fn max(&self) -> BandwidthStat { + self.to_vec_f64() + .iter() + .map(|(_, speed)| *speed) + .max_by(|a, b| a.total_cmp(b)) + .map(|m| BandwidthStat(m as u64)) + .unwrap_or(BandwidthStat(0)) + } + + pub fn push(&mut self, x: u64) { + if self.0.len() >= 60 { + self.0.pop_front(); + } + self.0.push_back(BandwidthStat(x)); + } + + /// Get the current total amount of traffic on a given device + pub fn current_total(&self) -> ByteStats { + self.0 + .back() + .map_or(ByteStats::default(), |i| ByteStats::new(i.0)) + } + + /// Convert to f64 for use in the network graph + pub fn to_vec_f64(&self) -> Vec<(f64, f64)> { + self.0 + .iter() + .zip(self.0.iter().skip(1)) + .enumerate() + .map(|(i, (prev, current))| (i as f64, current.0.saturating_sub(prev.0) as f64)) + .collect() + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct ChartsData { + pub memory: ChartSeries, + pub cpu: ChartSeries, + pub rx: ChartSeries, + pub tx: ChartSeries, + pub state: State, +} + +#[derive(Debug, Clone, PartialEq)] +pub struct ChartSeries { + pub dataset: Vec<(f64, f64)>, + pub max: T, + pub current: T, +} /// Used to make sure that each log entry, for each container, is unique, /// will only push a log entry into the logs vec if timestamp of said log entry isn't in the hashset @@ -615,7 +729,8 @@ pub struct Logs { tz: HashSet, search_results: Vec, search_term: Option, - offset: u16, + offset: usize, + max_offset: usize, max_log_len: usize, adjusted_max_width: usize, adjust_max_width_text_len: usize, @@ -629,6 +744,7 @@ impl Default for Logs { lines, tz: HashSet::new(), offset: 0, + max_offset: 0, search_term: None, search_results: vec![], adjusted_max_width: 0, @@ -687,23 +803,26 @@ impl Logs { .position(|i| i == ¤t_selected) { if let Some(new_index) = match sd { - ScrollDirection::Next => current_position.checked_add(1), - ScrollDirection::Previous => current_position.checked_sub(1), - } { - if let Some(f) = self.search_results.get(new_index) { - self.lines.state.select(Some(*f)); - return Some(()); - } + ScrollDirection::Down => current_position.checked_add(1), + ScrollDirection::Up => current_position.checked_sub(1), + // TODO set offset + _ => None, + } && let Some(f) = self.search_results.get(new_index) + { + self.lines.state.select(Some(*f)); + return Some(()); } } else { let range = match sd { - ScrollDirection::Previous => (0..=current_selected).rev().collect::>(), - ScrollDirection::Next => (current_selected + ScrollDirection::Up => (0..=current_selected).rev().collect::>(), + ScrollDirection::Down => (current_selected ..=self .search_results .last() .map_or_else(|| current_selected, |i| *i)) .collect::>(), + // TODO set offset + _ => vec![], }; for i in range { if self.search_results.contains(&i) { @@ -821,7 +940,7 @@ impl Logs { if self.horizontal_scroll_able(width) { let text_width = self.adjust_max_width_text_len; let arrow_left = if self.offset > 0 { " โ†" } else { " " }; - let arrow_right = if usize::from(self.offset) < self.adjusted_max_width { + let arrow_right = if self.offset < self.adjusted_max_width { "โ†’ " } else { " " @@ -884,10 +1003,10 @@ impl Logs { pub fn get_visible_logs(&self, size: Size, padding: usize) -> Vec> { let current_index = self.lines.state.selected().unwrap_or_default(); let height_padding = usize::from(size.height) + padding; - let char_offset = if usize::from(self.offset) > self.max_log_len { + let char_offset = if self.offset > self.max_log_len { self.max_log_len } else { - self.offset.into() + self.offset }; self.lines @@ -921,11 +1040,12 @@ impl Logs { /// Add a padding so one char will always be visilbe? pub fn forward(&mut self, width: u16) { - let offset = usize::from(self.offset); - if self.horizontal_scroll_able(width) { - if self.adjusted_max_width > 0 && offset < self.adjusted_max_width { - self.offset = self.offset.saturating_add(1); - } + // Need to set a max_offset, instead of using a width each time + if self.horizontal_scroll_able(width) + && self.adjusted_max_width > 0 + && self.offset < self.adjusted_max_width + { + self.offset = self.offset.saturating_add(1); } } @@ -970,6 +1090,7 @@ pub struct ContainerItem { pub cpu_stats: VecDeque, pub created: u64, pub docker_controls: StatefulList, + pub health: Option, pub id: ContainerId, pub image: ContainerImage, pub is_oxker: bool, @@ -979,10 +1100,10 @@ pub struct ContainerItem { pub mem_stats: VecDeque, pub name: ContainerName, pub ports: Vec, - pub rx: ByteStats, + pub rx: NetworkBandwidth, pub state: State, pub status: ContainerStatus, - pub tx: ByteStats, + pub tx: NetworkBandwidth, } /// Basic display information, for when running in debug mode @@ -1019,6 +1140,7 @@ impl ContainerItem { cpu_stats: VecDeque::with_capacity(60), created, docker_controls, + health: None, id, image: image.into(), is_oxker, @@ -1028,10 +1150,10 @@ impl ContainerItem { mem_stats: VecDeque::with_capacity(60), name: name.into(), ports, - rx: ByteStats::default(), + rx: NetworkBandwidth::new(), state, status, - tx: ByteStats::default(), + tx: NetworkBandwidth::new(), } } @@ -1057,7 +1179,7 @@ impl ContainerItem { self.cpu_stats .iter() .enumerate() - .map(|i| (i.0 as f64, i.1.0)) + .map(|(i, v)| (i as f64, v.0)) .collect::>() } @@ -1067,24 +1189,65 @@ impl ContainerItem { self.mem_stats .iter() .enumerate() - .map(|i| (i.0 as f64, i.1.0 as f64)) + .map(|(i, v)| (i as f64, v.0 as f64)) .collect::>() } /// Get all cpu chart data - fn get_cpu_chart_data(&self) -> CpuTuple { - (self.get_cpu_dataset(), self.max_cpu_stats(), self.state) + fn get_cpu_chart_data(&self) -> ChartSeries { + ChartSeries { + dataset: self.get_cpu_dataset(), + max: self.max_cpu_stats(), + current: self + .cpu_stats + .back() + .map_or_else(|| CpuStats::new(0.0), |i| *i), + } } /// Get all mem chart data - fn get_mem_chart_data(&self) -> MemTuple { - (self.get_mem_dataset(), self.max_mem_stats(), self.state) + fn get_mem_chart_data(&self) -> ChartSeries { + ChartSeries { + dataset: self.get_mem_dataset(), + max: self.max_mem_stats(), + current: self + .mem_stats + .back() + .map_or_else(|| ByteStats::new(0), |i| *i), + } + } + + /// Get all mem chart data + /// Don't understand what we are doing here + fn get_bandwidth_chart_tx_data(&self) -> ChartSeries { + let data = self.tx.to_vec_f64(); + ChartSeries { + current: BandwidthStat(data.last().map_or(0, |i| i.1 as u64)), + dataset: data, + max: self.tx.max(), + } + } + + /// Get all mem chart data + fn get_bandwidth_chart_rx_data(&self) -> ChartSeries { + let data = self.rx.to_vec_f64(); + ChartSeries { + current: BandwidthStat(data.last().map_or(0, |i| i.1 as u64)), + dataset: data, + max: self.rx.max(), + } } /// Get chart info for cpu & memory in one function /// So only need to call .lock() once - pub fn get_chart_data(&self) -> (CpuTuple, MemTuple) { - (self.get_cpu_chart_data(), self.get_mem_chart_data()) + pub fn get_chart_data(&self) -> ChartsData { + ChartsData { + memory: self.get_mem_chart_data(), + cpu: self.get_cpu_chart_data(), + rx: self.get_bandwidth_chart_rx_data(), + tx: self.get_bandwidth_chart_tx_data(), + state: self.state, + } } } diff --git a/src/app_data/mod.rs b/src/app_data/mod.rs index 1139ad6..9e05986 100644 --- a/src/app_data/mod.rs +++ b/src/app_data/mod.rs @@ -1,4 +1,4 @@ -use bollard::models::ContainerSummary; +use bollard::{models::ContainerSummary, secret::ContainerInspectResponse}; use core::fmt; use parking_lot::Mutex; use ratatui::{layout::Size, text::Text, widgets::ListState}; @@ -114,6 +114,45 @@ impl Filter { } } +#[derive(Debug, Clone)] +pub struct InspectData { + pub width: usize, + pub height: usize, + pub as_string: String, + pub name: String, + pub id: ContainerId, // pub as_lines: Vec>, +} + +impl From for InspectData { + fn from(input: ContainerInspectResponse) -> Self { + let as_string = serde_json::to_string_pretty(&input) + .unwrap_or_default() + .lines() + .skip(1) + .collect::>() + .split_last() + .map(|(_, data)| data) + .unwrap_or_default() + .join("\n"); + + let height = as_string.lines().count(); + + let mut width = 0; + for i in as_string.lines() { + width = width.max(i.chars().count()); + } + + Self { + name: input.name.unwrap_or_default(), + // TODO maybe make this an Option? + id: ContainerId::from(input.id.unwrap_or_default().as_str()), + width, + height, + as_string, + } + } +} + /// Global app_state, stored in an Arc #[derive(Debug, Clone)] #[cfg(not(test))] @@ -122,6 +161,7 @@ pub struct AppData { error: Option, filter: Filter, hidden_containers: Vec, + inspect_data: Option, rerender: Arc, sorted_by: Option<(Header, SortedOrder)>, current_sorted_id: Vec, @@ -136,6 +176,7 @@ pub struct AppData { pub error: Option, pub filter: Filter, pub hidden_containers: Vec, + pub inspect_data: Option, pub current_sorted_id: Vec, pub rerender: Arc, pub sorted_by: Option<(Header, SortedOrder)>, @@ -151,6 +192,7 @@ impl AppData { error: None, filter: Filter::new(), hidden_containers: vec![], + inspect_data: None, rerender: Arc::clone(redraw), sorted_by: None, } @@ -165,6 +207,18 @@ impl AppData { .as_secs() } + pub fn clear_inspect_data(&mut self) { + self.inspect_data = None; + } + + pub fn set_inspect_data(&mut self, data: ContainerInspectResponse) { + self.inspect_data = Some(InspectData::from(data)) + // self.inspect_data = Some(data) + } + + pub fn get_inspect_data(&self) -> Option { + self.inspect_data.clone() + } /// Filter related methods /// Get the filterby and filter_term pub const fn get_filter(&self) -> (FilterBy, Option<&String>) { @@ -172,10 +226,10 @@ impl AppData { } pub fn log_search_scroll(&mut self, np: &ScrollDirection) { - if let Some(i) = self.get_mut_selected_container() { - if i.logs.search_scroll(np).is_some() { - self.rerender.update_draw(); - } + if let Some(i) = self.get_mut_selected_container() + && i.logs.search_scroll(np).is_some() + { + self.rerender.update_draw(); } } @@ -329,6 +383,7 @@ impl AppData { .iter() .position(|i| self.get_selected_container_id().as_ref() == Some(&i.id)), ); + self.rerender.update_draw(); } /// Remove the sorted header & order, and sort by default - created datetime @@ -340,12 +395,12 @@ impl AppData { /// Sort containers based on a given header, if headings match, and already ascending, remove sorting pub fn set_sort_by_header(&mut self, selected_header: Header) { let mut output = Some((selected_header, SortedOrder::Asc)); - if let Some((current_header, order)) = self.get_sorted() { - if current_header == selected_header { - match order { - SortedOrder::Desc => output = None, - SortedOrder::Asc => output = Some((selected_header, SortedOrder::Desc)), - } + if let Some((current_header, order)) = self.get_sorted() + && current_header == selected_header + { + match order { + SortedOrder::Desc => output = None, + SortedOrder::Asc => output = Some((selected_header, SortedOrder::Desc)), } } self.set_sorted(output); @@ -412,12 +467,14 @@ impl AppData { Header::Rx => item_ord .0 .rx - .cmp(&item_ord.1.rx) + .current_total() + .cmp(&item_ord.1.rx.current_total()) .then_with(|| item_ord.0.name.get().cmp(item_ord.1.name.get())), Header::Tx => item_ord .0 .tx - .cmp(&item_ord.1.tx) + .current_total() + .cmp(&item_ord.1.tx.current_total()) .then_with(|| item_ord.0.name.get().cmp(item_ord.1.name.get())), Header::Name => item_ord .0 @@ -459,7 +516,6 @@ impl AppData { /// Get all the ContainerItems /// Thnk this allow block can be removed with the 1.87 release of Clippy - #[allow(clippy::missing_const_for_fn)] pub fn get_container_items(&self) -> &[ContainerItem] { &self.containers.items } @@ -556,10 +612,17 @@ impl AppData { } /// Get a mutable container by given id + #[cfg(not(test))] fn get_container_by_id(&mut self, id: &ContainerId) -> Option<&mut ContainerItem> { self.containers.items.iter_mut().find(|i| &i.id == id) } + /// As above, but make it public to testing + #[cfg(test)] + pub fn get_container_by_id(&mut self, id: &ContainerId) -> Option<&mut ContainerItem> { + self.containers.items.iter_mut().find(|i| &i.id == id) + } + /// Get a mutable container by given id in the tmp_container vec fn get_hidden_container_by_id(&mut self, id: &ContainerId) -> Option<&mut ContainerItem> { self.hidden_containers.iter_mut().find(|i| &i.id == id) @@ -637,7 +700,7 @@ impl AppData { .map(|i| &mut i.docker_controls.state) } - /// Get mutable Option of the currently selected container DockerConmand items + /// Get mutable Option of the currently selected container DockerCommand items pub fn get_control_items(&mut self) -> Option<&mut Vec> { self.get_mut_selected_container() .map(|i| &mut i.docker_controls.items) @@ -668,19 +731,22 @@ impl AppData { } pub fn logs_horizontal_scroll(&mut self, sd: &ScrollDirection, width: u16) { + // Change this to set a max_offset, instead of taking in width each time, then can be combined with the log_scroll beneath match sd { - ScrollDirection::Next => { + ScrollDirection::Down => { if let Some(i) = self.get_mut_selected_container() { i.logs.forward(width); self.rerender.update_draw(); } } - ScrollDirection::Previous => { + ScrollDirection::Up => { if let Some(i) = self.get_mut_selected_container() { i.logs.back(); self.rerender.update_draw(); } } + // TODO set offset + _ => (), } } @@ -688,8 +754,10 @@ impl AppData { pub fn log_scroll(&mut self, scroll: &ScrollDirection) { if let Some(i) = self.get_mut_selected_container() { match scroll { - ScrollDirection::Next => i.logs.next(), - ScrollDirection::Previous => i.logs.previous(), + ScrollDirection::Down => i.logs.next(), + ScrollDirection::Up => i.logs.previous(), + // TODO set offset + _ => (), } self.rerender.update_draw(); } @@ -731,7 +799,7 @@ impl AppData { /// Chart data related methods /// Get mutable Option of the currently selected container chart data - pub fn get_chart_data(&self) -> Option<(CpuTuple, MemTuple)> { + pub fn get_chart_data(&self) -> Option { self.containers .state .selected() @@ -780,6 +848,7 @@ impl AppData { for container in [&self.containers.items, &self.hidden_containers] { for container in container { + // TODO refactor these let cpu_count = container.cpu_stats.back().map_or_else( || count(&CpuStats::default().to_string()), |i| count(&i.to_string()), @@ -789,14 +858,19 @@ impl AppData { || count(&ByteStats::default().to_string()), |i| count(&i.to_string()), ); - columns.cpu.1 = columns.cpu.1.max(cpu_count); columns.image.1 = columns.image.1.max(count(&container.image.to_string())); columns.mem.1 = columns.mem.1.max(mem_current_count); columns.mem.2 = columns.mem.2.max(count(&container.mem_limit.to_string())); columns.name.1 = columns.name.1.max(count(&container.name.to_string())); - columns.net_rx.1 = columns.net_rx.1.max(count(&container.rx.to_string())); - columns.net_tx.1 = columns.net_tx.1.max(count(&container.tx.to_string())); + columns.net_rx.1 = columns + .net_rx + .1 + .max(count(&container.rx.current_total().to_string())); + columns.net_tx.1 = columns + .net_tx + .1 + .max(count(&container.tx.current_total().to_string())); columns.state.1 = columns.state.1.max(count(&container.state.to_string())); columns.status.1 = columns.status.1.max(count(container.status.get())); } @@ -840,8 +914,12 @@ impl AppData { container.mem_stats.push_back(ByteStats::new(mem)); } - container.rx.update(rx); - container.tx.update(tx); + // Only insert if alive, or if is empty, need two to create an entry in the bandwidth chart, so instead this fills in the RX/TX total columns + if container.rx.is_empty() || container.state.is_alive() { + container.rx.push(rx); + container.tx.push(tx); + } + container.mem_limit.update(mem_limit); } if self.is_selected_container(id) { @@ -877,7 +955,7 @@ impl AppData { // If removed container is currently selected, then change selected to previous // This will default to 0 in any edge cases if self.containers.state.selected().is_some() { - self.containers.scroll(&ScrollDirection::Previous); + self.containers.scroll(&ScrollDirection::Up); } // Check is some, else can cause out of bounds error, if containers get removed before a docker update if self.containers.items.get(index).is_some() { @@ -896,7 +974,7 @@ impl AppData { if f.starts_with('/') { f.remove(0); } - (*f).to_string() + (*f).clone() }) }); @@ -1277,13 +1355,16 @@ mod tests { assert_eq!(result, &containers); if let Some(i) = app_data.get_container_by_id(&ContainerId::from("1")) { - i.rx = ByteStats::new(40); + i.rx = NetworkBandwidth::new(); + i.rx.push(40); } if let Some(i) = app_data.get_container_by_id(&ContainerId::from("2")) { - i.rx = ByteStats::new(80); + i.rx = NetworkBandwidth::new(); + i.rx.push(80); } if let Some(i) = app_data.get_container_by_id(&ContainerId::from("3")) { - i.rx = ByteStats::new(2); + i.rx = NetworkBandwidth::new(); + i.rx.push(2); } // descending @@ -1314,13 +1395,16 @@ mod tests { assert_eq!(result, &containers); if let Some(i) = app_data.get_container_by_id(&ContainerId::from("1")) { - i.rx = ByteStats::new(400); + i.rx = NetworkBandwidth::new(); + i.rx.push(400); } if let Some(i) = app_data.get_container_by_id(&ContainerId::from("2")) { - i.rx = ByteStats::new(80); + i.rx = NetworkBandwidth::new(); + i.rx.push(80); } if let Some(i) = app_data.get_container_by_id(&ContainerId::from("3")) { - i.rx = ByteStats::new(83); + i.rx = NetworkBandwidth::new(); + i.rx.push(83); } // descending @@ -1378,13 +1462,16 @@ mod tests { assert_eq!(result, &containers); if let Some(i) = app_data.get_container_by_id(&ContainerId::from("1")) { - i.rx = ByteStats::new(400); + i.rx = NetworkBandwidth::new(); + i.rx.push(400); } if let Some(i) = app_data.get_container_by_id(&ContainerId::from("2")) { - i.rx = ByteStats::new(80); + i.rx = NetworkBandwidth::new(); + i.rx.push(80); } if let Some(i) = app_data.get_container_by_id(&ContainerId::from("3")) { - i.rx = ByteStats::new(83); + i.rx = NetworkBandwidth::new(); + i.rx.push(83); } app_data.set_sorted(Some((Header::Rx, SortedOrder::Asc))); @@ -1444,7 +1531,7 @@ mod tests { ); // Calling previous when at start has no effect - app_data.containers_scroll(&ScrollDirection::Previous); + app_data.containers_scroll(&ScrollDirection::Up); let result = app_data.get_selected_container_id(); assert_eq!(result, Some(ContainerId::from("1"))); let result = app_data.get_selected_container_id_state_name(); @@ -1467,7 +1554,7 @@ mod tests { // Advance list state by 1 app_data.containers_start(); - app_data.containers.scroll(&ScrollDirection::Next); + app_data.containers.scroll(&ScrollDirection::Down); let result = app_data.get_container_state(); assert_eq!(result.selected(), Some(1)); @@ -1511,7 +1598,7 @@ mod tests { ); // Calling previous when at end has no effect - app_data.containers.scroll(&ScrollDirection::Next); + app_data.containers.scroll(&ScrollDirection::Down); let result = app_data.get_selected_container_id(); assert_eq!(result, Some(ContainerId::from("3"))); let result = app_data.get_selected_container_id_state_name(); @@ -1532,7 +1619,7 @@ mod tests { let mut app_data = gen_appdata(&containers); app_data.containers_end(); - app_data.containers.scroll(&ScrollDirection::Previous); + app_data.containers.scroll(&ScrollDirection::Up); let result = app_data.get_container_state(); assert_eq!(result.selected(), Some(1)); assert_eq!(result.offset(), 0); @@ -1548,7 +1635,7 @@ mod tests { assert_eq!(result, None); app_data.containers.start(); - app_data.containers.scroll(&ScrollDirection::Next); + app_data.containers.scroll(&ScrollDirection::Down); let result = app_data.get_selected_container(); assert_eq!(result, Some(&containers[1])); @@ -1635,7 +1722,7 @@ mod tests { let mut app_data = gen_appdata(&containers); app_data.containers_start(); app_data.docker_controls_start(); - app_data.docker_controls_scroll(&ScrollDirection::Next); + app_data.docker_controls_scroll(&ScrollDirection::Down); let result = app_data.selected_docker_controls(); assert_eq!(result, Some(DockerCommand::Restart)); @@ -1653,7 +1740,7 @@ mod tests { assert_eq!(result, Some(DockerCommand::Delete)); // Next has no effect when at end - app_data.docker_controls_scroll(&ScrollDirection::Next); + app_data.docker_controls_scroll(&ScrollDirection::Down); let result = app_data.selected_docker_controls(); assert_eq!(result, Some(DockerCommand::Delete)); } @@ -1665,14 +1752,14 @@ mod tests { let mut app_data = gen_appdata(&containers); app_data.containers_start(); app_data.docker_controls_end(); - app_data.docker_controls_scroll(&ScrollDirection::Previous); + app_data.docker_controls_scroll(&ScrollDirection::Up); let result = app_data.selected_docker_controls(); assert_eq!(result, Some(DockerCommand::Stop)); // previous has no effect when at start app_data.docker_controls_start(); - app_data.docker_controls_scroll(&ScrollDirection::Previous); + app_data.docker_controls_scroll(&ScrollDirection::Up); let result = app_data.selected_docker_controls(); assert_eq!(result, Some(DockerCommand::Pause)); } @@ -1936,7 +2023,7 @@ mod tests { assert_eq!(result, " 3/3 - container_1 - image_1"); // Change log state to no longer be at the end - app_data.log_scroll(&ScrollDirection::Previous); + app_data.log_scroll(&ScrollDirection::Up); let result = app_data.get_log_title(); assert_eq!(result, " 2/3 - container_1 - image_1"); } @@ -1957,7 +2044,7 @@ mod tests { assert_eq!(result, " - container_1 - image_1"); // change container - app_data.containers_scroll(&ScrollDirection::Next); + app_data.containers_scroll(&ScrollDirection::Down); let result = app_data.get_log_title(); assert_eq!(result, " - container_2 - image_2"); @@ -1968,7 +2055,7 @@ mod tests { assert_eq!(result, " 3/3 - container_2 - image_2"); // Change log state to no longer be at the end - app_data.log_scroll(&ScrollDirection::Previous); + app_data.log_scroll(&ScrollDirection::Up); let result = app_data.get_log_title(); assert_eq!(result, " 2/3 - container_2 - image_2"); } @@ -2075,7 +2162,7 @@ mod tests { let result = app_data.get_log_title(); assert_eq!(result, " 1/3 - container_1 - image_1"); - app_data.log_scroll(&ScrollDirection::Next); + app_data.log_scroll(&ScrollDirection::Down); let result = app_data.get_log_state(); assert!(result.is_some()); assert_eq!(result.as_ref().unwrap().selected(), Some(1)); @@ -2084,7 +2171,7 @@ mod tests { let result = app_data.get_log_title(); assert_eq!(result, " 2/3 - container_1 - image_1"); - app_data.log_scroll(&ScrollDirection::Next); + app_data.log_scroll(&ScrollDirection::Down); let result = app_data.get_log_state(); assert!(result.is_some()); assert_eq!(result.as_ref().unwrap().selected(), Some(2)); @@ -2092,7 +2179,7 @@ mod tests { let result = app_data.get_log_title(); assert_eq!(result, " 3/3 - container_1 - image_1"); - app_data.log_scroll(&ScrollDirection::Next); + app_data.log_scroll(&ScrollDirection::Down); let result = app_data.get_log_state(); assert!(result.is_some()); @@ -2123,7 +2210,7 @@ mod tests { let result = app_data.get_log_title(); assert_eq!(result, " 3/3 - container_1 - image_1"); - app_data.log_scroll(&ScrollDirection::Previous); + app_data.log_scroll(&ScrollDirection::Up); let result = app_data.get_log_state(); assert!(result.is_some()); @@ -2132,7 +2219,7 @@ mod tests { let result = app_data.get_log_title(); assert_eq!(result, " 2/3 - container_1 - image_1"); - app_data.log_scroll(&ScrollDirection::Previous); + app_data.log_scroll(&ScrollDirection::Up); let result = app_data.get_log_state(); assert!(result.is_some()); assert_eq!(result.as_ref().unwrap().selected(), Some(0)); @@ -2140,7 +2227,7 @@ mod tests { let result = app_data.get_log_title(); assert_eq!(result, " 1/3 - container_1 - image_1"); - app_data.log_scroll(&ScrollDirection::Previous); + app_data.log_scroll(&ScrollDirection::Up); let result = app_data.get_log_state(); assert!(result.is_some()); assert_eq!(result.as_ref().unwrap().selected(), Some(0)); @@ -2164,26 +2251,49 @@ mod tests { app_data.containers_start(); + let mut rx = NetworkBandwidth::new(); + rx.push(200); + rx.push(100); + rx.push(200); + + let mut tx = NetworkBandwidth::new(); + tx.push(300); + tx.push(600); + tx.push(900); + if let Some(item) = app_data.get_container_by_id(&ContainerId::from("1")) { - item.cpu_stats = VecDeque::from([CpuStats::new(1.1), CpuStats::new(1.2)]); + item.cpu_stats = VecDeque::from([CpuStats::new(1.2), CpuStats::new(1.2)]); item.mem_stats = VecDeque::from([ByteStats::new(1), ByteStats::new(2)]); + item.rx = rx; + item.tx = tx; } let result = app_data.get_chart_data(); assert_eq!( result, - Some(( - ( - vec![(0.0, 1.1), (1.0, 1.2)], - CpuStats::new(1.2), - State::Running(RunningState::Healthy), - ), - ( - vec![(0.0, 1.0), (1.0, 2.0)], - ByteStats::new(2), - State::Running(RunningState::Healthy), - ) - )) + Some(ChartsData { + memory: ChartSeries { + dataset: vec![(0.0, 1.0), (1.0, 2.0)], + max: ByteStats::new(2), + current: ByteStats::new(2) + }, + cpu: ChartSeries { + dataset: vec![(0.0, 1.2), (1.0, 1.2)], + max: CpuStats::new(1.2), + current: CpuStats::new(1.2) + }, + rx: ChartSeries { + dataset: vec![(0.0, 0.0), (1.0, 100.0)], + max: BandwidthStat::new(100), + current: BandwidthStat::new(100) + }, + tx: ChartSeries { + dataset: vec![(0.0, 300.0), (1.0, 300.0)], + max: BandwidthStat::new(300), + current: BandwidthStat::new(300) + }, + state: State::Running(RunningState::Healthy) + }) ); } @@ -2333,8 +2443,15 @@ mod tests { assert_eq!(result[0].cpu_stats, VecDeque::from([CpuStats::new(10.0)])); assert_eq!(result[0].mem_stats, VecDeque::from([ByteStats::new(10)])); assert_eq!(result[0].mem_limit, ByteStats::new(10)); - assert_eq!(result[0].rx, ByteStats::new(10)); - assert_eq!(result[0].tx, ByteStats::new(10)); + + let mut rx = NetworkBandwidth::new(); + rx.push(10); + let mut tx = NetworkBandwidth::new(); + tx.push(10); + assert_eq!(result[0].rx, rx); + // VecDeque::from([ByteStats::new(10)])); + assert_eq!(result[0].tx, tx); + // VecDeque::from([ByteStats::new(10)])); } #[test] @@ -2434,7 +2551,7 @@ mod tests { } for _ in 0..=500 { - app_data.log_scroll(&ScrollDirection::Next); + app_data.log_scroll(&ScrollDirection::Down); } let result = app_data.get_logs( Size { diff --git a/src/app_error.rs b/src/app_error.rs index 74d7d3c..ce4b5f6 100644 --- a/src/app_error.rs +++ b/src/app_error.rs @@ -2,7 +2,7 @@ use crate::app_data::DockerCommand; use std::fmt; /// app errors to set in global state -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub enum AppError { DockerCommand(DockerCommand), DockerExec, diff --git a/src/config/color_parser.rs b/src/config/color_parser.rs index 87dacd7..8afded3 100644 --- a/src/config/color_parser.rs +++ b/src/config/color_parser.rs @@ -1,5 +1,8 @@ use ratatui::style::Color; +static COLOR_RX: Color = Color::Rgb(255, 233, 193); +static COLOR_TX: Color = Color::Rgb(205, 140, 140); + /// The macro accepts a list of struct names with key names /// Returns a struct where every key name is an Option, with the correct derived attributes macro_rules! optional_config_struct { @@ -58,7 +61,7 @@ impl From> for AppColors { ); } - // Seletable panel borders + // Selectable panel borders if let Some(b) = config_colors.borders { Self::map_color(b.selected.as_deref(), &mut app_colors.borders.selected); Self::map_color(b.unselected.as_deref(), &mut app_colors.borders.unselected); @@ -249,6 +252,8 @@ optional_config_struct!( ConfigBackgroundText, background, text; ConfigBackgroundTextHighlight, background, text, text_highlight; ConfigBorders, selected, unselected; + ConfigChartBandwidth, background, border, max_rx, max_tx, title_tx, title_rx, points_rx, points_tx, y_axis; + ConfigChartCpu, background, border, order, title, max, points,y_axis; ConfigChartMemory, background, border, title, max, points, y_axis; ConfigChartPorts, background, border, title, headings, text; @@ -265,6 +270,8 @@ config_struct!( Borders, selected, unselected; ChartCpu, background, border, title, max, points, y_axis; ChartMemory, background, border, title, max, points, y_axis; + ChartBandwidth, background, border, max_rx, max_tx, title_rx, title_tx, points_rx, points_tx, y_axis; + ChartPorts, background, border, title, headings, text; Commands, background, pause, restart, stop, delete, resume, start; Containers, background, icon, text, text_rx, text_tx; @@ -284,6 +291,7 @@ pub struct ConfigColors { borders: Option, chart_cpu: Option, chart_memory: Option, + chart_bandwidth: Option, chart_ports: Option, commands: Option, container_state: Option, @@ -335,7 +343,24 @@ impl Commands { } } -/// Default colours for the help popup +/// Default colours for the Bandwidth chart +impl ChartBandwidth { + const fn new() -> Self { + Self { + background: Color::Reset, + border: Color::White, + max_rx: COLOR_RX, + title_rx: COLOR_RX, + title_tx: COLOR_TX, + max_tx: COLOR_TX, + points_rx: COLOR_RX, + points_tx: COLOR_TX, + y_axis: Color::White, + } + } +} + +/// Default colours for the CPU chart impl ChartCpu { const fn new() -> Self { Self { @@ -383,8 +408,8 @@ impl Containers { background: Color::Reset, icon: Color::White, text: Color::Blue, - text_rx: Color::Rgb(255, 233, 193), - text_tx: Color::Rgb(205, 140, 140), + text_rx: COLOR_RX, + text_tx: COLOR_TX, } } } @@ -487,6 +512,7 @@ pub struct AppColors { pub borders: Borders, pub chart_cpu: ChartCpu, pub chart_memory: ChartMemory, + pub chart_bandwidth: ChartBandwidth, pub chart_ports: ChartPorts, pub commands: Commands, pub container_state: ContainerState, @@ -507,6 +533,7 @@ impl AppColors { borders: Borders::new(), chart_cpu: ChartCpu::new(), chart_memory: ChartMemory::new(), + chart_bandwidth: ChartBandwidth::new(), chart_ports: ChartPorts::new(), commands: Commands::new(), container_state: ContainerState::new(), diff --git a/src/config/config.toml b/src/config/config.toml index 8d5f1db..f70c116 100644 --- a/src/config/config.toml +++ b/src/config/config.toml @@ -24,8 +24,8 @@ show_timestamp = true # Don't draw gui - for debugging - mostly pointless gui = true -# Docker host location -host = "/var/run/docker.sock" +# Docker host location. Will take priority over a DOCKER_HOST env. +# host = "/var/run/docker.sock" # Display the container logs timestamp with a given timezone, if timezone is unknown, defaults to UTC timezone = "Etc/UTC" @@ -95,8 +95,8 @@ scroll_start = ["home"] # scroll up a list by one item scroll_up = ["up", "k"] # Horizontal scroll of the logs -log_scroll_forward = ["right"] -log_scroll_back = ["left"] +scroll_forward = ["right"] +scroll_back = ["left"] # Select next panel select_next_panel = ["tab"] # Select previous panel @@ -122,8 +122,8 @@ log_section_height_decrease = ["-"] log_section_height_increase = ["+"] # Toggle visibility of the log section log_section_toggle = ["\\"] - - +# Toggle to inspect container screen +inspect = ["i"] # Force a complete clear & redraw of the screen force_redraw = ["f"] @@ -254,6 +254,27 @@ points = "cyan" # The charts y-axis y_axis = "white" +# The bandwidth chart +[colors.chart_bandwidth] +# Background color of panel +background = "reset" +# Border color +border = "white" +# Maximum RX value - again paused & stopped colors not yet customizable +max_rx = "#FFE9C1" +# Maximum TX value - again paused & stopped colors not yet customizable +max_tx = "#CD8C8C" +# RX points on the chart - again paused & stopped colors not yet customizable +points_rx = "#FFE9C1" +# TX points on the chart - again paused & stopped colors not yet customizable +points_tx = "#CD8C8C" +# TX title color +title_rx = "#FFE9C1" +# RX title color +title_tx = "#CD8C8C" +# The charts y-axis +y_axis = "white" + # The ports chart [colors.chart_ports] # Background color of panel diff --git a/src/config/keymap_parser.rs b/src/config/keymap_parser.rs index f75ff16..34fde03 100644 --- a/src/config/keymap_parser.rs +++ b/src/config/keymap_parser.rs @@ -42,8 +42,9 @@ optional_config_struct!( exec, filter_mode, force_redraw, - log_scroll_back, - log_scroll_forward, + inspect, + scroll_back, + scroll_forward, log_search_mode, log_section_height_decrease, log_section_height_increase, @@ -77,9 +78,10 @@ config_struct!( delete_deny, exec, filter_mode, + inspect, force_redraw, - log_scroll_back, - log_scroll_forward, + scroll_back, + scroll_forward, log_search_mode, log_section_height_decrease, log_section_height_increase, @@ -113,10 +115,11 @@ impl Keymap { delete_confirm: (KeyCode::Char('y'), None), delete_deny: (KeyCode::Char('n'), None), exec: (KeyCode::Char('e'), None), + inspect: (KeyCode::Char('i'), None), filter_mode: (KeyCode::Char('/'), Some(KeyCode::F(1))), force_redraw: (KeyCode::Char('f'), None), - log_scroll_back: (KeyCode::Left, None), - log_scroll_forward: (KeyCode::Right, None), + scroll_back: (KeyCode::Left, None), + scroll_forward: (KeyCode::Right, None), log_search_mode: (KeyCode::Char('#'), None), log_section_height_decrease: (KeyCode::Char('-'), None), log_section_height_increase: (KeyCode::Char('='), None), @@ -158,20 +161,20 @@ impl From> for Keymap { |vec_str: Option>, keymap_field: &mut (KeyCode, Option), keymap_clash: &mut HashSet| { - if let Some(vec_str) = vec_str { - if let Some(vec_keycode) = Self::try_parse_keycode(&vec_str) { - if let Some(first) = vec_keycode.first() { - keymap_clash.insert(*first); - counter += 1; - keymap_field.0 = *first; - } - if let Some(second) = vec_keycode.get(1) { - keymap_clash.insert(*second); - counter += 1; - keymap_field.1 = Some(*second); - } else { - keymap_field.1 = None; - } + if let Some(vec_str) = vec_str + && let Some(vec_keycode) = Self::try_parse_keycode(&vec_str) + { + if let Some(first) = vec_keycode.first() { + keymap_clash.insert(*first); + counter += 1; + keymap_field.0 = *first; + } + if let Some(second) = vec_keycode.get(1) { + keymap_clash.insert(*second); + counter += 1; + keymap_field.1 = Some(*second); + } else { + keymap_field.1 = None; } } }; @@ -206,12 +209,8 @@ impl From> for Keymap { update_keymap(ck.scroll_start, &mut keymap.scroll_start, &mut clash); update_keymap(ck.scroll_up, &mut keymap.scroll_up, &mut clash); update_keymap(ck.log_search_mode, &mut keymap.log_search_mode, &mut clash); - update_keymap( - ck.log_scroll_forward, - &mut keymap.log_scroll_forward, - &mut clash, - ); - update_keymap(ck.log_scroll_back, &mut keymap.log_scroll_back, &mut clash); + update_keymap(ck.scroll_forward, &mut keymap.scroll_forward, &mut clash); + update_keymap(ck.scroll_back, &mut keymap.scroll_back, &mut clash); update_keymap( ck.select_next_panel, &mut keymap.select_next_panel, @@ -276,16 +275,16 @@ impl Keymap { for key in input.iter().take(2) { if key.chars().count() == 1 { - if let Some(first_char) = key.chars().next() { - if let Some(first_char) = match first_char { + if let Some(first_char) = key.chars().next() + && let Some(first_char) = match first_char { x if x.is_ascii_alphabetic() || x.is_ascii_digit() => Some(first_char), '/' | '\\' | ',' | '.' | '#' | '\'' | '[' | ']' | ';' | '=' | '-' => { Some(first_char) } _ => None, - } { - output.push(KeyCode::Char(first_char)); } + { + output.push(KeyCode::Char(first_char)); } } else { let keycode = match key.to_lowercase().as_str() { @@ -327,7 +326,7 @@ impl Keymap { if output.is_empty() { None } else { - // Remove any duplicates for a single deinition + // Remove any duplicates for a single definition if output.first() == output.get(1) { output.pop(); } @@ -395,9 +394,10 @@ mod tests { exec: None, filter_mode: None, force_redraw: None, - log_scroll_back: None, + inspect: None, + scroll_back: None, log_search_mode: None, - log_scroll_forward: None, + scroll_forward: None, log_section_height_decrease: None, log_section_height_increase: None, log_section_toggle: None, @@ -441,8 +441,9 @@ mod tests { exec: gen_v(("g", "h")), filter_mode: gen_v(("i", "j")), force_redraw: gen_v(("k", "l")), - log_scroll_back: gen_v(("s", "t")), - log_scroll_forward: gen_v(("q", "r")), + inspect: gen_v(("m", "n")), + scroll_back: gen_v(("s", "t")), + scroll_forward: gen_v(("q", "r")), log_search_mode: gen_v(("1", "2")), log_section_height_decrease: gen_v(("m", "n")), log_section_height_increase: gen_v(("o", "p")), @@ -479,8 +480,9 @@ mod tests { exec: (KeyCode::Char('g'), Some(KeyCode::Char('h'))), filter_mode: (KeyCode::Char('i'), Some(KeyCode::Char('j'))), force_redraw: (KeyCode::Char('k'), Some(KeyCode::Char('l'))), - log_scroll_back: (KeyCode::Char('s'), Some(KeyCode::Char('t'))), - log_scroll_forward: (KeyCode::Char('q'), Some(KeyCode::Char('r'))), + inspect: (KeyCode::Char('i'), None), + scroll_back: (KeyCode::Char('s'), Some(KeyCode::Char('t'))), + scroll_forward: (KeyCode::Char('q'), Some(KeyCode::Char('r'))), log_search_mode: (KeyCode::Char('1'), Some(KeyCode::Char('2'))), log_section_height_decrease: (KeyCode::Char('m'), Some(KeyCode::Char('n'))), log_section_height_increase: (KeyCode::Char('o'), Some(KeyCode::Char('p'))), diff --git a/src/config/mod.rs b/src/config/mod.rs index 699768e..db79281 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -13,9 +13,6 @@ pub use {color_parser::AppColors, keymap_parser::Keymap}; mod parse_args; mod parse_config_file; -// TODO use a global pub static oncelock for the config -// static CELL: OnceLock = OnceLock::new(); - #[derive(Debug, Clone)] #[allow(clippy::struct_excessive_bools)] pub struct Config { @@ -28,7 +25,8 @@ pub struct Config { pub keymap: Keymap, pub log_search_case_sensitive: bool, pub raw_logs: bool, - pub save_dir: Option, + pub dir_config: Option, + pub dir_save: Option, pub show_logs: bool, pub show_self: bool, pub show_std_err: bool, @@ -50,7 +48,8 @@ impl From<&Args> for Config { keymap: Keymap::new(), log_search_case_sensitive: true, raw_logs: args.raw, - save_dir: Self::try_get_logs_dir(args.save_dir.as_ref()), + dir_save: Self::try_get_logs_dir(args.save_dir.as_ref()), + dir_config: args.config_file.as_ref().map(|i| PathBuf::from(&i)), show_logs: true, show_self: !args.show_self, show_std_err: !args.no_std_err, @@ -62,19 +61,20 @@ impl From<&Args> for Config { } } -impl From for Config { - fn from(config_file: ConfigFile) -> Self { +impl From<(ConfigFile, Option)> for Config { + fn from((config_file, dir): (ConfigFile, Option)) -> Self { Self { app_colors: AppColors::from(config_file.colors), color_logs: config_file.color_logs.unwrap_or(false), docker_interval_ms: config_file.docker_interval.unwrap_or(1000), + dir_config: dir, gui: config_file.gui.unwrap_or(true), host: config_file.host, in_container: Self::check_if_in_container(), keymap: Keymap::from(config_file.keymap), log_search_case_sensitive: config_file.log_search_case_sensitive.unwrap_or(true), raw_logs: config_file.raw_logs.unwrap_or(false), - save_dir: Self::try_get_logs_dir(config_file.save_dir.as_ref()), + dir_save: Self::try_get_logs_dir(config_file.save_dir.as_ref()), show_logs: config_file.show_logs.unwrap_or(true), show_self: config_file.show_self.unwrap_or(false), show_std_err: config_file.show_std_err.unwrap_or(true), @@ -185,8 +185,8 @@ impl Config { self.host = Some(host); } - if let Some(x) = config_from_cli.save_dir { - self.save_dir = Some(x); + if let Some(x) = config_from_cli.dir_save { + self.dir_save = Some(x); } if let Some(tz) = config_from_cli.timezone { @@ -211,16 +211,16 @@ impl Config { let args = Args::parse(); let config_from_cli = Self::from(&args); - if let Some(config_file) = &args.config_file { - if let Some(config_file) = - parse_config_file::ConfigFile::try_parse_from_file(config_file) - { - return Self::from(config_file).merge_args(config_from_cli); - } + if let Some(dir_config_file) = &args.config_file + && let Some(config_file) = + parse_config_file::ConfigFile::try_parse_from_file(dir_config_file) + { + return Self::from((config_file, Some(PathBuf::from(dir_config_file)))) + .merge_args(config_from_cli); } - if let Some(config_file) = parse_config_file::ConfigFile::try_parse(in_container) { - return Self::from(config_file).merge_args(config_from_cli); + if let Some((config_file, dir)) = parse_config_file::ConfigFile::try_parse(in_container) { + return Self::from((config_file, Some(dir))).merge_args(config_from_cli); } config_from_cli } diff --git a/src/config/parse_config_file.rs b/src/config/parse_config_file.rs index d61991a..e685d57 100644 --- a/src/config/parse_config_file.rs +++ b/src/config/parse_config_file.rs @@ -81,7 +81,7 @@ pub struct ConfigFile { impl ConfigFile { /// Attempt to create a config.toml file, will attempt to recursively create the directories as well - fn crate_config_file(in_container: bool) -> Result<(), AppError> { + fn create_config_file(in_container: bool) -> Result<(), AppError> { if in_container { return Ok(()); } @@ -146,28 +146,26 @@ impl ConfigFile { /// Parse a config file using default config_file location /// This is executed first, then the CLI args are read, and if they contain a "--config-file" entry, then Self::try_parse_from_file() is executed - pub fn try_parse(in_container: bool) -> Option { - let mut config = None; + pub fn try_parse(in_container: bool) -> Option<(Self, PathBuf)> { + let mut output = None; for file_format in [ ConfigFileFormat::Toml, ConfigFileFormat::Jsonc, ConfigFileFormat::JsoncAsJson, ConfigFileFormat::Json, ] { - if let Ok(config_file) = Self::parse_config_file( - file_format, - &file_format.get_default_path_name(in_container), - ) { - config = Some(config_file); + let path = file_format.get_default_path_name(in_container); + if let Ok(config_file) = Self::parse_config_file(file_format, &path) { + output = Some((config_file, path)); break; } } - if config.is_none() { - Self::crate_config_file(in_container).ok(); + if output.is_none() { + Self::create_config_file(in_container).ok(); } - config + output } } diff --git a/src/docker_data/message.rs b/src/docker_data/message.rs index b0af01a..6030e29 100644 --- a/src/docker_data/message.rs +++ b/src/docker_data/message.rs @@ -9,5 +9,6 @@ pub enum DockerMessage { ConfirmDelete(ContainerId), Control((DockerCommand, ContainerId)), Exec(Sender>), + Inspect(ContainerId), Update, } diff --git a/src/docker_data/mod.rs b/src/docker_data/mod.rs index 52ecc24..038dc6b 100644 --- a/src/docker_data/mod.rs +++ b/src/docker_data/mod.rs @@ -1,8 +1,8 @@ use bollard::{ Docker, query_parameters::{ - ListContainersOptions, LogsOptions, RemoveContainerOptions, RestartContainerOptions, - StartContainerOptions, StatsOptions, StopContainerOptions, + InspectContainerOptions, ListContainersOptions, LogsOptions, RemoveContainerOptions, + RestartContainerOptions, StartContainerOptions, StatsOptions, StopContainerOptions, }, secret::ContainerStatsResponse, service::ContainerSummary, @@ -10,13 +10,10 @@ use bollard::{ use futures_util::StreamExt; use parking_lot::Mutex; use std::{ - collections::HashMap, + collections::HashSet, sync::{Arc, atomic::AtomicUsize}, }; -use tokio::{ - sync::mpsc::{Receiver, Sender}, - task::JoinHandle, -}; +use tokio::sync::mpsc::{Receiver, Sender}; use uuid::Uuid; use crate::{ @@ -70,7 +67,7 @@ pub struct DockerData { docker: Arc, gui_state: Arc>, receiver: Receiver, - spawns: Arc>>>, + spawns: Arc>>, } impl DockerData { @@ -132,7 +129,7 @@ impl DockerData { docker: Arc, state: State, spawn_id: SpawnId, - spawns: Arc>>>, + spawns: Arc>>, ) { let id = spawn_id.get_id(); let mut stream = docker @@ -200,16 +197,13 @@ impl DockerData { for (state, id) in all_ids { let spawn_id = SpawnId::Stats((id, self.binate)); - if let std::collections::hash_map::Entry::Vacant(spawns) = - self.spawns.lock().entry(spawn_id.clone()) - { - spawns.insert(tokio::spawn(Self::update_container_stat( - Arc::clone(&self.app_data), - Arc::clone(&self.docker), - state, - spawn_id, - Arc::clone(&self.spawns), - ))); + if !self.spawns.lock().contains(&spawn_id) { + let app_data = Arc::clone(&self.app_data); + let docker = Arc::clone(&self.docker); + let spawns = Arc::clone(&self.spawns); + tokio::spawn(Self::update_container_stat( + app_data, docker, state, spawn_id, spawns, + )); } } self.binate = self.binate.toggle(); @@ -256,7 +250,7 @@ impl DockerData { docker: Arc, id: ContainerId, since: u64, - spawns: Arc>>>, + spawns: Arc>>, stderr: bool, ) { let options = Some(LogsOptions { @@ -290,13 +284,13 @@ impl DockerData { let spawns = Arc::clone(&self.spawns); let std_err = self.config.show_std_err; let init = Arc::clone(&init); - self.spawns.lock().insert( - SpawnId::Log(id.clone()), - tokio::spawn(async move { - Self::update_log(app_data, docker, id, 0, spawns, std_err).await; - init.fetch_add(1, std::sync::atomic::Ordering::SeqCst); - }), - ); + + self.spawns.lock().insert(SpawnId::Log(id.clone())); + + tokio::spawn(async move { + Self::update_log(app_data, docker, id, 0, spawns, std_err).await; + init.fetch_add(1, std::sync::atomic::Ordering::SeqCst); + }); } init } @@ -327,17 +321,16 @@ impl DockerData { let last_updated = container.last_updated; let spawn_id = SpawnId::Log(container.id.clone()); // Only spawn if not already spawned with a given id/binate pair - if let std::collections::hash_map::Entry::Vacant(spawns) = - self.spawns.lock().entry(spawn_id) - { - spawns.insert(tokio::spawn(Self::update_log( + if !self.spawns.lock().contains(&spawn_id) { + self.spawns.lock().insert(spawn_id.clone()); + tokio::spawn(Self::update_log( Arc::clone(&self.app_data), Arc::clone(&self.docker), container.id.clone(), last_updated, Arc::clone(&self.spawns), self.config.show_std_err, - ))); + )); } } self.update_all_container_stats(); @@ -420,6 +413,18 @@ impl DockerData { docker_tx.send(Arc::clone(&self.docker)).ok(); } DockerMessage::Update => self.update_everything().await, + DockerMessage::Inspect(id) => { + let t = self + .docker + .inspect_container(id.get(), Some(InspectContainerOptions { size: true })) + .await; + if let Ok(t) = t { + self.app_data.lock().set_inspect_data(t); + self.gui_state.lock().status_push(Status::Inspect); + } else { + // Set error here, can't inspect container + } + } } } } @@ -457,7 +462,7 @@ impl DockerData { docker: Arc::new(docker), gui_state, receiver: docker_rx, - spawns: Arc::new(Mutex::new(HashMap::new())), + spawns: Arc::new(Mutex::new(HashSet::new())), }; inner.initialise_container_data().await; Self::heartbeat(&inner.config, docker_tx); @@ -478,6 +483,7 @@ mod tests { fn gen_stats() -> ContainerStatsResponse { ContainerStatsResponse { read: None, + os_type: None, preread: None, num_procs: Some(1), pids_stats: None, diff --git a/src/exec.rs b/src/exec.rs index a1604b4..bc4444e 100644 --- a/src/exec.rs +++ b/src/exec.rs @@ -1,5 +1,5 @@ use std::{ - io::{Read, Stdout, Write}, + io::{Read, Write}, sync::{Arc, atomic::AtomicBool, mpsc::Sender}, }; @@ -10,7 +10,7 @@ use bollard::{ use crossterm::terminal::enable_raw_mode; use futures_util::StreamExt; use parking_lot::Mutex; -use ratatui::{Terminal, backend::CrosstermBackend}; +use ratatui::layout::Size; use tokio::{ fs::File, io::{AsyncReadExt, AsyncWriteExt}, @@ -123,23 +123,29 @@ impl AsyncTTY { } } -/// This is used to set the terminal size when exec via the Internal method -#[derive(Debug, Clone)] -pub struct TerminalSize { - width: u16, - height: u16, -} +// impl TryFrom<&Terminal>> for HWU16 { +// type Error = None; +// fn try_from(terminal: &Terminal>) -> Option { +// terminal.size().map_or(None, |i| { +// Some(Self { +// width: i.width, +// height: i.height, +// }) +// }) +// } -impl TerminalSize { - pub fn new(terminal: &Terminal>) -> Option { - terminal.size().map_or(None, |i| { - Some(Self { - width: i.width, - height: i.height, - }) - }) - } -} +// } + +// impl TerminalSize { +// pub fn new(terminal: &Terminal>) -> Option { +// terminal.size().map_or(None, |i| { +// Some(Self { +// width: i.width, +// height: i.height, +// }) +// }) +// } +// } #[derive(Debug, Clone)] pub enum ExecMode { @@ -161,51 +167,41 @@ impl ExecMode { let use_cli = app_data.lock().config.use_cli; let container = app_data.lock().get_selected_container_id_state_name(); - if let Some((id, state, _)) = container { - if [ + if let Some((id, state, _)) = container + && [ State::Running(RunningState::Healthy), State::Running(RunningState::Unhealthy), ] .contains(&state) + { + if tty_readable() + && !use_cli + && let Ok(exec) = docker + .create_exec( + id.get(), + CreateExecOptions { + attach_stdout: Some(true), + attach_stderr: Some(true), + cmd: Some(vec![command::PWD]), + ..Default::default() + }, + ) + .await + && let Ok(StartExecResults::Attached { mut output, .. }) = + docker.start_exec(&exec.id, None).await + && let Some(Ok(msg)) = output.next().await + && !msg.to_string().starts_with(OCI_ERROR) { - if tty_readable() && !use_cli { - if let Ok(exec) = docker - .create_exec( - id.get(), - CreateExecOptions { - attach_stdout: Some(true), - attach_stderr: Some(true), - cmd: Some(vec![command::PWD]), - ..Default::default() - }, - ) - .await - { - if let Ok(StartExecResults::Attached { mut output, .. }) = - docker.start_exec(&exec.id, None).await - { - if let Some(Ok(msg)) = output.next().await { - if !msg.to_string().starts_with(OCI_ERROR) { - return Some(Self::Internal(( - Arc::new(id), - Arc::clone(docker), - ))); - } - } - } - } - } + return Some(Self::Internal((Arc::new(id), Arc::clone(docker)))); + } - if let Ok(output) = std::process::Command::new(command::DOCKER) - .args([command::EXEC, id.get(), command::PWD]) - .output() - { - if let Ok(output) = String::from_utf8(output.stdout) { - if !output.starts_with(OCI_ERROR) { - return Some(Self::External(Arc::new(id))); - } - } - } + if let Ok(output) = std::process::Command::new(command::DOCKER) + .args([command::EXEC, id.get(), command::PWD]) + .output() + && let Ok(output) = String::from_utf8(output.stdout) + && !output.starts_with(OCI_ERROR) + { + return Some(Self::External(Arc::new(id))); } } None @@ -235,7 +231,7 @@ impl ExecMode { &self, id: &ContainerId, docker: &Arc, - terminal_size: Option, + terminal_size: Option, ) -> Result<(), AppError> { let cancel_token = CancellationToken::new(); @@ -351,7 +347,7 @@ impl ExecMode { } } - pub async fn run(&self, tty_size: Option) -> Result<(), AppError> { + pub async fn run(&self, tty_size: Option) -> Result<(), AppError> { match self { Self::External(id) => { Self::exec_external(id); diff --git a/src/input_handler/mod.rs b/src/input_handler/mod.rs index 000577b..4dd5ae3 100644 --- a/src/input_handler/mod.rs +++ b/src/input_handler/mod.rs @@ -119,6 +119,14 @@ impl InputHandler { self.gui_state.lock().set_delete_container(None); } + async fn inspect_key(&self) { + self.app_data.lock().clear_inspect_data(); + let selected = self.app_data.lock().get_selected_container().cloned(); + if let Some(g) = selected { + self.docker_tx.send(DockerMessage::Inspect(g.id)).await.ok(); + } + } + /// Validate that one can exec into a Docker container async fn exec_key(&self) { let is_oxker = self.app_data.lock().is_oxker(); @@ -178,58 +186,58 @@ impl InputHandler { async fn save_logs(&self) -> Result<(), Box> { let args = self.app_data.lock().config.clone(); let container = self.app_data.lock().get_selected_container_id_state_name(); - if let Some((id, _, name)) = container { - if let Some(log_path) = args.save_dir { - let (sx, rx) = tokio::sync::oneshot::channel(); - self.docker_tx.send(DockerMessage::Exec(sx)).await?; + if let Some((id, _, name)) = container + && let Some(log_path) = args.dir_save + { + let (sx, rx) = tokio::sync::oneshot::channel(); + self.docker_tx.send(DockerMessage::Exec(sx)).await?; - let now = SystemTime::now() - .duration_since(SystemTime::UNIX_EPOCH) - .map_or(0, |i| i.as_secs()); + let now = SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .map_or(0, |i| i.as_secs()); - let path = log_path.join(format!("{name}_{now}.log")); + let path = log_path.join(format!("{name}_{now}.log")); - let options = Some(LogsOptions { - stderr: true, - stdout: true, - timestamps: args.show_timestamp, - since: 0, - ..Default::default() - }); - let mut logs = rx.await?.logs(id.get(), options); - let mut output = vec![]; + let options = Some(LogsOptions { + stderr: true, + stdout: true, + timestamps: args.show_timestamp, + since: 0, + ..Default::default() + }); + let mut logs = rx.await?.logs(id.get(), options); + let mut output = vec![]; - while let Some(Ok(value)) = logs.next().await { - let data = value.to_string(); - if !data.trim().is_empty() { - output.push( - categorise_text(&data) - .into_iter() - .map(|i| i.text) - .collect::(), - ); - } - } - if !output.is_empty() { - let mut stream = BufWriter::new( - OpenOptions::new() - .read(true) - .write(true) - .create(true) - .truncate(true) - .open(&path)?, + while let Some(Ok(value)) = logs.next().await { + let data = value.to_string(); + if !data.trim().is_empty() { + output.push( + categorise_text(&data) + .into_iter() + .map(|i| i.text) + .collect::(), ); - - for line in &output { - stream.write_all(line.as_bytes())?; - } - stream.flush()?; - - self.gui_state - .lock() - .set_info_box(&format!("saved to {}", path.display())); } } + if !output.is_empty() { + let mut stream = BufWriter::new( + OpenOptions::new() + .read(true) + .write(true) + .create(true) + .truncate(true) + .open(&path)?, + ); + + for line in &output { + stream.write_all(line.as_bytes())?; + } + stream.flush()?; + + self.gui_state + .lock() + .set_info_box(&format!("saved to {}", path.display())); + } } Ok(()) } @@ -296,6 +304,18 @@ impl InputHandler { } } + fn inspect_scroll(&self, modifier: KeyModifiers, sd: &ScrollDirection) { + for _ in 0..self.get_modifier_total(modifier) { + self.gui_state.lock().set_inspect_offset(sd); + } + } + + // fn inspect_scroll(&self, modifier: KeyModifiers, sd: &ScrollDirection) { + // for _ in 0..self.get_modifier_total(modifier) { + // self.gui_state.lock().set_inspect_offset(sd); + // } + // } + fn logs_horizontal_scroll(&self, modifier: KeyModifiers, sd: &ScrollDirection) { let panel = self.gui_state.lock().get_selected_panel(); if panel == SelectablePanel::Logs { @@ -393,22 +413,22 @@ impl InputHandler { self.gui_state.lock().status_del(Status::SearchLogs); } - _ if self.keymap.log_scroll_back.0 == key_code - || self.keymap.log_scroll_back.1 == Some(key_code) => + _ if self.keymap.scroll_back.0 == key_code + || self.keymap.scroll_back.1 == Some(key_code) => { - self.logs_horizontal_scroll(modifier, &ScrollDirection::Previous); + self.logs_horizontal_scroll(modifier, &ScrollDirection::Up); } - _ if self.keymap.log_scroll_forward.0 == key_code - || self.keymap.log_scroll_forward.1 == Some(key_code) => + _ if self.keymap.scroll_forward.0 == key_code + || self.keymap.scroll_forward.1 == Some(key_code) => { - self.logs_horizontal_scroll(modifier, &ScrollDirection::Next); + self.logs_horizontal_scroll(modifier, &ScrollDirection::Down); } _ if self.keymap.scroll_down.0 == key_code => { self.app_data .lock() - .log_search_scroll(&ScrollDirection::Next); + .log_search_scroll(&ScrollDirection::Down); // TODO should only do this is log_search_scroll returns some // Need to wait til app_data and gui_data is combined self.gui_state @@ -418,9 +438,7 @@ impl InputHandler { } _ if self.keymap.scroll_up.0 == key_code => { - self.app_data - .lock() - .log_search_scroll(&ScrollDirection::Previous); + self.app_data.lock().log_search_scroll(&ScrollDirection::Up); // TODO should only do this is log_search_scroll returns some // Need to wait til app_data and gui_data is combined self.gui_state @@ -439,6 +457,62 @@ impl InputHandler { } } + /// Actions to take when Filter status active + fn handle_inspect(&mut self, key_code: KeyCode, modifier: KeyModifiers) { + match key_code { + _ if self.keymap.inspect.0 == key_code + || self.keymap.inspect.1 == Some(key_code) + || self.keymap.clear.0 == key_code + || self.keymap.clear.1 == Some(key_code) => + { + self.app_data.lock().clear_inspect_data(); + self.gui_state.lock().clear_inspect_offset(); + self.gui_state.lock().status_del(Status::Inspect); + } + + _ if self.keymap.scroll_down.0 == key_code + || self.keymap.scroll_down.1 == Some(key_code) => + { + self.inspect_scroll(modifier, &ScrollDirection::Down); + } + + _ if self.keymap.scroll_up.0 == key_code + || self.keymap.scroll_up.1 == Some(key_code) => + { + self.inspect_scroll(modifier, &ScrollDirection::Up); + } + + _ if self.keymap.scroll_forward.0 == key_code + || self.keymap.scroll_forward.1 == Some(key_code) => + { + self.inspect_scroll(modifier, &ScrollDirection::Right); + } + + _ if self.keymap.scroll_back.0 == key_code + || self.keymap.scroll_back.1 == Some(key_code) => + { + self.inspect_scroll(modifier, &ScrollDirection::Left); + } + + _ if self.keymap.toggle_mouse_capture.0 == key_code + || self.keymap.toggle_mouse_capture.1 == Some(key_code) => + { + self.mouse_capture_key(); + } + _ if self.keymap.scroll_start.0 == key_code + || self.keymap.scroll_start.1 == Some(key_code) => + { + self.gui_state.lock().clear_inspect_offset(); + } + _ if self.keymap.scroll_end.0 == key_code + || self.keymap.scroll_end.1 == Some(key_code) => + { + self.gui_state.lock().set_inspect_offset_y_to_max(); + } + _ => (), + } + } + /// Actions to take when Filter status active fn handle_filter(&self, key_code: KeyCode) { match key_code { @@ -596,6 +670,10 @@ impl InputHandler { self.save_key().await; } + _ if self.keymap.inspect.0 == key_code || self.keymap.inspect.1 == Some(key_code) => { + self.inspect_key().await; + } + _ if self.keymap.select_next_panel.0 == key_code || self.keymap.select_next_panel.1 == Some(key_code) => { @@ -623,13 +701,13 @@ impl InputHandler { _ if self.keymap.scroll_up.0 == key_code || self.keymap.scroll_up.1 == Some(key_code) => { - self.scroll(modifier, &ScrollDirection::Previous); + self.scroll(modifier, &ScrollDirection::Up); } _ if self.keymap.scroll_down.0 == key_code || self.keymap.scroll_down.1 == Some(key_code) => { - self.scroll(modifier, &ScrollDirection::Next); + self.scroll(modifier, &ScrollDirection::Down); } _ if self.keymap.filter_mode.0 == key_code @@ -648,18 +726,17 @@ impl InputHandler { self.gui_state.lock().status_push(Status::SearchLogs); } - _ if self.keymap.log_scroll_back.0 == key_code - || self.keymap.log_scroll_back.1 == Some(key_code) => + _ if self.keymap.scroll_back.0 == key_code + || self.keymap.scroll_back.1 == Some(key_code) => { - self.logs_horizontal_scroll(modifier, &ScrollDirection::Previous); + self.logs_horizontal_scroll(modifier, &ScrollDirection::Up); // self.logs_back(modifier); } - _ if self.keymap.log_scroll_forward.0 == key_code - || self.keymap.log_scroll_forward.1 == Some(key_code) => + _ if self.keymap.scroll_forward.0 == key_code + || self.keymap.scroll_forward.1 == Some(key_code) => { - self.logs_horizontal_scroll(modifier, &ScrollDirection::Next); - // self.logs_forward(modifier); + self.logs_horizontal_scroll(modifier, &ScrollDirection::Down); } KeyCode::Enter => self.enter_key().await, @@ -678,6 +755,7 @@ impl InputHandler { let contains_filter = contains(Status::Filter); let contains_delete = contains(Status::DeleteConfirm); let contains_search_logs = contains(Status::SearchLogs); + let contains_inspect = contains(Status::Inspect); if !contains_exec { let is_q = || key_code == self.keymap.quit.0 || Some(key_code) == self.keymap.quit.1; @@ -698,6 +776,8 @@ impl InputHandler { self.handle_search_logs(key_code, key_modifier); } else if contains_delete { self.handle_delete(key_code).await; + } else if contains_inspect { + self.handle_inspect(key_code, key_modifier); } else { self.handle_others(key_code, key_modifier).await; } @@ -726,7 +806,18 @@ impl InputHandler { /// Handle mouse button events fn mouse_press(&self, mouse_event: MouseEvent, modifier: KeyModifiers) { let status = self.gui_state.lock().get_status(); - if status.contains(&Status::Help) { + + if status.contains(&Status::Inspect) { + match mouse_event.kind { + MouseEventKind::ScrollDown => self.inspect_scroll(modifier, &ScrollDirection::Down), + MouseEventKind::ScrollUp => self.inspect_scroll(modifier, &ScrollDirection::Up), + MouseEventKind::ScrollRight => { + self.inspect_scroll(modifier, &ScrollDirection::Right) + } + MouseEventKind::ScrollLeft => self.inspect_scroll(modifier, &ScrollDirection::Left), + _ => (), + } + } else if status.contains(&Status::Help) { let mouse_point = Rect::new(mouse_event.column, mouse_event.row, 1, 1); let help_intersect = self.gui_state.lock().get_intersect_help(mouse_point); if help_intersect { @@ -734,8 +825,9 @@ impl InputHandler { } } else { match mouse_event.kind { - MouseEventKind::ScrollUp => self.scroll(modifier, &ScrollDirection::Previous), - MouseEventKind::ScrollDown => self.scroll(modifier, &ScrollDirection::Next), + MouseEventKind::ScrollUp => self.scroll(modifier, &ScrollDirection::Up), + MouseEventKind::ScrollDown => self.scroll(modifier, &ScrollDirection::Down), + // TODO left and right for log offsets MouseEventKind::Down(MouseButton::Left) => { let mouse_point = Rect::new(mouse_event.column, mouse_event.row, 1, 1); let header = self.gui_state.lock().get_intersect_header(mouse_point); diff --git a/src/main.rs b/src/main.rs index 30ea787..8ad73f5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,3 @@ -#![allow(clippy::collapsible_if)] // #![allow(unused)] // Zigbuild is stuck on 1.87.0, which means Mac builds won't work when using collapsible ifs @@ -43,12 +42,18 @@ fn setup_tracing() { tracing_subscriber::fmt().with_max_level(Level::INFO).init(); } -/// Read the optional docker_host path, the cli args take priority over the DOCKER_HOST env +/// Read the optional docker_host path +/// Bollard will use DOCKER_HOST env, so might be pointless here, although it will fix it's priority over any config setting fn read_docker_host(config: &Config) -> Option { - config - .host - .as_ref() - .map_or_else(|| std::env::var(DOCKER_HOST).ok(), |x| Some(x.to_string())) + if let Some(x) = &config.host { + Some(x.to_string()) + } else if let Ok(env) = std::env::var(DOCKER_HOST) + && !env.trim().is_empty() + { + Some(env) + } else { + None + } } /// Create docker daemon handler, and only spawn up the docker data handler if a ping returns non-error @@ -60,25 +65,27 @@ async fn docker_init( ) { let host = read_docker_host(&app_data.lock().config); - let connection = host.map_or_else(Docker::connect_with_socket_defaults, |host| { - Docker::connect_with_socket(&host, 120, API_DEFAULT_VERSION) - }); - - if let Ok(docker) = connection { - if docker.ping().await.is_ok() { - tokio::spawn(DockerData::start( - Arc::clone(app_data), - docker, - docker_rx, - docker_tx, - Arc::clone(gui_state), - )); - return; - } + if let Ok(docker) = host + .as_ref() + .map_or_else(Docker::connect_with_defaults, |host| { + Docker::connect_with_socket(host, 120, API_DEFAULT_VERSION) + }) + && docker.ping().await.is_ok() + { + tokio::spawn(DockerData::start( + Arc::clone(app_data), + docker, + docker_rx, + docker_tx, + Arc::clone(gui_state), + )); + } else { + app_data.lock().set_error( + AppError::DockerConnect, + gui_state, + Status::DockerConnect(host), + ); } - app_data - .lock() - .set_error(AppError::DockerConnect, gui_state, Status::DockerConnect); } /// Create data for, and then spawn a tokio thread, for the input handler @@ -155,7 +162,7 @@ mod tests { use std::{str::FromStr, sync::Arc}; - use bollard::service::{ContainerSummary, Port}; + use bollard::service::{ContainerSummary, PortSummary}; use crate::{ app_data::{ @@ -169,23 +176,24 @@ mod tests { /// Default test config, has timestamps turned off pub fn gen_config() -> Config { Config { + app_colors: AppColors::new(), color_logs: false, + dir_save: None, + dir_config: None, docker_interval_ms: 1000, gui: true, host: None, - show_std_err: false, in_container: false, - save_dir: None, + keymap: Keymap::new(), log_search_case_sensitive: true, raw_logs: false, - show_self: false, - app_colors: AppColors::new(), - keymap: Keymap::new(), - timestamp_format: "HH:MM:SS.NNNNN dd-mm-yyyy".to_owned(), - show_timestamp: false, - use_cli: false, show_logs: true, + show_self: false, + show_std_err: false, + show_timestamp: false, + timestamp_format: "HH:MM:SS.NNNNN dd-mm-yyyy".to_owned(), timezone: None, + use_cli: false, } } @@ -211,6 +219,7 @@ mod tests { containers: StatefulList::new(containers.to_vec()), hidden_containers: vec![], current_sorted_id: vec![], + inspect_data: None, error: None, sorted_by: None, rerender: Arc::new(Rerender::new()), @@ -234,13 +243,14 @@ mod tests { pub fn gen_container_summary(index: usize, state: &str) -> ContainerSummary { ContainerSummary { image_manifest_descriptor: None, + health: None, id: Some(format!("{index}")), names: Some(vec![format!("container_{}", index)]), image: Some(format!("image_{index}")), image_id: Some(format!("{index}")), command: None, created: Some(i64::try_from(index).unwrap()), - ports: Some(vec![Port { + ports: Some(vec![PortSummary { ip: None, private_port: u16::try_from(index).unwrap_or(1) + 8000, public_port: None, diff --git a/src/ui/draw_blocks/chart_bandwidth.rs b/src/ui/draw_blocks/chart_bandwidth.rs new file mode 100644 index 0000000..79ffd48 --- /dev/null +++ b/src/ui/draw_blocks/chart_bandwidth.rs @@ -0,0 +1,700 @@ +use std::fmt::Display; + +use ratatui::{ + Frame, + layout::{Alignment, Rect}, + style::{Color, Modifier, Style, Stylize}, + symbols::{self, Marker}, + text::{Line, Span}, + widgets::{Axis, Block, BorderType, Borders, Chart, Dataset, GraphType}, +}; + +use super::FrameData; +use crate::{ + app_data::{State, Stats}, + config::AppColors, +}; + +fn make_chart<'a, T: Stats + Display>( + state: State, + colors: AppColors, + dataset: Vec>, + current_rx: &'a T, + max_rx: &'a T, + current_tx: &'a T, + max_tx: &'a T, +) -> Chart<'a> { + let gen_color = |state: &State, default: Color| { + if state.is_healthy() { + default + } else { + state.get_color(colors) + } + }; + + let mut labels = [ + Span::raw(""), + Span::styled( + format!("{max_rx}"), + Style::default() + .add_modifier(Modifier::BOLD) + .fg(gen_color(&state, colors.chart_bandwidth.max_rx)), + ), + Span::styled( + format!("{max_tx}"), + Style::default() + .add_modifier(Modifier::BOLD) + .fg(gen_color(&state, colors.chart_bandwidth.max_tx)), + ), + Span::raw(""), + ]; + + // Set the order of rx/tx on the y axis, based on which is the highest value + if max_rx.get_value() > max_tx.get_value() { + labels.reverse(); + } + + Chart::new(dataset) + .bg(colors.chart_bandwidth.background) + .block( + Block::default() + .title_alignment(Alignment::Center) + .title(Line::from(vec![ + Span::styled( + format!(" rx: {current_rx}"), + Style::default() + .add_modifier(Modifier::BOLD) + .fg(gen_color(&state, colors.chart_bandwidth.title_rx)), + ), + Span::styled( + format!(" tx: {current_tx} "), + Style::default() + .add_modifier(Modifier::BOLD) + .fg(gen_color(&state, colors.chart_bandwidth.title_tx)), + ), + ])) + .borders(Borders::ALL) + .border_type(BorderType::Rounded) + .border_style(Style::default().fg(colors.chart_bandwidth.border)), + ) + .x_axis(Axis::default().bounds([0.0, 60.0])) + .y_axis( + Axis::default() + .labels(labels) + .style(Style::default().fg(colors.chart_bandwidth.y_axis)) + .bounds([0.0, (max_rx.get_value()).max(max_tx.get_value()) + 0.01]), + ) +} + +/// Draw bandwidth chart +pub fn draw(area: Rect, colors: AppColors, f: &mut Frame, fd: &FrameData) { + if let Some(x) = fd.chart_data.as_ref() { + let mut dataset = vec![ + Dataset::default() + .marker(symbols::Marker::Dot) + .style(Style::default().fg(colors.chart_bandwidth.points_tx)) + .graph_type(GraphType::Line) + .marker(Marker::Dot) + .style(Style::default().fg(colors.chart_bandwidth.points_tx)) + .data(&x.tx.dataset), + ]; + dataset.extend(vec![ + Dataset::default() + .marker(symbols::Marker::Dot) + .style(Style::default().fg(colors.chart_bandwidth.points_rx)) + .marker(Marker::Dot) + .style(Style::default().fg(colors.chart_bandwidth.points_rx)) + .graph_type(GraphType::Line) + .data(&x.rx.dataset), + ]); + + let chart = make_chart( + x.state, + colors, + dataset, + &x.rx.current, + &x.rx.max, + &x.tx.current, + &x.tx.max, + ); + + f.render_widget(chart, area); + } +} + +#[cfg(test)] +#[allow(clippy::unwrap_used)] +mod tests { + use insta::assert_snapshot; + use ratatui::style::Color; + + use crate::{ + app_data::{ContainerId, NetworkBandwidth, State}, + config::AppColors, + ui::{ + FrameData, + draw_blocks::tests::{COLOR_RX, COLOR_TX, get_result, test_setup}, + }, + }; + + const TX_DOTS: [(usize, usize); 14] = [ + (1, 21), + (2, 19), + (2, 20), + (3, 18), + (3, 19), + (4, 10), + (4, 11), + (4, 17), + (4, 18), + (5, 16), + (6, 14), + (6, 15), + (7, 13), + (7, 14), + ]; + + const RX_DOTS: [(usize, usize); 15] = [ + (1, 21), + (2, 19), + (2, 20), + (3, 18), + (3, 19), + (4, 10), + (4, 11), + (4, 17), + (4, 18), + (5, 16), + (6, 16), + (6, 15), + (7, 13), + (7, 14), + (8, 13), + ]; + + const COMBINED_DOTS_RX: [(usize, usize); 15] = [ + (1, 21), + (2, 19), + (2, 20), + (3, 18), + (3, 19), + (4, 10), + (4, 11), + (4, 17), + (4, 18), + (5, 16), + (6, 15), + (6, 16), + (7, 13), + (7, 14), + (8, 13), + ]; + + const COMBINED_DOTS_TX: [(usize, usize); 8] = [ + (7, 19), + (7, 20), + (7, 21), + (8, 14), + (8, 15), + (8, 16), + (8, 17), + (8, 18), + ]; + + #[test] + /// When status is Running, but not data, charts drawn without dots etc, colours correct + fn test_draw_blocks_charts_running_none() { + let mut setup = test_setup(40, 10, true, true); + + let fd = FrameData::from((&setup.app_data, &setup.gui_state)); + setup + .terminal + .draw(|f| { + super::draw(setup.area, setup.app_data.lock().config.app_colors, f, &fd); + }) + .unwrap(); + assert_snapshot!(setup.terminal.backend()); + for (row_index, result_row) in get_result(&setup) { + for (result_cell_index, result_cell) in result_row.iter().enumerate() { + match (row_index, result_cell_index) { + // border + (9, _) | (1..=9, 0 | 39) => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, Color::White); + } + // Border first row only + (0, 0..=4 | 34..=39) => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, Color::White); + } + // Title RX + (0, 5..=18) => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, COLOR_RX); + } + // Title TX + (0, 19..=33) => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, COLOR_TX); + } + // Y axis + (1..=8, 10) => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, Color::White); + } + // TX max + (4, 1..=9) => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, COLOR_TX); + } + // RX max + (6, 1..=9) => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, COLOR_RX); + } + _ => { + assert_eq!(result_cell.fg, Color::Reset); + assert_eq!(result_cell.bg, Color::Reset); + } + } + } + } + } + + #[test] + /// Test with TX data + fn test_draw_blocks_charts_running_with_data_tx() { + let mut setup = test_setup(40, 10, true, true); + let mut tx = NetworkBandwidth::new(); + + for i in 0..=20 { + tx.push(1000 * i * (10 + 5 * i)); + } + + if let Some(item) = setup + .app_data + .lock() + .get_container_by_id(&ContainerId::from("1")) + { + item.tx = tx; + } + + let fd = FrameData::from((&setup.app_data, &setup.gui_state)); + + setup + .terminal + .draw(|f| { + super::draw(setup.area, setup.app_data.lock().config.app_colors, f, &fd); + }) + .unwrap(); + assert_snapshot!(setup.terminal.backend()); + for (row_index, result_row) in get_result(&setup) { + for (result_cell_index, result_cell) in result_row.iter().enumerate() { + match (row_index, result_cell_index) { + // border + (9, _) | (1..=9, 0 | 39) => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, Color::White); + } + // Border first row only + (0, 0..=3 | 35..=39) => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, Color::White); + } + // Title RX + (0, 4..=17) => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, COLOR_RX); + } + // Title TX + (0, 18..=34) => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, COLOR_TX); + } + // Y axis + (1..=8, 12) => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, Color::White); + } + // TX max + (4, 1..=9) => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, COLOR_TX); + } + // RX max + (6, 1..=9) => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, COLOR_RX); + } + // TX dots + x if TX_DOTS.contains(&(row_index, result_cell_index)) => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, COLOR_TX); + } + _ => { + assert_eq!(result_cell.fg, Color::Reset); + assert_eq!(result_cell.bg, Color::Reset); + } + } + } + } + } + + #[test] + /// Test with RX data + fn test_draw_blocks_charts_running_with_data_rx() { + let mut setup = test_setup(40, 10, true, true); + let mut rx = NetworkBandwidth::new(); + + for i in 0..=20 { + rx.push(2000 * i * (10 + 7 * i)); + } + + if let Some(item) = setup + .app_data + .lock() + .get_container_by_id(&ContainerId::from("1")) + { + item.rx = rx; + } + + let fd = FrameData::from((&setup.app_data, &setup.gui_state)); + + setup + .terminal + .draw(|f| { + super::draw(setup.area, setup.app_data.lock().config.app_colors, f, &fd); + }) + .unwrap(); + assert_snapshot!(setup.terminal.backend()); + for (row_index, result_row) in get_result(&setup) { + for (result_cell_index, result_cell) in result_row.iter().enumerate() { + match (row_index, result_cell_index) { + // border + (9, _) | (1..=9, 0 | 39) => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, Color::White); + } + // Border first row only + (0, 0..=3 | 35..=39) => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, Color::White); + } + // Title RX + (0, 4..=19) => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, COLOR_RX); + } + // Title TX + (0, 20..=34) => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, COLOR_TX); + } + // Y axis + (1..=8, 12) => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, Color::White); + } + // RX max + (4, 1..=9) => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, COLOR_RX); + } + // TX max + (6, 1..=9) => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, COLOR_TX); + } + // RX dots + x if RX_DOTS.contains(&(row_index, result_cell_index)) => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, COLOR_RX); + } + _ => { + assert_eq!(result_cell.fg, Color::Reset); + assert_eq!(result_cell.bg, Color::Reset); + } + } + } + } + } + + #[test] + /// Test with RX & TX data + fn test_draw_blocks_charts_running_with_data_tx_and_rx() { + let mut setup = test_setup(40, 10, true, true); + let mut rx = NetworkBandwidth::new(); + let mut tx = NetworkBandwidth::new(); + for i in 0..=20 { + rx.push(2000 * i * (10 + 7 * i)); + tx.push(200 * i * (10 + 7 * i)); + } + + if let Some(item) = setup + .app_data + .lock() + .get_container_by_id(&ContainerId::from("1")) + { + item.rx = rx; + item.tx = tx; + } + + let fd = FrameData::from((&setup.app_data, &setup.gui_state)); + + setup + .terminal + .draw(|f| { + super::draw(setup.area, setup.app_data.lock().config.app_colors, f, &fd); + }) + .unwrap(); + assert_snapshot!(setup.terminal.backend()); + for (row_index, result_row) in get_result(&setup) { + for (result_cell_index, result_cell) in result_row.iter().enumerate() { + match (row_index, result_cell_index) { + // border + (9, _) | (1..=9, 0 | 39) => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, Color::White); + } + // Border first row only + (0, 0..=3 | 36..=39) => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, Color::White); + } + // Title RX + (0, 4..=19) => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, COLOR_RX); + } + // Title TX + (0, 20..=35) => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, COLOR_TX); + } + // Y axis + (1..=8, 12) => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, Color::White); + } + // RX max + (4, 1..=9) => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, COLOR_RX); + } + // TX max + (6, 1..=10) => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, COLOR_TX); + } + // TX dots + x if COMBINED_DOTS_TX.contains(&(row_index, result_cell_index)) => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, COLOR_TX); + } + // RX dots + x if COMBINED_DOTS_RX.contains(&(row_index, result_cell_index)) => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, COLOR_RX); + } + _ => { + assert_eq!(result_cell.fg, Color::Reset); + assert_eq!(result_cell.bg, Color::Reset); + } + } + } + } + } + + #[test] + /// Whens status paused, some text is now Yellow + fn test_draw_blocks_charts_paused() { + let mut setup = test_setup(40, 10, true, true); + setup.app_data.lock().containers.items[0].state = State::Paused; + + let fd = FrameData::from((&setup.app_data, &setup.gui_state)); + setup + .terminal + .draw(|f| { + super::draw(setup.area, setup.app_data.lock().config.app_colors, f, &fd); + }) + .unwrap(); + assert_snapshot!(setup.terminal.backend()); + for (row_index, result_row) in get_result(&setup) { + for (result_cell_index, result_cell) in result_row.iter().enumerate() { + match (row_index, result_cell_index) { + // border + (9, _) | (1..=9, 0 | 39) => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, Color::White); + } + // Border first row only + (0, 0..=4 | 34..=39) => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, Color::White); + } + // Title & y-axis max + (0, 5..=33) | (4 | 6, 1..=9) => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, Color::Yellow); + } + // Y axis + (1..=8, 10) => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, Color::White); + } + _ => { + assert_eq!(result_cell.fg, Color::Reset); + assert_eq!(result_cell.bg, Color::Reset); + } + } + } + } + } + + #[test] + /// Whens status dead, some text is now red + fn test_draw_blocks_charts_dead() { + let mut setup = test_setup(40, 10, true, true); + setup.app_data.lock().containers.items[0].state = State::Dead; + + let fd = FrameData::from((&setup.app_data, &setup.gui_state)); + setup + .terminal + .draw(|f| { + super::draw(setup.area, setup.app_data.lock().config.app_colors, f, &fd); + }) + .unwrap(); + assert_snapshot!(setup.terminal.backend()); + for (row_index, result_row) in get_result(&setup) { + for (result_cell_index, result_cell) in result_row.iter().enumerate() { + match (row_index, result_cell_index) { + // border + (9, _) | (1..=9, 0 | 39) => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, Color::White); + } + // Border first row only + (0, 0..=4 | 34..=39) => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, Color::White); + } + // Title & y-axis max + (0, 5..=33) | (4 | 6, 1..=9) => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, Color::Red); + } + // Y axis + (1..=8, 10) => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, Color::White); + } + _ => { + assert_eq!(result_cell.fg, Color::Reset); + assert_eq!(result_cell.bg, Color::Reset); + } + } + } + } + } + + #[test] + /// Custom colours correctly applied to each part of the charts + fn test_draw_blocks_charts_custom_colors() { + let mut colors = AppColors::new(); + + colors.chart_bandwidth.background = Color::White; + colors.chart_bandwidth.border = Color::Red; + colors.chart_bandwidth.max_rx = Color::Green; + colors.chart_bandwidth.max_tx = Color::Magenta; + colors.chart_bandwidth.title_rx = Color::LightGreen; + colors.chart_bandwidth.title_tx = Color::LightRed; + colors.chart_bandwidth.points_rx = Color::Black; + colors.chart_bandwidth.points_tx = Color::Blue; + colors.chart_bandwidth.y_axis = Color::Yellow; + + let mut setup = test_setup(40, 10, true, true); + + let mut rx = NetworkBandwidth::new(); + let mut tx = NetworkBandwidth::new(); + for i in 0..=20 { + rx.push(2000 * i * (10 + 7 * i)); + tx.push(200 * i * (10 + 7 * i)); + } + + if let Some(item) = setup + .app_data + .lock() + .get_container_by_id(&ContainerId::from("1")) + { + item.rx = rx; + item.tx = tx; + } + + let fd = FrameData::from((&setup.app_data, &setup.gui_state)); + + setup + .terminal + .draw(|f| { + super::draw(setup.area, colors, f, &fd); + }) + .unwrap(); + + assert_snapshot!(setup.terminal.backend()); + + for (row_index, result_row) in get_result(&setup) { + for (result_cell_index, result_cell) in result_row.iter().enumerate() { + match (row_index, result_cell_index) { + // border + (9, _) | (1..=9, 0 | 39) => { + assert_eq!(result_cell.bg, Color::White); + assert_eq!(result_cell.fg, Color::Red); + } + // Border first row only + (0, 0..=3 | 36..=39) => { + assert_eq!(result_cell.bg, Color::White); + assert_eq!(result_cell.fg, Color::Red); + } + // Title RX + (0, 4..=19) => { + assert_eq!(result_cell.bg, Color::White); + assert_eq!(result_cell.fg, Color::LightGreen); + } + // Title TX + (0, 20..=35) => { + assert_eq!(result_cell.bg, Color::White); + assert_eq!(result_cell.fg, Color::LightRed); + } + // Y axis + (1..=8, 12) => { + assert_eq!(result_cell.bg, Color::White); + assert_eq!(result_cell.fg, Color::Yellow); + } + // RX max + (4, 1..=11) => { + assert_eq!(result_cell.bg, Color::White); + assert_eq!(result_cell.fg, Color::Green); + } + // TX max + (6, 1..=10) => { + assert_eq!(result_cell.bg, Color::White); + assert_eq!(result_cell.fg, Color::Magenta); + } + // TX dots + x if COMBINED_DOTS_TX.contains(&(row_index, result_cell_index)) => { + assert_eq!(result_cell.bg, Color::White); + assert_eq!(result_cell.fg, Color::Blue); + } + // RX dots + x if COMBINED_DOTS_RX.contains(&(row_index, result_cell_index)) => { + assert_eq!(result_cell.bg, Color::White); + assert_eq!(result_cell.fg, Color::Black); + } + _ => { + assert_eq!(result_cell.bg, Color::White); + assert_eq!(result_cell.fg, Color::Reset); + } + } + } + } + } +} diff --git a/src/ui/draw_blocks/charts.rs b/src/ui/draw_blocks/chart_cpu_mem.rs similarity index 93% rename from src/ui/draw_blocks/charts.rs rename to src/ui/draw_blocks/chart_cpu_mem.rs index 6926213..1cd9952 100644 --- a/src/ui/draw_blocks/charts.rs +++ b/src/ui/draw_blocks/chart_cpu_mem.rs @@ -11,7 +11,7 @@ use ratatui::{ use super::{CONSTRAINT_50_50, FrameData}; use crate::{ - app_data::{ByteStats, CpuStats, State, Stats}, + app_data::{State, Stats}, config::AppColors, }; @@ -118,7 +118,7 @@ fn make_chart<'a, T: Stats + Display>( /// Draw the cpu + mem charts pub fn draw(area: Rect, colors: AppColors, f: &mut Frame, fd: &FrameData) { - if let Some((cpu, mem)) = fd.chart_data.as_ref() { + if let Some(x) = fd.chart_data.as_ref() { let area = Layout::default() .direction(Direction::Horizontal) .constraints(CONSTRAINT_50_50) @@ -129,34 +129,34 @@ pub fn draw(area: Rect, colors: AppColors, f: &mut Frame, fd: &FrameData) { .marker(symbols::Marker::Dot) .style(Style::default().fg(colors.chart_cpu.points)) .graph_type(GraphType::Line) - .data(&cpu.0), + .data(&x.cpu.dataset), ]; let mem_dataset = vec![ Dataset::default() .marker(symbols::Marker::Dot) .style(Style::default().fg(colors.chart_memory.points)) .graph_type(GraphType::Line) - .data(&mem.0), + .data(&x.memory.dataset), ]; - let cpu_stats = CpuStats::new(cpu.0.last().map_or(0.00, |f| f.1)); - #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] - let mem_stats = ByteStats::new(mem.0.last().map_or(0, |f| f.1 as u64)); + // let cpu_stats = CpuStats::new(cpu.0.last().map_or(0.00, |f| f.1)); + // #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] + // let mem_stats = ByteStats::new(mem.0.last().map_or(0, |f| f.1 as u64)); let cpu_chart = make_chart( ChartVariant::Cpu, colors, - &cpu_stats, + &x.cpu.current, cpu_dataset, - &cpu.1, - cpu.2, + &x.cpu.max, + x.state, ); let mem_chart = make_chart( ChartVariant::Memory, colors, - &mem_stats, + &x.memory.current, mem_dataset, - &mem.1, - mem.2, + &x.memory.max, + x.state, ); f.render_widget(cpu_chart, area[0]); @@ -175,7 +175,7 @@ mod tests { config::AppColors, ui::{ FrameData, - draw_blocks::tests::{COLOR_ORANGE, get_result, insert_chart_data, test_setup}, + draw_blocks::tests::{COLOR_ORANGE, get_result, insert_all_chart_data, test_setup}, }, }; @@ -194,42 +194,41 @@ mod tests { ]; // co-ordinates of the dots from the cpu chart - const CPU_XY: [(usize, usize); 15] = [ - (1, 12), - (2, 11), + const CPU_XY: [(usize, usize); 16] = [ + (1, 13), (2, 12), - (3, 10), + (2, 13), (3, 11), - (3, 12), - (4, 10), - (4, 12), - (5, 9), + (3, 13), + (4, 11), + (4, 13), + (5, 10), (5, 13), - (5, 14), - (6, 8), + (6, 9), (6, 13), + (6, 14), (7, 8), + (7, 9), (7, 13), + (7, 14), ]; // co-ordinates of the dots from the memory chart - const MEM_XY: [(usize, usize); 16] = [ - (1, 54), + const MEM_XY: [(usize, usize); 14] = [ (1, 55), (2, 54), (2, 55), - (3, 53), + (3, 54), (3, 55), - (4, 52), + (4, 53), (4, 55), - (5, 51), (5, 52), - (5, 55), + (5, 53), (5, 56), - (6, 51), - (6, 55), + (6, 52), + (6, 56), (7, 51), - (7, 55), + (7, 56), ]; #[test] @@ -275,7 +274,7 @@ mod tests { fn test_draw_blocks_charts_running_some() { let mut setup = test_setup(80, 10, true, true); - insert_chart_data(&setup); + insert_all_chart_data(&setup); let fd = FrameData::from((&setup.app_data, &setup.gui_state)); setup @@ -324,7 +323,7 @@ mod tests { fn test_draw_blocks_charts_paused() { let mut setup = test_setup(80, 10, true, true); - insert_chart_data(&setup); + insert_all_chart_data(&setup); setup.app_data.lock().containers.items[0].state = State::Paused; let fd = FrameData::from((&setup.app_data, &setup.gui_state)); @@ -336,6 +335,7 @@ mod tests { .unwrap(); assert_snapshot!(setup.terminal.backend()); + // for (row_index, result_row) in get_result(&setup) { for (result_cell_index, result_cell) in result_row.iter().enumerate() { @@ -369,7 +369,7 @@ mod tests { /// When dead, text is red fn test_draw_blocks_charts_dead() { let mut setup = test_setup(80, 10, true, true); - insert_chart_data(&setup); + insert_all_chart_data(&setup); setup.app_data.lock().containers.items[0].state = State::Dead; let fd = FrameData::from((&setup.app_data, &setup.gui_state)); @@ -429,7 +429,7 @@ mod tests { let mut setup = test_setup(80, 10, true, true); - insert_chart_data(&setup); + insert_all_chart_data(&setup); let fd = FrameData::from((&setup.app_data, &setup.gui_state)); setup diff --git a/src/ui/draw_blocks/commands.rs b/src/ui/draw_blocks/commands.rs index d01af86..2ca6221 100644 --- a/src/ui/draw_blocks/commands.rs +++ b/src/ui/draw_blocks/commands.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use super::RIGHT_ARROW; +use super::SELECT_ARROW; use crate::{ app_data::AppData, config::AppColors, @@ -44,7 +44,7 @@ pub fn draw( let items = List::new(items) .block(block) .highlight_style(Style::default().add_modifier(Modifier::BOLD)) - .highlight_symbol(RIGHT_ARROW); + .highlight_symbol(SELECT_ARROW); f.render_stateful_widget(items, area, i); } else { let paragraph = Paragraph::new("").block(block).alignment(Alignment::Center); @@ -173,7 +173,7 @@ mod tests { setup .app_data .lock() - .docker_controls_scroll(&ScrollDirection::Next); + .docker_controls_scroll(&ScrollDirection::Down); setup .terminal @@ -370,7 +370,7 @@ mod tests { setup .app_data .lock() - .docker_controls_scroll(&ScrollDirection::Next); + .docker_controls_scroll(&ScrollDirection::Down); setup .terminal diff --git a/src/ui/draw_blocks/containers.rs b/src/ui/draw_blocks/containers.rs index a63283c..7078d1b 100644 --- a/src/ui/draw_blocks/containers.rs +++ b/src/ui/draw_blocks/containers.rs @@ -82,11 +82,19 @@ fn format_containers<'a>(colors: AppColors, i: &ContainerItem, widths: &Columns) colors.containers.text, ), Span::styled( - format!("{:>width$}{MARGIN}", i.rx, width = widths.net_rx.1.into()), + format!( + "{:>width$}{MARGIN}", + i.rx.current_total(), + width = widths.net_rx.1.into() + ), Style::default().fg(colors.containers.text_rx), ), Span::styled( - format!("{:>width$}{MARGIN}", i.tx, width = widths.net_tx.1.into()), + format!( + "{:>width$}{MARGIN}", + i.tx.current_total(), + width = widths.net_tx.1.into() + ), Style::default().fg(colors.containers.text_tx), ), ]) diff --git a/src/ui/draw_blocks/error.rs b/src/ui/draw_blocks/error.rs index 0d69f12..ddb85ef 100644 --- a/src/ui/draw_blocks/error.rs +++ b/src/ui/draw_blocks/error.rs @@ -22,6 +22,7 @@ pub fn draw( colors: AppColors, error: &AppError, f: &mut Frame, + host: Option, keymap: &Keymap, seconds: Option, ) { @@ -31,13 +32,21 @@ pub fn draw( .title_alignment(Alignment::Center) .borders(Borders::ALL); - let to_push = if matches!(error, AppError::DockerConnect) { - format!( - "\n\n {}::v{} closing in {:02} seconds", + let mut text = format!("\n{error}"); + + if error == &AppError::DockerConnect { + let s = if let Some(host) = host { + format!(" @ \"{host}\"") + } else { + String::new() + }; + text.push_str(&format!( + "{}\n\n {}::v{} closing in {:02} seconds", + s, NAME, VERSION, - seconds.unwrap_or(5) - ) + seconds.unwrap_or(5), + )) } else { let clear_text = if keymap.clear == Keymap::new().clear { format!("( {} ) {SUFFIX_CLEAR}", keymap.clear.0) @@ -46,20 +55,17 @@ pub fn draw( } else { format!(" ( {} ) {SUFFIX_CLEAR}", keymap.clear.0) }; + text.push_str(&format!("\n\n{clear_text}")); + } - let quit_text = if keymap.quit == Keymap::new().quit { - format!("( {} ) {SUFFIX_QUIT}", keymap.quit.0) - } else if let Some(secondary) = keymap.quit.1 { - format!(" ( {} | {secondary} ) {SUFFIX_QUIT}", keymap.quit.0) - } else { - format!(" ( {} ) {SUFFIX_QUIT}", keymap.quit.0) - }; - format!("\n\n{clear_text}\n\n{quit_text}") + let quit_text = if keymap.quit == Keymap::new().quit { + format!("( {} ) {SUFFIX_QUIT}", keymap.quit.0) + } else if let Some(secondary) = keymap.quit.1 { + format!(" ( {} | {secondary} ) {SUFFIX_QUIT}", keymap.quit.0) + } else { + format!(" ( {} ) {SUFFIX_QUIT}", keymap.quit.0) }; - - let mut text = format!("\n{error}"); - - text.push_str(to_push.as_str()); + text.push_str(&format!("\n\n{quit_text}")); // Find the maximum line width & height let padded_width = max_line_width(&text) + 8; @@ -106,8 +112,7 @@ mod tests { #[test] /// Test that the error popup is centered, red background, white border, white text, and displays the correct text fn test_draw_blocks_error_docker_connect_error() { - let mut setup = test_setup(46, 9, true, true); - + let mut setup = test_setup(50, 11, true, true); setup .terminal .draw(|f| { @@ -115,6 +120,7 @@ mod tests { AppColors::new(), &AppError::DockerConnect, f, + None, &Keymap::new(), Some(4), ); @@ -123,12 +129,50 @@ mod tests { assert_snapshot!(setup.terminal.backend()); for (row_index, result_row) in get_result(&setup) { for (result_cell_index, result_cell) in result_row.iter().enumerate() { - if let (0 | 8, _) = (row_index, result_cell_index) { - assert_eq!(result_cell.bg, Color::Reset); - assert_eq!(result_cell.fg, Color::Reset); - } else { - assert_eq!(result_cell.bg, Color::Red); - assert_eq!(result_cell.fg, Color::White); + match (row_index, result_cell_index) { + (0 | 10, _) | (_, 0 | 49) => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, Color::Reset); + } + _ => { + assert_eq!(result_cell.bg, Color::Red); + assert_eq!(result_cell.fg, Color::White); + } + } + } + } + } + + #[test] + /// Test that the error popup is centered, red background, white border, white text, and displays the correct text with the custom docker host address + fn test_draw_blocks_error_docker_connect_error_custom_host() { + let mut setup = test_setup(60, 11, true, true); + + setup + .terminal + .draw(|f| { + super::draw( + AppColors::new(), + &AppError::DockerConnect, + f, + Some("/test/host.sock".to_owned()), + &Keymap::new(), + Some(4), + ); + }) + .unwrap(); + assert_snapshot!(setup.terminal.backend()); + for (row_index, result_row) in get_result(&setup) { + for (result_cell_index, result_cell) in result_row.iter().enumerate() { + match (row_index, result_cell_index) { + (0 | 10, _) | (_, 0 | 59) => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, Color::Reset); + } + _ => { + assert_eq!(result_cell.bg, Color::Red); + assert_eq!(result_cell.fg, Color::White); + } } } } @@ -146,6 +190,7 @@ mod tests { AppColors::new(), &AppError::DockerExec, f, + None, &Keymap::new(), Some(4), ); @@ -183,7 +228,14 @@ mod tests { setup .terminal .draw(|f| { - super::draw(colors, &AppError::DockerExec, f, &Keymap::new(), Some(4)); + super::draw( + colors, + &AppError::DockerExec, + f, + None, + &Keymap::new(), + Some(4), + ); }) .unwrap(); @@ -218,7 +270,14 @@ mod tests { setup .terminal .draw(|f| { - super::draw(AppColors::new(), &AppError::DockerExec, f, &keymap, None); + super::draw( + AppColors::new(), + &AppError::DockerExec, + f, + None, + &keymap, + None, + ); }) .unwrap(); assert_snapshot!(setup.terminal.backend()); @@ -235,7 +294,14 @@ mod tests { setup .terminal .draw(|f| { - super::draw(AppColors::new(), &AppError::DockerExec, f, &keymap, None); + super::draw( + AppColors::new(), + &AppError::DockerExec, + f, + None, + &keymap, + None, + ); }) .unwrap(); assert_snapshot!(setup.terminal.backend()); @@ -252,7 +318,14 @@ mod tests { setup .terminal .draw(|f| { - super::draw(AppColors::new(), &AppError::DockerExec, f, &keymap, None); + super::draw( + AppColors::new(), + &AppError::DockerExec, + f, + None, + &keymap, + None, + ); }) .unwrap(); assert_snapshot!(setup.terminal.backend()); diff --git a/src/ui/draw_blocks/filter.rs b/src/ui/draw_blocks/filter.rs index 101ce2d..1091cee 100644 --- a/src/ui/draw_blocks/filter.rs +++ b/src/ui/draw_blocks/filter.rs @@ -5,7 +5,14 @@ use ratatui::{ text::{Line, Span}, }; -use crate::{app_data::FilterBy, config::AppColors, ui::FrameData}; +use crate::{ + app_data::FilterBy, + config::AppColors, + ui::{ + FrameData, + draw_blocks::{LEFT_ARROW, RIGHT_ARROW}, + }, +}; /// Create the filter_by by spans, coloured dependant on which one is selected fn filter_by_spans(colors: AppColors, fd: &'_ FrameData) -> [Span<'_>; 4] { @@ -46,7 +53,7 @@ pub fn draw(area: Rect, colors: AppColors, frame: &mut Frame, fd: &FrameData) { let mut line = vec![ Span::styled(" Esc ", style_but), Span::styled(" clear ", style_desc), - Span::styled(" โ† by โ†’ ", style_but), + Span::styled(format!(" {LEFT_ARROW} by {RIGHT_ARROW} "), style_but), Span::from(" "), ]; line.extend_from_slice(&filter_by_spans(colors, fd)); diff --git a/src/ui/draw_blocks/headers.rs b/src/ui/draw_blocks/headers.rs index 0e24e22..01a0e7d 100644 --- a/src/ui/draw_blocks/headers.rs +++ b/src/ui/draw_blocks/headers.rs @@ -39,14 +39,14 @@ fn gen_header<'a>( fn gen_header_block<'a>(colors: AppColors, fd: &FrameData, header: Header) -> (Color, &'a str) { let mut color = colors.headers_bar.text; let mut suffix = ""; - if let Some((a, b)) = &fd.sorted_by { - if &header == a { - match b { - SortedOrder::Asc => suffix = " โ–ฒ", - SortedOrder::Desc => suffix = " โ–ผ", - } - color = colors.headers_bar.text_selected; + if let Some((a, b)) = &fd.sorted_by + && &header == a + { + match b { + SortedOrder::Asc => suffix = " โ–ฒ", + SortedOrder::Desc => suffix = " โ–ผ", } + color = colors.headers_bar.text_selected; } (color, suffix) diff --git a/src/ui/draw_blocks/help.rs b/src/ui/draw_blocks/help.rs index 7e16abc..1c1fd76 100644 --- a/src/ui/draw_blocks/help.rs +++ b/src/ui/draw_blocks/help.rs @@ -1,410 +1,751 @@ -use crossterm::event::KeyCode; -use jiff::tz::TimeZone; +use std::sync::LazyLock; + use ratatui::{ Frame, - layout::{Alignment, Constraint, Direction, Layout}, - style::{Color, Modifier, Style}, + layout::{Alignment, Constraint, Direction, Layout, Rect, Size}, + style::{Color, Style, Stylize}, text::{Line, Span}, - widgets::{Block, BorderType, Borders, Clear, Paragraph}, + widgets::{Block, BorderType, Borders, Clear, Padding, Paragraph}, }; use crate::{ - config::{AppColors, Keymap}, + config::{AppColors, Config, Keymap}, ui::gui_state::BoxLocation, }; use super::{DESCRIPTION, NAME_TEXT, REPO, VERSION, popup}; -/// Help popup box needs these three pieces of information -struct HelpInfo { - lines: Vec>, - width: usize, - height: usize, +macro_rules! to_u16 { + ($value:expr) => { + u16::try_from($value).unwrap_or_default() + }; } -impl HelpInfo { - /// Find the max width of a Span in &[Line] - fn calc_width(lines: &[Line]) -> usize { - lines - .iter() - .map(ratatui::prelude::Line::width) - .max() - .unwrap_or(1) +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +enum KeyDescriptions { + Clear, + Command, + Exec, + FilterMode, + Help, + InspectMode, + LogHeight, + LogVisibility, + MouseCapture, + Panel, + Quit, + Redraw, + Save, + ScrollEnd, + ScrollH, + ScrollSpeed, + ScrollStart, + ScrollV, + SearchMode, + SortCpu, + SortHeader, + SortId, + SortImage, + SortMem, + SortName, + SortRX, + SortState, + SortStatus, + SortStop, + SortTX, +} + +type Column = Vec<(Vec>, KeyDescriptions)>; + +#[derive(Debug, Clone, Hash)] +struct KeymapColumns { + left: Column, + right: Column, +} + +impl KeymapColumns { + fn default(keymap: &Keymap) -> Self { + Self { + left: vec![ + ( + vec![ + Some(keymap.quit.0.to_string()), + keymap.quit.1.as_ref().map(|i| i.to_string()), + ], + KeyDescriptions::Quit, + ), + ( + vec![ + Some(keymap.scroll_down.0.to_string()), + Some(keymap.scroll_up.0.to_string()), + keymap.scroll_down.1.as_ref().map(|i| i.to_string()), + keymap.scroll_up.1.as_ref().map(|i| i.to_string()), + Some(keymap.scroll_start.0.to_string()), + Some(keymap.scroll_end.0.to_string()), + keymap.scroll_start.1.as_ref().map(|i| i.to_string()), + keymap.scroll_end.1.as_ref().map(|i| i.to_string()), + ], + KeyDescriptions::ScrollV, + ), + ( + vec![Some(keymap.scroll_many.to_string())], + KeyDescriptions::ScrollSpeed, + ), + ( + vec![ + Some(keymap.exec.0.to_string()), + keymap.exec.1.as_ref().map(|i| i.to_string()), + ], + KeyDescriptions::Exec, + ), + ( + vec![ + Some(keymap.filter_mode.0.to_string()), + keymap.filter_mode.1.as_ref().map(|i| i.to_string()), + ], + KeyDescriptions::FilterMode, + ), + ( + vec![ + Some(keymap.toggle_help.0.to_string()), + keymap.toggle_help.1.as_ref().map(|i| i.to_string()), + ], + KeyDescriptions::Help, + ), + ( + vec![ + Some(keymap.log_section_height_decrease.0.to_string()), + Some(keymap.log_section_height_increase.0.to_string()), + keymap + .log_section_height_decrease + .1 + .as_ref() + .map(|i| i.to_string()), + keymap + .log_section_height_increase + .1 + .as_ref() + .map(|i| i.to_string()), + ], + KeyDescriptions::LogHeight, + ), + (vec![Some("1 ~ 9".to_owned())], KeyDescriptions::SortHeader), + ( + vec![ + Some(keymap.select_next_panel.0.to_string()), + Some(keymap.select_previous_panel.0.to_string()), + keymap + .select_previous_panel + .1 + .as_ref() + .map(|i| i.to_string()), + keymap.select_next_panel.1.as_ref().map(|i| i.to_string()), + ], + KeyDescriptions::Panel, + ), + ( + vec![ + Some(keymap.save_logs.0.to_string()), + keymap.save_logs.1.as_ref().map(|i| i.to_string()), + ], + KeyDescriptions::Save, + ), + ], + right: vec![ + ( + vec![ + Some(keymap.clear.0.to_string()), + keymap.clear.1.as_ref().map(|i| i.to_string()), + ], + KeyDescriptions::Clear, + ), + ( + vec![ + Some(keymap.scroll_back.0.to_string()), + Some(keymap.scroll_forward.0.to_string()), + keymap.scroll_back.1.as_ref().map(|i| i.to_string()), + keymap.scroll_forward.1.as_ref().map(|i| i.to_string()), + ], + KeyDescriptions::ScrollH, + ), + (vec![Some(String::from("Enter"))], KeyDescriptions::Command), + ( + vec![ + Some(keymap.inspect.0.to_string()), + keymap.inspect.1.as_ref().map(|i| i.to_string()), + ], + KeyDescriptions::InspectMode, + ), + ( + vec![ + Some(keymap.log_search_mode.0.to_string()), + keymap.log_search_mode.1.as_ref().map(|i| i.to_string()), + ], + KeyDescriptions::SearchMode, + ), + ( + vec![ + Some(keymap.force_redraw.0.to_string()), + keymap.force_redraw.1.as_ref().map(|i| i.to_string()), + ], + KeyDescriptions::Redraw, + ), + ( + vec![ + Some(keymap.log_section_toggle.0.to_string()), + keymap.log_section_toggle.1.as_ref().map(|i| i.to_string()), + ], + KeyDescriptions::LogVisibility, + ), + (vec![Some("0".to_owned())], KeyDescriptions::SortStop), + ( + vec![ + Some(keymap.toggle_mouse_capture.0.to_string()), + keymap + .toggle_mouse_capture + .1 + .as_ref() + .map(|i| i.to_string()), + ], + KeyDescriptions::MouseCapture, + ), + ], + } } + fn custom(config: &Config) -> Self { + Self { + left: vec![ + ( + vec![ + Some(config.keymap.quit.0.to_string()), + config.keymap.quit.1.as_ref().map(|i| i.to_string()), + ], + KeyDescriptions::Quit, + ), + ( + vec![ + Some(config.keymap.scroll_down.0.to_string()), + Some(config.keymap.scroll_up.0.to_string()), + config.keymap.scroll_down.1.as_ref().map(|i| i.to_string()), + config.keymap.scroll_up.1.as_ref().map(|i| i.to_string()), + ], + KeyDescriptions::ScrollV, + ), + ( + vec![ + Some(config.keymap.scroll_start.0.to_string()), + config.keymap.scroll_start.1.as_ref().map(|i| i.to_string()), + ], + KeyDescriptions::ScrollStart, + ), + ( + vec![Some(config.keymap.scroll_many.to_string())], + KeyDescriptions::ScrollSpeed, + ), + ( + vec![ + Some(config.keymap.exec.0.to_string()), + config.keymap.exec.1.as_ref().map(|i| i.to_string()), + ], + KeyDescriptions::Exec, + ), + ( + vec![ + Some(config.keymap.filter_mode.0.to_string()), + config.keymap.filter_mode.1.as_ref().map(|i| i.to_string()), + ], + KeyDescriptions::FilterMode, + ), + ( + vec![ + Some(config.keymap.toggle_help.0.to_string()), + config.keymap.toggle_help.1.as_ref().map(|i| i.to_string()), + ], + KeyDescriptions::Help, + ), + ( + vec![ + Some(config.keymap.log_section_height_decrease.0.to_string()), + Some(config.keymap.log_section_height_increase.0.to_string()), + config + .keymap + .log_section_height_decrease + .1 + .as_ref() + .map(|i| i.to_string()), + config + .keymap + .log_section_height_increase + .1 + .as_ref() + .map(|i| i.to_string()), + ], + KeyDescriptions::LogHeight, + ), + ( + vec![ + Some(config.keymap.sort_by_name.0.to_string()), + config.keymap.sort_by_name.1.as_ref().map(|i| i.to_string()), + ], + KeyDescriptions::SortName, + ), + ( + vec![ + Some(config.keymap.sort_by_status.0.to_string()), + config + .keymap + .sort_by_status + .1 + .as_ref() + .map(|i| i.to_string()), + ], + KeyDescriptions::SortStatus, + ), + ( + vec![ + Some(config.keymap.sort_by_memory.0.to_string()), + config + .keymap + .sort_by_memory + .1 + .as_ref() + .map(|i| i.to_string()), + ], + KeyDescriptions::SortMem, + ), + ( + vec![ + Some(config.keymap.sort_by_image.0.to_string()), + config + .keymap + .sort_by_image + .1 + .as_ref() + .map(|i| i.to_string()), + ], + KeyDescriptions::SortImage, + ), + ( + vec![ + Some(config.keymap.sort_by_tx.0.to_string()), + config.keymap.sort_by_tx.1.as_ref().map(|i| i.to_string()), + ], + KeyDescriptions::SortTX, + ), + ( + vec![ + Some(config.keymap.select_next_panel.0.to_string()), + Some(config.keymap.select_previous_panel.0.to_string()), + config + .keymap + .select_previous_panel + .1 + .as_ref() + .map(|i| i.to_string()), + config + .keymap + .select_next_panel + .1 + .as_ref() + .map(|i| i.to_string()), + ], + KeyDescriptions::Panel, + ), + ( + vec![ + Some(config.keymap.save_logs.0.to_string()), + config.keymap.save_logs.1.as_ref().map(|i| i.to_string()), + ], + KeyDescriptions::Save, + ), + ], + + right: vec![ + ( + vec![ + Some(config.keymap.clear.0.to_string()), + config.keymap.clear.1.as_ref().map(|i| i.to_string()), + ], + KeyDescriptions::Clear, + ), + ( + vec![ + Some(config.keymap.scroll_back.0.to_string()), + Some(config.keymap.scroll_forward.0.to_string()), + config.keymap.scroll_back.1.as_ref().map(|i| i.to_string()), + config + .keymap + .scroll_forward + .1 + .as_ref() + .map(|i| i.to_string()), + ], + KeyDescriptions::ScrollH, + ), + ( + vec![ + Some(config.keymap.scroll_end.0.to_string()), + config.keymap.scroll_end.1.as_ref().map(|i| i.to_string()), + ], + KeyDescriptions::ScrollEnd, + ), + (vec![Some(String::from("Enter"))], KeyDescriptions::Command), + ( + vec![ + Some(config.keymap.inspect.0.to_string()), + config.keymap.inspect.1.as_ref().map(|i| i.to_string()), + ], + KeyDescriptions::InspectMode, + ), + ( + vec![ + Some(config.keymap.log_search_mode.0.to_string()), + config + .keymap + .log_search_mode + .1 + .as_ref() + .map(|i| i.to_string()), + ], + KeyDescriptions::SearchMode, + ), + ( + vec![ + Some(config.keymap.force_redraw.0.to_string()), + config.keymap.force_redraw.1.as_ref().map(|i| i.to_string()), + ], + KeyDescriptions::Redraw, + ), + ( + vec![ + Some(config.keymap.log_section_toggle.0.to_string()), + config + .keymap + .log_section_toggle + .1 + .as_ref() + .map(|i| i.to_string()), + ], + KeyDescriptions::LogVisibility, + ), + ( + vec![ + Some(config.keymap.sort_by_state.0.to_string()), + config + .keymap + .sort_by_state + .1 + .as_ref() + .map(|i| i.to_string()), + ], + KeyDescriptions::SortState, + ), + ( + vec![ + Some(config.keymap.sort_by_cpu.0.to_string()), + config.keymap.sort_by_cpu.1.as_ref().map(|i| i.to_string()), + ], + KeyDescriptions::SortCpu, + ), + ( + vec![ + Some(config.keymap.sort_by_id.0.to_string()), + config.keymap.sort_by_id.1.as_ref().map(|i| i.to_string()), + ], + KeyDescriptions::SortId, + ), + ( + vec![ + Some(config.keymap.sort_by_rx.0.to_string()), + config.keymap.sort_by_rx.1.as_ref().map(|i| i.to_string()), + ], + KeyDescriptions::SortRX, + ), + ( + vec![ + Some(config.keymap.sort_reset.0.to_string()), + config.keymap.sort_reset.1.as_ref().map(|i| i.to_string()), + ], + KeyDescriptions::SortStop, + ), + ( + vec![ + Some(config.keymap.toggle_mouse_capture.0.to_string()), + config + .keymap + .toggle_mouse_capture + .1 + .as_ref() + .map(|i| i.to_string()), + ], + KeyDescriptions::MouseCapture, + ), + ], + } + } + + /// Add 1 to allow spacing between the key and the definition + fn longest_line(column: &Column) -> usize { + column + .iter() + .map(|(keys, _)| { + keys.iter() + .filter_map(|k| k.as_deref()) + .collect::>() + .join(" ") + .len() + }) + .max() + .unwrap_or(0) + .saturating_add(1) + } + + fn create_button_line(column: &Column, colors: &AppColors) -> Vec> { + let longest_button = Self::longest_line(column); + column + .iter() + .map(|(keys, desc)| HelpInfo::create_button_line(keys, desc, *colors, longest_button)) + .collect::>() + } + + fn to_helpinfo(&self, config: &Config) -> (HelpInfo, HelpInfo) { + let left = Self::create_button_line(&self.left, &config.app_colors); + let right = Self::create_button_line(&self.right, &config.app_colors); + + let size_left = HelpInfo::calc_size(&left); + let size_right = HelpInfo::calc_size(&right); + ( + HelpInfo { + lines: left, + size: size_left, + }, + HelpInfo { + lines: right, + size: size_right, + }, + ) + } +} + +impl KeyDescriptions { + fn as_str(&self) -> &'static str { + match self { + Self::Clear => "close dialog", + Self::Command => "send docker command", + Self::Exec => "exec into a container", + Self::FilterMode => "filter mode", + Self::Help => "toggle this panel", + Self::InspectMode => "container inspect mode", + Self::LogHeight => "change log section height", + Self::LogVisibility => "toggle of section visibility", + Self::MouseCapture => "toggle mouse capture - allows text selection", + Self::Panel => "change panel", + Self::Quit => "quit", + Self::Redraw => "force clear screen and redraw", + Self::Save => "save logs to file", + Self::ScrollH => "scroll horizontally", + Self::ScrollStart => "scroll to start", + Self::ScrollEnd => "scroll to end", + Self::ScrollSpeed => "increase scroll speed", + Self::ScrollV => "scroll vertically", + Self::SearchMode => "log search mode", + Self::SortHeader => "sort by header - or click header", + Self::SortStop => "stop sort", + Self::SortCpu => "sort by CPU", + Self::SortId => "sort by ID", + Self::SortImage => "sort by Image", + Self::SortMem => "sort by memory", + Self::SortName => "sort by name", + Self::SortRX => "sort by RX", + Self::SortState => "sort by state", + Self::SortStatus => "sort by status", + Self::SortTX => "sort by TX", + } + } +} + +/// Help popup box needs these three pieces of information +/// Change this to a trait +#[derive(Debug, Clone, Hash)] +struct HelpInfo { + lines: Vec>, + size: Size, +} + +static DEFAULT_NAME: LazyLock = LazyLock::new(|| { + let colors = AppColors::new(); + HelpInfo::gen_name_description(colors) +}); + +static DEFAULT_COLUMNS: LazyLock = + LazyLock::new(|| KeymapColumns::default(&Keymap::new())); + +impl HelpInfo { + /// Find the height and width of an array of lines + fn calc_size(lines: &[Line]) -> Size { + Size { + width: to_u16!( + lines + .iter() + .map(ratatui::prelude::Line::width) + .max() + .unwrap_or(1) + ), + height: to_u16!(lines.len()), + } + } /// Just an empty span, i.e. a new line - fn empty_span<'a>() -> Line<'a> { + fn empty_line<'a>() -> Line<'a> { Line::from(String::new()) } /// generate a span, of given &str and given color - fn span<'a>(input: &str, color: Color) -> Span<'a> { - Span::styled(input.to_owned(), Style::default().fg(color)) + fn span<'a>(input: String, color: Color) -> Span<'a> { + Span::styled(input, Style::default().fg(color)) } /// &str to black text span - fn text_span<'a>(input: &str, color: AppColors) -> Span<'a> { + fn text_span<'a>(input: String, color: AppColors) -> Span<'a> { Self::span(input, color.popup_help.text) } /// &str to white text span - fn highlighted_text_span<'a>(input: &str, color: AppColors) -> Span<'a> { + fn highlighted_text_span<'a>(input: String, color: AppColors) -> Span<'a> { Self::span(input, color.popup_help.text_highlight) } - /// Generate the `oxker` name span + metadata - fn gen_name(colors: AppColors) -> Self { + /// Generate the `oxker` name section + fn gen_name_description(colors: AppColors) -> Self { let mut lines = NAME_TEXT .lines() - .map(|i| Line::from(Self::highlighted_text_span(i, colors))) + .map(|i| Line::from(Self::highlighted_text_span(i.to_owned(), colors))) .collect::>(); - lines.insert(0, Self::empty_span()); - let width = Self::calc_width(&lines); - let height = lines.len(); - - Self { - lines, - width, - height, - } + lines.extend([ + Self::empty_line(), + Line::from(Self::highlighted_text_span(DESCRIPTION.to_owned(), colors)).centered(), + ]); + let size = Self::calc_size(&lines); + Self { lines, size } } - /// Generate the description span + metadata - fn gen_description(colors: AppColors) -> Self { - let lines = [ - Self::empty_span(), - Line::from(Self::highlighted_text_span(DESCRIPTION, colors)), - Self::empty_span(), - ]; + fn create_button<'a>( + input: &[Option], // Use a slice for better flexibility + color: AppColors, + spacing: usize, + ) -> Span<'a> { + let label = input + .iter() + .flatten() + .map(|s| s.as_str()) + .collect::>() + .join(" "); - Self { - lines: lines.to_vec(), - width: Self::calc_width(&lines), - height: lines.len(), - } + let padded_text = format!("{label:, show_timestamp: bool) -> Self { - let button_item = |x: &str| Self::highlighted_text_span(&format!(" ( {x} ) "), colors); - let button_desc = |x: &str| Self::text_span(x, colors); - let or = || button_desc("or"); - let space = || button_desc(" "); + fn create_button_line<'a>( + keys: &[Option], + desc: &KeyDescriptions, + app_colors: AppColors, + longest_button: usize, + ) -> Line<'a> { + Line::from(vec![ + Self::create_button(keys, app_colors, longest_button), + Span::from(desc.as_str()), + ]) + } - let descriptions = [ - Line::from(vec![ - space(), - button_item("tab"), - or(), - button_item("shift+tab"), - button_desc("change panels"), - ]), - Line::from(vec![ - space(), - button_item("โ†‘ โ†“"), - or(), - button_item("j k"), - or(), - button_item("Home End"), - button_desc("scroll vertically"), - ]), - Line::from(vec![ - space(), - button_item("โ† โ†’"), - button_desc("horizontal scroll across logs"), - ]), - Line::from(vec![ - space(), - button_item("ctrl"), - button_desc("increase scroll speed, used in conjunction scroll keys"), - ]), - Line::from(vec![ - space(), - button_item("enter"), - button_desc("send docker container command"), - ]), - Line::from(vec![ - space(), - button_item("e"), - button_desc("exec into a container"), - #[cfg(target_os = "windows")] - button_desc(" - not available on Windows"), - ]), - Line::from(vec![ - space(), - button_item("f"), - button_desc("force clear the screen & redraw the gui"), - ]), - Line::from(vec![ - space(), - button_item("h"), - button_desc("toggle this help information - or click heading"), - ]), - Line::from(vec![ - space(), - button_item("s"), - button_desc("save logs to file"), - ]), - Line::from(vec![ - space(), - button_item("m"), - button_desc( - "toggle mouse capture - if disabled, text on screen can be selected & copied", - ), - ]), - Line::from(vec![ - space(), - button_item("F1"), - or(), - button_item("/"), - button_desc("enter filter mode"), - ]), - Line::from(vec![ - space(), - button_item("#"), - button_desc("enter log search mode"), - ]), - Line::from(vec![space(), button_item("0"), button_desc("stop sort")]), - Line::from(vec![ - space(), - button_item("1 - 9"), - button_desc("sort by header - or click header"), - ]), - Line::from(vec![ - space(), - button_item("- ="), - button_desc("change log section height"), - ]), - Line::from(vec![ - space(), - button_item("\\"), - button_desc("toggle log section visibility"), - ]), - Line::from(vec![ - space(), - button_item("esc"), - button_desc("close dialog"), - ]), - Line::from(vec![ - space(), - button_item("q"), - button_desc("quit at any time"), - ]), + fn gen_keymap_title(style: Style) -> Self { + let lines = vec![ + Self::empty_line(), + Line::from(Span::from("Keymap")) + .style(style) + .centered() + .underlined(), ]; + let size = Self::calc_size(&lines); + Self { lines, size } + } - let mut lines = if show_timestamp { - Vec::from([ - Self::custom_text(colors, &Keymap::new(), zone), - Self::empty_span(), - ]) + fn gen_keymap(config: &Config) -> (Self, Self) { + let columns = if config.keymap == Keymap::new() { + DEFAULT_COLUMNS.clone() } else { - vec![] + KeymapColumns::custom(config) }; - - lines.extend_from_slice(&descriptions); - let width = Self::calc_width(&lines); - let height = lines.len(); - - Self { - lines, - width, - height, - } + columns.to_helpinfo(config) } - /// Generate the final lines, GitHub link etc, + metadata - fn gen_final(colors: AppColors) -> Self { - let lines = [ - Self::empty_span(), - Line::from(vec![Self::text_span( - "currently an early work in progress, all and any input appreciated", - colors, - )]), - Line::from(vec![Span::styled( - REPO, - Style::default() - .fg(colors.popup_help.text_highlight) - .add_modifier(Modifier::UNDERLINED), - )]), - ]; + fn gen_locations(config: &Config) -> Self { + let mut entries = Vec::new(); - Self { - lines: lines.to_vec(), - width: Self::calc_width(&lines), - height: lines.len(), + if let Some(path) = &config.dir_config { + entries.push(("config location: ", path.display().to_string())); } - } - - /// Display timezone in timestamps are visible - /// Has ability to display if keymap or colors are customized, but currently not in use - fn custom_text<'a>(colors: AppColors, _keymap: &Keymap, zone: Option<&TimeZone>) -> Line<'a> { - let highlighted = |x: &str| Self::highlighted_text_span(x, colors); - let text = |x: &str| Self::text_span(x, colors); - let zone = zone.and_then(|i| i.iana_name()).unwrap_or("Etc/UTC"); - Line::from(Vec::from([text("logs timezone: "), highlighted(zone)])).centered() - } - - /// Generate the display information when a custom keymap is being used - fn gen_custom_keymap_info( - colors: AppColors, - km: &Keymap, - zone: Option<&TimeZone>, - show_timestamp: bool, - ) -> Self { - let button_item = |x: &str| Self::highlighted_text_span(&format!(" ( {x} ) "), colors); - let button_desc = |x: &str| Self::text_span(x, colors); - let or = || button_desc("or"); - let space = || button_desc(" "); - - let or_secondary = |a: (KeyCode, Option), desc: &str| { - a.1.map_or_else( - || { - Line::from(vec![ - space(), - button_item(&a.0.to_string()), - button_desc(desc), - ]) - }, - |secondary| { - Line::from(vec![ - space(), - button_item(&a.0.to_string()), - or(), - button_item(&secondary.to_string()), - button_desc(desc), - ]) - }, - ) - }; - let descriptions = [ - or_secondary(km.select_next_panel, "select next panel"), - or_secondary(km.select_previous_panel, "select previous panel"), - or_secondary(km.scroll_down, "scroll list down by one"), - or_secondary(km.scroll_up, "scroll list up by one"), - or_secondary(km.scroll_end, "scroll list to end"), - or_secondary(km.scroll_start, "scroll list to start"), - or_secondary(km.log_scroll_forward, "horizontal scroll logs right"), - or_secondary(km.log_scroll_back, "horizontal scroll logs left"), - Line::from(vec![ - space(), - button_item(km.scroll_many.to_string().as_str()), - button_desc("increase scroll speed, used in conjunction scroll keys"), - ]), - Line::from(vec![ - space(), - button_item("enter"), - button_desc("send docker container command"), - ]), - #[cfg(not(target_os = "windows"))] - or_secondary(km.exec, "exec into a container"), - #[cfg(target_os = "windows")] - or_secondary(km.exec, "exec into a container - not available on Windows"), - or_secondary(km.force_redraw, "force clear the screen & redraw the gui"), - or_secondary( - km.toggle_help, - "toggle this help information - or click heading", - ), - or_secondary(km.save_logs, "save logs to file"), - or_secondary( - km.toggle_mouse_capture, - "toggle mouse capture - if disabled, text on screen can be selected & copied", - ), - or_secondary(km.filter_mode, "enter filter mode"), - or_secondary(km.log_search_mode, "enter log search mode"), - or_secondary(km.sort_reset, "reset container sorting"), - or_secondary(km.sort_by_name, "sort containers by name"), - or_secondary(km.sort_by_state, "sort containers by state"), - or_secondary(km.sort_by_status, "sort containers by status"), - or_secondary(km.sort_by_cpu, "sort containers by cpu"), - or_secondary(km.sort_by_memory, "sort containers by memory"), - or_secondary(km.sort_by_id, "sort containers by id"), - or_secondary(km.sort_by_image, "sort containers by image"), - or_secondary(km.sort_by_rx, "sort containers by rx"), - or_secondary(km.sort_by_tx, "sort containers by tx"), - or_secondary( - km.log_section_height_decrease, - "decrease log section height", - ), - or_secondary( - km.log_section_height_increase, - "increase log section height", - ), - or_secondary(km.log_section_toggle, "toggle log section visibility"), - or_secondary(km.clear, "close dialog"), - or_secondary(km.quit, "quit at any time"), - ]; - - let mut lines = if show_timestamp { - Vec::from([Self::custom_text(colors, km, zone), Self::empty_span()]) - } else { - vec![] - }; - - lines.extend_from_slice(&descriptions); - let width = Self::calc_width(&lines); - let height = lines.len(); - - Self { - lines, - width, - height, + if let Some(path) = &config.dir_save { + entries.push(("export location: ", path.display().to_string())); } + if config.show_timestamp { + let tz = config + .timezone + .as_ref() + .and_then(|t| t.iana_name()) + .unwrap_or("Etc/UTC"); + entries.push((" logs timezone: ", tz.to_string())); + } + + let max_len = entries + .iter() + .map(|(_, val)| val.chars().count()) + .max() + .unwrap_or_default(); + + // 3. Map entries to Lines + let mut lines = entries + .into_iter() + .map(|(label, val)| { + let spacing = " ".repeat(max_len.saturating_sub(val.chars().count())); + Line::from(vec![ + Self::text_span(label.to_owned(), config.app_colors), + Self::highlighted_text_span(format!("{spacing}{val}"), config.app_colors), + ]) + .right_aligned() + }) + .collect::>(); + + lines.extend([ + Self::empty_line(), + Line::from(Self::text_span( + "a work in progress, all and any input appreciated".to_owned(), + config.app_colors, + )), + Self::highlighted_text_span(REPO.to_owned(), config.app_colors) + .underlined() + .into_centered_line(), + ]); + + let size = Self::calc_size(&lines); + Self { lines, size } } } -/// Draw the help box in the centre of the screen -pub fn draw( - colors: AppColors, +// Draw the oxker name on one half, other half shoe logs location, save location, timezone +fn draw_top_section( f: &mut Frame, - keymap: &Keymap, - show_timestamp: bool, - zone: Option<&TimeZone>, + area: Rect, + colors: AppColors, + style: Style, + name: HelpInfo, + config_dir: HelpInfo, ) { - let title = format!(" {VERSION} "); - - let name_info = HelpInfo::gen_name(colors); - let description_info = HelpInfo::gen_description(colors); - let final_info = HelpInfo::gen_final(colors); - - let button_info = if keymap == &Keymap::new() { - HelpInfo::gen_keymap_info(colors, zone, show_timestamp) - } else { - HelpInfo::gen_custom_keymap_info(colors, keymap, zone, show_timestamp) - }; - - let max_line_width = [ - name_info.width, - description_info.width, - button_info.width, - final_info.width, - ] - .into_iter() - .max() - .unwrap_or_default() - + 2; - - let max_height = - name_info.height + description_info.height + button_info.height + final_info.height + 2; - - let area = popup::draw( - max_height, - max_line_width, - f.area(), - BoxLocation::MiddleCentre, - ); - - let split_popup = Layout::default() - .direction(Direction::Vertical) - .constraints([ - Constraint::Max(name_info.height.try_into().unwrap_or_default()), - Constraint::Max(description_info.height.try_into().unwrap_or_default()), - Constraint::Max(button_info.height.try_into().unwrap_or_default()), - Constraint::Min(final_info.height.try_into().unwrap_or_default()), - ]) + let horizontal_split = Layout::default() + .direction(Direction::Horizontal) + .constraints([Constraint::Percentage(50), Constraint::Percentage(50)]) .split(area); - let name_paragraph = Paragraph::new(name_info.lines) + let name_paragraph = Paragraph::new(name.lines) .style( Style::default() .bg(colors.popup_help.background) @@ -412,71 +753,159 @@ pub fn draw( ) .alignment(Alignment::Center); - let style = || { - Style::default() - .bg(colors.popup_help.background) - .fg(colors.popup_help.text) + let location_top_padding = name.size.height.saturating_sub(config_dir.size.height); + + let right_vertical_split = Layout::default() + .direction(Direction::Vertical) + .constraints([ + Constraint::Max(location_top_padding), + Constraint::Max(config_dir.size.height), + ]) + .split(horizontal_split[1]); + + let config_paragraph = Paragraph::new(config_dir.lines).style(style); + + f.render_widget(name_paragraph, horizontal_split[0]); + f.render_widget(config_paragraph, right_vertical_split[1]); +} + +fn draw_keymap_title(f: &mut Frame, area: Rect, title: HelpInfo) { + f.render_widget(Paragraph::new(title.lines), area); +} + +fn draw_keymap(f: &mut Frame, area: Rect, style: Style, columns: (HelpInfo, HelpInfo)) { + // Calculate some padding + let horizontal_padding = area + .width + .saturating_sub(columns.0.size.width) + .saturating_sub(columns.1.size.width) + .saturating_div(2) + .saturating_sub(1); + + let horizontal_split = Layout::default() + .direction(Direction::Horizontal) + .constraints([ + Constraint::Max(horizontal_padding), + Constraint::Max(columns.0.size.width), + Constraint::Max(2), + Constraint::Max(columns.1.size.width), + Constraint::Max(horizontal_padding), + ]) + .split(area); + + let left_column = Paragraph::new(columns.0.lines).style(style).left_aligned(); + let right_column = Paragraph::new(columns.1.lines).style(style).left_aligned(); + f.render_widget(left_column, horizontal_split[1]); + f.render_widget(right_column, horizontal_split[3]); +} + +pub fn draw(config: &Config, f: &mut Frame) { + let default_colors = config.app_colors == AppColors::new(); + let title = format!(" {VERSION} "); + let style = Style::default() + .bg(config.app_colors.popup_help.background) + .fg(config.app_colors.popup_help.text); + + let name_info = if default_colors { + DEFAULT_NAME.clone() + } else { + HelpInfo::gen_name_description(config.app_colors) }; - let description_paragraph = Paragraph::new(description_info.lines) - .style(style()) - .alignment(Alignment::Center); + let locations = HelpInfo::gen_locations(config); + let keymap_title = HelpInfo::gen_keymap_title(style); + let keymap_columns = HelpInfo::gen_keymap(config); - let help_paragraph = Paragraph::new(button_info.lines) - .style(style()) - .alignment(Alignment::Left); - - let final_paragraph = Paragraph::new(final_info.lines) - .style(style()) - .alignment(Alignment::Center); + let total_width = name_info + .size + .width + // Account for spacing between the two sections + .saturating_add(locations.size.width) + .saturating_add(2) + .max( + keymap_columns + .0 + .size + .width + .saturating_add(keymap_columns.1.size.width) + // Account for the spacing spacing between each column + .saturating_add(2), + ); + let top_height = name_info.size.height.max(locations.size.height); + let keymap_height = keymap_columns + .0 + .size + .height + .max(keymap_columns.1.size.height); + let total_height = top_height + .saturating_add(keymap_title.size.height) + .saturating_add(keymap_height); let block = Block::default() .title(title) .borders(Borders::ALL) .border_type(BorderType::Rounded) - .border_style( - Style::default() - .fg(colors.popup_help.text) - .bg(colors.popup_help.background), - ); + .border_style(style) + .style(style) + .padding(Padding::horizontal(1)); + + let area = popup::draw( + (total_height + 2).into(), + (total_width + 4).into(), + f.area(), + BoxLocation::MiddleCentre, + ); + + let inner_area = block.inner(area); + + let vertical_split = Layout::default() + .direction(Direction::Vertical) + .constraints([ + Constraint::Length(top_height), + Constraint::Length(keymap_title.size.height), + Constraint::Length(keymap_height), + ]) + .split(inner_area); - // Order is important here f.render_widget(Clear, area); - f.render_widget(name_paragraph, split_popup[0]); - f.render_widget(description_paragraph, split_popup[1]); - f.render_widget(help_paragraph, split_popup[2]); - f.render_widget(final_paragraph, split_popup[3]); f.render_widget(block, area); + draw_top_section( + f, + vertical_split[0], + config.app_colors, + style, + name_info, + locations, + ); + draw_keymap_title(f, vertical_split[1], keymap_title); + draw_keymap(f, vertical_split[2], style, keymap_columns); } #[cfg(test)] #[allow(clippy::unwrap_used, clippy::too_many_lines)] mod tests { + use std::path::PathBuf; + use crate::config::{AppColors, Keymap}; use crossterm::event::{KeyCode, KeyModifiers}; use insta::assert_snapshot; - use jiff::tz::TimeZone; - use ratatui::style::{Color, Modifier}; + use ratatui::style::Color; use crate::ui::draw_blocks::tests::{get_result, test_setup}; #[test] - /// This will cause issues once the version has more than the current 5 chars (0.5.0) /// This test is incredibly annoying /// println!("{} {} {} {} {}", row_index, result_cell_index, result_cell.symbol(), result_cell.bg, result_cell.fg); fn test_draw_blocks_help() { - let mut setup = test_setup(87, 39, true, true); - let tz = setup.app_data.lock().config.timezone.clone(); + let mut setup = test_setup(118, 25, true, true); + setup.app_data.lock().config.dir_save = Some(PathBuf::from("/test_dir")); + setup.app_data.lock().config.dir_config = + Some(PathBuf::from("/home/user/.config/oxker/config.toml")); + setup.app_data.lock().config.show_timestamp = true; setup .terminal .draw(|f| { - super::draw( - AppColors::new(), - f, - &setup.app_data.lock().config.keymap, - false, - tz.as_ref(), - ); + super::draw(&setup.app_data.lock().config, f); }) .unwrap(); @@ -485,100 +914,209 @@ mod tests { for (row_index, result_row) in get_result(&setup) { for (result_cell_index, result_cell) in result_row.iter().enumerate() { match (row_index, result_cell_index) { - // first & last row, and first & last char on each row, is reset/reset, making sure that the help info is centered in the given area - (0 | 38, _) | (0..=37, 0 | 86) => { - assert_eq!(result_cell.bg, Color::Reset); - assert_eq!(result_cell.fg, Color::Reset); - } - // Buttons - (2..=10, 2..=84) - | (12, 19..=66) - | (14, 2..=10 | 13..=27) - | (15, 2..=10 | 13..=21 | 24..=37) - | (16 | 28 | 30, 2..=10) - | (19..=26 | 29 | 31, 2..=8) - | (17, 2..=11) - | (18 | 27, 2..=12) - | (24, 2..=9 | 12..=18) => { - assert_eq!(result_cell.bg, Color::Magenta); - assert_eq!(result_cell.fg, Color::White); - } - // The URL is white on yellow and underlined - (34, 25..=60) => { - assert_eq!(result_cell.bg, Color::Magenta); - assert_eq!(result_cell.fg, Color::White); - assert_eq!(result_cell.modifier, Modifier::UNDERLINED); - } - // The rest is black on magenta - _ => { - assert_eq!(result_cell.bg, Color::Magenta); - assert_eq!(result_cell.fg, Color::Black); - } - } + // The space around the popup + (0|24, _) | (_, 0|117) => assert_eq!((result_cell.bg, result_cell.fg), (Color::Reset, Color::Reset)), + // The borders + (1|23, 1..=23) | (_, 1|116) => assert_eq!((result_cell.bg, result_cell.fg), (Color::Magenta, Color::Black)), + // The oxker logo + // The description + (2..=10, 3..=58)| + // Config location + (5, 79..=114) | + // Export location + (6, 79..=114) | + // Timezone + (7, 79..=114) | + //url + (10, 69..=104) | + // Left column + (13..=22, 4..=24) | + // Right Column + (13..=21,59..=69) + => assert_eq!((result_cell.bg, result_cell.fg), (Color::Magenta, Color::White)), + _ => assert_eq!((result_cell.bg, result_cell.fg), (Color::Magenta, Color::Black)), + }; } } } #[test] - /// Test that the help panel gets drawn with custom colors - /// This test is incredibly annoying - /// println!("{} {} {} {} {}", row_index, result_cell_index, result_cell.symbol(), result_cell.bg, result_cell.fg); - fn test_draw_blocks_help_custom_colors() { - let mut setup = test_setup(87, 39, true, true); - let mut colors = AppColors::new(); - let tz = setup.app_data.lock().config.timezone.clone(); - - colors.popup_help.background = Color::Black; - colors.popup_help.text = Color::Red; - colors.popup_help.text_highlight = Color::Yellow; + fn test_draw_blocks_help_no_config() { + let mut setup = test_setup(116, 25, true, true); + setup.app_data.lock().config.dir_save = Some(PathBuf::from("/test_dir")); + setup.app_data.lock().config.show_timestamp = true; setup .terminal .draw(|f| { - super::draw( - colors, - f, - &setup.app_data.lock().config.keymap, - false, - tz.as_ref(), - ); + super::draw(&setup.app_data.lock().config, f); }) .unwrap(); assert_snapshot!(setup.terminal.backend()); + for (row_index, result_row) in get_result(&setup) { for (result_cell_index, result_cell) in result_row.iter().enumerate() { match (row_index, result_cell_index) { - // first & last row, and first & last char on each row, is reset/reset, making sure that the help info is centered in the given area - (0 | 38, _) | (0..=37, 0 | 86) => { - assert_eq!(result_cell.bg, Color::Reset); - assert_eq!(result_cell.fg, Color::Reset); - } - // Buttons - (2..=10, 2..=84) - | (12, 19..=66) - | (14, 2..=10 | 13..=27) - | (15, 2..=10 | 13..=21 | 24..=37) - | (16 | 28 | 30, 2..=10) - | (19..=26 | 29 | 31, 2..=8) - | (17, 2..=11) - | (18 | 27, 2..=12) - | (24, 2..=9 | 12..=18) => { - assert_eq!(result_cell.bg, Color::Black); - assert_eq!(result_cell.fg, Color::Yellow); - } - // The URL is white on yellow and underlined - (34, 25..=60) => { - assert_eq!(result_cell.bg, Color::Black); - assert_eq!(result_cell.fg, Color::Yellow); - assert_eq!(result_cell.modifier, Modifier::UNDERLINED); - } - // The rest is black on magenta - _ => { - assert_eq!(result_cell.bg, Color::Black); - assert_eq!(result_cell.fg, Color::Red); - } - } + // The space around the popup + (0|24, _) | (_, 0|115) => assert_eq!((result_cell.bg, result_cell.fg), (Color::Reset, Color::Reset)), + // The borders + (1|23, 1..=23) | (_, 1|114) => assert_eq!((result_cell.bg, result_cell.fg), (Color::Magenta, Color::Black)), + // The oxker logo + // The description + (2..=10, 3..=57)| + // Export location + (6, 104..=112) | + // Timezone + (7, 104..=112) | + //url + (10, 67..=102) | + // Left column + (13..=22, 3..=23) | + // Right Column + (13..=21,58..=68) + => assert_eq!((result_cell.bg, result_cell.fg), (Color::Magenta, Color::White)), + _ => assert_eq!((result_cell.bg, result_cell.fg), (Color::Magenta, Color::Black)), + }; + } + } + } + + #[test] + fn test_draw_blocks_help_no_save() { + let mut setup = test_setup(118, 25, true, true); + setup.app_data.lock().config.dir_config = + Some(PathBuf::from("/home/user/.config/oxker/config.toml")); + setup.app_data.lock().config.show_timestamp = true; + + setup + .terminal + .draw(|f| { + super::draw(&setup.app_data.lock().config, f); + }) + .unwrap(); + + assert_snapshot!(setup.terminal.backend()); + + for (row_index, result_row) in get_result(&setup) { + for (result_cell_index, result_cell) in result_row.iter().enumerate() { + match (row_index, result_cell_index) { + // The space around the popup + (0|24, _) | (_, 0|117) => assert_eq!((result_cell.bg, result_cell.fg), (Color::Reset, Color::Reset)), + // The borders + (1|23, 1..=23) | (_, 1|116) => assert_eq!((result_cell.bg, result_cell.fg), (Color::Magenta, Color::Black)), + // The oxker logo + // The description + (2..=10, 3..=58)| + // Config location + (6, 79..=114) | + // Timezone + (7, 79..=114) | + //url + (10, 69..=104) | + // Left column + (13..=22, 4..=24) | + // Right Column + (13..=21,59..=69) + => assert_eq!((result_cell.bg, result_cell.fg), (Color::Magenta, Color::White)), + _ => assert_eq!((result_cell.bg, result_cell.fg), (Color::Magenta, Color::Black)), + }; + } + } + } + + #[test] + fn test_draw_blocks_help_no_timezone() { + let mut setup = test_setup(118, 25, true, true); + setup.app_data.lock().config.dir_save = Some(PathBuf::from("/test_dir")); + setup.app_data.lock().config.dir_config = + Some(PathBuf::from("/home/user/.config/oxker/config.toml")); + + setup + .terminal + .draw(|f| { + super::draw(&setup.app_data.lock().config, f); + }) + .unwrap(); + + assert_snapshot!(setup.terminal.backend()); + + for (row_index, result_row) in get_result(&setup) { + for (result_cell_index, result_cell) in result_row.iter().enumerate() { + match (row_index, result_cell_index) { + // The space around the popup + (0|24, _) | (_, 0|117) => assert_eq!((result_cell.bg, result_cell.fg), (Color::Reset, Color::Reset)), + // The borders + (1|23, 1..=23) | (_, 1|116) => assert_eq!((result_cell.bg, result_cell.fg), (Color::Magenta, Color::Black)), + // The oxker logo + // The description + (2..=10, 3..=58)| + // Config location + (6, 79..=114) | + // Export location + (7, 79..=114) | + //url + (10, 69..=104) | + // Left column + (13..=22, 4..=24) | + // Right Column + (13..=21,59..=69) + => assert_eq!((result_cell.bg, result_cell.fg), (Color::Magenta, Color::White)), + _ => assert_eq!((result_cell.bg, result_cell.fg), (Color::Magenta, Color::Black)), + }; + } + } + } + + #[test] + fn test_draw_blocks_help_custom_color() { + let mut setup = test_setup(118, 25, true, true); + setup.app_data.lock().config.dir_save = Some(PathBuf::from("/test_dir")); + setup.app_data.lock().config.dir_config = + Some(PathBuf::from("/home/user/.config/oxker/config.toml")); + setup.app_data.lock().config.show_timestamp = true; + + let mut colors = AppColors::new(); + colors.popup_help.background = Color::Black; + colors.popup_help.text = Color::Red; + colors.popup_help.text_highlight = Color::Yellow; + + setup.app_data.lock().config.app_colors = colors; + + setup + .terminal + .draw(|f| { + super::draw(&setup.app_data.lock().config, f); + }) + .unwrap(); + + assert_snapshot!(setup.terminal.backend()); + + for (row_index, result_row) in get_result(&setup) { + for (result_cell_index, result_cell) in result_row.iter().enumerate() { + match (row_index, result_cell_index) { + // The space around the popup + (0|24, _) | (_, 0|117) => assert_eq!((result_cell.bg, result_cell.fg), (Color::Reset, Color::Reset)), + // The borders + (1|23, 1..=23) | (_, 1|116) => assert_eq!((result_cell.bg, result_cell.fg), (Color::Black, Color::Red)), + // The oxker logo + // The description + (2..=10, 3..=58)| + // Config location + (5, 79..=114) | + // Export location + (6, 79..=114) | + // Timezone + (7, 79..=114) | + //url + (10, 69..=104) | + // Left column + (13..=22, 4..=24) | + // Right Column + (13..=21,59..=69) + => assert_eq!((result_cell.bg, result_cell.fg), (Color::Black, Color::Yellow)), + _ => assert_eq!((result_cell.bg, result_cell.fg), (Color::Black, Color::Red)), + }; } } } @@ -586,27 +1124,33 @@ mod tests { #[test] /// Help panel will show custom keymap if in use, with one definition for each entry fn test_draw_blocks_help_custom_keymap_one_definition() { - let mut setup = test_setup(98, 50, true, true); + let mut setup = test_setup(118, 25, true, true); - let input = Keymap { + setup.app_data.lock().config.dir_save = Some(PathBuf::from("/test_dir")); + setup.app_data.lock().config.dir_config = + Some(PathBuf::from("/home/user/.config/oxker/config.toml")); + setup.app_data.lock().config.show_timestamp = true; + + let keymap = Keymap { clear: (KeyCode::Char('a'), None), delete_confirm: (KeyCode::Char('b'), None), delete_deny: (KeyCode::Char('c'), None), exec: (KeyCode::Char('d'), None), - filter_mode: (KeyCode::Char('e'), None), - log_search_mode: (KeyCode::Char('7'), None), - force_redraw: (KeyCode::Char('f'), None), - log_scroll_back: (KeyCode::Char('g'), None), - log_scroll_forward: (KeyCode::Char('h'), None), - log_section_height_decrease: (KeyCode::Char('i'), None), - log_section_height_increase: (KeyCode::Char('j'), None), - log_section_toggle: (KeyCode::Char('k'), None), - quit: (KeyCode::Char('l'), None), - save_logs: (KeyCode::Char('m'), None), - scroll_down: (KeyCode::Char('o'), None), - scroll_end: (KeyCode::Char('p'), None), + inspect: (KeyCode::Char('e'), None), + filter_mode: (KeyCode::Char('f'), None), + log_search_mode: (KeyCode::Char('g'), None), + force_redraw: (KeyCode::Char('h'), None), + scroll_back: (KeyCode::Char('i'), None), + scroll_forward: (KeyCode::Char('j'), None), + log_section_height_decrease: (KeyCode::Char('k'), None), + log_section_height_increase: (KeyCode::Char('l'), None), + log_section_toggle: (KeyCode::Char('m'), None), + quit: (KeyCode::Char('n'), None), + save_logs: (KeyCode::Char('o'), None), + scroll_down: (KeyCode::Char('p'), None), + scroll_end: (KeyCode::Char('q'), None), scroll_many: KeyModifiers::ALT, - scroll_start: (KeyCode::Char('q'), None), + scroll_start: (KeyCode::Char('r'), None), scroll_up: (KeyCode::Char('s'), None), select_next_panel: (KeyCode::Char('t'), None), select_previous_panel: (KeyCode::Char('u'), None), @@ -624,10 +1168,12 @@ mod tests { toggle_mouse_capture: (KeyCode::Char('6'), None), }; + setup.app_data.lock().config.keymap = keymap; + setup .terminal .draw(|f| { - super::draw(AppColors::new(), f, &input, false, None); + super::draw(&setup.app_data.lock().config, f); }) .unwrap(); @@ -635,50 +1181,58 @@ mod tests { } #[test] - /// Help panel will show custom keymap if in use, with two definition for each entry - fn test_draw_blocks_help_custom_keymap_two_definitions() { - let mut setup = test_setup(110, 50, true, true); + /// Help panel will show custom keymap if in use, with two definitions for each entry + fn test_draw_blocks_help_custom_keymap_two_definition() { + let mut setup = test_setup(124, 30, true, true); + + setup.app_data.lock().config.dir_save = Some(PathBuf::from("/test_dir")); + setup.app_data.lock().config.dir_config = + Some(PathBuf::from("/home/user/.config/oxker/config.toml")); + setup.app_data.lock().config.show_timestamp = true; let keymap = Keymap { - clear: (KeyCode::Char('a'), Some(KeyCode::Char('A'))), - delete_confirm: (KeyCode::Char('b'), Some(KeyCode::Char('B'))), - delete_deny: (KeyCode::Char('c'), Some(KeyCode::Char('C'))), - exec: (KeyCode::Char('d'), Some(KeyCode::Char('D'))), - filter_mode: (KeyCode::Char('e'), Some(KeyCode::Char('E'))), - log_search_mode: (KeyCode::Char('m'), Some(KeyCode::Char('M'))), - force_redraw: (KeyCode::Char('f'), Some(KeyCode::Char('F'))), - log_scroll_back: (KeyCode::Char('f'), Some(KeyCode::Char('F'))), - log_scroll_forward: (KeyCode::Char('g'), Some(KeyCode::Char('G'))), - log_section_height_decrease: (KeyCode::Char('h'), Some(KeyCode::Char('H'))), - log_section_height_increase: (KeyCode::Char('i'), Some(KeyCode::Char('I'))), - log_section_toggle: (KeyCode::Char('j'), Some(KeyCode::Char('J'))), - quit: (KeyCode::Char('k'), Some(KeyCode::Char('K'))), - save_logs: (KeyCode::Char('l'), Some(KeyCode::Char('L'))), - scroll_down: (KeyCode::Char('n'), Some(KeyCode::Char('N'))), - scroll_end: (KeyCode::Char('o'), Some(KeyCode::Char('O'))), + clear: (KeyCode::Char('a'), Some(KeyCode::Char('b'))), + delete_confirm: (KeyCode::Char('c'), Some(KeyCode::Char('d'))), + delete_deny: (KeyCode::Char('e'), Some(KeyCode::Char('f'))), + exec: (KeyCode::Char('g'), Some(KeyCode::Char('h'))), + inspect: (KeyCode::Char('i'), Some(KeyCode::Char('j'))), + filter_mode: (KeyCode::Char('k'), Some(KeyCode::Char('l'))), + log_search_mode: (KeyCode::Char('m'), Some(KeyCode::Char('n'))), + force_redraw: (KeyCode::Char('o'), Some(KeyCode::Char('p'))), + scroll_back: (KeyCode::Char('q'), Some(KeyCode::Char('r'))), + scroll_forward: (KeyCode::Char('s'), Some(KeyCode::Char('t'))), + log_section_height_decrease: (KeyCode::Char('u'), Some(KeyCode::Char('v'))), + log_section_height_increase: (KeyCode::Char('w'), Some(KeyCode::Char('x'))), + log_section_toggle: (KeyCode::Char('y'), Some(KeyCode::Char('z'))), + quit: (KeyCode::Char('0'), Some(KeyCode::Char('1'))), + save_logs: (KeyCode::Char('2'), Some(KeyCode::Char('3'))), + scroll_down: (KeyCode::Char('4'), Some(KeyCode::Char('5'))), + scroll_end: (KeyCode::Char('6'), Some(KeyCode::Char('7'))), scroll_many: KeyModifiers::ALT, - scroll_start: (KeyCode::Char('p'), Some(KeyCode::Char('P'))), - scroll_up: (KeyCode::Char('r'), Some(KeyCode::Char('R'))), - select_next_panel: (KeyCode::Char('s'), Some(KeyCode::Char('S'))), - select_previous_panel: (KeyCode::Char('t'), Some(KeyCode::Char('T'))), - sort_by_cpu: (KeyCode::Char('u'), Some(KeyCode::Char('U'))), - sort_by_id: (KeyCode::Char('v'), Some(KeyCode::Char('V'))), - sort_by_image: (KeyCode::Char('w'), Some(KeyCode::Char('W'))), - sort_by_memory: (KeyCode::Char('x'), Some(KeyCode::Char('X'))), - sort_by_name: (KeyCode::Char('y'), Some(KeyCode::Char('Y'))), - sort_by_rx: (KeyCode::Char('z'), Some(KeyCode::Char('Z'))), - sort_by_state: (KeyCode::Char('0'), Some(KeyCode::Char('9'))), - sort_by_status: (KeyCode::Char('1'), Some(KeyCode::Char('8'))), - sort_by_tx: (KeyCode::Char('2'), Some(KeyCode::Char('7'))), - sort_reset: (KeyCode::Char('3'), Some(KeyCode::Char('6'))), - toggle_help: (KeyCode::Char('4'), Some(KeyCode::Char('5'))), - toggle_mouse_capture: (KeyCode::Char('5'), Some(KeyCode::PageDown)), + scroll_start: (KeyCode::Char('8'), Some(KeyCode::Char('9'))), + scroll_up: (KeyCode::CapsLock, Some(KeyCode::ScrollLock)), + select_next_panel: (KeyCode::PrintScreen, Some(KeyCode::Right)), + select_previous_panel: (KeyCode::Left, Some(KeyCode::Up)), + sort_by_cpu: (KeyCode::Down, Some(KeyCode::Delete)), + sort_by_id: (KeyCode::BackTab, Some(KeyCode::Backspace)), + sort_by_image: (KeyCode::End, Some(KeyCode::Esc)), + sort_by_memory: (KeyCode::Home, Some(KeyCode::Insert)), + sort_by_name: (KeyCode::KeypadBegin, Some(KeyCode::Menu)), + sort_by_rx: (KeyCode::NumLock, Some(KeyCode::PageDown)), + sort_by_state: (KeyCode::PageUp, Some(KeyCode::Pause)), + sort_by_status: (KeyCode::PrintScreen, Some(KeyCode::Tab)), + sort_by_tx: (KeyCode::F(1), Some(KeyCode::F(2))), + sort_reset: (KeyCode::F(3), Some(KeyCode::F(4))), + toggle_help: (KeyCode::F(5), Some(KeyCode::F(6))), + toggle_mouse_capture: (KeyCode::F(7), Some(KeyCode::F(8))), }; + setup.app_data.lock().config.keymap = keymap; + setup .terminal .draw(|f| { - super::draw(AppColors::new(), f, &keymap, false, None); + super::draw(&setup.app_data.lock().config, f); }) .unwrap(); @@ -686,90 +1240,61 @@ mod tests { } #[test] - /// Help panel will show custom keymap if in use, with either one or two definition for each entry - #[allow(clippy::todo)] - fn test_draw_blocks_help_one_and_two_definitions() { - let mut setup = test_setup(110, 50, true, true); + /// Help panel will show custom keymap if in use, with one or two definitions for each entry + fn test_draw_blocks_help_custom_keymap_one_two_definition() { + let mut setup = test_setup(124, 30, true, true); + + setup.app_data.lock().config.dir_save = Some(PathBuf::from("/test_dir")); + setup.app_data.lock().config.dir_config = + Some(PathBuf::from("/home/user/.config/oxker/config.toml")); + setup.app_data.lock().config.show_timestamp = true; let keymap = Keymap { - clear: (KeyCode::Char('a'), Some(KeyCode::Char('A'))), - delete_confirm: (KeyCode::Char('b'), None), - delete_deny: (KeyCode::Char('c'), Some(KeyCode::Char('C'))), - exec: (KeyCode::Char('d'), None), - filter_mode: (KeyCode::Char('e'), Some(KeyCode::Char('E'))), - log_search_mode: (KeyCode::Char('8'), None), - force_redraw: (KeyCode::Char('f'), None), - log_scroll_back: (KeyCode::Char('g'), Some(KeyCode::Char('G'))), - log_scroll_forward: (KeyCode::Char('h'), None), - log_section_height_decrease: (KeyCode::Char('i'), Some(KeyCode::Char('I'))), - log_section_height_increase: (KeyCode::Char('j'), None), - log_section_toggle: (KeyCode::Char('k'), Some(KeyCode::Char('K'))), - quit: (KeyCode::Char('l'), None), - save_logs: (KeyCode::Char('m'), Some(KeyCode::Char('M'))), - scroll_down: (KeyCode::Char('o'), Some(KeyCode::Char('O'))), - scroll_end: (KeyCode::Char('p'), None), + clear: (KeyCode::Char('a'), Some(KeyCode::Char('b'))), + delete_confirm: (KeyCode::Char('c'), None), + delete_deny: (KeyCode::Char('e'), Some(KeyCode::Char('f'))), + exec: (KeyCode::Char('g'), None), + inspect: (KeyCode::Char('i'), Some(KeyCode::Char('j'))), + filter_mode: (KeyCode::Char('k'), None), + log_search_mode: (KeyCode::Char('m'), Some(KeyCode::Char('n'))), + force_redraw: (KeyCode::Char('o'), None), + scroll_back: (KeyCode::Char('q'), Some(KeyCode::Char('r'))), + scroll_forward: (KeyCode::Char('s'), None), + log_section_height_decrease: (KeyCode::Char('u'), Some(KeyCode::Char('v'))), + log_section_height_increase: (KeyCode::Char('w'), None), + log_section_toggle: (KeyCode::Char('y'), Some(KeyCode::Char('z'))), + quit: (KeyCode::Char('0'), None), + save_logs: (KeyCode::Char('2'), Some(KeyCode::Char('3'))), + scroll_down: (KeyCode::Char('4'), None), + scroll_end: (KeyCode::Char('6'), Some(KeyCode::Char('7'))), scroll_many: KeyModifiers::ALT, - scroll_start: (KeyCode::Char('q'), Some(KeyCode::Char('Q'))), - scroll_up: (KeyCode::Char('s'), Some(KeyCode::Char('S'))), - select_next_panel: (KeyCode::Char('t'), None), - select_previous_panel: (KeyCode::Char('u'), Some(KeyCode::Char('U'))), - sort_by_cpu: (KeyCode::Char('v'), None), - sort_by_id: (KeyCode::Char('w'), Some(KeyCode::Char('W'))), - sort_by_image: (KeyCode::Char('x'), None), - sort_by_memory: (KeyCode::Char('y'), Some(KeyCode::Char('Y'))), - sort_by_name: (KeyCode::Char('z'), None), - sort_by_rx: (KeyCode::Char('0'), Some(KeyCode::Char('9'))), - sort_by_state: (KeyCode::Char('1'), None), - sort_by_status: (KeyCode::Char('2'), Some(KeyCode::Char('7'))), - sort_by_tx: (KeyCode::Char('3'), None), - sort_reset: (KeyCode::Char('4'), Some(KeyCode::Char('5'))), - toggle_help: (KeyCode::Char('5'), None), - toggle_mouse_capture: (KeyCode::Char('6'), Some(KeyCode::Char('#'))), + scroll_start: (KeyCode::Char('8'), None), + scroll_up: (KeyCode::CapsLock, Some(KeyCode::ScrollLock)), + select_next_panel: (KeyCode::PrintScreen, None), + select_previous_panel: (KeyCode::Left, Some(KeyCode::Up)), + sort_by_cpu: (KeyCode::Down, None), + sort_by_id: (KeyCode::BackTab, None), + sort_by_image: (KeyCode::End, Some(KeyCode::Esc)), + sort_by_memory: (KeyCode::Home, None), + sort_by_name: (KeyCode::KeypadBegin, Some(KeyCode::Menu)), + sort_by_rx: (KeyCode::NumLock, None), + sort_by_state: (KeyCode::PageUp, Some(KeyCode::Pause)), + sort_by_status: (KeyCode::PrintScreen, None), + sort_by_tx: (KeyCode::F(1), Some(KeyCode::F(2))), + sort_reset: (KeyCode::F(3), None), + toggle_help: (KeyCode::F(5), Some(KeyCode::F(6))), + toggle_mouse_capture: (KeyCode::F(7), None), }; - let tz = setup.app_data.lock().config.timezone.clone(); + setup.app_data.lock().config.keymap = keymap; setup .terminal .draw(|f| { - super::draw(AppColors::new(), f, &keymap, false, tz.as_ref()); + super::draw(&setup.app_data.lock().config, f); }) .unwrap(); assert_snapshot!(setup.terminal.backend()); } - - #[test] - fn test_draw_blocks_help_show_timezone() { - let mut setup = test_setup(87, 39, true, true); - - setup - .terminal - .draw(|f| { - super::draw( - AppColors::new(), - f, - &Keymap::new(), - true, - Some(&TimeZone::get("asia/tokyo").unwrap()), - ); - }) - .unwrap(); - - assert_snapshot!(setup.terminal.backend()); - - for (row_index, result_row) in get_result(&setup) { - for (result_cell_index, result_cell) in result_row.iter().enumerate() { - match (row_index, result_cell_index) { - (13, 31..=45) => { - assert_eq!(result_cell.fg, AppColors::new().popup_help.text); - } - (13, 46..=55) => { - assert_eq!(result_cell.fg, AppColors::new().popup_help.text_highlight); - } - _ => (), - } - } - } - } } diff --git a/src/ui/draw_blocks/inspect.rs b/src/ui/draw_blocks/inspect.rs new file mode 100644 index 0000000..3c5917d --- /dev/null +++ b/src/ui/draw_blocks/inspect.rs @@ -0,0 +1,777 @@ +use std::sync::Arc; + +use parking_lot::Mutex; +use ratatui::{ + Frame, + layout::Rect, + style::{Style, Stylize}, + text::Line, + widgets::{Block, Borders, Paragraph, Wrap}, +}; + +use crate::{ + app_data::InspectData, + config::{AppColors, Keymap}, + ui::{ + GuiState, + draw_blocks::{DOWN_ARROW, LEFT_ARROW, RIGHT_ARROW, UP_ARROW}, + gui_state::ScrollOffset, + }, +}; + +/// Create a bordered block with a title. +fn title_block<'a>(upper_title: &'a str, lower_title: &'a str, colors: &AppColors) -> Block<'a> { + Block::default() + .borders(Borders::all()) + .border_type(ratatui::widgets::BorderType::Rounded) + .border_style(Style::default().fg(colors.borders.selected)) + .title(upper_title.bold().into_centered_line()) + .title_bottom(lower_title.bold().into_centered_line()) +} + +/// Create the upper title, with container name, id, and keymap to clear +fn generate_upper_title(data: &InspectData, keymap: &Keymap) -> String { + let mut output = String::from(" inspecting: "); + let name = if data.name.starts_with("/") { + data.name.replacen('/', "", 1) + } else { + data.name.clone() + }; + + output.push_str(&format!("{} {} ", name, data.id.get_short())); + let mut inspect_key = keymap.inspect.0.to_string(); + if let Some(x) = keymap.inspect.1 { + inspect_key.push_str(&format!(" or {x}")); + } + let mut clear_key = keymap.clear.0.to_string(); + if let Some(x) = keymap.clear.1 { + clear_key.push_str(&format!(" or {x}")); + } + output.push_str(&format!(" - {clear_key} or {inspect_key} to exit")); + output.push(' '); + output +} + +/// Generate the lower title, with the current scroll and the scrolling limits +fn generate_lower_title(length: usize, width: usize, offset: ScrollOffset) -> String { + let length_width = length + .to_string() + .chars() + .count() + .max(offset.y.to_string().chars().count()); + let width_width = width + .to_string() + .chars() + .count() + .max(offset.x.to_string().chars().count()); + + let left_arrow = if offset.x == 0 { " " } else { LEFT_ARROW }; + let right_arrow = if offset.x == width { " " } else { RIGHT_ARROW }; + let up_arrow = if offset.y == 0 { " " } else { UP_ARROW }; + let down_arrow = if offset.y == length { " " } else { DOWN_ARROW }; + + format!( + " {up_arrow} {:>length_width$}/{:>length_width$} {down_arrow} {left_arrow} {:>width_width$}/{:>width_width$} {right_arrow} ", + offset.y, length, offset.x, width + ) +} + +/// Generate the Lines, remove lines & chars based on the offset and viewport +fn gen_lines<'a>(data_as_str: &'a str, offset: &ScrollOffset, rect: &Rect) -> Vec> { + let first_line_index = offset.y.max(0); + let first_char_index = offset.x.max(0); + let last_char_index = usize::from(rect.width.saturating_sub(2)); + let take_lines = usize::from(rect.height); + //todo see if log scrolling does this - What? + + data_as_str + .lines() + .skip(first_line_index) + .take(take_lines) + .map(|line| { + Line::from( + line.chars() + .skip(first_char_index) + .take(last_char_index) + .collect::(), + ) + }) + .collect() +} + +/// Draw the InspectContainer widget to the entire screen +pub fn draw( + f: &mut Frame, + colors: AppColors, + data: InspectData, + gui_state: &Arc>, + keymap: &Keymap, +) { + let rect = f.area(); + let offset = gui_state.lock().get_inspect_offset(); + // +2 to account for the border + let height = data + .height + .saturating_sub(usize::from(rect.height)) + .saturating_add(2); + let width = data + .width + .saturating_sub(usize::from(rect.width)) + .saturating_add(2); + let upper_title = generate_upper_title(&data, keymap); + let lower_title = generate_lower_title(height, width, offset); + + gui_state.lock().set_inspect_offset_max(ScrollOffset { + x: width, + y: height, + }); + + let paragraph = Paragraph::new(gen_lines(&data.as_string, &offset, &rect)) + .block(title_block(&upper_title, &lower_title, &colors)) + .gray() + .left_aligned() + .wrap(Wrap { trim: false }); + f.render_widget(paragraph, rect); +} + +#[cfg(test)] +#[allow(clippy::unwrap_used)] +mod tests { + use std::{collections::HashMap, sync::LazyLock}; + + use crate::{ + app_data::InspectData, + config::{AppColors, Keymap}, + ui::draw_blocks::tests::{get_result, test_setup}, + }; + use bollard::secret::{ + ContainerConfig, ContainerInspectResponse, ContainerState, ContainerStateStatusEnum, + DriverData, EndpointSettings, HostConfig, HostConfigLogConfig, MountPoint, + MountPointTypeEnum, NetworkSettings, RestartPolicy, RestartPolicyNameEnum, + }; + use crossterm::event::KeyCode; + use insta::assert_snapshot; + use ratatui::style::Color; + + static INSPECT_DATA: LazyLock = + LazyLock::new(|| InspectData::from(gen_container_inspect_response())); + + #[test] + /// Test a inspect container with default settings, keymap, and position + fn test_draw_blocks_inspect_default_valid() { + let mut setup = test_setup(100, 50, true, true); + setup + .terminal + .draw(|f| { + super::draw( + f, + AppColors::new(), + INSPECT_DATA.clone(), + &setup.gui_state, + &Keymap::new(), + ); + }) + .unwrap(); + assert_snapshot!(setup.terminal.backend()); + + // Assert border colors + for (row_index, result_row) in get_result(&setup) { + for (result_cell_index, result_cell) in result_row.iter().enumerate() { + match (row_index, result_cell_index) { + (0 | 49, _) | (_, 0 | 99) => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, Color::LightCyan); + } + _ => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, Color::Gray); + } + } + } + } + } + + #[test] + /// Test a inspect container with custom colors + fn test_draw_blocks_inspect_custom_color() { + let mut setup = test_setup(100, 50, true, true); + + let mut colors = AppColors::new(); + colors.borders.selected = Color::Red; + setup + .terminal + .draw(|f| { + super::draw( + f, + colors, + INSPECT_DATA.clone(), + &setup.gui_state, + &Keymap::new(), + ); + }) + .unwrap(); + assert_snapshot!(setup.terminal.backend()); + + // Assert custom border colors + for (row_index, result_row) in get_result(&setup) { + for (result_cell_index, result_cell) in result_row.iter().enumerate() { + match (row_index, result_cell_index) { + (0 | 49, _) | (_, 0 | 99) => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, Color::Red); + } + _ => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, Color::Gray); + } + } + } + } + } + + #[test] + /// Test a inspect container with custom keymap for one clear key + fn test_draw_blocks_inspect_custom_keymap_clear_one() { + let mut setup = test_setup(100, 50, true, true); + + let mut keymap = Keymap::new(); + + keymap.clear.0 = KeyCode::Char('F'); + + setup + .terminal + .draw(|f| { + super::draw( + f, + AppColors::new(), + INSPECT_DATA.clone(), + &setup.gui_state, + &keymap, + ); + }) + .unwrap(); + assert_snapshot!(setup.terminal.backend()); + + // Assert border colors + for (row_index, result_row) in get_result(&setup) { + for (result_cell_index, result_cell) in result_row.iter().enumerate() { + match (row_index, result_cell_index) { + (0 | 49, _) | (_, 0 | 99) => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, Color::LightCyan); + } + _ => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, Color::Gray); + } + } + } + } + } + + #[test] + /// Test a inspect container with custom keymap for both clear keys + fn test_draw_blocks_inspect_custom_keymap_clear_two() { + let mut setup = test_setup(100, 50, true, true); + + let mut keymap = Keymap::new(); + + keymap.clear.0 = KeyCode::Char('F'); + keymap.clear.1 = Some(KeyCode::Char('Z')); + + setup + .terminal + .draw(|f| { + super::draw( + f, + AppColors::new(), + INSPECT_DATA.clone(), + &setup.gui_state, + &keymap, + ); + }) + .unwrap(); + assert_snapshot!(setup.terminal.backend()); + + // Assert border colors + for (row_index, result_row) in get_result(&setup) { + for (result_cell_index, result_cell) in result_row.iter().enumerate() { + match (row_index, result_cell_index) { + (0 | 49, _) | (_, 0 | 99) => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, Color::LightCyan); + } + _ => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, Color::Gray); + } + } + } + } + } + + #[test] + /// Test a inspect container with custom keymap for one inspect key + fn test_draw_blocks_inspect_custom_keymap_inspect_one() { + let mut setup = test_setup(100, 50, true, true); + + let mut keymap = Keymap::new(); + + keymap.inspect.0 = KeyCode::Char('4'); + + setup + .terminal + .draw(|f| { + super::draw( + f, + AppColors::new(), + INSPECT_DATA.clone(), + &setup.gui_state, + &keymap, + ); + }) + .unwrap(); + assert_snapshot!(setup.terminal.backend()); + + // Assert border colors + for (row_index, result_row) in get_result(&setup) { + for (result_cell_index, result_cell) in result_row.iter().enumerate() { + match (row_index, result_cell_index) { + (0 | 49, _) | (_, 0 | 99) => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, Color::LightCyan); + } + _ => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, Color::Gray); + } + } + } + } + } + + #[test] + /// Test a inspect container with custom keymap for both inspect keys + fn test_draw_blocks_inspect_custom_keymap_inspect_two() { + let mut setup = test_setup(100, 50, true, true); + + let mut keymap = Keymap::new(); + + keymap.inspect.0 = KeyCode::Char('4'); + keymap.inspect.1 = Some(KeyCode::Char('5')); + + setup + .terminal + .draw(|f| { + super::draw( + f, + AppColors::new(), + INSPECT_DATA.clone(), + &setup.gui_state, + &keymap, + ); + }) + .unwrap(); + assert_snapshot!(setup.terminal.backend()); + + // Assert border colors + for (row_index, result_row) in get_result(&setup) { + for (result_cell_index, result_cell) in result_row.iter().enumerate() { + match (row_index, result_cell_index) { + (0 | 49, _) | (_, 0 | 99) => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, Color::LightCyan); + } + _ => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, Color::Gray); + } + } + } + } + } + + #[test] + /// Test a inspect container with all custom keymaps + fn test_draw_blocks_inspect_custom_keymap_all() { + let mut setup = test_setup(100, 50, true, true); + + let mut keymap = Keymap::new(); + + keymap.clear.0 = KeyCode::Char('F'); + keymap.clear.1 = Some(KeyCode::Char('Z')); + keymap.inspect.0 = KeyCode::Char('4'); + keymap.inspect.1 = Some(KeyCode::Char('5')); + + setup + .terminal + .draw(|f| { + super::draw( + f, + AppColors::new(), + INSPECT_DATA.clone(), + &setup.gui_state, + &keymap, + ); + }) + .unwrap(); + assert_snapshot!(setup.terminal.backend()); + + // Assert border colors + for (row_index, result_row) in get_result(&setup) { + for (result_cell_index, result_cell) in result_row.iter().enumerate() { + match (row_index, result_cell_index) { + (0 | 49, _) | (_, 0 | 99) => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, Color::LightCyan); + } + _ => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, Color::Gray); + } + } + } + } + } + + #[test] + /// Inspect details are offset 10 in x and y axis + fn test_draw_blocks_inspect_offset() { + let mut setup = test_setup(100, 50, true, true); + + // Why does one need to draw first, although it *should* be impossible to scroll before an initial drawing + setup + .terminal + .draw(|f| { + super::draw( + f, + AppColors::new(), + INSPECT_DATA.clone(), + &setup.gui_state, + &Keymap::new(), + ); + }) + .unwrap(); + + { + let mut gui_state = setup.gui_state.lock(); + for _ in 0..=9 { + gui_state.set_inspect_offset(&crate::app_data::ScrollDirection::Down); + gui_state.set_inspect_offset(&crate::app_data::ScrollDirection::Right); + } + } + setup + .terminal + .draw(|f| { + super::draw( + f, + AppColors::new(), + INSPECT_DATA.clone(), + &setup.gui_state, + &Keymap::new(), + ); + }) + .unwrap(); + assert_snapshot!(setup.terminal.backend()); + + for (row_index, result_row) in get_result(&setup) { + for (result_cell_index, result_cell) in result_row.iter().enumerate() { + match (row_index, result_cell_index) { + (0 | 49, _) | (_, 0 | 99) => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, Color::LightCyan); + } + _ => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, Color::Gray); + } + } + } + } + } + + #[test] + /// Inspect details are offset to the maximum allowed + fn test_draw_blocks_inspect_offset_max() { + let mut setup = test_setup(100, 50, true, true); + + // Why does one need to draw first, although it *should* be impossible to scroll before an initial drawing + setup + .terminal + .draw(|f| { + super::draw( + f, + AppColors::new(), + INSPECT_DATA.clone(), + &setup.gui_state, + &Keymap::new(), + ); + }) + .unwrap(); + + // Lazy way of getting the max offset + { + let mut gui_state = setup.gui_state.lock(); + for _ in 0..=1000 { + gui_state.set_inspect_offset(&crate::app_data::ScrollDirection::Down); + gui_state.set_inspect_offset(&crate::app_data::ScrollDirection::Right); + } + } + setup + .terminal + .draw(|f| { + super::draw( + f, + AppColors::new(), + INSPECT_DATA.clone(), + &setup.gui_state, + &Keymap::new(), + ); + }) + .unwrap(); + assert_snapshot!(setup.terminal.backend()); + + for (row_index, result_row) in get_result(&setup) { + for (result_cell_index, result_cell) in result_row.iter().enumerate() { + match (row_index, result_cell_index) { + (0 | 49, _) | (_, 0 | 99) => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, Color::LightCyan); + } + _ => { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, Color::Gray); + } + } + } + } + } + + fn gen_container_inspect_response() -> ContainerInspectResponse { + ContainerInspectResponse { + id: Some("0bdea64212f9c75eb4a1184dd406c2c79a986a7a889a23c85358456cc1bb60c7".to_owned()), + created: Some("2026-01-23T22:20:19.927967311Z".to_owned()), + path: Some("docker-entrypoint.sh".to_owned()), + args: Some(vec!["postgres".to_owned()]), + state: Some(ContainerState { + status: Some(ContainerStateStatusEnum::RUNNING), + running: Some(true), + paused: Some(false), + restarting: Some(false), + oom_killed: Some(false), + dead: Some(false), + pid: Some(782), + exit_code: Some(0), + error: Some("".to_owned()), + started_at: Some("2026-01-30T08:09:01.574885915Z".to_owned()), + finished_at: Some("2026-01-30T08:09:01.180567927Z".to_owned()), + health: None, + }), + image: Some("sha256:aa3668fcbcb5ded731b7d5c27065a4edf545debb7f27bf514c709b1b4e032352".to_owned()), + resolv_conf_path: Some("/var/lib/docker/containers/0bdea64212f9c75eb4a1184dd406c2c79a986a7a889a23c85358456cc1bb60c7/resolv.conf".to_owned()), + hostname_path: Some("/var/lib/docker/containers/0bdea64212f9c75eb4a1184dd406c2c79a986a7a889a23c85358456cc1bb60c7/hostname".to_owned()), + hosts_path: Some("/var/lib/docker/containers/0bdea64212f9c75eb4a1184dd406c2c79a986a7a889a23c85358456cc1bb60c7/hosts".to_owned()), + log_path: Some("/var/lib/docker/containers/0bdea64212f9c75eb4a1184dd406c2c79a986a7a889a23c85358456cc1bb60c7/0bdea64212f9c75eb4a1184dd406c2c79a986a7a889a23c85358456cc1bb60c7-json.log".to_owned()), + name: Some("/postgres".to_owned()), + restart_count: Some(0), + driver: Some("overlay2".to_owned()), + platform: Some("linux".to_owned()), + image_manifest_descriptor: None, + mount_label: Some("".to_owned()), + process_label: Some("".to_owned()), + app_armor_profile: Some("".to_owned()), + exec_ids: None, + host_config: Some(HostConfig { + cpu_shares: Some(0), + memory: Some(1073741824), + cgroup_parent: Some("".to_owned()), + blkio_weight: Some(0), + blkio_weight_device: None, + blkio_device_read_bps: None, + blkio_device_write_bps: None, + blkio_device_read_iops: None, + blkio_device_write_iops: None, + cpu_period: Some(0), + cpu_quota: Some(0), + cpu_realtime_period: Some(0), + cpu_realtime_runtime: Some(0), + cpuset_cpus: Some("".to_owned()), + cpuset_mems: Some("".to_owned()), + devices: None, + device_cgroup_rules: None, + device_requests: None, + memory_reservation: Some(0), + memory_swap: Some(2147483648), + memory_swappiness: None, + nano_cpus: Some(0), + oom_kill_disable: Some(false), + init: None, + pids_limit: None, + ulimits: None, + cpu_count: Some(0), + cpu_percent: Some(0), + io_maximum_iops: Some(0), + io_maximum_bandwidth: Some(0), + binds: None, + container_id_file: Some("".to_owned()), + log_config: Some(HostConfigLogConfig { + typ: Some("json-file".to_owned()), + config: Some(HashMap::new()), + }), + network_mode: Some("oxker-examaple-net".to_owned()), + port_bindings: Some(HashMap::new()), + restart_policy: Some(RestartPolicy { + name: Some(RestartPolicyNameEnum::ALWAYS), + maximum_retry_count: Some(0), + }), + auto_remove: Some(false), + volume_driver: Some("".to_owned()), + volumes_from: None, + mounts: None, + console_size: Some(vec![0, 0]), + annotations: None, + cap_add: None, + cap_drop: None, + cgroupns_mode: Some(bollard::secret::HostConfigCgroupnsModeEnum::HOST), + dns: Some(vec![]), + dns_options: Some(vec![]), + dns_search: Some(vec![]), + extra_hosts: Some(vec![]), + group_add: None, + ipc_mode: Some("private".to_owned()), + cgroup: Some("".to_owned()), + links: None, + oom_score_adj: Some(0), + pid_mode: Some("".to_owned()), + privileged: Some(false), + publish_all_ports: Some(false), + readonly_rootfs: Some(false), + security_opt: None, + storage_opt: None, + tmpfs: None, + uts_mode: Some("".to_owned()), + userns_mode: Some("".to_owned()), + shm_size: Some(268435456), + sysctls: None, + runtime: Some("runc".to_owned()), + isolation: Some(bollard::secret::HostConfigIsolationEnum::EMPTY), + masked_paths: Some(vec![ + "/proc/acpi".to_owned(), + "/proc/asound".to_owned(), + "/proc/interrupts".to_owned(), + "/proc/kcore".to_owned(), + "/proc/keys".to_owned(), + "/proc/latency_stats".to_owned(), + "/proc/sched_debug".to_owned(), + "/proc/scsi".to_owned(), + "/proc/timer_list".to_owned(), + "/proc/timer_stats".to_owned(), + "/sys/devices/virtual/powercap".to_owned(), + "/sys/firmware".to_owned(), + ]), + readonly_paths: Some(vec![ + "/proc/bus".to_owned(), + "/proc/fs".to_owned(), + "/proc/irq".to_owned(), + "/proc/sys".to_owned(), + "/proc/sysrq-trigger".to_owned(), + ]), + }), + graph_driver: Some(DriverData { + name: "overlay2".to_owned(), + data: HashMap::from([ + ("LowerDir".to_owned(), "/var/lib/docker/overlay2/b8dae7c82251b8dadc084dbcaceec47b3d48a5ba9d055a59934a8b88d18569ea-init/diff:/var/lib/docker/overlay2/51b93846f7ba3e00cb1ed86564e3e1d7c30df2bb1cd5a8469d54625f1e5a2eca/diff:/var/lib/docker/overlay2/c1364ead843d3af87ce286013b6301329d3089422b22b001e156e45d29b5b4dd/diff:/var/lib/docker/overlay2/0e6dc322cad77b1db3906a3a4e5e6d6b80fbffd138437e550d8849fcf4f4c1f2/diff:/var/lib/docker/overlay2/cc0f967a7471cf06e0c9ad3d474650c668a4cf0c02efe20e9c250c436f93033b/diff:/var/lib/docker/overlay2/5c59e0919969987c96a5d0e0a512a0a1a0f67ea747596af9a9c14a9566198d91/diff:/var/lib/docker/overlay2/d7709b7685c9704e1e392c515b6155517270541f6ccde426ef784403e1681fca/diff:/var/lib/docker/overlay2/c891528563fff91bffaf07416e77bcd3bdebb03e5d32ed0e3d4ee1ec5e80e880/diff:/var/lib/docker/overlay2/2b25c179a432c35cc599a082cd709c8c9a1523f8d1959f72fda21fc76e50ad00/diff:/var/lib/docker/overlay2/3b409d2f7a2455578148892302823a7f03c7c36482d08bb68fd6c1aeeec05f05/diff:/var/lib/docker/overlay2/55dbb2fab0ae8bb3bfe8183093cdd576686f7333e2b2c41e6e4178a7b6407554/diff".to_owned()), + ("MergedDir".to_owned(), "/var/lib/docker/overlay2/b8dae7c82251b8dadc084dbcaceec47b3d48a5ba9d055a59934a8b88d18569ea/merged".to_owned()), + ("WorkDir".to_owned(), "/var/lib/docker/overlay2/b8dae7c82251b8dadc084dbcaceec47b3d48a5ba9d055a59934a8b88d18569ea/work".to_owned()), + ("ID".to_owned(), "0bdea64212f9c75eb4a1184dd406c2c79a986a7a889a23c85358456cc1bb60c7".to_owned()), + ("UpperDir".to_owned(), "/var/lib/docker/overlay2/b8dae7c82251b8dadc084dbcaceec47b3d48a5ba9d055a59934a8b88d18569ea/diff".to_owned()), + ]), + }), + storage: None, + size_rw: None, + size_root_fs: None, + mounts: Some(vec![MountPoint { + typ: Some(MountPointTypeEnum::VOLUME), + name: Some("93bc4e4c8d3823964b58105a99a7b3a7e02c801d5560338bdaf7589966a1b02d".to_owned()), + source: Some("/var/lib/docker/volumes/93bc4e4c8d3823964b58105a99a7b3a7e02c801d5560338bdaf7589966a1b02d/_data".to_owned()), + destination: Some("/var/lib/postgresql/data".to_owned()), + driver: Some("local".to_owned()), + mode: Some("".to_owned()), + rw: Some(true), + propagation: Some("".to_owned()), + }]), + config: Some(ContainerConfig { + hostname: Some("0bdea64212f9".to_owned()), + domainname: Some("".to_owned()), + user: Some("".to_owned()), + attach_stdin: Some(false), + attach_stdout: Some(true), + attach_stderr: Some(true), + exposed_ports: Some(vec!["5432/tcp".to_owned()]), + tty: Some(false), + open_stdin: Some(false), + stdin_once: Some(false), + env: Some(vec![ + "POSTGRES_PASSWORD=never_use_this_password_in_production".to_owned(), + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin".to_owned(), + "GOSU_VERSION=1.19".to_owned(), + "LANG=en_US.utf8".to_owned(), + "PG_MAJOR=17".to_owned(), + "PG_VERSION=17.7".to_owned(), + "PG_SHA256=ef9e343302eccd33112f1b2f0247be493cb5768313adeb558b02de8797a2e9b5".to_owned(), + "DOCKER_PG_LLVM_DEPS=llvm19-dev \t\tclang19".to_owned(), + "PGDATA=/var/lib/postgresql/data".to_owned(), + ]), + cmd: Some(vec!["postgres".to_owned()]), + healthcheck: None, + args_escaped: None, + image: Some("postgres:17-alpine".to_owned()), + volumes: Some(vec!["/var/lib/postgresql/data".to_owned()]), + working_dir: Some("/".to_owned()), + entrypoint: Some(vec!["docker-entrypoint.sh".to_owned()]), + network_disabled: None, + on_build: None, + labels: Some(HashMap::from([ + ("com.docker.compose.oneoff".to_owned(), "False".to_owned()), + ("com.docker.compose.project.config_files".to_owned(), "/workspaces/oxker/docker/docker-compose.yml".to_owned()), + ("com.docker.compose.image".to_owned(), "sha256:aa3668fcbcb5ded731b7d5c27065a4edf545debb7f27bf514c709b1b4e032352".to_owned()), + ("com.docker.compose.project.working_dir".to_owned(), "/workspaces/oxker/docker".to_owned()), + ("com.docker.compose.service".to_owned(), "postgres".to_owned()), + ("com.docker.compose.config-hash".to_owned(), "e06d69ffb3f9b69dd51b356b60c2297df57caf0da16792ccafaabffdb920e443".to_owned()), + ("com.docker.compose.depends_on".to_owned(), "".to_owned()), + ("com.docker.compose.container-number".to_owned(), "1".to_owned()), + ("com.docker.compose.version".to_owned(), "2.40.3".to_owned()), + ("com.docker.compose.project".to_owned(), "docker".to_owned()), + ])), + stop_signal: Some("SIGINT".to_owned()), + stop_timeout: None, + shell: None, + }), + network_settings: Some(NetworkSettings { + sandbox_id: Some("dab64a66594dd8d06478184e2928c81acdcd9c931f643bd5ca62b7edb6345f8d".to_owned()), + sandbox_key: Some("/var/run/docker/netns/dab64a66594d".to_owned()), + ports: Some(HashMap::from([("5432/tcp".to_owned(), None)])), + networks: Some(HashMap::from([( + "oxker-examaple-net".to_owned(), + EndpointSettings { + ipam_config: None, + links: None, + mac_address: Some("a2:bd:4e:61:25:c7".to_owned()), + aliases: Some(vec!["postgres".to_owned(), "postgres".to_owned()]), + driver_opts: None, + gw_priority: Some(0), + network_id: Some("3cbeb475d81676f89a7aa205d8749ec2ad78d685e45d77b638992956f6dc569a".to_owned()), + endpoint_id: Some("31718069b2a3ea77487f3ece36b014d5d1329bc3294568e2621e5c0999071bed".to_owned()), + gateway: Some("172.19.0.1".to_owned()), + ip_address: Some("172.19.0.4".to_owned()), + ip_prefix_len: Some(16), + ipv6_gateway: Some("".to_owned()), + global_ipv6_address: Some("".to_owned()), + global_ipv6_prefix_len: Some(0), + dns_names: Some(vec!["postgres".to_owned(), "0bdea64212f9".to_owned()]), + }, + )])), + }), +} + } +} diff --git a/src/ui/draw_blocks/logs.rs b/src/ui/draw_blocks/logs.rs index ebf6ec7..98b643f 100644 --- a/src/ui/draw_blocks/logs.rs +++ b/src/ui/draw_blocks/logs.rs @@ -14,7 +14,7 @@ use crate::{ ui::{FrameData, GuiState, SelectablePanel, Status}, }; -use super::{RIGHT_ARROW, generate_block}; +use super::{SELECT_ARROW, generate_block}; /// Draw the logs panel pub fn draw( @@ -52,7 +52,7 @@ pub fn draw( } else if fd.color_logs { let items = List::new(logs) .block(block) - .highlight_symbol(RIGHT_ARROW) + .highlight_symbol(SELECT_ARROW) .scroll_padding(padding) .highlight_style(Style::default().add_modifier(Modifier::BOLD)); // This should always return Some, as logs is not empty @@ -63,7 +63,7 @@ pub fn draw( let items = List::new(logs) .fg(colors.logs.text) .block(block) - .highlight_symbol(RIGHT_ARROW) + .highlight_symbol(SELECT_ARROW) .highlight_style(Style::default().add_modifier(Modifier::BOLD)); // This should always return Some, as logs is not empty if let Some(log_state) = app_data.lock().get_log_state() { @@ -309,7 +309,7 @@ mod tests { ); }) .unwrap(); - setup.app_data.lock().log_scroll(&ScrollDirection::Previous); + setup.app_data.lock().log_scroll(&ScrollDirection::Up); let fd = FrameData::from((&setup.app_data, &setup.gui_state)); setup diff --git a/src/ui/draw_blocks/mod.rs b/src/ui/draw_blocks/mod.rs index 507a913..147e166 100644 --- a/src/ui/draw_blocks/mod.rs +++ b/src/ui/draw_blocks/mod.rs @@ -11,7 +11,8 @@ use crate::config::AppColors; use super::{FrameData, GuiState, SelectablePanel, Status, gui_state::Region}; -pub mod charts; +pub mod chart_bandwidth; +pub mod chart_cpu_mem; pub mod commands; pub mod containers; pub mod delete_confirm; @@ -20,26 +21,29 @@ pub mod filter; pub mod headers; pub mod help; pub mod info; +pub mod inspect; pub mod logs; pub mod popup; pub mod ports; pub mod search_logs; -pub const NAME_TEXT: &str = r#" - 88 - 88 - 88 - ,adPPYba, 8b, ,d8 88 ,d8 ,adPPYba, 8b,dPPYba, -a8" "8a `Y8, ,8P' 88 ,a8" a8P_____88 88P' "Y8 -8b d8 )888( 8888[ 8PP""""""" 88 -"8a, ,a8" ,d8" "8b, 88`"Yba, "8b, ,aa 88 - `"YbbdP"' 8P' `Y8 88 `Y8a `"Ybbd8"' 88 "#; +pub const NAME_TEXT: &str = r#" 88 + 88 + ,adPPYba, 8b, ,d8 88 ,d8 ,adPPYba, 8b,dPPYba, +a8" "8a `Y8, ,8P' 88 ,a8" a8P_____88 88P' "Y8 +8b d8 )888( 8888( 8PP""""""" 88 +"8a, ,a8" ,d8" "8b, 88`"Yba, "8b, ,aa 88 + `"YbbdP"' 8P' `Y8 88 `Y8a `"Ybbd8"' 88 "#; pub const NAME: &str = env!("CARGO_PKG_NAME"); pub const REPO: &str = env!("CARGO_PKG_REPOSITORY"); pub const DESCRIPTION: &str = env!("CARGO_PKG_DESCRIPTION"); pub const MARGIN: &str = " "; -pub const RIGHT_ARROW: &str = "โ–ถ "; +pub const SELECT_ARROW: &str = "โ–ถ "; +pub const LEFT_ARROW: &str = "โ†"; +pub const RIGHT_ARROW: &str = "โ†’"; +pub const DOWN_ARROW: &str = "โ†“"; +pub const UP_ARROW: &str = "โ†‘"; pub const CIRCLE: &str = "โšช "; #[cfg(not(test))] @@ -103,12 +107,12 @@ fn generate_block<'a>( .border_type(BorderType::Rounded) .title(ratatui::text::Line::from(title).left_aligned()); - if panel == SelectablePanel::Logs { - if let Some(x) = fd.scroll_title.as_ref() { - block = block - .title_bottom(x.to_owned()) - .title_alignment(ratatui::layout::Alignment::Right); - } + if panel == SelectablePanel::Logs + && let Some(x) = fd.scroll_title.as_ref() + { + block = block + .title_bottom(x.to_owned()) + .title_alignment(ratatui::layout::Alignment::Right); } if !fd.status.contains(&Status::Filter) { if fd.selected_panel == panel { @@ -242,7 +246,7 @@ pub mod tests { #[allow(clippy::cast_precision_loss)] // Add fixed data to the cpu & mem vecdeques - pub fn insert_chart_data(setup: &TuiTestSetup) { + pub fn insert_all_chart_data(setup: &TuiTestSetup) { for i in 1..=10 { setup.app_data.lock().update_stats_by_id( &setup.ids[0], @@ -273,7 +277,7 @@ pub mod tests { fn test_draw_blocks_whole_layout() { let mut setup = test_setup(160, 30, true, true); - insert_chart_data(&setup); + insert_all_chart_data(&setup); insert_logs(&setup); setup.app_data.lock().containers.items[0] .ports @@ -301,7 +305,7 @@ pub mod tests { /// Check that the whole layout is drawn correctly fn test_draw_blocks_whole_layout_with_filter_bar() { let mut setup = test_setup(160, 30, true, true); - insert_chart_data(&setup); + insert_all_chart_data(&setup); insert_logs(&setup); setup.app_data.lock().containers.items[1] @@ -337,7 +341,7 @@ pub mod tests { fn test_draw_blocks_whole_layout_long_name() { let mut setup = test_setup(190, 30, true, true); - insert_chart_data(&setup); + insert_all_chart_data(&setup); insert_logs(&setup); setup.app_data.lock().containers.items[0] .ports @@ -370,7 +374,7 @@ pub mod tests { fn test_draw_blocks_whole_layout_no_logs() { let mut setup = test_setup(160, 30, true, true); - insert_chart_data(&setup); + insert_all_chart_data(&setup); insert_logs(&setup); setup.app_data.lock().containers.items[0] .ports @@ -399,7 +403,7 @@ pub mod tests { fn test_draw_blocks_whole_layout_short_height_logs() { let mut setup = test_setup(160, 30, true, true); - insert_chart_data(&setup); + insert_all_chart_data(&setup); insert_logs(&setup); setup.app_data.lock().containers.items[0] .ports @@ -431,7 +435,7 @@ pub mod tests { fn test_draw_blocks_whole_layout_help_panel() { let mut setup = test_setup(160, 40, true, true); - insert_chart_data(&setup); + insert_all_chart_data(&setup); insert_logs(&setup); setup.app_data.lock().containers.items[0] .ports @@ -461,7 +465,7 @@ pub mod tests { fn test_draw_blocks_whole_layout_error() { let mut setup = test_setup(160, 40, true, true); - insert_chart_data(&setup); + insert_all_chart_data(&setup); insert_logs(&setup); setup.app_data.lock().containers.items[0] .ports @@ -495,7 +499,7 @@ pub mod tests { fn test_draw_blocks_whole_layout_delete() { let mut setup = test_setup(160, 40, true, true); - insert_chart_data(&setup); + insert_all_chart_data(&setup); insert_logs(&setup); setup.app_data.lock().containers.items[0] .ports @@ -527,7 +531,7 @@ pub mod tests { fn test_draw_blocks_whole_layout_info_box() { let mut setup = test_setup(160, 40, true, true); - insert_chart_data(&setup); + insert_all_chart_data(&setup); insert_logs(&setup); setup.app_data.lock().containers.items[0] .ports diff --git a/src/ui/draw_blocks/search_logs.rs b/src/ui/draw_blocks/search_logs.rs index 81b1e31..335aa31 100644 --- a/src/ui/draw_blocks/search_logs.rs +++ b/src/ui/draw_blocks/search_logs.rs @@ -241,11 +241,11 @@ mod tests { setup .app_data .lock() - .log_scroll(&crate::app_data::ScrollDirection::Previous); + .log_scroll(&crate::app_data::ScrollDirection::Up); setup .app_data .lock() - .log_scroll(&crate::app_data::ScrollDirection::Previous); + .log_scroll(&crate::app_data::ScrollDirection::Up); let fd = FrameData::from((&setup.app_data, &setup.gui_state)); setup @@ -298,7 +298,7 @@ mod tests { setup .app_data .lock() - .log_scroll(&crate::app_data::ScrollDirection::Previous); + .log_scroll(&crate::app_data::ScrollDirection::Up); let fd = FrameData::from((&setup.app_data, &setup.gui_state)); setup @@ -404,7 +404,7 @@ mod tests { setup .app_data .lock() - .log_scroll(&crate::app_data::ScrollDirection::Previous); + .log_scroll(&crate::app_data::ScrollDirection::Up); let fd = FrameData::from((&setup.app_data, &setup.gui_state)); setup @@ -433,7 +433,7 @@ mod tests { setup .app_data .lock() - .log_scroll(&crate::app_data::ScrollDirection::Previous); + .log_scroll(&crate::app_data::ScrollDirection::Up); let mut colors = AppColors::new(); diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__chart_bandwidth__tests__draw_blocks_charts_custom_colors.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__chart_bandwidth__tests__draw_blocks_charts_custom_colors.snap new file mode 100644 index 0000000..699967c --- /dev/null +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__chart_bandwidth__tests__draw_blocks_charts_custom_colors.snap @@ -0,0 +1,14 @@ +--- +source: src/ui/draw_blocks/chart_bandwidth.rs +expression: setup.terminal.backend() +--- +"โ•ญโ”€โ”€โ”€ rx: 566.00 kb/s tx: 56.60 kb/s โ”€โ”€โ”€โ•ฎ" +"โ”‚ โ”‚ โ€ข โ”‚" +"โ”‚ โ”‚ โ€ขโ€ข โ”‚" +"โ”‚ โ”‚ โ€ขโ€ข โ”‚" +"โ”‚566.00 kb/sโ”‚ โ€ขโ€ข โ”‚" +"โ”‚ โ”‚ โ€ข โ”‚" +"โ”‚56.60 kb/s โ”‚ โ€ขโ€ข โ”‚" +"โ”‚ โ”‚โ€ขโ€ข โ€ขโ€ขโ€ข โ”‚" +"โ”‚ โ”‚โ€ขโ€ขโ€ขโ€ขโ€ขโ€ข โ”‚" +"โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__chart_bandwidth__tests__draw_blocks_charts_dead.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__chart_bandwidth__tests__draw_blocks_charts_dead.snap new file mode 100644 index 0000000..64b250a --- /dev/null +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__chart_bandwidth__tests__draw_blocks_charts_dead.snap @@ -0,0 +1,14 @@ +--- +source: src/ui/draw_blocks/chart_bandwidth.rs +expression: setup.terminal.backend() +--- +"โ•ญโ”€โ”€โ”€โ”€ rx: 0.00 kb/s tx: 0.00 kb/s โ”€โ”€โ”€โ”€โ”€โ•ฎ" +"โ”‚ โ”‚ โ”‚" +"โ”‚ โ”‚ โ”‚" +"โ”‚ โ”‚ โ”‚" +"โ”‚0.00 kb/sโ”‚ โ”‚" +"โ”‚ โ”‚ โ”‚" +"โ”‚0.00 kb/sโ”‚ โ”‚" +"โ”‚ โ”‚ โ”‚" +"โ”‚ โ”‚ โ”‚" +"โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__chart_bandwidth__tests__draw_blocks_charts_paused.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__chart_bandwidth__tests__draw_blocks_charts_paused.snap new file mode 100644 index 0000000..64b250a --- /dev/null +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__chart_bandwidth__tests__draw_blocks_charts_paused.snap @@ -0,0 +1,14 @@ +--- +source: src/ui/draw_blocks/chart_bandwidth.rs +expression: setup.terminal.backend() +--- +"โ•ญโ”€โ”€โ”€โ”€ rx: 0.00 kb/s tx: 0.00 kb/s โ”€โ”€โ”€โ”€โ”€โ•ฎ" +"โ”‚ โ”‚ โ”‚" +"โ”‚ โ”‚ โ”‚" +"โ”‚ โ”‚ โ”‚" +"โ”‚0.00 kb/sโ”‚ โ”‚" +"โ”‚ โ”‚ โ”‚" +"โ”‚0.00 kb/sโ”‚ โ”‚" +"โ”‚ โ”‚ โ”‚" +"โ”‚ โ”‚ โ”‚" +"โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__chart_bandwidth__tests__draw_blocks_charts_running_none.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__chart_bandwidth__tests__draw_blocks_charts_running_none.snap new file mode 100644 index 0000000..64b250a --- /dev/null +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__chart_bandwidth__tests__draw_blocks_charts_running_none.snap @@ -0,0 +1,14 @@ +--- +source: src/ui/draw_blocks/chart_bandwidth.rs +expression: setup.terminal.backend() +--- +"โ•ญโ”€โ”€โ”€โ”€ rx: 0.00 kb/s tx: 0.00 kb/s โ”€โ”€โ”€โ”€โ”€โ•ฎ" +"โ”‚ โ”‚ โ”‚" +"โ”‚ โ”‚ โ”‚" +"โ”‚ โ”‚ โ”‚" +"โ”‚0.00 kb/sโ”‚ โ”‚" +"โ”‚ โ”‚ โ”‚" +"โ”‚0.00 kb/sโ”‚ โ”‚" +"โ”‚ โ”‚ โ”‚" +"โ”‚ โ”‚ โ”‚" +"โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__chart_bandwidth__tests__draw_blocks_charts_running_with_data.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__chart_bandwidth__tests__draw_blocks_charts_running_with_data.snap new file mode 100644 index 0000000..f2451e2 --- /dev/null +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__chart_bandwidth__tests__draw_blocks_charts_running_with_data.snap @@ -0,0 +1,14 @@ +--- +source: src/ui/draw_blocks/chart_bandwidth.rs +expression: setup.terminal.backend() +--- +"โ•ญโ”€โ”€โ”€ rx: 0.00 kb/s tx: 205.00 kb/s โ”€โ”€โ”€โ”€โ•ฎ" +"โ”‚ โ”‚ โ€ข โ”‚" +"โ”‚ โ”‚ โ€ขโ€ข โ”‚" +"โ”‚ โ”‚ โ€ขโ€ข โ”‚" +"โ”‚205.00 kb/sโ”‚ โ€ขโ€ข โ”‚" +"โ”‚ โ”‚ โ€ข โ”‚" +"โ”‚0.00 kb/s โ”‚ โ€ขโ€ข โ”‚" +"โ”‚ โ”‚โ€ขโ€ข โ”‚" +"โ”‚ โ”‚ โ”‚" +"โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__chart_bandwidth__tests__draw_blocks_charts_running_with_data_rx.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__chart_bandwidth__tests__draw_blocks_charts_running_with_data_rx.snap new file mode 100644 index 0000000..bff8635 --- /dev/null +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__chart_bandwidth__tests__draw_blocks_charts_running_with_data_rx.snap @@ -0,0 +1,14 @@ +--- +source: src/ui/draw_blocks/chart_bandwidth.rs +expression: setup.terminal.backend() +--- +"โ•ญโ”€โ”€โ”€ rx: 566.00 kb/s tx: 0.00 kb/s โ”€โ”€โ”€โ”€โ•ฎ" +"โ”‚ โ”‚ โ€ข โ”‚" +"โ”‚ โ”‚ โ€ขโ€ข โ”‚" +"โ”‚ โ”‚ โ€ขโ€ข โ”‚" +"โ”‚566.00 kb/sโ”‚ โ€ขโ€ข โ”‚" +"โ”‚ โ”‚ โ€ข โ”‚" +"โ”‚0.00 kb/s โ”‚ โ€ขโ€ข โ”‚" +"โ”‚ โ”‚โ€ขโ€ข โ”‚" +"โ”‚ โ”‚โ€ข โ”‚" +"โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__chart_bandwidth__tests__draw_blocks_charts_running_with_data_tx.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__chart_bandwidth__tests__draw_blocks_charts_running_with_data_tx.snap new file mode 100644 index 0000000..f2451e2 --- /dev/null +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__chart_bandwidth__tests__draw_blocks_charts_running_with_data_tx.snap @@ -0,0 +1,14 @@ +--- +source: src/ui/draw_blocks/chart_bandwidth.rs +expression: setup.terminal.backend() +--- +"โ•ญโ”€โ”€โ”€ rx: 0.00 kb/s tx: 205.00 kb/s โ”€โ”€โ”€โ”€โ•ฎ" +"โ”‚ โ”‚ โ€ข โ”‚" +"โ”‚ โ”‚ โ€ขโ€ข โ”‚" +"โ”‚ โ”‚ โ€ขโ€ข โ”‚" +"โ”‚205.00 kb/sโ”‚ โ€ขโ€ข โ”‚" +"โ”‚ โ”‚ โ€ข โ”‚" +"โ”‚0.00 kb/s โ”‚ โ€ขโ€ข โ”‚" +"โ”‚ โ”‚โ€ขโ€ข โ”‚" +"โ”‚ โ”‚ โ”‚" +"โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__chart_bandwidth__tests__draw_blocks_charts_running_with_data_tx_and_rx.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__chart_bandwidth__tests__draw_blocks_charts_running_with_data_tx_and_rx.snap new file mode 100644 index 0000000..699967c --- /dev/null +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__chart_bandwidth__tests__draw_blocks_charts_running_with_data_tx_and_rx.snap @@ -0,0 +1,14 @@ +--- +source: src/ui/draw_blocks/chart_bandwidth.rs +expression: setup.terminal.backend() +--- +"โ•ญโ”€โ”€โ”€ rx: 566.00 kb/s tx: 56.60 kb/s โ”€โ”€โ”€โ•ฎ" +"โ”‚ โ”‚ โ€ข โ”‚" +"โ”‚ โ”‚ โ€ขโ€ข โ”‚" +"โ”‚ โ”‚ โ€ขโ€ข โ”‚" +"โ”‚566.00 kb/sโ”‚ โ€ขโ€ข โ”‚" +"โ”‚ โ”‚ โ€ข โ”‚" +"โ”‚56.60 kb/s โ”‚ โ€ขโ€ข โ”‚" +"โ”‚ โ”‚โ€ขโ€ข โ€ขโ€ขโ€ข โ”‚" +"โ”‚ โ”‚โ€ขโ€ขโ€ขโ€ขโ€ขโ€ข โ”‚" +"โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__chart_cpu_mem__tests__draw_blocks_charts_custom_colors.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__chart_cpu_mem__tests__draw_blocks_charts_custom_colors.snap new file mode 100644 index 0000000..8a75efc --- /dev/null +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__chart_cpu_mem__tests__draw_blocks_charts_custom_colors.snap @@ -0,0 +1,14 @@ +--- +source: src/ui/draw_blocks/chart_cpu_mem.rs +expression: setup.terminal.backend() +--- +"โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ cpu 03.00% โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎโ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ memory 30.00 kB โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ" +"โ”‚10.00%โ”‚ โ€ข โ”‚โ”‚100.00 kBโ”‚ โ€ข โ”‚" +"โ”‚ โ”‚ โ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ”‚" +"โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ”‚" +"โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ข โ€ข โ”‚" +"โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ€ข โ”‚" +"โ”‚ โ”‚ โ€ข โ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ข โ€ข โ”‚" +"โ”‚ โ”‚โ€ขโ€ข โ€ขโ€ข โ”‚โ”‚ โ”‚โ€ข โ€ข โ”‚" +"โ”‚ โ”‚ โ”‚โ”‚ โ”‚ โ”‚" +"โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__chart_cpu_mem__tests__draw_blocks_charts_dead.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__chart_cpu_mem__tests__draw_blocks_charts_dead.snap new file mode 100644 index 0000000..8a75efc --- /dev/null +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__chart_cpu_mem__tests__draw_blocks_charts_dead.snap @@ -0,0 +1,14 @@ +--- +source: src/ui/draw_blocks/chart_cpu_mem.rs +expression: setup.terminal.backend() +--- +"โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ cpu 03.00% โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎโ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ memory 30.00 kB โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ" +"โ”‚10.00%โ”‚ โ€ข โ”‚โ”‚100.00 kBโ”‚ โ€ข โ”‚" +"โ”‚ โ”‚ โ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ”‚" +"โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ”‚" +"โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ข โ€ข โ”‚" +"โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ€ข โ”‚" +"โ”‚ โ”‚ โ€ข โ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ข โ€ข โ”‚" +"โ”‚ โ”‚โ€ขโ€ข โ€ขโ€ข โ”‚โ”‚ โ”‚โ€ข โ€ข โ”‚" +"โ”‚ โ”‚ โ”‚โ”‚ โ”‚ โ”‚" +"โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__chart_cpu_mem__tests__draw_blocks_charts_paused.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__chart_cpu_mem__tests__draw_blocks_charts_paused.snap new file mode 100644 index 0000000..8a75efc --- /dev/null +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__chart_cpu_mem__tests__draw_blocks_charts_paused.snap @@ -0,0 +1,14 @@ +--- +source: src/ui/draw_blocks/chart_cpu_mem.rs +expression: setup.terminal.backend() +--- +"โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ cpu 03.00% โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎโ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ memory 30.00 kB โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ" +"โ”‚10.00%โ”‚ โ€ข โ”‚โ”‚100.00 kBโ”‚ โ€ข โ”‚" +"โ”‚ โ”‚ โ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ”‚" +"โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ”‚" +"โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ข โ€ข โ”‚" +"โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ€ข โ”‚" +"โ”‚ โ”‚ โ€ข โ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ข โ€ข โ”‚" +"โ”‚ โ”‚โ€ขโ€ข โ€ขโ€ข โ”‚โ”‚ โ”‚โ€ข โ€ข โ”‚" +"โ”‚ โ”‚ โ”‚โ”‚ โ”‚ โ”‚" +"โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__chart_cpu_mem__tests__draw_blocks_charts_running_none.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__chart_cpu_mem__tests__draw_blocks_charts_running_none.snap new file mode 100644 index 0000000..8632071 --- /dev/null +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__chart_cpu_mem__tests__draw_blocks_charts_running_none.snap @@ -0,0 +1,14 @@ +--- +source: src/ui/draw_blocks/chart_cpu_mem.rs +expression: setup.terminal.backend() +--- +"โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ cpu 00.00% โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎโ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ memory 0.00 kB โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ" +"โ”‚00.00%โ”‚ โ”‚โ”‚0.00 kBโ”‚ โ”‚" +"โ”‚ โ”‚ โ”‚โ”‚ โ”‚ โ”‚" +"โ”‚ โ”‚ โ”‚โ”‚ โ”‚ โ”‚" +"โ”‚ โ”‚ โ”‚โ”‚ โ”‚ โ”‚" +"โ”‚ โ”‚ โ”‚โ”‚ โ”‚ โ”‚" +"โ”‚ โ”‚ โ”‚โ”‚ โ”‚ โ”‚" +"โ”‚ โ”‚ โ”‚โ”‚ โ”‚ โ”‚" +"โ”‚ โ”‚ โ”‚โ”‚ โ”‚ โ”‚" +"โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__chart_cpu_mem__tests__draw_blocks_charts_running_some.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__chart_cpu_mem__tests__draw_blocks_charts_running_some.snap new file mode 100644 index 0000000..8a75efc --- /dev/null +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__chart_cpu_mem__tests__draw_blocks_charts_running_some.snap @@ -0,0 +1,14 @@ +--- +source: src/ui/draw_blocks/chart_cpu_mem.rs +expression: setup.terminal.backend() +--- +"โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ cpu 03.00% โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎโ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ memory 30.00 kB โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ" +"โ”‚10.00%โ”‚ โ€ข โ”‚โ”‚100.00 kBโ”‚ โ€ข โ”‚" +"โ”‚ โ”‚ โ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ”‚" +"โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ”‚" +"โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ข โ€ข โ”‚" +"โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ€ข โ”‚" +"โ”‚ โ”‚ โ€ข โ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ข โ€ข โ”‚" +"โ”‚ โ”‚โ€ขโ€ข โ€ขโ€ข โ”‚โ”‚ โ”‚โ€ข โ€ข โ”‚" +"โ”‚ โ”‚ โ”‚โ”‚ โ”‚ โ”‚" +"โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__charts__tests__draw_blocks_charts_custom_colors.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__charts__tests__draw_blocks_charts_custom_colors.snap index a92f2bd..90160a2 100644 --- a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__charts__tests__draw_blocks_charts_custom_colors.snap +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__charts__tests__draw_blocks_charts_custom_colors.snap @@ -3,12 +3,12 @@ source: src/ui/draw_blocks/charts.rs expression: setup.terminal.backend() --- "โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ cpu 03.00% โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎโ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ memory 30.00 kB โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ" -"โ”‚10.00%โ”‚ โ€ข โ”‚โ”‚100.00 kBโ”‚ โ€ขโ€ข โ”‚" -"โ”‚ โ”‚ โ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ”‚" -"โ”‚ โ”‚ โ€ขโ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ข โ€ข โ”‚" -"โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ข โ€ข โ”‚" -"โ”‚ โ”‚ โ€ข โ€ขโ€ข โ”‚โ”‚ โ”‚โ€ขโ€ข โ€ขโ€ข โ”‚" -"โ”‚ โ”‚โ€ข โ€ข โ”‚โ”‚ โ”‚โ€ข โ€ข โ”‚" -"โ”‚ โ”‚โ€ข โ€ข โ”‚โ”‚ โ”‚โ€ข โ€ข โ”‚" +"โ”‚10.00%โ”‚ โ€ข โ”‚โ”‚100.00 kBโ”‚ โ€ข โ”‚" +"โ”‚ โ”‚ โ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ”‚" +"โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ”‚" +"โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ข โ€ข โ”‚" +"โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ€ข โ”‚" +"โ”‚ โ”‚ โ€ข โ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ข โ€ข โ”‚" +"โ”‚ โ”‚โ€ขโ€ข โ€ขโ€ข โ”‚โ”‚ โ”‚โ€ข โ€ข โ”‚" "โ”‚ โ”‚ โ”‚โ”‚ โ”‚ โ”‚" "โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__charts__tests__draw_blocks_charts_dead.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__charts__tests__draw_blocks_charts_dead.snap index a92f2bd..90160a2 100644 --- a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__charts__tests__draw_blocks_charts_dead.snap +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__charts__tests__draw_blocks_charts_dead.snap @@ -3,12 +3,12 @@ source: src/ui/draw_blocks/charts.rs expression: setup.terminal.backend() --- "โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ cpu 03.00% โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎโ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ memory 30.00 kB โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ" -"โ”‚10.00%โ”‚ โ€ข โ”‚โ”‚100.00 kBโ”‚ โ€ขโ€ข โ”‚" -"โ”‚ โ”‚ โ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ”‚" -"โ”‚ โ”‚ โ€ขโ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ข โ€ข โ”‚" -"โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ข โ€ข โ”‚" -"โ”‚ โ”‚ โ€ข โ€ขโ€ข โ”‚โ”‚ โ”‚โ€ขโ€ข โ€ขโ€ข โ”‚" -"โ”‚ โ”‚โ€ข โ€ข โ”‚โ”‚ โ”‚โ€ข โ€ข โ”‚" -"โ”‚ โ”‚โ€ข โ€ข โ”‚โ”‚ โ”‚โ€ข โ€ข โ”‚" +"โ”‚10.00%โ”‚ โ€ข โ”‚โ”‚100.00 kBโ”‚ โ€ข โ”‚" +"โ”‚ โ”‚ โ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ”‚" +"โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ”‚" +"โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ข โ€ข โ”‚" +"โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ€ข โ”‚" +"โ”‚ โ”‚ โ€ข โ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ข โ€ข โ”‚" +"โ”‚ โ”‚โ€ขโ€ข โ€ขโ€ข โ”‚โ”‚ โ”‚โ€ข โ€ข โ”‚" "โ”‚ โ”‚ โ”‚โ”‚ โ”‚ โ”‚" "โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__charts__tests__draw_blocks_charts_paused.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__charts__tests__draw_blocks_charts_paused.snap index a92f2bd..90160a2 100644 --- a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__charts__tests__draw_blocks_charts_paused.snap +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__charts__tests__draw_blocks_charts_paused.snap @@ -3,12 +3,12 @@ source: src/ui/draw_blocks/charts.rs expression: setup.terminal.backend() --- "โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ cpu 03.00% โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎโ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ memory 30.00 kB โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ" -"โ”‚10.00%โ”‚ โ€ข โ”‚โ”‚100.00 kBโ”‚ โ€ขโ€ข โ”‚" -"โ”‚ โ”‚ โ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ”‚" -"โ”‚ โ”‚ โ€ขโ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ข โ€ข โ”‚" -"โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ข โ€ข โ”‚" -"โ”‚ โ”‚ โ€ข โ€ขโ€ข โ”‚โ”‚ โ”‚โ€ขโ€ข โ€ขโ€ข โ”‚" -"โ”‚ โ”‚โ€ข โ€ข โ”‚โ”‚ โ”‚โ€ข โ€ข โ”‚" -"โ”‚ โ”‚โ€ข โ€ข โ”‚โ”‚ โ”‚โ€ข โ€ข โ”‚" +"โ”‚10.00%โ”‚ โ€ข โ”‚โ”‚100.00 kBโ”‚ โ€ข โ”‚" +"โ”‚ โ”‚ โ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ”‚" +"โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ”‚" +"โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ข โ€ข โ”‚" +"โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ€ข โ”‚" +"โ”‚ โ”‚ โ€ข โ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ข โ€ข โ”‚" +"โ”‚ โ”‚โ€ขโ€ข โ€ขโ€ข โ”‚โ”‚ โ”‚โ€ข โ€ข โ”‚" "โ”‚ โ”‚ โ”‚โ”‚ โ”‚ โ”‚" "โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__charts__tests__draw_blocks_charts_running_some.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__charts__tests__draw_blocks_charts_running_some.snap index a92f2bd..90160a2 100644 --- a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__charts__tests__draw_blocks_charts_running_some.snap +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__charts__tests__draw_blocks_charts_running_some.snap @@ -3,12 +3,12 @@ source: src/ui/draw_blocks/charts.rs expression: setup.terminal.backend() --- "โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ cpu 03.00% โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎโ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ memory 30.00 kB โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ" -"โ”‚10.00%โ”‚ โ€ข โ”‚โ”‚100.00 kBโ”‚ โ€ขโ€ข โ”‚" -"โ”‚ โ”‚ โ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ”‚" -"โ”‚ โ”‚ โ€ขโ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ข โ€ข โ”‚" -"โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ข โ€ข โ”‚" -"โ”‚ โ”‚ โ€ข โ€ขโ€ข โ”‚โ”‚ โ”‚โ€ขโ€ข โ€ขโ€ข โ”‚" -"โ”‚ โ”‚โ€ข โ€ข โ”‚โ”‚ โ”‚โ€ข โ€ข โ”‚" -"โ”‚ โ”‚โ€ข โ€ข โ”‚โ”‚ โ”‚โ€ข โ€ข โ”‚" +"โ”‚10.00%โ”‚ โ€ข โ”‚โ”‚100.00 kBโ”‚ โ€ข โ”‚" +"โ”‚ โ”‚ โ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ”‚" +"โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ”‚" +"โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ข โ€ข โ”‚" +"โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ€ข โ”‚" +"โ”‚ โ”‚ โ€ข โ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ข โ€ข โ”‚" +"โ”‚ โ”‚โ€ขโ€ข โ€ขโ€ข โ”‚โ”‚ โ”‚โ€ข โ€ข โ”‚" "โ”‚ โ”‚ โ”‚โ”‚ โ”‚ โ”‚" "โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__error__tests__draw_blocks_error_docker_connect_error.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__error__tests__draw_blocks_error_docker_connect_error.snap index 629c56a..6bd72ae 100644 --- a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__error__tests__draw_blocks_error_docker_connect_error.snap +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__error__tests__draw_blocks_error_docker_connect_error.snap @@ -2,12 +2,14 @@ source: src/ui/draw_blocks/error.rs expression: setup.terminal.backend() --- -" " -"โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ Error โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ" -"โ”‚ โ”‚" -"โ”‚ Unable to access docker daemon โ”‚" -"โ”‚ โ”‚" -"โ”‚ oxker::v0.00.000 closing in 04 seconds โ”‚" -"โ”‚ โ”‚" -"โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" -" " +" " +" โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ Error โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ " +" โ”‚ โ”‚ " +" โ”‚ Unable to access docker daemon โ”‚ " +" โ”‚ โ”‚ " +" โ”‚ oxker::v0.00.000 closing in 04 seconds โ”‚ " +" โ”‚ โ”‚ " +" โ”‚ ( q ) quit oxker โ”‚ " +" โ”‚ โ”‚ " +" โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ " +" " diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__error__tests__draw_blocks_error_docker_connect_error_custom_host.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__error__tests__draw_blocks_error_docker_connect_error_custom_host.snap new file mode 100644 index 0000000..9dabfef --- /dev/null +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__error__tests__draw_blocks_error_docker_connect_error_custom_host.snap @@ -0,0 +1,15 @@ +--- +source: src/ui/draw_blocks/error.rs +expression: setup.terminal.backend() +--- +" " +" โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ Error โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ " +" โ”‚ โ”‚ " +" โ”‚ Unable to access docker daemon @ "/test/host.sock" โ”‚ " +" โ”‚ โ”‚ " +" โ”‚ oxker::v0.00.000 closing in 04 seconds โ”‚ " +" โ”‚ โ”‚ " +" โ”‚ ( q ) quit oxker โ”‚ " +" โ”‚ โ”‚ " +" โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ " +" " diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help.snap index 9d29eeb..a68dbd3 100644 --- a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help.snap +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help.snap @@ -2,42 +2,28 @@ source: src/ui/draw_blocks/help.rs expression: setup.terminal.backend() --- -" " -" โ•ญ 0.00.000 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ " -" โ”‚ โ”‚ " -" โ”‚ 88 โ”‚ " -" โ”‚ 88 โ”‚ " -" โ”‚ 88 โ”‚ " -" โ”‚ ,adPPYba, 8b, ,d8 88 ,d8 ,adPPYba, 8b,dPPYba, โ”‚ " -" โ”‚ a8" "8a `Y8, ,8P' 88 ,a8" a8P_____88 88P' "Y8 โ”‚ " -" โ”‚ 8b d8 )888( 8888[ 8PP""""""" 88 โ”‚ " -" โ”‚ "8a, ,a8" ,d8" "8b, 88`"Yba, "8b, ,aa 88 โ”‚ " -" โ”‚ `"YbbdP"' 8P' `Y8 88 `Y8a `"Ybbd8"' 88 โ”‚ " -" โ”‚ โ”‚ " -" โ”‚ A simple tui to view & control docker containers โ”‚ " -" โ”‚ โ”‚ " -" โ”‚ ( tab ) or ( shift+tab ) change panels โ”‚ " -" โ”‚ ( โ†‘ โ†“ ) or ( j k ) or ( Home End ) scroll vertically โ”‚ " -" โ”‚ ( โ† โ†’ ) horizontal scroll across logs โ”‚ " -" โ”‚ ( ctrl ) increase scroll speed, used in conjunction scroll keys โ”‚ " -" โ”‚ ( enter ) send docker container command โ”‚ " -" โ”‚ ( e ) exec into a container โ”‚ " -" โ”‚ ( f ) force clear the screen & redraw the gui โ”‚ " -" โ”‚ ( h ) toggle this help information - or click heading โ”‚ " -" โ”‚ ( s ) save logs to file โ”‚ " -" โ”‚ ( m ) toggle mouse capture - if disabled, text on screen can be selected & copied โ”‚ " -" โ”‚ ( F1 ) or ( / ) enter filter mode โ”‚ " -" โ”‚ ( # ) enter log search mode โ”‚ " -" โ”‚ ( 0 ) stop sort โ”‚ " -" โ”‚ ( 1 - 9 ) sort by header - or click header โ”‚ " -" โ”‚ ( - = ) change log section height โ”‚ " -" โ”‚ ( \ ) toggle log section visibility โ”‚ " -" โ”‚ ( esc ) close dialog โ”‚ " -" โ”‚ ( q ) quit at any time โ”‚ " -" โ”‚ โ”‚ " -" โ”‚ currently an early work in progress, all and any input appreciated โ”‚ " -" โ”‚ https://github.com/mrjackwills/oxker โ”‚ " -" โ”‚ โ”‚ " -" โ”‚ โ”‚ " -" โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ " -" " +" " +" โ•ญ 0.00.000 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ " +" โ”‚ 88 โ”‚ " +" โ”‚ 88 โ”‚ " +" โ”‚ ,adPPYba, 8b, ,d8 88 ,d8 ,adPPYba, 8b,dPPYba โ”‚ " +" โ”‚ a8" "8a `Y8, ,8P' 88 ,a8" a8P_____88 88P' "Y config location: /home/user/.config/oxker/config.toml โ”‚ " +" โ”‚ 8b d8 )888( 8888( 8PP""""""" 88 export location: /test_dir โ”‚ " +" โ”‚ "8a, ,a8" ,d8" "8b, 88`"Yba, "8b, ,aa 88 logs timezone: Etc/UTC โ”‚ " +" โ”‚ `"YbbdP"' 8P' `Y8 88 `Y8a `"Ybbd8"' 88 โ”‚ " +" โ”‚ a work in progress, all and any input appreciated โ”‚ " +" โ”‚ A simple tui to view & control docker containers https://github.com/mrjackwills/oxker โ”‚ " +" โ”‚ โ”‚ " +" โ”‚ Keymap โ”‚ " +" โ”‚ q quit c Esc close dialog โ”‚ " +" โ”‚ Down Up j k Home End scroll vertically Left Right scroll horizontally โ”‚ " +" โ”‚ Control increase scroll speed Enter send docker command โ”‚ " +" โ”‚ e exec into a container i container inspect mode โ”‚ " +" โ”‚ / F1 filter mode # log search mode โ”‚ " +" โ”‚ h toggle this panel f force clear screen and redraw โ”‚ " +" โ”‚ - = change log section height \ toggle of section visibility โ”‚ " +" โ”‚ 1 ~ 9 sort by header - or click header 0 stop sort โ”‚ " +" โ”‚ Tab Back Tab change panel m toggle mouse capture - allows text selection โ”‚ " +" โ”‚ s save logs to file โ”‚ " +" โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ " +" " diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_custom_color.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_custom_color.snap new file mode 100644 index 0000000..a68dbd3 --- /dev/null +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_custom_color.snap @@ -0,0 +1,29 @@ +--- +source: src/ui/draw_blocks/help.rs +expression: setup.terminal.backend() +--- +" " +" โ•ญ 0.00.000 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ " +" โ”‚ 88 โ”‚ " +" โ”‚ 88 โ”‚ " +" โ”‚ ,adPPYba, 8b, ,d8 88 ,d8 ,adPPYba, 8b,dPPYba โ”‚ " +" โ”‚ a8" "8a `Y8, ,8P' 88 ,a8" a8P_____88 88P' "Y config location: /home/user/.config/oxker/config.toml โ”‚ " +" โ”‚ 8b d8 )888( 8888( 8PP""""""" 88 export location: /test_dir โ”‚ " +" โ”‚ "8a, ,a8" ,d8" "8b, 88`"Yba, "8b, ,aa 88 logs timezone: Etc/UTC โ”‚ " +" โ”‚ `"YbbdP"' 8P' `Y8 88 `Y8a `"Ybbd8"' 88 โ”‚ " +" โ”‚ a work in progress, all and any input appreciated โ”‚ " +" โ”‚ A simple tui to view & control docker containers https://github.com/mrjackwills/oxker โ”‚ " +" โ”‚ โ”‚ " +" โ”‚ Keymap โ”‚ " +" โ”‚ q quit c Esc close dialog โ”‚ " +" โ”‚ Down Up j k Home End scroll vertically Left Right scroll horizontally โ”‚ " +" โ”‚ Control increase scroll speed Enter send docker command โ”‚ " +" โ”‚ e exec into a container i container inspect mode โ”‚ " +" โ”‚ / F1 filter mode # log search mode โ”‚ " +" โ”‚ h toggle this panel f force clear screen and redraw โ”‚ " +" โ”‚ - = change log section height \ toggle of section visibility โ”‚ " +" โ”‚ 1 ~ 9 sort by header - or click header 0 stop sort โ”‚ " +" โ”‚ Tab Back Tab change panel m toggle mouse capture - allows text selection โ”‚ " +" โ”‚ s save logs to file โ”‚ " +" โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ " +" " diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_custom_colors.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_custom_colors.snap deleted file mode 100644 index 9d29eeb..0000000 --- a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_custom_colors.snap +++ /dev/null @@ -1,43 +0,0 @@ ---- -source: src/ui/draw_blocks/help.rs -expression: setup.terminal.backend() ---- -" " -" โ•ญ 0.00.000 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ " -" โ”‚ โ”‚ " -" โ”‚ 88 โ”‚ " -" โ”‚ 88 โ”‚ " -" โ”‚ 88 โ”‚ " -" โ”‚ ,adPPYba, 8b, ,d8 88 ,d8 ,adPPYba, 8b,dPPYba, โ”‚ " -" โ”‚ a8" "8a `Y8, ,8P' 88 ,a8" a8P_____88 88P' "Y8 โ”‚ " -" โ”‚ 8b d8 )888( 8888[ 8PP""""""" 88 โ”‚ " -" โ”‚ "8a, ,a8" ,d8" "8b, 88`"Yba, "8b, ,aa 88 โ”‚ " -" โ”‚ `"YbbdP"' 8P' `Y8 88 `Y8a `"Ybbd8"' 88 โ”‚ " -" โ”‚ โ”‚ " -" โ”‚ A simple tui to view & control docker containers โ”‚ " -" โ”‚ โ”‚ " -" โ”‚ ( tab ) or ( shift+tab ) change panels โ”‚ " -" โ”‚ ( โ†‘ โ†“ ) or ( j k ) or ( Home End ) scroll vertically โ”‚ " -" โ”‚ ( โ† โ†’ ) horizontal scroll across logs โ”‚ " -" โ”‚ ( ctrl ) increase scroll speed, used in conjunction scroll keys โ”‚ " -" โ”‚ ( enter ) send docker container command โ”‚ " -" โ”‚ ( e ) exec into a container โ”‚ " -" โ”‚ ( f ) force clear the screen & redraw the gui โ”‚ " -" โ”‚ ( h ) toggle this help information - or click heading โ”‚ " -" โ”‚ ( s ) save logs to file โ”‚ " -" โ”‚ ( m ) toggle mouse capture - if disabled, text on screen can be selected & copied โ”‚ " -" โ”‚ ( F1 ) or ( / ) enter filter mode โ”‚ " -" โ”‚ ( # ) enter log search mode โ”‚ " -" โ”‚ ( 0 ) stop sort โ”‚ " -" โ”‚ ( 1 - 9 ) sort by header - or click header โ”‚ " -" โ”‚ ( - = ) change log section height โ”‚ " -" โ”‚ ( \ ) toggle log section visibility โ”‚ " -" โ”‚ ( esc ) close dialog โ”‚ " -" โ”‚ ( q ) quit at any time โ”‚ " -" โ”‚ โ”‚ " -" โ”‚ currently an early work in progress, all and any input appreciated โ”‚ " -" โ”‚ https://github.com/mrjackwills/oxker โ”‚ " -" โ”‚ โ”‚ " -" โ”‚ โ”‚ " -" โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ " -" " diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_custom_keymap_one_definition.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_custom_keymap_one_definition.snap index 97b4d43..a7b1daf 100644 --- a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_custom_keymap_one_definition.snap +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_custom_keymap_one_definition.snap @@ -2,53 +2,28 @@ source: src/ui/draw_blocks/help.rs expression: setup.terminal.backend() --- -" โ•ญ 0.00.000 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ " -" โ”‚ โ”‚ " -" โ”‚ 88 โ”‚ " -" โ”‚ 88 โ”‚ " -" โ”‚ 88 โ”‚ " -" โ”‚ ,adPPYba, 8b, ,d8 88 ,d8 ,adPPYba, 8b,dPPYba, โ”‚ " -" โ”‚ a8" "8a `Y8, ,8P' 88 ,a8" a8P_____88 88P' "Y8 โ”‚ " -" โ”‚ 8b d8 )888( 8888[ 8PP""""""" 88 โ”‚ " -" โ”‚ "8a, ,a8" ,d8" "8b, 88`"Yba, "8b, ,aa 88 โ”‚ " -" โ”‚ `"YbbdP"' 8P' `Y8 88 `Y8a `"Ybbd8"' 88 โ”‚ " -" โ”‚ โ”‚ " -" โ”‚ A simple tui to view & control docker containers โ”‚ " -" โ”‚ โ”‚ " -" โ”‚ ( t ) select next panel โ”‚ " -" โ”‚ ( u ) select previous panel โ”‚ " -" โ”‚ ( o ) scroll list down by one โ”‚ " -" โ”‚ ( s ) scroll list up by one โ”‚ " -" โ”‚ ( p ) scroll list to end โ”‚ " -" โ”‚ ( q ) scroll list to start โ”‚ " -" โ”‚ ( h ) horizontal scroll logs right โ”‚ " -" โ”‚ ( g ) horizontal scroll logs left โ”‚ " -" โ”‚ ( Alt ) increase scroll speed, used in conjunction scroll keys โ”‚ " -" โ”‚ ( enter ) send docker container command โ”‚ " -" โ”‚ ( d ) exec into a container โ”‚ " -" โ”‚ ( f ) force clear the screen & redraw the gui โ”‚ " -" โ”‚ ( 5 ) toggle this help information - or click heading โ”‚ " -" โ”‚ ( m ) save logs to file โ”‚ " -" โ”‚ ( 6 ) toggle mouse capture - if disabled, text on screen can be selected & copied โ”‚ " -" โ”‚ ( e ) enter filter mode โ”‚ " -" โ”‚ ( 7 ) enter log search mode โ”‚ " -" โ”‚ ( 4 ) reset container sorting โ”‚ " -" โ”‚ ( z ) sort containers by name โ”‚ " -" โ”‚ ( 1 ) sort containers by state โ”‚ " -" โ”‚ ( 2 ) sort containers by status โ”‚ " -" โ”‚ ( v ) sort containers by cpu โ”‚ " -" โ”‚ ( y ) sort containers by memory โ”‚ " -" โ”‚ ( w ) sort containers by id โ”‚ " -" โ”‚ ( x ) sort containers by image โ”‚ " -" โ”‚ ( 0 ) sort containers by rx โ”‚ " -" โ”‚ ( 3 ) sort containers by tx โ”‚ " -" โ”‚ ( i ) decrease log section height โ”‚ " -" โ”‚ ( j ) increase log section height โ”‚ " -" โ”‚ ( k ) toggle log section visibility โ”‚ " -" โ”‚ ( a ) close dialog โ”‚ " -" โ”‚ ( l ) quit at any time โ”‚ " -" โ”‚ โ”‚ " -" โ”‚ currently an early work in progress, all and any input appreciated โ”‚ " -" โ”‚ https://github.com/mrjackwills/oxker โ”‚ " -" โ”‚ โ”‚ " -" โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ " +" โ•ญ 0.00.000 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ " +" โ”‚ 88 โ”‚ " +" โ”‚ 88 โ”‚ " +" โ”‚ ,adPPYba, 8b, ,d8 88 ,d8 ,adPPYba, 8b,dPPYba โ”‚ " +" โ”‚ a8" "8a `Y8, ,8P' 88 ,a8" a8P_____88 88P' "Y config location: /home/user/.config/oxker/config.toml โ”‚ " +" โ”‚ 8b d8 )888( 8888( 8PP""""""" 88 export location: /test_dir โ”‚ " +" โ”‚ "8a, ,a8" ,d8" "8b, 88`"Yba, "8b, ,aa 88 logs timezone: Etc/UTC โ”‚ " +" โ”‚ โ”‚ " +" โ”‚ Keymap โ”‚ " +" โ”‚ n quit a close dialog โ”‚ " +" โ”‚ p s scroll vertically i j scroll horizontally โ”‚ " +" โ”‚ r scroll to start q scroll to end โ”‚ " +" โ”‚ Alt increase scroll speed Enter send docker command โ”‚ " +" โ”‚ d exec into a container e container inspect mode โ”‚ " +" โ”‚ f filter mode g log search mode โ”‚ " +" โ”‚ 5 toggle this panel h force clear screen and redraw โ”‚ " +" โ”‚ k l change log section height m toggle of section visibility โ”‚ " +" โ”‚ z sort by name 1 sort by state โ”‚ " +" โ”‚ 2 sort by status v sort by CPU โ”‚ " +" โ”‚ y sort by memory w sort by ID โ”‚ " +" โ”‚ x sort by Image 0 sort by RX โ”‚ " +" โ”‚ 3 sort by TX 4 stop sort โ”‚ " +" โ”‚ t u change panel 6 toggle mouse capture - allows text selection โ”‚ " +" โ”‚ o save logs to file โ”‚ " +" โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ " diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_custom_keymap_one_two_definition.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_custom_keymap_one_two_definition.snap new file mode 100644 index 0000000..ed14a24 --- /dev/null +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_custom_keymap_one_two_definition.snap @@ -0,0 +1,34 @@ +--- +source: src/ui/draw_blocks/help.rs +expression: setup.terminal.backend() +--- +" " +" โ•ญ 0.00.000 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ " +" โ”‚ 88 โ”‚ " +" โ”‚ 88 โ”‚ " +" โ”‚ ,adPPYba, 8b, ,d8 88 ,d8 ,adPPYba, 8b,dPPYba โ”‚ " +" โ”‚ a8" "8a `Y8, ,8P' 88 ,a8" a8P_____88 88P' "Y config location: /home/user/.config/oxker/config.toml โ”‚ " +" โ”‚ 8b d8 )888( 8888( 8PP""""""" 88 export location: /test_dir โ”‚ " +" โ”‚ "8a, ,a8" ,d8" "8b, 88`"Yba, "8b, ,aa 88 logs timezone: Etc/UTC โ”‚ " +" โ”‚ `"YbbdP"' 8P' `Y8 88 `Y8a `"Ybbd8"' 88 โ”‚ " +" โ”‚ a work in progress, all and any input appreciated โ”‚ " +" โ”‚ A simple tui to view & control docker containers https://github.com/mrjackwills/oxker โ”‚ " +" โ”‚ โ”‚ " +" โ”‚ Keymap โ”‚ " +" โ”‚ 0 quit a b close dialog โ”‚ " +" โ”‚ 4 Caps Lock Scroll Lock scroll vertically q s r scroll horizontally โ”‚ " +" โ”‚ 8 scroll to start 6 7 scroll to end โ”‚ " +" โ”‚ Alt increase scroll speed Enter send docker command โ”‚ " +" โ”‚ g exec into a container i j container inspect mode โ”‚ " +" โ”‚ k filter mode m n log search mode โ”‚ " +" โ”‚ F5 F6 toggle this panel o force clear screen and redraw โ”‚ " +" โ”‚ u w v change log section height y z toggle of section visibility โ”‚ " +" โ”‚ Begin Menu sort by name Page Up Pause sort by state โ”‚ " +" โ”‚ Print Screen sort by status Down sort by CPU โ”‚ " +" โ”‚ Home sort by memory Back Tab sort by ID โ”‚ " +" โ”‚ End Esc sort by Image Num Lock sort by RX โ”‚ " +" โ”‚ F1 F2 sort by TX F3 stop sort โ”‚ " +" โ”‚ Print Screen Left Up change panel F7 toggle mouse capture - allows text selection โ”‚ " +" โ”‚ 2 3 save logs to file โ”‚ " +" โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ " +" " diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_custom_keymap_two_definition.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_custom_keymap_two_definition.snap new file mode 100644 index 0000000..1aecc71 --- /dev/null +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_custom_keymap_two_definition.snap @@ -0,0 +1,34 @@ +--- +source: src/ui/draw_blocks/help.rs +expression: setup.terminal.backend() +--- +" " +" โ•ญ 0.00.000 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ " +" โ”‚ 88 โ”‚ " +" โ”‚ 88 โ”‚ " +" โ”‚ ,adPPYba, 8b, ,d8 88 ,d8 ,adPPYba, 8b,dPPYba, โ”‚ " +" โ”‚ a8" "8a `Y8, ,8P' 88 ,a8" a8P_____88 88P' "Y8 config location: /home/user/.config/oxker/config.toml โ”‚ " +" โ”‚ 8b d8 )888( 8888( 8PP""""""" 88 export location: /test_dir โ”‚ " +" โ”‚ "8a, ,a8" ,d8" "8b, 88`"Yba, "8b, ,aa 88 logs timezone: Etc/UTC โ”‚ " +" โ”‚ `"YbbdP"' 8P' `Y8 88 `Y8a `"Ybbd8"' 88 โ”‚ " +" โ”‚ a work in progress, all and any input appreciated โ”‚ " +" โ”‚ A simple tui to view & control docker containers https://github.com/mrjackwills/oxker โ”‚ " +" โ”‚ โ”‚ " +" โ”‚ Keymap โ”‚ " +" โ”‚ 0 1 quit a b close dialog โ”‚ " +" โ”‚ 4 Caps Lock 5 Scroll Lock scroll vertically q s r t scroll horizontally โ”‚ " +" โ”‚ 8 9 scroll to start 6 7 scroll to end โ”‚ " +" โ”‚ Alt increase scroll speed Enter send docker command โ”‚ " +" โ”‚ g h exec into a container i j container inspect mode โ”‚ " +" โ”‚ k l filter mode m n log search mode โ”‚ " +" โ”‚ F5 F6 toggle this panel o p force clear screen and redraw โ”‚ " +" โ”‚ u w v x change log section height y z toggle of section visibility โ”‚ " +" โ”‚ Begin Menu sort by name Page Up Pause sort by state โ”‚ " +" โ”‚ Print Screen Tab sort by status Down Del sort by CPU โ”‚ " +" โ”‚ Home Insert sort by memory Back Tab Backspace sort by ID โ”‚ " +" โ”‚ End Esc sort by Image Num Lock Page Down sort by RX โ”‚ " +" โ”‚ F1 F2 sort by TX F3 F4 stop sort โ”‚ " +" โ”‚ Print Screen Left Up Right change panel F7 F8 toggle mouse capture - allows text selection โ”‚ " +" โ”‚ 2 3 save logs to file โ”‚ " +" โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ " +" " diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_custom_keymap_two_definitions.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_custom_keymap_two_definitions.snap deleted file mode 100644 index f884948..0000000 --- a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_custom_keymap_two_definitions.snap +++ /dev/null @@ -1,54 +0,0 @@ ---- -source: src/ui/draw_blocks/help.rs -expression: setup.terminal.backend() ---- -" โ•ญ 0.00.000 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ " -" โ”‚ โ”‚ " -" โ”‚ 88 โ”‚ " -" โ”‚ 88 โ”‚ " -" โ”‚ 88 โ”‚ " -" โ”‚ ,adPPYba, 8b, ,d8 88 ,d8 ,adPPYba, 8b,dPPYba, โ”‚ " -" โ”‚ a8" "8a `Y8, ,8P' 88 ,a8" a8P_____88 88P' "Y8 โ”‚ " -" โ”‚ 8b d8 )888( 8888[ 8PP""""""" 88 โ”‚ " -" โ”‚ "8a, ,a8" ,d8" "8b, 88`"Yba, "8b, ,aa 88 โ”‚ " -" โ”‚ `"YbbdP"' 8P' `Y8 88 `Y8a `"Ybbd8"' 88 โ”‚ " -" โ”‚ โ”‚ " -" โ”‚ A simple tui to view & control docker containers โ”‚ " -" โ”‚ โ”‚ " -" โ”‚ ( s ) or ( S ) select next panel โ”‚ " -" โ”‚ ( t ) or ( T ) select previous panel โ”‚ " -" โ”‚ ( n ) or ( N ) scroll list down by one โ”‚ " -" โ”‚ ( r ) or ( R ) scroll list up by one โ”‚ " -" โ”‚ ( o ) or ( O ) scroll list to end โ”‚ " -" โ”‚ ( p ) or ( P ) scroll list to start โ”‚ " -" โ”‚ ( g ) or ( G ) horizontal scroll logs right โ”‚ " -" โ”‚ ( f ) or ( F ) horizontal scroll logs left โ”‚ " -" โ”‚ ( Alt ) increase scroll speed, used in conjunction scroll keys โ”‚ " -" โ”‚ ( enter ) send docker container command โ”‚ " -" โ”‚ ( d ) or ( D ) exec into a container โ”‚ " -" โ”‚ ( f ) or ( F ) force clear the screen & redraw the gui โ”‚ " -" โ”‚ ( 4 ) or ( 5 ) toggle this help information - or click heading โ”‚ " -" โ”‚ ( l ) or ( L ) save logs to file โ”‚ " -" โ”‚ ( 5 ) or ( Page Down ) toggle mouse capture - if disabled, text on screen can be selected & copied โ”‚ " -" โ”‚ ( e ) or ( E ) enter filter mode โ”‚ " -" โ”‚ ( m ) or ( M ) enter log search mode โ”‚ " -" โ”‚ ( 3 ) or ( 6 ) reset container sorting โ”‚ " -" โ”‚ ( y ) or ( Y ) sort containers by name โ”‚ " -" โ”‚ ( 0 ) or ( 9 ) sort containers by state โ”‚ " -" โ”‚ ( 1 ) or ( 8 ) sort containers by status โ”‚ " -" โ”‚ ( u ) or ( U ) sort containers by cpu โ”‚ " -" โ”‚ ( x ) or ( X ) sort containers by memory โ”‚ " -" โ”‚ ( v ) or ( V ) sort containers by id โ”‚ " -" โ”‚ ( w ) or ( W ) sort containers by image โ”‚ " -" โ”‚ ( z ) or ( Z ) sort containers by rx โ”‚ " -" โ”‚ ( 2 ) or ( 7 ) sort containers by tx โ”‚ " -" โ”‚ ( h ) or ( H ) decrease log section height โ”‚ " -" โ”‚ ( i ) or ( I ) increase log section height โ”‚ " -" โ”‚ ( j ) or ( J ) toggle log section visibility โ”‚ " -" โ”‚ ( a ) or ( A ) close dialog โ”‚ " -" โ”‚ ( k ) or ( K ) quit at any time โ”‚ " -" โ”‚ โ”‚ " -" โ”‚ currently an early work in progress, all and any input appreciated โ”‚ " -" โ”‚ https://github.com/mrjackwills/oxker โ”‚ " -" โ”‚ โ”‚ " -" โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ " diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_no_config.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_no_config.snap new file mode 100644 index 0000000..b6ae220 --- /dev/null +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_no_config.snap @@ -0,0 +1,29 @@ +--- +source: src/ui/draw_blocks/help.rs +expression: setup.terminal.backend() +--- +" " +" โ•ญ 0.00.000 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ " +" โ”‚ 88 โ”‚ " +" โ”‚ 88 โ”‚ " +" โ”‚ ,adPPYba, 8b, ,d8 88 ,d8 ,adPPYba, 8b,dPPYb โ”‚ " +" โ”‚ a8" "8a `Y8, ,8P' 88 ,a8" a8P_____88 88P' " โ”‚ " +" โ”‚ 8b d8 )888( 8888( 8PP""""""" 88 export location: /test_dir โ”‚ " +" โ”‚ "8a, ,a8" ,d8" "8b, 88`"Yba, "8b, ,aa 88 logs timezone: Etc/UTC โ”‚ " +" โ”‚ `"YbbdP"' 8P' `Y8 88 `Y8a `"Ybbd8"' 88 โ”‚ " +" โ”‚ a work in progress, all and any input appreciated โ”‚ " +" โ”‚ A simple tui to view & control docker containers https://github.com/mrjackwills/oxker โ”‚ " +" โ”‚ โ”‚ " +" โ”‚ Keymap โ”‚ " +" โ”‚ q quit c Esc close dialog โ”‚ " +" โ”‚ Down Up j k Home End scroll vertically Left Right scroll horizontally โ”‚ " +" โ”‚ Control increase scroll speed Enter send docker command โ”‚ " +" โ”‚ e exec into a container i container inspect mode โ”‚ " +" โ”‚ / F1 filter mode # log search mode โ”‚ " +" โ”‚ h toggle this panel f force clear screen and redraw โ”‚ " +" โ”‚ - = change log section height \ toggle of section visibility โ”‚ " +" โ”‚ 1 ~ 9 sort by header - or click header 0 stop sort โ”‚ " +" โ”‚ Tab Back Tab change panel m toggle mouse capture - allows text selection โ”‚ " +" โ”‚ s save logs to file โ”‚ " +" โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ " +" " diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_no_save.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_no_save.snap new file mode 100644 index 0000000..a1240bf --- /dev/null +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_no_save.snap @@ -0,0 +1,29 @@ +--- +source: src/ui/draw_blocks/help.rs +expression: setup.terminal.backend() +--- +" " +" โ•ญ 0.00.000 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ " +" โ”‚ 88 โ”‚ " +" โ”‚ 88 โ”‚ " +" โ”‚ ,adPPYba, 8b, ,d8 88 ,d8 ,adPPYba, 8b,dPPYba โ”‚ " +" โ”‚ a8" "8a `Y8, ,8P' 88 ,a8" a8P_____88 88P' "Y โ”‚ " +" โ”‚ 8b d8 )888( 8888( 8PP""""""" 88 config location: /home/user/.config/oxker/config.toml โ”‚ " +" โ”‚ "8a, ,a8" ,d8" "8b, 88`"Yba, "8b, ,aa 88 logs timezone: Etc/UTC โ”‚ " +" โ”‚ `"YbbdP"' 8P' `Y8 88 `Y8a `"Ybbd8"' 88 โ”‚ " +" โ”‚ a work in progress, all and any input appreciated โ”‚ " +" โ”‚ A simple tui to view & control docker containers https://github.com/mrjackwills/oxker โ”‚ " +" โ”‚ โ”‚ " +" โ”‚ Keymap โ”‚ " +" โ”‚ q quit c Esc close dialog โ”‚ " +" โ”‚ Down Up j k Home End scroll vertically Left Right scroll horizontally โ”‚ " +" โ”‚ Control increase scroll speed Enter send docker command โ”‚ " +" โ”‚ e exec into a container i container inspect mode โ”‚ " +" โ”‚ / F1 filter mode # log search mode โ”‚ " +" โ”‚ h toggle this panel f force clear screen and redraw โ”‚ " +" โ”‚ - = change log section height \ toggle of section visibility โ”‚ " +" โ”‚ 1 ~ 9 sort by header - or click header 0 stop sort โ”‚ " +" โ”‚ Tab Back Tab change panel m toggle mouse capture - allows text selection โ”‚ " +" โ”‚ s save logs to file โ”‚ " +" โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ " +" " diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_no_timezone.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_no_timezone.snap new file mode 100644 index 0000000..01aa561 --- /dev/null +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_no_timezone.snap @@ -0,0 +1,29 @@ +--- +source: src/ui/draw_blocks/help.rs +expression: setup.terminal.backend() +--- +" " +" โ•ญ 0.00.000 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ " +" โ”‚ 88 โ”‚ " +" โ”‚ 88 โ”‚ " +" โ”‚ ,adPPYba, 8b, ,d8 88 ,d8 ,adPPYba, 8b,dPPYba โ”‚ " +" โ”‚ a8" "8a `Y8, ,8P' 88 ,a8" a8P_____88 88P' "Y โ”‚ " +" โ”‚ 8b d8 )888( 8888( 8PP""""""" 88 config location: /home/user/.config/oxker/config.toml โ”‚ " +" โ”‚ "8a, ,a8" ,d8" "8b, 88`"Yba, "8b, ,aa 88 export location: /test_dir โ”‚ " +" โ”‚ `"YbbdP"' 8P' `Y8 88 `Y8a `"Ybbd8"' 88 โ”‚ " +" โ”‚ a work in progress, all and any input appreciated โ”‚ " +" โ”‚ A simple tui to view & control docker containers https://github.com/mrjackwills/oxker โ”‚ " +" โ”‚ โ”‚ " +" โ”‚ Keymap โ”‚ " +" โ”‚ q quit c Esc close dialog โ”‚ " +" โ”‚ Down Up j k Home End scroll vertically Left Right scroll horizontally โ”‚ " +" โ”‚ Control increase scroll speed Enter send docker command โ”‚ " +" โ”‚ e exec into a container i container inspect mode โ”‚ " +" โ”‚ / F1 filter mode # log search mode โ”‚ " +" โ”‚ h toggle this panel f force clear screen and redraw โ”‚ " +" โ”‚ - = change log section height \ toggle of section visibility โ”‚ " +" โ”‚ 1 ~ 9 sort by header - or click header 0 stop sort โ”‚ " +" โ”‚ Tab Back Tab change panel m toggle mouse capture - allows text selection โ”‚ " +" โ”‚ s save logs to file โ”‚ " +" โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ " +" " diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_one_and_two_definitions.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_one_and_two_definitions.snap deleted file mode 100644 index 6fcfd2e..0000000 --- a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_one_and_two_definitions.snap +++ /dev/null @@ -1,54 +0,0 @@ ---- -source: src/ui/draw_blocks/help.rs -expression: setup.terminal.backend() ---- -" โ•ญ 0.00.000 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ " -" โ”‚ โ”‚ " -" โ”‚ 88 โ”‚ " -" โ”‚ 88 โ”‚ " -" โ”‚ 88 โ”‚ " -" โ”‚ ,adPPYba, 8b, ,d8 88 ,d8 ,adPPYba, 8b,dPPYba, โ”‚ " -" โ”‚ a8" "8a `Y8, ,8P' 88 ,a8" a8P_____88 88P' "Y8 โ”‚ " -" โ”‚ 8b d8 )888( 8888[ 8PP""""""" 88 โ”‚ " -" โ”‚ "8a, ,a8" ,d8" "8b, 88`"Yba, "8b, ,aa 88 โ”‚ " -" โ”‚ `"YbbdP"' 8P' `Y8 88 `Y8a `"Ybbd8"' 88 โ”‚ " -" โ”‚ โ”‚ " -" โ”‚ A simple tui to view & control docker containers โ”‚ " -" โ”‚ โ”‚ " -" โ”‚ ( t ) select next panel โ”‚ " -" โ”‚ ( u ) or ( U ) select previous panel โ”‚ " -" โ”‚ ( o ) or ( O ) scroll list down by one โ”‚ " -" โ”‚ ( s ) or ( S ) scroll list up by one โ”‚ " -" โ”‚ ( p ) scroll list to end โ”‚ " -" โ”‚ ( q ) or ( Q ) scroll list to start โ”‚ " -" โ”‚ ( h ) horizontal scroll logs right โ”‚ " -" โ”‚ ( g ) or ( G ) horizontal scroll logs left โ”‚ " -" โ”‚ ( Alt ) increase scroll speed, used in conjunction scroll keys โ”‚ " -" โ”‚ ( enter ) send docker container command โ”‚ " -" โ”‚ ( d ) exec into a container โ”‚ " -" โ”‚ ( f ) force clear the screen & redraw the gui โ”‚ " -" โ”‚ ( 5 ) toggle this help information - or click heading โ”‚ " -" โ”‚ ( m ) or ( M ) save logs to file โ”‚ " -" โ”‚ ( 6 ) or ( # ) toggle mouse capture - if disabled, text on screen can be selected & copied โ”‚ " -" โ”‚ ( e ) or ( E ) enter filter mode โ”‚ " -" โ”‚ ( 8 ) enter log search mode โ”‚ " -" โ”‚ ( 4 ) or ( 5 ) reset container sorting โ”‚ " -" โ”‚ ( z ) sort containers by name โ”‚ " -" โ”‚ ( 1 ) sort containers by state โ”‚ " -" โ”‚ ( 2 ) or ( 7 ) sort containers by status โ”‚ " -" โ”‚ ( v ) sort containers by cpu โ”‚ " -" โ”‚ ( y ) or ( Y ) sort containers by memory โ”‚ " -" โ”‚ ( w ) or ( W ) sort containers by id โ”‚ " -" โ”‚ ( x ) sort containers by image โ”‚ " -" โ”‚ ( 0 ) or ( 9 ) sort containers by rx โ”‚ " -" โ”‚ ( 3 ) sort containers by tx โ”‚ " -" โ”‚ ( i ) or ( I ) decrease log section height โ”‚ " -" โ”‚ ( j ) increase log section height โ”‚ " -" โ”‚ ( k ) or ( K ) toggle log section visibility โ”‚ " -" โ”‚ ( a ) or ( A ) close dialog โ”‚ " -" โ”‚ ( l ) quit at any time โ”‚ " -" โ”‚ โ”‚ " -" โ”‚ currently an early work in progress, all and any input appreciated โ”‚ " -" โ”‚ https://github.com/mrjackwills/oxker โ”‚ " -" โ”‚ โ”‚ " -" โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ " diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_show_timezone.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_show_timezone.snap deleted file mode 100644 index 6c8b9ef..0000000 --- a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_show_timezone.snap +++ /dev/null @@ -1,43 +0,0 @@ ---- -source: src/ui/draw_blocks/help.rs -expression: setup.terminal.backend() ---- -" โ•ญ 0.00.000 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ " -" โ”‚ โ”‚ " -" โ”‚ 88 โ”‚ " -" โ”‚ 88 โ”‚ " -" โ”‚ 88 โ”‚ " -" โ”‚ ,adPPYba, 8b, ,d8 88 ,d8 ,adPPYba, 8b,dPPYba, โ”‚ " -" โ”‚ a8" "8a `Y8, ,8P' 88 ,a8" a8P_____88 88P' "Y8 โ”‚ " -" โ”‚ 8b d8 )888( 8888[ 8PP""""""" 88 โ”‚ " -" โ”‚ "8a, ,a8" ,d8" "8b, 88`"Yba, "8b, ,aa 88 โ”‚ " -" โ”‚ `"YbbdP"' 8P' `Y8 88 `Y8a `"Ybbd8"' 88 โ”‚ " -" โ”‚ โ”‚ " -" โ”‚ A simple tui to view & control docker containers โ”‚ " -" โ”‚ โ”‚ " -" โ”‚ logs timezone: Asia/Tokyo โ”‚ " -" โ”‚ โ”‚ " -" โ”‚ ( tab ) or ( shift+tab ) change panels โ”‚ " -" โ”‚ ( โ†‘ โ†“ ) or ( j k ) or ( Home End ) scroll vertically โ”‚ " -" โ”‚ ( โ† โ†’ ) horizontal scroll across logs โ”‚ " -" โ”‚ ( ctrl ) increase scroll speed, used in conjunction scroll keys โ”‚ " -" โ”‚ ( enter ) send docker container command โ”‚ " -" โ”‚ ( e ) exec into a container โ”‚ " -" โ”‚ ( f ) force clear the screen & redraw the gui โ”‚ " -" โ”‚ ( h ) toggle this help information - or click heading โ”‚ " -" โ”‚ ( s ) save logs to file โ”‚ " -" โ”‚ ( m ) toggle mouse capture - if disabled, text on screen can be selected & copied โ”‚ " -" โ”‚ ( F1 ) or ( / ) enter filter mode โ”‚ " -" โ”‚ ( # ) enter log search mode โ”‚ " -" โ”‚ ( 0 ) stop sort โ”‚ " -" โ”‚ ( 1 - 9 ) sort by header - or click header โ”‚ " -" โ”‚ ( - = ) change log section height โ”‚ " -" โ”‚ ( \ ) toggle log section visibility โ”‚ " -" โ”‚ ( esc ) close dialog โ”‚ " -" โ”‚ ( q ) quit at any time โ”‚ " -" โ”‚ โ”‚ " -" โ”‚ currently an early work in progress, all and any input appreciated โ”‚ " -" โ”‚ https://github.com/mrjackwills/oxker โ”‚ " -" โ”‚ โ”‚ " -" โ”‚ โ”‚ " -" โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ " diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__inspect__tests__draw_blocks_inspect_custom_color.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__inspect__tests__draw_blocks_inspect_custom_color.snap new file mode 100644 index 0000000..0c5cf65 --- /dev/null +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__inspect__tests__draw_blocks_inspect_custom_color.snap @@ -0,0 +1,54 @@ +--- +source: src/ui/draw_blocks/inspect.rs +expression: setup.terminal.backend() +--- +"โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ inspecting: postgres 0bdea642 - c or Esc or i to exit โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ" +"โ”‚ "Id": "0bdea64212f9c75eb4a1184dd406c2c79a986a7a889a23c85358456cc1bb60c7", โ”‚" +"โ”‚ "Created": "2026-01-23T22:20:19.927967311Z", โ”‚" +"โ”‚ "Path": "docker-entrypoint.sh", โ”‚" +"โ”‚ "Args": [ โ”‚" +"โ”‚ "postgres" โ”‚" +"โ”‚ ], โ”‚" +"โ”‚ "State": { โ”‚" +"โ”‚ "Status": "running", โ”‚" +"โ”‚ "Running": true, โ”‚" +"โ”‚ "Paused": false, โ”‚" +"โ”‚ "Restarting": false, โ”‚" +"โ”‚ "OOMKilled": false, โ”‚" +"โ”‚ "Dead": false, โ”‚" +"โ”‚ "Pid": 782, โ”‚" +"โ”‚ "ExitCode": 0, โ”‚" +"โ”‚ "Error": "", โ”‚" +"โ”‚ "StartedAt": "2026-01-30T08:09:01.574885915Z", โ”‚" +"โ”‚ "FinishedAt": "2026-01-30T08:09:01.180567927Z" โ”‚" +"โ”‚ }, โ”‚" +"โ”‚ "Image": "sha256:aa3668fcbcb5ded731b7d5c27065a4edf545debb7f27bf514c709b1b4e032352", โ”‚" +"โ”‚ "ResolvConfPath": "/var/lib/docker/containers/0bdea64212f9c75eb4a1184dd406c2c79a986a7a889a23c853โ”‚" +"โ”‚ "HostnamePath": "/var/lib/docker/containers/0bdea64212f9c75eb4a1184dd406c2c79a986a7a889a23c85358โ”‚" +"โ”‚ "HostsPath": "/var/lib/docker/containers/0bdea64212f9c75eb4a1184dd406c2c79a986a7a889a23c85358456โ”‚" +"โ”‚ "LogPath": "/var/lib/docker/containers/0bdea64212f9c75eb4a1184dd406c2c79a986a7a889a23c85358456ccโ”‚" +"โ”‚ "Name": "/postgres", โ”‚" +"โ”‚ "RestartCount": 0, โ”‚" +"โ”‚ "Driver": "overlay2", โ”‚" +"โ”‚ "Platform": "linux", โ”‚" +"โ”‚ "MountLabel": "", โ”‚" +"โ”‚ "ProcessLabel": "", โ”‚" +"โ”‚ "AppArmorProfile": "", โ”‚" +"โ”‚ "HostConfig": { โ”‚" +"โ”‚ "CpuShares": 0, โ”‚" +"โ”‚ "Memory": 1073741824, โ”‚" +"โ”‚ "CgroupParent": "", โ”‚" +"โ”‚ "BlkioWeight": 0, โ”‚" +"โ”‚ "CpuPeriod": 0, โ”‚" +"โ”‚ "CpuQuota": 0, โ”‚" +"โ”‚ "CpuRealtimePeriod": 0, โ”‚" +"โ”‚ "CpuRealtimeRuntime": 0, โ”‚" +"โ”‚ "CpusetCpus": "", โ”‚" +"โ”‚ "CpusetMems": "", โ”‚" +"โ”‚ "MemoryReservation": 0, โ”‚" +"โ”‚ "MemorySwap": 2147483648, โ”‚" +"โ”‚ "NanoCpus": 0, โ”‚" +"โ”‚ "OomKillDisable": false, โ”‚" +"โ”‚ "CpuCount": 0, โ”‚" +"โ”‚ "CpuPercent": 0, โ”‚" +"โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ 0/158 โ†“ 0/972 โ†’ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__inspect__tests__draw_blocks_inspect_custom_keymap_all.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__inspect__tests__draw_blocks_inspect_custom_keymap_all.snap new file mode 100644 index 0000000..9ab52b8 --- /dev/null +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__inspect__tests__draw_blocks_inspect_custom_keymap_all.snap @@ -0,0 +1,54 @@ +--- +source: src/ui/draw_blocks/inspect.rs +expression: setup.terminal.backend() +--- +"โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ inspecting: postgres 0bdea642 - F or Z or 4 or 5 to exit โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ" +"โ”‚ "Id": "0bdea64212f9c75eb4a1184dd406c2c79a986a7a889a23c85358456cc1bb60c7", โ”‚" +"โ”‚ "Created": "2026-01-23T22:20:19.927967311Z", โ”‚" +"โ”‚ "Path": "docker-entrypoint.sh", โ”‚" +"โ”‚ "Args": [ โ”‚" +"โ”‚ "postgres" โ”‚" +"โ”‚ ], โ”‚" +"โ”‚ "State": { โ”‚" +"โ”‚ "Status": "running", โ”‚" +"โ”‚ "Running": true, โ”‚" +"โ”‚ "Paused": false, โ”‚" +"โ”‚ "Restarting": false, โ”‚" +"โ”‚ "OOMKilled": false, โ”‚" +"โ”‚ "Dead": false, โ”‚" +"โ”‚ "Pid": 782, โ”‚" +"โ”‚ "ExitCode": 0, โ”‚" +"โ”‚ "Error": "", โ”‚" +"โ”‚ "StartedAt": "2026-01-30T08:09:01.574885915Z", โ”‚" +"โ”‚ "FinishedAt": "2026-01-30T08:09:01.180567927Z" โ”‚" +"โ”‚ }, โ”‚" +"โ”‚ "Image": "sha256:aa3668fcbcb5ded731b7d5c27065a4edf545debb7f27bf514c709b1b4e032352", โ”‚" +"โ”‚ "ResolvConfPath": "/var/lib/docker/containers/0bdea64212f9c75eb4a1184dd406c2c79a986a7a889a23c853โ”‚" +"โ”‚ "HostnamePath": "/var/lib/docker/containers/0bdea64212f9c75eb4a1184dd406c2c79a986a7a889a23c85358โ”‚" +"โ”‚ "HostsPath": "/var/lib/docker/containers/0bdea64212f9c75eb4a1184dd406c2c79a986a7a889a23c85358456โ”‚" +"โ”‚ "LogPath": "/var/lib/docker/containers/0bdea64212f9c75eb4a1184dd406c2c79a986a7a889a23c85358456ccโ”‚" +"โ”‚ "Name": "/postgres", โ”‚" +"โ”‚ "RestartCount": 0, โ”‚" +"โ”‚ "Driver": "overlay2", โ”‚" +"โ”‚ "Platform": "linux", โ”‚" +"โ”‚ "MountLabel": "", โ”‚" +"โ”‚ "ProcessLabel": "", โ”‚" +"โ”‚ "AppArmorProfile": "", โ”‚" +"โ”‚ "HostConfig": { โ”‚" +"โ”‚ "CpuShares": 0, โ”‚" +"โ”‚ "Memory": 1073741824, โ”‚" +"โ”‚ "CgroupParent": "", โ”‚" +"โ”‚ "BlkioWeight": 0, โ”‚" +"โ”‚ "CpuPeriod": 0, โ”‚" +"โ”‚ "CpuQuota": 0, โ”‚" +"โ”‚ "CpuRealtimePeriod": 0, โ”‚" +"โ”‚ "CpuRealtimeRuntime": 0, โ”‚" +"โ”‚ "CpusetCpus": "", โ”‚" +"โ”‚ "CpusetMems": "", โ”‚" +"โ”‚ "MemoryReservation": 0, โ”‚" +"โ”‚ "MemorySwap": 2147483648, โ”‚" +"โ”‚ "NanoCpus": 0, โ”‚" +"โ”‚ "OomKillDisable": false, โ”‚" +"โ”‚ "CpuCount": 0, โ”‚" +"โ”‚ "CpuPercent": 0, โ”‚" +"โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ 0/158 โ†“ 0/972 โ†’ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__inspect__tests__draw_blocks_inspect_custom_keymap_clear_one.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__inspect__tests__draw_blocks_inspect_custom_keymap_clear_one.snap new file mode 100644 index 0000000..ed18a89 --- /dev/null +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__inspect__tests__draw_blocks_inspect_custom_keymap_clear_one.snap @@ -0,0 +1,54 @@ +--- +source: src/ui/draw_blocks/inspect.rs +expression: setup.terminal.backend() +--- +"โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ inspecting: postgres 0bdea642 - F or Esc or i to exit โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ" +"โ”‚ "Id": "0bdea64212f9c75eb4a1184dd406c2c79a986a7a889a23c85358456cc1bb60c7", โ”‚" +"โ”‚ "Created": "2026-01-23T22:20:19.927967311Z", โ”‚" +"โ”‚ "Path": "docker-entrypoint.sh", โ”‚" +"โ”‚ "Args": [ โ”‚" +"โ”‚ "postgres" โ”‚" +"โ”‚ ], โ”‚" +"โ”‚ "State": { โ”‚" +"โ”‚ "Status": "running", โ”‚" +"โ”‚ "Running": true, โ”‚" +"โ”‚ "Paused": false, โ”‚" +"โ”‚ "Restarting": false, โ”‚" +"โ”‚ "OOMKilled": false, โ”‚" +"โ”‚ "Dead": false, โ”‚" +"โ”‚ "Pid": 782, โ”‚" +"โ”‚ "ExitCode": 0, โ”‚" +"โ”‚ "Error": "", โ”‚" +"โ”‚ "StartedAt": "2026-01-30T08:09:01.574885915Z", โ”‚" +"โ”‚ "FinishedAt": "2026-01-30T08:09:01.180567927Z" โ”‚" +"โ”‚ }, โ”‚" +"โ”‚ "Image": "sha256:aa3668fcbcb5ded731b7d5c27065a4edf545debb7f27bf514c709b1b4e032352", โ”‚" +"โ”‚ "ResolvConfPath": "/var/lib/docker/containers/0bdea64212f9c75eb4a1184dd406c2c79a986a7a889a23c853โ”‚" +"โ”‚ "HostnamePath": "/var/lib/docker/containers/0bdea64212f9c75eb4a1184dd406c2c79a986a7a889a23c85358โ”‚" +"โ”‚ "HostsPath": "/var/lib/docker/containers/0bdea64212f9c75eb4a1184dd406c2c79a986a7a889a23c85358456โ”‚" +"โ”‚ "LogPath": "/var/lib/docker/containers/0bdea64212f9c75eb4a1184dd406c2c79a986a7a889a23c85358456ccโ”‚" +"โ”‚ "Name": "/postgres", โ”‚" +"โ”‚ "RestartCount": 0, โ”‚" +"โ”‚ "Driver": "overlay2", โ”‚" +"โ”‚ "Platform": "linux", โ”‚" +"โ”‚ "MountLabel": "", โ”‚" +"โ”‚ "ProcessLabel": "", โ”‚" +"โ”‚ "AppArmorProfile": "", โ”‚" +"โ”‚ "HostConfig": { โ”‚" +"โ”‚ "CpuShares": 0, โ”‚" +"โ”‚ "Memory": 1073741824, โ”‚" +"โ”‚ "CgroupParent": "", โ”‚" +"โ”‚ "BlkioWeight": 0, โ”‚" +"โ”‚ "CpuPeriod": 0, โ”‚" +"โ”‚ "CpuQuota": 0, โ”‚" +"โ”‚ "CpuRealtimePeriod": 0, โ”‚" +"โ”‚ "CpuRealtimeRuntime": 0, โ”‚" +"โ”‚ "CpusetCpus": "", โ”‚" +"โ”‚ "CpusetMems": "", โ”‚" +"โ”‚ "MemoryReservation": 0, โ”‚" +"โ”‚ "MemorySwap": 2147483648, โ”‚" +"โ”‚ "NanoCpus": 0, โ”‚" +"โ”‚ "OomKillDisable": false, โ”‚" +"โ”‚ "CpuCount": 0, โ”‚" +"โ”‚ "CpuPercent": 0, โ”‚" +"โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ 0/158 โ†“ 0/972 โ†’ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__inspect__tests__draw_blocks_inspect_custom_keymap_clear_two.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__inspect__tests__draw_blocks_inspect_custom_keymap_clear_two.snap new file mode 100644 index 0000000..c4b4d46 --- /dev/null +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__inspect__tests__draw_blocks_inspect_custom_keymap_clear_two.snap @@ -0,0 +1,54 @@ +--- +source: src/ui/draw_blocks/inspect.rs +expression: setup.terminal.backend() +--- +"โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ inspecting: postgres 0bdea642 - F or Z or i to exit โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ" +"โ”‚ "Id": "0bdea64212f9c75eb4a1184dd406c2c79a986a7a889a23c85358456cc1bb60c7", โ”‚" +"โ”‚ "Created": "2026-01-23T22:20:19.927967311Z", โ”‚" +"โ”‚ "Path": "docker-entrypoint.sh", โ”‚" +"โ”‚ "Args": [ โ”‚" +"โ”‚ "postgres" โ”‚" +"โ”‚ ], โ”‚" +"โ”‚ "State": { โ”‚" +"โ”‚ "Status": "running", โ”‚" +"โ”‚ "Running": true, โ”‚" +"โ”‚ "Paused": false, โ”‚" +"โ”‚ "Restarting": false, โ”‚" +"โ”‚ "OOMKilled": false, โ”‚" +"โ”‚ "Dead": false, โ”‚" +"โ”‚ "Pid": 782, โ”‚" +"โ”‚ "ExitCode": 0, โ”‚" +"โ”‚ "Error": "", โ”‚" +"โ”‚ "StartedAt": "2026-01-30T08:09:01.574885915Z", โ”‚" +"โ”‚ "FinishedAt": "2026-01-30T08:09:01.180567927Z" โ”‚" +"โ”‚ }, โ”‚" +"โ”‚ "Image": "sha256:aa3668fcbcb5ded731b7d5c27065a4edf545debb7f27bf514c709b1b4e032352", โ”‚" +"โ”‚ "ResolvConfPath": "/var/lib/docker/containers/0bdea64212f9c75eb4a1184dd406c2c79a986a7a889a23c853โ”‚" +"โ”‚ "HostnamePath": "/var/lib/docker/containers/0bdea64212f9c75eb4a1184dd406c2c79a986a7a889a23c85358โ”‚" +"โ”‚ "HostsPath": "/var/lib/docker/containers/0bdea64212f9c75eb4a1184dd406c2c79a986a7a889a23c85358456โ”‚" +"โ”‚ "LogPath": "/var/lib/docker/containers/0bdea64212f9c75eb4a1184dd406c2c79a986a7a889a23c85358456ccโ”‚" +"โ”‚ "Name": "/postgres", โ”‚" +"โ”‚ "RestartCount": 0, โ”‚" +"โ”‚ "Driver": "overlay2", โ”‚" +"โ”‚ "Platform": "linux", โ”‚" +"โ”‚ "MountLabel": "", โ”‚" +"โ”‚ "ProcessLabel": "", โ”‚" +"โ”‚ "AppArmorProfile": "", โ”‚" +"โ”‚ "HostConfig": { โ”‚" +"โ”‚ "CpuShares": 0, โ”‚" +"โ”‚ "Memory": 1073741824, โ”‚" +"โ”‚ "CgroupParent": "", โ”‚" +"โ”‚ "BlkioWeight": 0, โ”‚" +"โ”‚ "CpuPeriod": 0, โ”‚" +"โ”‚ "CpuQuota": 0, โ”‚" +"โ”‚ "CpuRealtimePeriod": 0, โ”‚" +"โ”‚ "CpuRealtimeRuntime": 0, โ”‚" +"โ”‚ "CpusetCpus": "", โ”‚" +"โ”‚ "CpusetMems": "", โ”‚" +"โ”‚ "MemoryReservation": 0, โ”‚" +"โ”‚ "MemorySwap": 2147483648, โ”‚" +"โ”‚ "NanoCpus": 0, โ”‚" +"โ”‚ "OomKillDisable": false, โ”‚" +"โ”‚ "CpuCount": 0, โ”‚" +"โ”‚ "CpuPercent": 0, โ”‚" +"โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ 0/158 โ†“ 0/972 โ†’ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__inspect__tests__draw_blocks_inspect_custom_keymap_inspect_one.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__inspect__tests__draw_blocks_inspect_custom_keymap_inspect_one.snap new file mode 100644 index 0000000..f1144ec --- /dev/null +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__inspect__tests__draw_blocks_inspect_custom_keymap_inspect_one.snap @@ -0,0 +1,54 @@ +--- +source: src/ui/draw_blocks/inspect.rs +expression: setup.terminal.backend() +--- +"โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ inspecting: postgres 0bdea642 - c or Esc or 4 to exit โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ" +"โ”‚ "Id": "0bdea64212f9c75eb4a1184dd406c2c79a986a7a889a23c85358456cc1bb60c7", โ”‚" +"โ”‚ "Created": "2026-01-23T22:20:19.927967311Z", โ”‚" +"โ”‚ "Path": "docker-entrypoint.sh", โ”‚" +"โ”‚ "Args": [ โ”‚" +"โ”‚ "postgres" โ”‚" +"โ”‚ ], โ”‚" +"โ”‚ "State": { โ”‚" +"โ”‚ "Status": "running", โ”‚" +"โ”‚ "Running": true, โ”‚" +"โ”‚ "Paused": false, โ”‚" +"โ”‚ "Restarting": false, โ”‚" +"โ”‚ "OOMKilled": false, โ”‚" +"โ”‚ "Dead": false, โ”‚" +"โ”‚ "Pid": 782, โ”‚" +"โ”‚ "ExitCode": 0, โ”‚" +"โ”‚ "Error": "", โ”‚" +"โ”‚ "StartedAt": "2026-01-30T08:09:01.574885915Z", โ”‚" +"โ”‚ "FinishedAt": "2026-01-30T08:09:01.180567927Z" โ”‚" +"โ”‚ }, โ”‚" +"โ”‚ "Image": "sha256:aa3668fcbcb5ded731b7d5c27065a4edf545debb7f27bf514c709b1b4e032352", โ”‚" +"โ”‚ "ResolvConfPath": "/var/lib/docker/containers/0bdea64212f9c75eb4a1184dd406c2c79a986a7a889a23c853โ”‚" +"โ”‚ "HostnamePath": "/var/lib/docker/containers/0bdea64212f9c75eb4a1184dd406c2c79a986a7a889a23c85358โ”‚" +"โ”‚ "HostsPath": "/var/lib/docker/containers/0bdea64212f9c75eb4a1184dd406c2c79a986a7a889a23c85358456โ”‚" +"โ”‚ "LogPath": "/var/lib/docker/containers/0bdea64212f9c75eb4a1184dd406c2c79a986a7a889a23c85358456ccโ”‚" +"โ”‚ "Name": "/postgres", โ”‚" +"โ”‚ "RestartCount": 0, โ”‚" +"โ”‚ "Driver": "overlay2", โ”‚" +"โ”‚ "Platform": "linux", โ”‚" +"โ”‚ "MountLabel": "", โ”‚" +"โ”‚ "ProcessLabel": "", โ”‚" +"โ”‚ "AppArmorProfile": "", โ”‚" +"โ”‚ "HostConfig": { โ”‚" +"โ”‚ "CpuShares": 0, โ”‚" +"โ”‚ "Memory": 1073741824, โ”‚" +"โ”‚ "CgroupParent": "", โ”‚" +"โ”‚ "BlkioWeight": 0, โ”‚" +"โ”‚ "CpuPeriod": 0, โ”‚" +"โ”‚ "CpuQuota": 0, โ”‚" +"โ”‚ "CpuRealtimePeriod": 0, โ”‚" +"โ”‚ "CpuRealtimeRuntime": 0, โ”‚" +"โ”‚ "CpusetCpus": "", โ”‚" +"โ”‚ "CpusetMems": "", โ”‚" +"โ”‚ "MemoryReservation": 0, โ”‚" +"โ”‚ "MemorySwap": 2147483648, โ”‚" +"โ”‚ "NanoCpus": 0, โ”‚" +"โ”‚ "OomKillDisable": false, โ”‚" +"โ”‚ "CpuCount": 0, โ”‚" +"โ”‚ "CpuPercent": 0, โ”‚" +"โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ 0/158 โ†“ 0/972 โ†’ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__inspect__tests__draw_blocks_inspect_custom_keymap_inspect_two.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__inspect__tests__draw_blocks_inspect_custom_keymap_inspect_two.snap new file mode 100644 index 0000000..e2eb4bc --- /dev/null +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__inspect__tests__draw_blocks_inspect_custom_keymap_inspect_two.snap @@ -0,0 +1,54 @@ +--- +source: src/ui/draw_blocks/inspect.rs +expression: setup.terminal.backend() +--- +"โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ inspecting: postgres 0bdea642 - c or Esc or 4 or 5 to exit โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ" +"โ”‚ "Id": "0bdea64212f9c75eb4a1184dd406c2c79a986a7a889a23c85358456cc1bb60c7", โ”‚" +"โ”‚ "Created": "2026-01-23T22:20:19.927967311Z", โ”‚" +"โ”‚ "Path": "docker-entrypoint.sh", โ”‚" +"โ”‚ "Args": [ โ”‚" +"โ”‚ "postgres" โ”‚" +"โ”‚ ], โ”‚" +"โ”‚ "State": { โ”‚" +"โ”‚ "Status": "running", โ”‚" +"โ”‚ "Running": true, โ”‚" +"โ”‚ "Paused": false, โ”‚" +"โ”‚ "Restarting": false, โ”‚" +"โ”‚ "OOMKilled": false, โ”‚" +"โ”‚ "Dead": false, โ”‚" +"โ”‚ "Pid": 782, โ”‚" +"โ”‚ "ExitCode": 0, โ”‚" +"โ”‚ "Error": "", โ”‚" +"โ”‚ "StartedAt": "2026-01-30T08:09:01.574885915Z", โ”‚" +"โ”‚ "FinishedAt": "2026-01-30T08:09:01.180567927Z" โ”‚" +"โ”‚ }, โ”‚" +"โ”‚ "Image": "sha256:aa3668fcbcb5ded731b7d5c27065a4edf545debb7f27bf514c709b1b4e032352", โ”‚" +"โ”‚ "ResolvConfPath": "/var/lib/docker/containers/0bdea64212f9c75eb4a1184dd406c2c79a986a7a889a23c853โ”‚" +"โ”‚ "HostnamePath": "/var/lib/docker/containers/0bdea64212f9c75eb4a1184dd406c2c79a986a7a889a23c85358โ”‚" +"โ”‚ "HostsPath": "/var/lib/docker/containers/0bdea64212f9c75eb4a1184dd406c2c79a986a7a889a23c85358456โ”‚" +"โ”‚ "LogPath": "/var/lib/docker/containers/0bdea64212f9c75eb4a1184dd406c2c79a986a7a889a23c85358456ccโ”‚" +"โ”‚ "Name": "/postgres", โ”‚" +"โ”‚ "RestartCount": 0, โ”‚" +"โ”‚ "Driver": "overlay2", โ”‚" +"โ”‚ "Platform": "linux", โ”‚" +"โ”‚ "MountLabel": "", โ”‚" +"โ”‚ "ProcessLabel": "", โ”‚" +"โ”‚ "AppArmorProfile": "", โ”‚" +"โ”‚ "HostConfig": { โ”‚" +"โ”‚ "CpuShares": 0, โ”‚" +"โ”‚ "Memory": 1073741824, โ”‚" +"โ”‚ "CgroupParent": "", โ”‚" +"โ”‚ "BlkioWeight": 0, โ”‚" +"โ”‚ "CpuPeriod": 0, โ”‚" +"โ”‚ "CpuQuota": 0, โ”‚" +"โ”‚ "CpuRealtimePeriod": 0, โ”‚" +"โ”‚ "CpuRealtimeRuntime": 0, โ”‚" +"โ”‚ "CpusetCpus": "", โ”‚" +"โ”‚ "CpusetMems": "", โ”‚" +"โ”‚ "MemoryReservation": 0, โ”‚" +"โ”‚ "MemorySwap": 2147483648, โ”‚" +"โ”‚ "NanoCpus": 0, โ”‚" +"โ”‚ "OomKillDisable": false, โ”‚" +"โ”‚ "CpuCount": 0, โ”‚" +"โ”‚ "CpuPercent": 0, โ”‚" +"โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ 0/158 โ†“ 0/972 โ†’ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__inspect__tests__draw_blocks_inspect_default_valid.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__inspect__tests__draw_blocks_inspect_default_valid.snap new file mode 100644 index 0000000..0c5cf65 --- /dev/null +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__inspect__tests__draw_blocks_inspect_default_valid.snap @@ -0,0 +1,54 @@ +--- +source: src/ui/draw_blocks/inspect.rs +expression: setup.terminal.backend() +--- +"โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ inspecting: postgres 0bdea642 - c or Esc or i to exit โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ" +"โ”‚ "Id": "0bdea64212f9c75eb4a1184dd406c2c79a986a7a889a23c85358456cc1bb60c7", โ”‚" +"โ”‚ "Created": "2026-01-23T22:20:19.927967311Z", โ”‚" +"โ”‚ "Path": "docker-entrypoint.sh", โ”‚" +"โ”‚ "Args": [ โ”‚" +"โ”‚ "postgres" โ”‚" +"โ”‚ ], โ”‚" +"โ”‚ "State": { โ”‚" +"โ”‚ "Status": "running", โ”‚" +"โ”‚ "Running": true, โ”‚" +"โ”‚ "Paused": false, โ”‚" +"โ”‚ "Restarting": false, โ”‚" +"โ”‚ "OOMKilled": false, โ”‚" +"โ”‚ "Dead": false, โ”‚" +"โ”‚ "Pid": 782, โ”‚" +"โ”‚ "ExitCode": 0, โ”‚" +"โ”‚ "Error": "", โ”‚" +"โ”‚ "StartedAt": "2026-01-30T08:09:01.574885915Z", โ”‚" +"โ”‚ "FinishedAt": "2026-01-30T08:09:01.180567927Z" โ”‚" +"โ”‚ }, โ”‚" +"โ”‚ "Image": "sha256:aa3668fcbcb5ded731b7d5c27065a4edf545debb7f27bf514c709b1b4e032352", โ”‚" +"โ”‚ "ResolvConfPath": "/var/lib/docker/containers/0bdea64212f9c75eb4a1184dd406c2c79a986a7a889a23c853โ”‚" +"โ”‚ "HostnamePath": "/var/lib/docker/containers/0bdea64212f9c75eb4a1184dd406c2c79a986a7a889a23c85358โ”‚" +"โ”‚ "HostsPath": "/var/lib/docker/containers/0bdea64212f9c75eb4a1184dd406c2c79a986a7a889a23c85358456โ”‚" +"โ”‚ "LogPath": "/var/lib/docker/containers/0bdea64212f9c75eb4a1184dd406c2c79a986a7a889a23c85358456ccโ”‚" +"โ”‚ "Name": "/postgres", โ”‚" +"โ”‚ "RestartCount": 0, โ”‚" +"โ”‚ "Driver": "overlay2", โ”‚" +"โ”‚ "Platform": "linux", โ”‚" +"โ”‚ "MountLabel": "", โ”‚" +"โ”‚ "ProcessLabel": "", โ”‚" +"โ”‚ "AppArmorProfile": "", โ”‚" +"โ”‚ "HostConfig": { โ”‚" +"โ”‚ "CpuShares": 0, โ”‚" +"โ”‚ "Memory": 1073741824, โ”‚" +"โ”‚ "CgroupParent": "", โ”‚" +"โ”‚ "BlkioWeight": 0, โ”‚" +"โ”‚ "CpuPeriod": 0, โ”‚" +"โ”‚ "CpuQuota": 0, โ”‚" +"โ”‚ "CpuRealtimePeriod": 0, โ”‚" +"โ”‚ "CpuRealtimeRuntime": 0, โ”‚" +"โ”‚ "CpusetCpus": "", โ”‚" +"โ”‚ "CpusetMems": "", โ”‚" +"โ”‚ "MemoryReservation": 0, โ”‚" +"โ”‚ "MemorySwap": 2147483648, โ”‚" +"โ”‚ "NanoCpus": 0, โ”‚" +"โ”‚ "OomKillDisable": false, โ”‚" +"โ”‚ "CpuCount": 0, โ”‚" +"โ”‚ "CpuPercent": 0, โ”‚" +"โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ 0/158 โ†“ 0/972 โ†’ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__inspect__tests__draw_blocks_inspect_offset.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__inspect__tests__draw_blocks_inspect_offset.snap new file mode 100644 index 0000000..503c826 --- /dev/null +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__inspect__tests__draw_blocks_inspect_offset.snap @@ -0,0 +1,54 @@ +--- +source: src/ui/draw_blocks/inspect.rs +expression: setup.terminal.backend() +--- +"โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ inspecting: postgres 0bdea642 - c or Esc or i to exit โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ" +"โ”‚rting": false, โ”‚" +"โ”‚lled": false, โ”‚" +"โ”‚: false, โ”‚" +"โ”‚ 782, โ”‚" +"โ”‚ode": 0, โ”‚" +"โ”‚": "", โ”‚" +"โ”‚edAt": "2026-01-30T08:09:01.574885915Z", โ”‚" +"โ”‚hedAt": "2026-01-30T08:09:01.180567927Z" โ”‚" +"โ”‚ โ”‚" +"โ”‚ "sha256:aa3668fcbcb5ded731b7d5c27065a4edf545debb7f27bf514c709b1b4e032352", โ”‚" +"โ”‚onfPath": "/var/lib/docker/containers/0bdea64212f9c75eb4a1184dd406c2c79a986a7a889a23c85358456cc1bbโ”‚" +"โ”‚ePath": "/var/lib/docker/containers/0bdea64212f9c75eb4a1184dd406c2c79a986a7a889a23c85358456cc1bb60โ”‚" +"โ”‚th": "/var/lib/docker/containers/0bdea64212f9c75eb4a1184dd406c2c79a986a7a889a23c85358456cc1bb60c7/โ”‚" +"โ”‚": "/var/lib/docker/containers/0bdea64212f9c75eb4a1184dd406c2c79a986a7a889a23c85358456cc1bb60c7/0bโ”‚" +"โ”‚"/postgres", โ”‚" +"โ”‚Count": 0, โ”‚" +"โ”‚: "overlay2", โ”‚" +"โ”‚m": "linux", โ”‚" +"โ”‚bel": "", โ”‚" +"โ”‚Label": "", โ”‚" +"โ”‚rProfile": "", โ”‚" +"โ”‚fig": { โ”‚" +"โ”‚ares": 0, โ”‚" +"โ”‚y": 1073741824, โ”‚" +"โ”‚pParent": "", โ”‚" +"โ”‚Weight": 0, โ”‚" +"โ”‚riod": 0, โ”‚" +"โ”‚ota": 0, โ”‚" +"โ”‚altimePeriod": 0, โ”‚" +"โ”‚altimeRuntime": 0, โ”‚" +"โ”‚tCpus": "", โ”‚" +"โ”‚tMems": "", โ”‚" +"โ”‚yReservation": 0, โ”‚" +"โ”‚ySwap": 2147483648, โ”‚" +"โ”‚pus": 0, โ”‚" +"โ”‚llDisable": false, โ”‚" +"โ”‚unt": 0, โ”‚" +"โ”‚rcent": 0, โ”‚" +"โ”‚imumIOps": 0, โ”‚" +"โ”‚imumBandwidth": 0, โ”‚" +"โ”‚inerIDFile": "", โ”‚" +"โ”‚nfig": { โ”‚" +"โ”‚e": "json-file", โ”‚" +"โ”‚fig": {} โ”‚" +"โ”‚ โ”‚" +"โ”‚rkMode": "oxker-examaple-net", โ”‚" +"โ”‚indings": {}, โ”‚" +"โ”‚rtPolicy": { โ”‚" +"โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ†‘ 10/158 โ†“ โ† 10/972 โ†’ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__inspect__tests__draw_blocks_inspect_offset_max.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__inspect__tests__draw_blocks_inspect_offset_max.snap new file mode 100644 index 0000000..b7e3d72 --- /dev/null +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__inspect__tests__draw_blocks_inspect_offset_max.snap @@ -0,0 +1,54 @@ +--- +source: src/ui/draw_blocks/inspect.rs +expression: setup.terminal.backend() +--- +"โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ inspecting: postgres 0bdea642 - c or Esc or i to exit โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ" +"โ”‚ โ”‚" +"โ”‚ โ”‚" +"โ”‚ โ”‚" +"โ”‚ โ”‚" +"โ”‚ โ”‚" +"โ”‚ โ”‚" +"โ”‚ โ”‚" +"โ”‚ โ”‚" +"โ”‚ โ”‚" +"โ”‚ โ”‚" +"โ”‚ โ”‚" +"โ”‚ โ”‚" +"โ”‚ โ”‚" +"โ”‚ โ”‚" +"โ”‚ โ”‚" +"โ”‚ โ”‚" +"โ”‚ โ”‚" +"โ”‚ โ”‚" +"โ”‚ โ”‚" +"โ”‚ โ”‚" +"โ”‚ โ”‚" +"โ”‚ โ”‚" +"โ”‚ โ”‚" +"โ”‚ โ”‚" +"โ”‚ โ”‚" +"โ”‚ โ”‚" +"โ”‚ โ”‚" +"โ”‚ โ”‚" +"โ”‚ โ”‚" +"โ”‚ โ”‚" +"โ”‚ โ”‚" +"โ”‚ โ”‚" +"โ”‚ โ”‚" +"โ”‚ โ”‚" +"โ”‚ โ”‚" +"โ”‚ โ”‚" +"โ”‚ โ”‚" +"โ”‚ โ”‚" +"โ”‚ โ”‚" +"โ”‚ โ”‚" +"โ”‚ โ”‚" +"โ”‚ โ”‚" +"โ”‚ โ”‚" +"โ”‚ โ”‚" +"โ”‚ โ”‚" +"โ”‚ โ”‚" +"โ”‚ โ”‚" +"โ”‚ โ”‚" +"โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ†‘ 158/158 โ† 972/972 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout.snap index df648f6..b9fa938 100644 --- a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout.snap +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout.snap @@ -25,10 +25,10 @@ expression: setup.terminal.backend() "โ”‚ โ”‚" "โ”‚ โ”‚" "โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" -"โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ cpu 03.00% โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎโ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ memory 30.00 kB โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎโ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ports โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ" -"โ”‚10.00%โ”‚ โ€ขโ€ขโ€ข โ”‚โ”‚100.00 kBโ”‚ โ€ขโ€ข โ”‚โ”‚ ip private publicโ”‚" -"โ”‚ โ”‚ โ€ขโ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ€ข โ”‚โ”‚ 8001 โ”‚" -"โ”‚ โ”‚ โ€ขโ€ขโ€ข โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ขโ€ข โ€ข โ€ข โ”‚โ”‚127.0.0.1 8003 8003โ”‚" -"โ”‚ โ”‚โ€ข โ€ขโ€ข โ”‚โ”‚ โ”‚โ€ข โ€ขโ€ข โ”‚โ”‚ โ”‚" -"โ”‚ โ”‚ โ”‚โ”‚ โ”‚ โ”‚โ”‚ โ”‚" -"โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" +"โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ cpu 03.00% โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎโ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ memory 30.00 kB โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎโ•ญโ”€โ”€โ”€โ”€โ”€โ”€ rx: 0.00 kb/s tx: 0.00 kb/s โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ports โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ" +"โ”‚10.00%โ”‚ โ€ขโ€ข โ”‚โ”‚100.00 kBโ”‚ โ€ขโ€ข โ”‚โ”‚ โ”‚โ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ข โ”‚ โ”‚ ip private publicโ”‚" +"โ”‚ โ”‚ โ€ขโ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ”‚ โ”‚ 8001 โ”‚" +"โ”‚ โ”‚ โ€ขโ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ€ข โ”‚โ”‚0.00 kb/sโ”‚ โ€ขโ€ข โ”‚ โ”‚127.0.0.1 8003 8003โ”‚" +"โ”‚ โ”‚ โ€ข โ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ข โ€ขโ€ข โ”‚โ”‚0.00 kb/sโ”‚ โ€ข โ”‚ โ”‚ โ”‚" +"โ”‚ โ”‚โ€ข โ€ข โ”‚โ”‚ โ”‚โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ข โ”‚ โ”‚ โ”‚" +"โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_delete.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_delete.snap index 37af4fd..0f52b1d 100644 --- a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_delete.snap +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_delete.snap @@ -32,13 +32,13 @@ expression: setup.terminal.backend() "โ”‚ โ”‚" "โ”‚ โ”‚" "โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" -"โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ cpu 03.00% โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎโ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ memory 30.00 kB โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎโ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ports โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ" -"โ”‚10.00%โ”‚ โ€ขโ€ข โ”‚โ”‚100.00 kBโ”‚ โ€ขโ€ข โ”‚โ”‚ ip private publicโ”‚" -"โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ”‚โ”‚ 8001 โ”‚" -"โ”‚ โ”‚ โ€ขโ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ€ข โ”‚โ”‚127.0.0.1 8003 8003โ”‚" -"โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚" -"โ”‚ โ”‚ โ€ขโ€ข โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ€ข โ€ข โ”‚โ”‚ โ”‚" -"โ”‚ โ”‚โ€ข โ€ขโ€ข โ”‚โ”‚ โ”‚โ€ข โ€ขโ€ข โ”‚โ”‚ โ”‚" -"โ”‚ โ”‚โ€ข โ€ข โ”‚โ”‚ โ”‚โ€ข โ€ข โ”‚โ”‚ โ”‚" -"โ”‚ โ”‚ โ”‚โ”‚ โ”‚ โ”‚โ”‚ โ”‚" -"โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" +"โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ cpu 03.00% โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎโ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ memory 30.00 kB โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎโ•ญโ”€โ”€โ”€โ”€โ”€โ”€ rx: 0.00 kb/s tx: 0.00 kb/s โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ports โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ" +"โ”‚10.00%โ”‚ โ€ข โ”‚โ”‚100.00 kBโ”‚ โ€ข โ”‚โ”‚ โ”‚โ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ข โ”‚ โ”‚ ip private publicโ”‚" +"โ”‚ โ”‚ โ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ”‚ โ”‚ 8001 โ”‚" +"โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ”‚ โ”‚127.0.0.1 8003 8003โ”‚" +"โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚0.00 kb/sโ”‚ โ€ขโ€ข โ”‚ โ”‚ โ”‚" +"โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ข โ”‚ โ”‚ โ”‚" +"โ”‚ โ”‚ โ€ข โ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ข โ€ขโ€ข โ”‚โ”‚0.00 kb/sโ”‚ โ€ข โ”‚ โ”‚ โ”‚" +"โ”‚ โ”‚โ€ขโ€ข โ€ข โ”‚โ”‚ โ”‚โ€ขโ€ข โ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ข โ”‚ โ”‚ โ”‚" +"โ”‚ โ”‚ โ”‚โ”‚ โ”‚ โ”‚โ”‚ โ”‚ โ€ข โ”‚ โ”‚ โ”‚" +"โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_error.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_error.snap index 49919e8..90db02f 100644 --- a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_error.snap +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_error.snap @@ -32,13 +32,13 @@ expression: setup.terminal.backend() "โ”‚ โ”‚" "โ”‚ โ”‚" "โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" -"โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ cpu 03.00% โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎโ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ memory 30.00 kB โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎโ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ports โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ" -"โ”‚10.00%โ”‚ โ€ขโ€ข โ”‚โ”‚100.00 kBโ”‚ โ€ขโ€ข โ”‚โ”‚ ip private publicโ”‚" -"โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ”‚โ”‚ 8001 โ”‚" -"โ”‚ โ”‚ โ€ขโ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ€ข โ”‚โ”‚127.0.0.1 8003 8003โ”‚" -"โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚" -"โ”‚ โ”‚ โ€ขโ€ข โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ€ข โ€ข โ”‚โ”‚ โ”‚" -"โ”‚ โ”‚โ€ข โ€ขโ€ข โ”‚โ”‚ โ”‚โ€ข โ€ขโ€ข โ”‚โ”‚ โ”‚" -"โ”‚ โ”‚โ€ข โ€ข โ”‚โ”‚ โ”‚โ€ข โ€ข โ”‚โ”‚ โ”‚" -"โ”‚ โ”‚ โ”‚โ”‚ โ”‚ โ”‚โ”‚ โ”‚" -"โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" +"โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ cpu 03.00% โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎโ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ memory 30.00 kB โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎโ•ญโ”€โ”€โ”€โ”€โ”€โ”€ rx: 0.00 kb/s tx: 0.00 kb/s โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ports โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ" +"โ”‚10.00%โ”‚ โ€ข โ”‚โ”‚100.00 kBโ”‚ โ€ข โ”‚โ”‚ โ”‚โ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ข โ”‚ โ”‚ ip private publicโ”‚" +"โ”‚ โ”‚ โ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ”‚ โ”‚ 8001 โ”‚" +"โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ”‚ โ”‚127.0.0.1 8003 8003โ”‚" +"โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚0.00 kb/sโ”‚ โ€ขโ€ข โ”‚ โ”‚ โ”‚" +"โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ข โ”‚ โ”‚ โ”‚" +"โ”‚ โ”‚ โ€ข โ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ข โ€ขโ€ข โ”‚โ”‚0.00 kb/sโ”‚ โ€ข โ”‚ โ”‚ โ”‚" +"โ”‚ โ”‚โ€ขโ€ข โ€ข โ”‚โ”‚ โ”‚โ€ขโ€ข โ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ข โ”‚ โ”‚ โ”‚" +"โ”‚ โ”‚ โ”‚โ”‚ โ”‚ โ”‚โ”‚ โ”‚ โ€ข โ”‚ โ”‚ โ”‚" +"โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_help_panel.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_help_panel.snap index a69c2d8..706c766 100644 --- a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_help_panel.snap +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_help_panel.snap @@ -4,41 +4,41 @@ expression: setup.terminal.backend() --- " name state status cpu memory/limit id image โ†“ rx โ†‘ tx ( h ) exit help " "โ•ญ Containers 1/3 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎโ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ" -"โ”‚โšช container_1 โœ“ running Up 1 hoโ•ญ 0.00.000 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ โ”‚โ”‚โ–ถ pause โ”‚" Hidden by multi-width symbols: [(2, " ")] -"โ”‚ container_2 โœ“ running Up 2 hoโ”‚ โ”‚ โ”‚โ”‚ restart โ”‚" -"โ”‚ container_3 โœ“ running Up 3 hoโ”‚ 88 โ”‚ โ”‚โ”‚ stop โ”‚" -"โ”‚ โ”‚ 88 โ”‚ โ”‚โ”‚ delete โ”‚" -"โ”‚ โ”‚ 88 โ”‚ โ”‚โ”‚ โ”‚" -"โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚ ,adPPYba, 8b, ,d8 88 ,d8 ,adPPYba, 8b,dPPYba, โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" -"โ•ญ Logs 3/3 - container_1 - image_1 โ”€โ”€โ”‚ a8" "8a `Y8, ,8P' 88 ,a8" a8P_____88 88P' "Y8 โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ" -"โ”‚ line 1 โ”‚ 8b d8 )888( 8888[ 8PP""""""" 88 โ”‚ โ”‚" -"โ”‚ line 2 โ”‚ "8a, ,a8" ,d8" "8b, 88`"Yba, "8b, ,aa 88 โ”‚ โ”‚" -"โ”‚โ–ถ line 3 โ”‚ `"YbbdP"' 8P' `Y8 88 `Y8a `"Ybbd8"' 88 โ”‚ โ”‚" -"โ”‚ โ”‚ โ”‚ โ”‚" -"โ”‚ โ”‚ A simple tui to view & control docker containers โ”‚ โ”‚" -"โ”‚ โ”‚ โ”‚ โ”‚" -"โ”‚ โ”‚ ( tab ) or ( shift+tab ) change panels โ”‚ โ”‚" -"โ”‚ โ”‚ ( โ†‘ โ†“ ) or ( j k ) or ( Home End ) scroll vertically โ”‚ โ”‚" -"โ”‚ โ”‚ ( โ† โ†’ ) horizontal scroll across logs โ”‚ โ”‚" -"โ”‚ โ”‚ ( ctrl ) increase scroll speed, used in conjunction scroll keys โ”‚ โ”‚" -"โ”‚ โ”‚ ( enter ) send docker container command โ”‚ โ”‚" -"โ”‚ โ”‚ ( e ) exec into a container โ”‚ โ”‚" -"โ”‚ โ”‚ ( f ) force clear the screen & redraw the gui โ”‚ โ”‚" -"โ”‚ โ”‚ ( h ) toggle this help information - or click heading โ”‚ โ”‚" -"โ”‚ โ”‚ ( s ) save logs to file โ”‚ โ”‚" -"โ”‚ โ”‚ ( m ) toggle mouse capture - if disabled, text on screen can be selected & copied โ”‚ โ”‚" -"โ”‚ โ”‚ ( F1 ) or ( / ) enter filter mode โ”‚ โ”‚" -"โ”‚ โ”‚ ( # ) enter log search mode โ”‚ โ”‚" -"โ”‚ โ”‚ ( 0 ) stop sort โ”‚ โ”‚" -"โ”‚ โ”‚ ( 1 - 9 ) sort by header - or click header โ”‚ โ”‚" -"โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚ ( - = ) change log section height โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" -"โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ cpu 03.00%โ”‚ ( \ ) toggle log section visibility โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎโ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ports โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ" -"โ”‚10.00%โ”‚ โ€ขโ€ข โ”‚ ( esc ) close dialog โ”‚ โ”‚โ”‚ ip private publicโ”‚" -"โ”‚ โ”‚ โ€ข โ€ข โ”‚ ( q ) quit at any time โ”‚ โ”‚โ”‚ 8001 โ”‚" -"โ”‚ โ”‚ โ€ขโ€ข โ€ข โ”‚ โ”‚ โ”‚โ”‚127.0.0.1 8003 8003โ”‚" -"โ”‚ โ”‚ โ€ข โ€ข โ”‚ currently an early work in progress, all and any input appreciated โ”‚ โ”‚โ”‚ โ”‚" -"โ”‚ โ”‚ โ€ขโ€ข โ€ข โ€ข โ”‚ https://github.com/mrjackwills/oxker โ”‚ โ”‚โ”‚ โ”‚" -"โ”‚ โ”‚โ€ข โ€ขโ€ข โ”‚ โ”‚ โ”‚โ”‚ โ”‚" -"โ”‚ โ”‚โ€ข โ€ข โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ โ”‚โ”‚ โ”‚" -"โ”‚ โ”‚ โ”‚โ”‚ โ”‚ โ”‚โ”‚ โ”‚" -"โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" +"โ”‚โšช container_1 โœ“ running Up 1 hour 03.00% 30.00 kB / 30.00 kB 1 image_1 0.00 kB 0.00 kB โ”‚โ”‚โ–ถ pause โ”‚" Hidden by multi-width symbols: [(2, " ")] +"โ”‚ container_2 โœ“ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB โ”‚โ”‚ restart โ”‚" +"โ”‚ container_3 โœ“ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB โ”‚โ”‚ stop โ”‚" +"โ”‚ โ”‚โ”‚ delete โ”‚" +"โ”‚ โ”‚โ”‚ โ”‚" +"โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" +"โ•ญ Logs 3/3 - container_โ•ญ 0.00.000 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ" +"โ”‚ line 1 โ”‚ 88 โ”‚ โ”‚" +"โ”‚ line 2 โ”‚ 88 โ”‚ โ”‚" +"โ”‚โ–ถ line 3 โ”‚ ,adPPYba, 8b, ,d8 88 ,d8 ,adPPYba, 8b,dPPYb โ”‚ โ”‚" +"โ”‚ โ”‚ a8" "8a `Y8, ,8P' 88 ,a8" a8P_____88 88P' " โ”‚ โ”‚" +"โ”‚ โ”‚ 8b d8 )888( 8888( 8PP""""""" 88 โ”‚ โ”‚" +"โ”‚ โ”‚ "8a, ,a8" ,d8" "8b, 88`"Yba, "8b, ,aa 88 โ”‚ โ”‚" +"โ”‚ โ”‚ `"YbbdP"' 8P' `Y8 88 `Y8a `"Ybbd8"' 88 โ”‚ โ”‚" +"โ”‚ โ”‚ a work in progress, all and any input appreciated โ”‚ โ”‚" +"โ”‚ โ”‚ A simple tui to view & control docker containers https://github.com/mrjackwills/oxker โ”‚ โ”‚" +"โ”‚ โ”‚ โ”‚ โ”‚" +"โ”‚ โ”‚ Keymap โ”‚ โ”‚" +"โ”‚ โ”‚ q quit c Esc close dialog โ”‚ โ”‚" +"โ”‚ โ”‚ Down Up j k Home End scroll vertically Left Right scroll horizontally โ”‚ โ”‚" +"โ”‚ โ”‚ Control increase scroll speed Enter send docker command โ”‚ โ”‚" +"โ”‚ โ”‚ e exec into a container i container inspect mode โ”‚ โ”‚" +"โ”‚ โ”‚ / F1 filter mode # log search mode โ”‚ โ”‚" +"โ”‚ โ”‚ h toggle this panel f force clear screen and redraw โ”‚ โ”‚" +"โ”‚ โ”‚ - = change log section height \ toggle of section visibility โ”‚ โ”‚" +"โ”‚ โ”‚ 1 ~ 9 sort by header - or click header 0 stop sort โ”‚ โ”‚" +"โ”‚ โ”‚ Tab Back Tab change panel m toggle mouse capture - allows text selection โ”‚ โ”‚" +"โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚ s save logs to file โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" +"โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ cpu 03.โ”‚ โ”‚โ”€โ”€โ”€โ”€ ports โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ" +"โ”‚10.00%โ”‚ โ€ข โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ ip private publicโ”‚" +"โ”‚ โ”‚ โ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ”‚ โ”‚ 8001 โ”‚" +"โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ”‚ โ”‚127.0.0.1 8003 8003โ”‚" +"โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚0.00 kb/sโ”‚ โ€ขโ€ข โ”‚ โ”‚ โ”‚" +"โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ข โ”‚ โ”‚ โ”‚" +"โ”‚ โ”‚ โ€ข โ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ข โ€ขโ€ข โ”‚โ”‚0.00 kb/sโ”‚ โ€ข โ”‚ โ”‚ โ”‚" +"โ”‚ โ”‚โ€ขโ€ข โ€ข โ”‚โ”‚ โ”‚โ€ขโ€ข โ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ข โ”‚ โ”‚ โ”‚" +"โ”‚ โ”‚ โ”‚โ”‚ โ”‚ โ”‚โ”‚ โ”‚ โ€ข โ”‚ โ”‚ โ”‚" +"โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_info_box.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_info_box.snap index a9abbf8..b12a958 100644 --- a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_info_box.snap +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_info_box.snap @@ -32,13 +32,13 @@ expression: setup.terminal.backend() "โ”‚ โ”‚" "โ”‚ โ”‚" "โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" -"โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ cpu 03.00% โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎโ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ memory 30.00 kB โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎโ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ports โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ" -"โ”‚10.00%โ”‚ โ€ขโ€ข โ”‚โ”‚100.00 kBโ”‚ โ€ขโ€ข โ”‚โ”‚ ip private publicโ”‚" -"โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ”‚โ”‚ 8001 โ”‚" -"โ”‚ โ”‚ โ€ขโ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ€ข โ”‚โ”‚127.0.0.1 8003 8003โ”‚" -"โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚" -"โ”‚ โ”‚ โ€ขโ€ข โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ€ข โ€ข โ”‚โ”‚ โ”‚" -"โ”‚ โ”‚โ€ข โ€ขโ€ข โ”‚โ”‚ โ”‚โ€ข โ€ขโ€ข โ”‚โ”‚ " -"โ”‚ โ”‚โ€ข โ€ข โ”‚โ”‚ โ”‚โ€ข โ€ข โ”‚โ”‚ This is a test " -"โ”‚ โ”‚ โ”‚โ”‚ โ”‚ โ”‚โ”‚ " -"โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€ " +"โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ cpu 03.00% โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎโ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ memory 30.00 kB โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎโ•ญโ”€โ”€โ”€โ”€โ”€โ”€ rx: 0.00 kb/s tx: 0.00 kb/s โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ports โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ" +"โ”‚10.00%โ”‚ โ€ข โ”‚โ”‚100.00 kBโ”‚ โ€ข โ”‚โ”‚ โ”‚โ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ข โ”‚ โ”‚ ip private publicโ”‚" +"โ”‚ โ”‚ โ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ”‚ โ”‚ 8001 โ”‚" +"โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ”‚ โ”‚127.0.0.1 8003 8003โ”‚" +"โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚0.00 kb/sโ”‚ โ€ขโ€ข โ”‚ โ”‚ โ”‚" +"โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ข โ”‚ โ”‚ โ”‚" +"โ”‚ โ”‚ โ€ข โ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ข โ€ขโ€ข โ”‚โ”‚0.00 kb/sโ”‚ โ€ข โ”‚ โ”‚ " +"โ”‚ โ”‚โ€ขโ€ข โ€ข โ”‚โ”‚ โ”‚โ€ขโ€ข โ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ข โ”‚ โ”‚ This is a test " +"โ”‚ โ”‚ โ”‚โ”‚ โ”‚ โ”‚โ”‚ โ”‚ โ€ข โ”‚ โ”‚ " +"โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€ " diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_long_name.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_long_name.snap index e0a34fb..e7b9019 100644 --- a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_long_name.snap +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_long_name.snap @@ -25,10 +25,10 @@ expression: setup.terminal.backend() "โ”‚ โ”‚" "โ”‚ โ”‚" "โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" -"โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ cpu 03.00% โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎโ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ memory 30.00 kB โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎโ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ports โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ" -"โ”‚10.00%โ”‚ โ€ขโ€ขโ€ข โ”‚โ”‚100.00 kBโ”‚ โ€ขโ€ขโ€ขโ€ข โ”‚โ”‚ ip private publicโ”‚" -"โ”‚ โ”‚ โ€ขโ€ขโ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ€ข โ”‚โ”‚ 8001 โ”‚" -"โ”‚ โ”‚ โ€ขโ€ขโ€ข โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ขโ€ข โ€ข โ€ข โ”‚โ”‚127.0.0.1 8003 8003โ”‚" -"โ”‚ โ”‚โ€ขโ€ข โ€ขโ€ขโ€ข โ”‚โ”‚ โ”‚โ€ขโ€ข โ€ขโ€ข โ”‚โ”‚ โ”‚" -"โ”‚ โ”‚ โ”‚โ”‚ โ”‚ โ”‚โ”‚ โ”‚" -"โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" +"โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ cpu 03.00% โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎโ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ memory 30.00 kB โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎโ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ rx: 0.00 kb/s tx: 0.00 kb/s โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ports โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ" +"โ”‚10.00%โ”‚ โ€ข โ”‚โ”‚100.00 kBโ”‚ โ€ขโ€ข โ”‚โ”‚ โ”‚โ€ขโ€ขโ€ขโ€ขโ€ขโ€ข โ€ข โ”‚ โ”‚ ip private publicโ”‚" +"โ”‚ โ”‚ โ€ขโ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ข โ€ข โ”‚ โ”‚ 8001 โ”‚" +"โ”‚ โ”‚ โ€ขโ€ขโ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ€ข โ”‚โ”‚0.00 kb/sโ”‚ โ€ข โ€ข โ”‚ โ”‚127.0.0.1 8003 8003โ”‚" +"โ”‚ โ”‚ โ€ข โ€ขโ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ข โ€ขโ€ข โ”‚โ”‚0.00 kb/sโ”‚ โ€ข โ”‚ โ”‚ โ”‚" +"โ”‚ โ”‚โ€ข โ€ข โ”‚โ”‚ โ”‚โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ข โ”‚ โ”‚ โ”‚" +"โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_no_logs.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_no_logs.snap index fd83747..e0131b5 100644 --- a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_no_logs.snap +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_no_logs.snap @@ -25,10 +25,10 @@ expression: setup.terminal.backend() "โ”‚ โ”‚โ”‚ โ”‚" "โ”‚ โ”‚โ”‚ โ”‚" "โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" -"โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ cpu 03.00% โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎโ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ memory 30.00 kB โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎโ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ports โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ" -"โ”‚10.00%โ”‚ โ€ขโ€ขโ€ข โ”‚โ”‚100.00 kBโ”‚ โ€ขโ€ข โ”‚โ”‚ ip private publicโ”‚" -"โ”‚ โ”‚ โ€ขโ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ€ข โ”‚โ”‚ 8001 โ”‚" -"โ”‚ โ”‚ โ€ขโ€ขโ€ข โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ขโ€ข โ€ข โ€ข โ”‚โ”‚127.0.0.1 8003 8003โ”‚" -"โ”‚ โ”‚โ€ข โ€ขโ€ข โ”‚โ”‚ โ”‚โ€ข โ€ขโ€ข โ”‚โ”‚ โ”‚" -"โ”‚ โ”‚ โ”‚โ”‚ โ”‚ โ”‚โ”‚ โ”‚" -"โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" +"โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ cpu 03.00% โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎโ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ memory 30.00 kB โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎโ•ญโ”€โ”€โ”€โ”€โ”€โ”€ rx: 0.00 kb/s tx: 0.00 kb/s โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ports โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ" +"โ”‚10.00%โ”‚ โ€ขโ€ข โ”‚โ”‚100.00 kBโ”‚ โ€ขโ€ข โ”‚โ”‚ โ”‚โ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ข โ”‚ โ”‚ ip private publicโ”‚" +"โ”‚ โ”‚ โ€ขโ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ”‚ โ”‚ 8001 โ”‚" +"โ”‚ โ”‚ โ€ขโ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ€ข โ”‚โ”‚0.00 kb/sโ”‚ โ€ขโ€ข โ”‚ โ”‚127.0.0.1 8003 8003โ”‚" +"โ”‚ โ”‚ โ€ข โ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ข โ€ขโ€ข โ”‚โ”‚0.00 kb/sโ”‚ โ€ข โ”‚ โ”‚ โ”‚" +"โ”‚ โ”‚โ€ข โ€ข โ”‚โ”‚ โ”‚โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ข โ”‚ โ”‚ โ”‚" +"โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_short_height_logs.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_short_height_logs.snap index 046a12e..06bb20f 100644 --- a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_short_height_logs.snap +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_short_height_logs.snap @@ -25,10 +25,10 @@ expression: setup.terminal.backend() "โ”‚ line 2 โ”‚" "โ”‚โ–ถ line 3 โ”‚" "โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" -"โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ cpu 03.00% โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎโ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ memory 30.00 kB โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎโ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ports โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ" -"โ”‚10.00%โ”‚ โ€ขโ€ขโ€ข โ”‚โ”‚100.00 kBโ”‚ โ€ขโ€ข โ”‚โ”‚ ip private publicโ”‚" -"โ”‚ โ”‚ โ€ขโ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ€ข โ”‚โ”‚ 8001 โ”‚" -"โ”‚ โ”‚ โ€ขโ€ขโ€ข โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ขโ€ข โ€ข โ€ข โ”‚โ”‚127.0.0.1 8003 8003โ”‚" -"โ”‚ โ”‚โ€ข โ€ขโ€ข โ”‚โ”‚ โ”‚โ€ข โ€ขโ€ข โ”‚โ”‚ โ”‚" -"โ”‚ โ”‚ โ”‚โ”‚ โ”‚ โ”‚โ”‚ โ”‚" -"โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" +"โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ cpu 03.00% โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎโ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ memory 30.00 kB โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎโ•ญโ”€โ”€โ”€โ”€โ”€โ”€ rx: 0.00 kb/s tx: 0.00 kb/s โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ports โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ" +"โ”‚10.00%โ”‚ โ€ขโ€ข โ”‚โ”‚100.00 kBโ”‚ โ€ขโ€ข โ”‚โ”‚ โ”‚โ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ข โ”‚ โ”‚ ip private publicโ”‚" +"โ”‚ โ”‚ โ€ขโ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ”‚ โ”‚ 8001 โ”‚" +"โ”‚ โ”‚ โ€ขโ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ€ข โ”‚โ”‚0.00 kb/sโ”‚ โ€ขโ€ข โ”‚ โ”‚127.0.0.1 8003 8003โ”‚" +"โ”‚ โ”‚ โ€ข โ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ข โ€ขโ€ข โ”‚โ”‚0.00 kb/sโ”‚ โ€ข โ”‚ โ”‚ โ”‚" +"โ”‚ โ”‚โ€ข โ€ข โ”‚โ”‚ โ”‚โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ข โ”‚ โ”‚ โ”‚" +"โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_with_filter_bar.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_with_filter_bar.snap index 4a576be..d3a3751 100644 --- a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_with_filter_bar.snap +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_with_filter_bar.snap @@ -24,11 +24,11 @@ expression: setup.terminal.backend() "โ”‚ โ”‚" "โ”‚ โ”‚" "โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" -"โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ cpu 03.00% โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎโ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ memory 30.00 kB โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎโ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ports โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ" -"โ”‚10.00%โ”‚ โ€ขโ€ขโ€ข โ”‚โ”‚100.00 kBโ”‚ โ€ขโ€ข โ”‚โ”‚ ip private publicโ”‚" -"โ”‚ โ”‚ โ€ขโ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ€ข โ”‚โ”‚ 8001 โ”‚" -"โ”‚ โ”‚ โ€ขโ€ขโ€ข โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ขโ€ข โ€ข โ€ข โ”‚โ”‚ โ”‚" -"โ”‚ โ”‚โ€ข โ€ขโ€ข โ”‚โ”‚ โ”‚โ€ข โ€ขโ€ข โ”‚โ”‚ โ”‚" -"โ”‚ โ”‚ โ”‚โ”‚ โ”‚ โ”‚โ”‚ โ”‚" -"โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" +"โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ cpu 03.00% โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎโ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ memory 30.00 kB โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎโ•ญโ”€โ”€โ”€โ”€โ”€โ”€ rx: 0.00 kb/s tx: 0.00 kb/s โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ports โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ" +"โ”‚10.00%โ”‚ โ€ขโ€ข โ”‚โ”‚100.00 kBโ”‚ โ€ขโ€ข โ”‚โ”‚ โ”‚โ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ข โ”‚ โ”‚ ip private publicโ”‚" +"โ”‚ โ”‚ โ€ขโ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ”‚ โ”‚ 8001 โ”‚" +"โ”‚ โ”‚ โ€ขโ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ขโ€ข โ€ข โ”‚โ”‚0.00 kb/sโ”‚ โ€ขโ€ข โ”‚ โ”‚ โ”‚" +"โ”‚ โ”‚ โ€ข โ€ขโ€ข โ”‚โ”‚ โ”‚ โ€ข โ€ขโ€ข โ”‚โ”‚0.00 kb/sโ”‚ โ€ข โ”‚ โ”‚ โ”‚" +"โ”‚ โ”‚โ€ข โ€ข โ”‚โ”‚ โ”‚โ€ข โ€ข โ”‚โ”‚ โ”‚ โ€ข โ”‚ โ”‚ โ”‚" +"โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ" " Esc clear โ† by โ†’ Name Image Status All filter term: r_1 " diff --git a/src/ui/gui_state.rs b/src/ui/gui_state.rs index 9b598c4..46ad5f1 100644 --- a/src/ui/gui_state.rs +++ b/src/ui/gui_state.rs @@ -9,7 +9,7 @@ use tokio::task::JoinHandle; use uuid::Uuid; use crate::{ - app_data::{AppData, ContainerId, Header}, + app_data::{AppData, ContainerId, Header, ScrollDirection}, exec::ExecMode, }; @@ -160,19 +160,26 @@ const FRAMES_LEN: u8 = 9; /// The application gui state can be in multiple of these four states at the same time /// Various functions (e.g input handler), operate differently depending upon current Status -#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] +#[derive(Debug, Clone, Hash, PartialEq, Eq)] pub enum Status { DeleteConfirm, - DockerConnect, + DockerConnect(Option), Error, Exec, Filter, Help, Init, + Inspect, Logs, SearchLogs, } +#[derive(Debug, Default, Copy, Clone, Hash, PartialEq, Eq)] +pub struct ScrollOffset { + pub x: usize, + pub y: usize, +} + /// Global gui_state, stored in an Arc #[derive(Debug)] pub struct GuiState { @@ -190,6 +197,8 @@ pub struct GuiState { selected_panel: SelectablePanel, screen_width: u16, show_logs: bool, + inspect_offset: ScrollOffset, + inspect_offset_max: ScrollOffset, status: HashSet, pub info_box_text: Option<(String, Instant)>, } @@ -203,6 +212,8 @@ impl GuiState { intersect_heading: HashMap::new(), intersect_help: None, intersect_panel: HashMap::new(), + inspect_offset: ScrollOffset::default(), + inspect_offset_max: ScrollOffset::default(), loading_handle: None, loading_index: 0, loading_set: HashSet::new(), @@ -235,6 +246,50 @@ impl GuiState { } } + pub fn set_inspect_offset(&mut self, sd: &ScrollDirection) { + match sd { + ScrollDirection::Up => self.inspect_offset.y = self.inspect_offset.y.saturating_sub(1), + ScrollDirection::Down => { + self.inspect_offset.y = self + .inspect_offset + .y + .saturating_add(1) + .min(self.inspect_offset_max.y) + } + ScrollDirection::Left => { + self.inspect_offset.x = self.inspect_offset.x.saturating_sub(1) + } + ScrollDirection::Right => { + self.inspect_offset.x = self + .inspect_offset + .x + .saturating_add(1) + .min(self.inspect_offset_max.x) + } + } + self.rerender.update_draw(); + } + + pub fn get_inspect_offset(&self) -> ScrollOffset { + self.inspect_offset + } + + pub fn set_inspect_offset_max(&mut self, offset: ScrollOffset) { + self.inspect_offset_max = offset + } + + pub fn set_inspect_offset_y_to_max(&mut self) { + self.inspect_offset.y = self.inspect_offset_max.y; + self.rerender.update_draw(); + } + + pub fn clear_inspect_offset(&mut self) { + self.inspect_offset.x = 0; + self.inspect_offset.y = 0; + self.inspect_offset_max = ScrollOffset::default(); + self.rerender.update_draw(); + } + /// Set the screen width, used for offset char calculations pub const fn set_screen_width(&mut self, width: u16) { self.screen_width = width; diff --git a/src/ui/mod.rs b/src/ui/mod.rs index bb7a458..096191c 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -30,12 +30,11 @@ pub use self::color_match::*; pub use self::gui_state::{DeleteButton, GuiState, SelectablePanel, Status}; use crate::{ app_data::{ - AppData, Columns, ContainerId, ContainerPorts, CpuTuple, FilterBy, Header, LogSearch, - MemTuple, SortedOrder, State, + AppData, ChartsData, Columns, ContainerId, ContainerPorts, FilterBy, Header, LogSearch, + SortedOrder, State, }, app_error::AppError, config::{AppColors, Keymap}, - exec::TerminalSize, input_handler::InputMessages, }; @@ -132,12 +131,12 @@ impl Ui { } /// Draw the the error message ui, for 5 seconds, with a countdown - fn err_loop(&mut self) -> Result<(), AppError> { + async fn err_loop(&mut self, host: Option) -> Result<(), AppError> { let mut seconds = 5; let colors = self.app_data.lock().config.app_colors; let keymap = self.app_data.lock().config.keymap.clone(); let mut redraw = true; - loop { + while self.is_running.load(Ordering::SeqCst) { if self.now.elapsed() >= std::time::Duration::from_secs(1) { seconds -= 1; self.now = Instant::now(); @@ -155,6 +154,7 @@ impl Ui { colors, &AppError::DockerConnect, f, + host.clone(), &keymap, Some(seconds), ); @@ -163,8 +163,18 @@ impl Ui { { return Err(AppError::Terminal); } + if crossterm::event::poll(POLL_RATE).unwrap_or(false) + && let Ok(event) = event::read() + && let Event::Key(key) = event + && key.kind == event::KeyEventKind::Press + { + self.input_tx + .send(InputMessages::ButtonPress((key.code, key.modifiers))) + .await + .ok(); + } redraw = false; - std::thread::sleep(POLL_RATE); + // std::thread::sleep(POLL_RATE); } Ok(()) } @@ -183,7 +193,8 @@ impl Ui { if let Some(mode) = exec_mode { self.reset_terminal().ok(); self.terminal.clear().ok(); - if let Err(e) = mode.run(TerminalSize::new(&self.terminal)).await { + + if let Err(e) = mode.run(self.terminal.size().ok()).await { self.app_data .lock() .set_error(e, &self.gui_state, Status::Error); @@ -237,32 +248,32 @@ impl Ui { } } - if crossterm::event::poll(POLL_RATE).unwrap_or(false) { - if let Ok(event) = event::read() { - if let Event::Key(key) = event { - if key.kind == event::KeyEventKind::Press { + if crossterm::event::poll(POLL_RATE).unwrap_or(false) + && let Ok(event) = event::read() + { + if let Event::Key(key) = event { + if key.kind == event::KeyEventKind::Press { + self.input_tx + .send(InputMessages::ButtonPress((key.code, key.modifiers))) + .await + .ok(); + } + } else if let Event::Mouse(m) = event { + match m.kind { + event::MouseEventKind::Down(_) + | event::MouseEventKind::ScrollDown + | event::MouseEventKind::ScrollUp => { self.input_tx - .send(InputMessages::ButtonPress((key.code, key.modifiers))) + .send(InputMessages::MouseEvent((m, m.modifiers))) .await .ok(); } - } else if let Event::Mouse(m) = event { - match m.kind { - event::MouseEventKind::Down(_) - | event::MouseEventKind::ScrollDown - | event::MouseEventKind::ScrollUp => { - self.input_tx - .send(InputMessages::MouseEvent((m, m.modifiers))) - .await - .ok(); - } - _ => (), - } - } else if let Event::Resize(width, _) = event { - self.gui_state.lock().clear_area_map(); - self.terminal.autoresize().ok(); - self.gui_state.lock().set_screen_width(width); + _ => (), } + } else if let Event::Resize(width, _) = event { + self.gui_state.lock().clear_area_map(); + self.terminal.autoresize().ok(); + self.gui_state.lock().set_screen_width(width); } } self.check_clear(); @@ -273,8 +284,11 @@ impl Ui { /// Draw either the Error, or main oxker ui, to the terminal async fn draw_ui(&mut self) -> Result<(), AppError> { let status = self.gui_state.lock().get_status(); - if status.contains(&Status::DockerConnect) { - self.err_loop()?; + if let Some(Status::DockerConnect(msg)) = status + .iter() + .find(|s| matches!(s, Status::DockerConnect(_))) + { + self.err_loop(msg.clone()).await?; } else { self.gui_loop().await?; } @@ -287,7 +301,7 @@ impl Ui { #[derive(Debug, Clone)] #[allow(clippy::struct_excessive_bools)] pub struct FrameData { - chart_data: Option<(CpuTuple, MemTuple)>, + chart_data: Option, color_logs: bool, columns: Columns, container_title: String, @@ -355,114 +369,122 @@ fn draw_frame( let contains_filter = fd.status.contains(&Status::Filter); let contains_search_logs = fd.status.contains(&Status::SearchLogs); - let whole_layout = Layout::default() - .direction(Direction::Vertical) - .constraints(if contains_filter || contains_search_logs { - vec![Constraint::Max(1), Constraint::Min(1), Constraint::Max(1)] - } else { - vec![Constraint::Max(1), Constraint::Min(1)] - }) - .split(f.area()); + let contains_inspect = fd.status.contains(&Status::Inspect); - draw_blocks::headers::draw(whole_layout[0], colors, f, fd, gui_state, keymap); + let inspect_data = app_data.lock().get_inspect_data(); + if contains_inspect && let Some(inspect_data) = inspect_data { + draw_blocks::inspect::draw(f, colors, inspect_data, gui_state, keymap); + } else { + let whole_layout = Layout::default() + .direction(Direction::Vertical) + .constraints(if contains_filter || contains_search_logs { + vec![Constraint::Max(1), Constraint::Min(1), Constraint::Max(1)] + } else { + vec![Constraint::Max(1), Constraint::Min(1)] + }) + .split(f.area()); - if let Some(rect) = whole_layout.get(2) { - if contains_filter { - draw_blocks::filter::draw(*rect, colors, f, fd); - } else { - draw_blocks::search_logs::draw(*rect, colors, f, fd, keymap); + draw_blocks::headers::draw(whole_layout[0], colors, f, fd, gui_state, keymap); + + if let Some(rect) = whole_layout.get(2) { + if contains_filter { + draw_blocks::filter::draw(*rect, colors, f, fd); + } else { + draw_blocks::search_logs::draw(*rect, colors, f, fd, keymap); + } } - } - let upper_main = Layout::default() - .direction(Direction::Vertical) - .constraints(if fd.has_containers { - vec![Constraint::Percentage(75), Constraint::Percentage(25)] - } else { - vec![Constraint::Percentage(100), Constraint::Percentage(0)] - }) - .split(whole_layout[1]); + let upper_main = Layout::default() + .direction(Direction::Vertical) + .constraints(if fd.has_containers { + vec![Constraint::Percentage(75), Constraint::Percentage(25)] + } else { + vec![Constraint::Percentage(100), Constraint::Percentage(0)] + }) + .split(whole_layout[1]); - let containers_logs_section = Layout::default() - .direction(Direction::Vertical) - .constraints(if fd.show_logs { - vec![Constraint::Min(6), Constraint::Percentage(fd.log_height)] - } else { - vec![Constraint::Percentage(100)] - }) - .split(upper_main[0]); + let containers_logs_section = Layout::default() + .direction(Direction::Vertical) + .constraints(if fd.show_logs { + vec![Constraint::Min(6), Constraint::Percentage(fd.log_height)] + } else { + vec![Constraint::Percentage(100)] + }) + .split(upper_main[0]); - // Containers + docker commands - let containers_commands = Layout::default() - .direction(Direction::Horizontal) - .constraints(if fd.has_containers { - vec![Constraint::Percentage(90), Constraint::Percentage(10)] - } else { - vec![Constraint::Percentage(100)] - }) - .split(containers_logs_section[0]); - - draw_blocks::containers::draw(app_data, containers_commands[0], colors, f, fd, gui_state); - - if fd.show_logs { - draw_blocks::logs::draw( - app_data, - containers_logs_section[1], - colors, - f, - fd, - gui_state, - ); - } - - if let Some(id) = fd.delete_confirm.as_ref() { - app_data.lock().get_container_name_by_id(id).map_or_else( - || { - // If a container is deleted outside of oxker but whilst the Delete Confirm dialog is open, it can get caught in kind of a dead lock situation - // so if in that unique situation, just clear the delete_container id - gui_state.lock().set_delete_container(None); - }, - |name| { - draw_blocks::delete_confirm::draw(colors, f, gui_state, keymap, name); - }, - ); - } - - // only draw commands + charts if there are containers - if let Some(rect) = containers_commands.get(1) { - draw_blocks::commands::draw(app_data, *rect, colors, f, fd, gui_state); - - // Can calculate the max string length here, and then use that to keep the ports section as small as possible (+4 for some padding + border) - let ports_len = - u16::try_from(fd.port_max_lens.0 + fd.port_max_lens.1 + fd.port_max_lens.2 + 2) - .unwrap_or(26); - - let lower = Layout::default() + // Containers + docker commands + let containers_commands = Layout::default() .direction(Direction::Horizontal) - .constraints([Constraint::Min(1), Constraint::Max(ports_len)]) - .split(upper_main[1]); + .constraints(if fd.has_containers { + vec![Constraint::Percentage(90), Constraint::Percentage(10)] + } else { + vec![Constraint::Percentage(100)] + }) + .split(containers_logs_section[0]); - draw_blocks::charts::draw(lower[0], colors, f, fd); - draw_blocks::ports::draw(lower[1], colors, f, fd); + draw_blocks::containers::draw(app_data, containers_commands[0], colors, f, fd, gui_state); + + if fd.show_logs { + draw_blocks::logs::draw( + app_data, + containers_logs_section[1], + colors, + f, + fd, + gui_state, + ); + } + + if let Some(id) = fd.delete_confirm.as_ref() { + app_data.lock().get_container_name_by_id(id).map_or_else( + || { + // If a container is deleted outside of oxker but whilst the Delete Confirm dialog is open, it can get caught in kind of a dead lock situation + // so if in that unique situation, just clear the delete_container id + gui_state.lock().set_delete_container(None); + }, + |name| { + draw_blocks::delete_confirm::draw(colors, f, gui_state, keymap, name); + }, + ); + } + + // only draw commands + charts if there are containers + if let Some(rect) = containers_commands.get(1) { + draw_blocks::commands::draw(app_data, *rect, colors, f, fd, gui_state); + + // Can calculate the max string length here, and then use that to keep the ports section as small as possible (+4 for some padding + border) + let ports_len = + u16::try_from(fd.port_max_lens.0 + fd.port_max_lens.1 + fd.port_max_lens.2 + 2) + .unwrap_or(26); + + let lower = Layout::default() + .direction(Direction::Horizontal) + .constraints([Constraint::Min(1), Constraint::Max(ports_len)]) + .split(upper_main[1]); + + let charts_rect = Layout::default() + .direction(Direction::Horizontal) + .constraints([Constraint::Percentage(66), Constraint::Percentage(33)]) + .split(lower[0]); + + draw_blocks::chart_cpu_mem::draw(charts_rect[0], colors, f, fd); + draw_blocks::chart_bandwidth::draw(charts_rect[1], colors, f, fd); + + draw_blocks::ports::draw(lower[1], colors, f, fd); + } + + // Check if error, and show popup if so + if fd.status.contains(&Status::Help) { + let config = app_data.lock().config.clone(); + draw_blocks::help::draw(&config, f); + } } if let Some((text, instant)) = fd.info_text.as_ref() { draw_blocks::info::draw(colors, f, gui_state, instant, text.to_owned()); } - // Check if error, and show popup if so - if fd.status.contains(&Status::Help) { - let tz = app_data.lock().config.timezone.clone(); - draw_blocks::help::draw( - colors, - f, - keymap, - app_data.lock().config.show_timestamp, - tz.as_ref(), - ); - } - if let Some(error) = fd.has_error.as_ref() { - draw_blocks::error::draw(colors, error, f, keymap, None); + draw_blocks::error::draw(colors, error, f, None, keymap, None); } }