diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index 0fc5312..fb2d38a 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -38,8 +38,7 @@
// VS Code don't watch files under ./target
"files.watcherExclude": {
"**/target/**": true
- },
- "rust-analyzer.checkOnSave.command": "clippy"
+ }
}
}
},
diff --git a/.github/release-body.md b/.github/release-body.md
index cdef158..49666c0 100644
--- a/.github/release-body.md
+++ b/.github/release-body.md
@@ -1,9 +1,26 @@
-### 2025-04-22
+### 2025-06-18
### Chores
-+ dependencies updated, [bbfd2462a1f45008587b488e8c6049ee76da72f2]
++ .devcontainer updated, [324f8268278081504d5357f2ed89b78ca2c25d04]
++ dependencies updated, [0ace9dd662144a589341779a64d7fcd8de7d9978], [a636007547280b3b3db69374601dbece4bc21eef]
++ Rust 1.87.0 linting, [395b1aa7e997a528e4f21e66f5f859001c1c3ec1], [67e5888e008cfd504c10e47f678f9351c838be99]
+
+### Docs
++ example config files updated, [63ab7de72897de460f31181c5a42befbee2f91d3], [8fb5ac4a945b75f3fcd118c53be1202ccbc43c59]
++ README.md updated, link to directories crate, closes #65, [c2bfe3296563daf4b7f077469f3eeff6895720b0]
+
+### Features
++ log panel size configurable, closes #50, use the `-` or `=` keys to change the height of the logs panel, or `\` to toggle visibility. Automatically hide the logs panel using a new config item `show_logs`, see `example_config/*` files for more details, [6edf99e0846bb4134d8ee5b646065b8cda8074d7]
++ build release binaries for aarch64-apple-darwin, closes #62, personally untested on MacOS - but others suggest it works as expected, [e7114d2f5e0ed8935943be64726fc2d90464a777], [2e8500902a515a246f9d9a503b4350849d634978]
+
+### Fixes
++ merge args color/raw fix, [d198398795698a21d81d3fd20231c482cc346ab5]
+
+### Refactors
++ reduce cloning of the logs text items, can expect 40-50% reduction in CPU and memory usage in certain common situations, [ecefa302b9ef5320ad4cce0b606aca70a7b459e2]
++ dead code removed, [b40b6b197e4e5fbdab083bc918d1a5d2750597f3]
### Tests
-+ fix tests for MacOS, closes #61, [cfc2decd8d237f1ac3f0bdb2b3d5581684064448]
++ add more whole layout tests, [4b81c6caaf12028d7527c3f23cd2de6d1503e223]
see CHANGELOG.md for more details
diff --git a/.github/workflows/create_release_and_build.yml b/.github/workflows/create_release_and_build.yml
index 67e569a..c3e89ca 100644
--- a/.github/workflows/create_release_and_build.yml
+++ b/.github/workflows/create_release_and_build.yml
@@ -22,6 +22,9 @@ jobs:
- target: arm-unknown-linux-musleabihf
output_name: linux_armv6.tar.gz
+ - target: aarch64-apple-darwin
+ output_name: apple_darwin_aarch64.tar.gz
+
- target: x86_64-pc-windows-gnu
output_name: windows_x86_64.zip
@@ -38,8 +41,15 @@ jobs:
- name: install cross
run: cargo install cross --git https://github.com/cross-rs/cross
- # Build binary
+ # 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
+
+ # Build all other targets using Cross
+ - name: build
+ if: matrix.target != 'aarch64-apple-darwin'
run: cross build --target ${{ matrix.target }} --release
# Compress the output
@@ -126,8 +136,8 @@ jobs:
docker build --platform linux/arm/v6,linux/arm64,linux/amd64 \
-t ${{ secrets.DOCKERHUB_USERNAME }}/oxker:latest \
-t ${{ secrets.DOCKERHUB_USERNAME }}/oxker:${{env.CURRENT_SEMVER}} \
- -t ghcr.io/${{ github.repository_owner }}/oxker:latest \
- -t ghcr.io/${{ github.repository_owner }}/oxker:${{env.CURRENT_SEMVER}} \
+ -t ghcr.io/${{ github.repository_owner }}/${{ github.ref_name }}:latest \
+ -t ghcr.io/${{ github.repository_owner }}/${{ github.ref_name }}:${{env.CURRENT_SEMVER}} \
--provenance=false --sbom=false \
--push \
-f containerised/Dockerfile .
diff --git a/.gitignore b/.gitignore
index 1a14b40..5388e4b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,4 @@
/target
-/releases
\ No newline at end of file
+/releases
+# Used in the zigbuild for aarch64-apple-darwin
+.intentionally-empty-file.o
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 05cf8d3..5992cb4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,29 @@
+# v0.10.4
+### 2025-06-18
+
+### Chores
++ .devcontainer updated, [324f8268](https://github.com/mrjackwills/oxker/commit/324f8268278081504d5357f2ed89b78ca2c25d04)
++ dependencies updated, [0ace9dd6](https://github.com/mrjackwills/oxker/commit/0ace9dd662144a589341779a64d7fcd8de7d9978), [a6360075](https://github.com/mrjackwills/oxker/commit/a636007547280b3b3db69374601dbece4bc21eef)
++ Rust 1.87.0 linting, [395b1aa7](https://github.com/mrjackwills/oxker/commit/395b1aa7e997a528e4f21e66f5f859001c1c3ec1), [67e5888e](https://github.com/mrjackwills/oxker/commit/67e5888e008cfd504c10e47f678f9351c838be99)
+
+### Docs
++ example config files updated, [63ab7de7](https://github.com/mrjackwills/oxker/commit/63ab7de72897de460f31181c5a42befbee2f91d3), [8fb5ac4a](https://github.com/mrjackwills/oxker/commit/8fb5ac4a945b75f3fcd118c53be1202ccbc43c59)
++ README.md updated, link to directories crate, closes [#65](https://github.com/mrjackwills/oxker/issues/65), [c2bfe329](https://github.com/mrjackwills/oxker/commit/c2bfe3296563daf4b7f077469f3eeff6895720b0)
+
+### Features
++ log panel size configurable, closes [#50](https://github.com/mrjackwills/oxker/issues/50), use the `-` or `=` keys to change the height of the logs panel, or `\` to toggle visibility. Automatically hide the logs panel using a new config item `show_logs`, see `example_config/*` files for more details, [6edf99e0](https://github.com/mrjackwills/oxker/commit/6edf99e0846bb4134d8ee5b646065b8cda8074d7)
++ build release binaries for aarch64-apple-darwin, closes [#62](https://github.com/mrjackwills/oxker/issues/62), personally untested on MacOS - but others suggest it works as expected, [e7114d2f](https://github.com/mrjackwills/oxker/commit/e7114d2f5e0ed8935943be64726fc2d90464a777), [2e850090](https://github.com/mrjackwills/oxker/commit/2e8500902a515a246f9d9a503b4350849d634978)
+
+### Fixes
++ merge args color/raw fix, [d1983987](https://github.com/mrjackwills/oxker/commit/d198398795698a21d81d3fd20231c482cc346ab5)
+
+### Refactors
++ reduce cloning of the logs text items, can expect 40-50% reduction in CPU and memory usage in certain common situations, [ecefa302](https://github.com/mrjackwills/oxker/commit/ecefa302b9ef5320ad4cce0b606aca70a7b459e2)
++ dead code removed, [b40b6b19](https://github.com/mrjackwills/oxker/commit/b40b6b197e4e5fbdab083bc918d1a5d2750597f3)
+
+### Tests
++ add more whole layout tests, [4b81c6ca](https://github.com/mrjackwills/oxker/commit/4b81c6caaf12028d7527c3f23cd2de6d1503e223)
+
# v0.10.3
### 2025-04-22
diff --git a/Cargo.lock b/Cargo.lock
index 51f6e24..e107f9b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -13,9 +13,9 @@ dependencies = [
[[package]]
name = "adler2"
-version = "2.0.0"
+version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
+checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
name = "allocator-api2"
@@ -40,9 +40,9 @@ dependencies = [
[[package]]
name = "anstream"
-version = "0.6.18"
+version = "0.6.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
+checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933"
dependencies = [
"anstyle",
"anstyle-parse",
@@ -55,36 +55,36 @@ dependencies = [
[[package]]
name = "anstyle"
-version = "1.0.10"
+version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
+checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd"
[[package]]
name = "anstyle-parse"
-version = "0.2.6"
+version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
+checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
-version = "1.1.2"
+version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
+checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9"
dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "anstyle-wincon"
-version = "3.0.7"
+version = "3.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e"
+checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882"
dependencies = [
"anstyle",
- "once_cell",
+ "once_cell_polyfill",
"windows-sys 0.59.0",
]
@@ -102,9 +102,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]]
name = "backtrace"
-version = "0.3.74"
+version = "0.3.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
+checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002"
dependencies = [
"addr2line",
"cfg-if",
@@ -112,7 +112,7 @@ dependencies = [
"miniz_oxide",
"object",
"rustc-demangle",
- "windows-targets",
+ "windows-targets 0.52.6",
]
[[package]]
@@ -123,15 +123,15 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "bitflags"
-version = "2.9.0"
+version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
+checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
[[package]]
name = "bollard"
-version = "0.18.1"
+version = "0.19.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97ccca1260af6a459d75994ad5acc1651bcabcbdbc41467cc9786519ab854c30"
+checksum = "899ca34eb6924d6ec2a77c6f7f5c7339e60fd68235eaf91edd5a15f12958bb06"
dependencies = [
"base64",
"bollard-stubs",
@@ -162,20 +162,21 @@ dependencies = [
[[package]]
name = "bollard-stubs"
-version = "1.47.1-rc.27.3.1"
+version = "1.48.3-rc.28.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f179cfbddb6e77a5472703d4b30436bff32929c0aa8a9008ecf23d1d3cdd0da"
+checksum = "64ea257e555d16a2c01e5593f40b73865cdf12efbceda33c6d14a2d8d1490368"
dependencies = [
"serde",
+ "serde_json",
"serde_repr",
"serde_with",
]
[[package]]
name = "bumpalo"
-version = "3.17.0"
+version = "3.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
+checksum = "793db76d6187cd04dff33004d8e6c9cc4e05cd330500379d2394209271b4aeee"
[[package]]
name = "bytes"
@@ -206,24 +207,24 @@ dependencies = [
[[package]]
name = "cc"
-version = "1.2.19"
+version = "1.2.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362"
+checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc"
dependencies = [
"shlex",
]
[[package]]
name = "cfg-if"
-version = "1.0.0"
+version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
[[package]]
name = "chrono"
-version = "0.4.40"
+version = "0.4.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c"
+checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d"
dependencies = [
"android-tzdata",
"iana-time-zone",
@@ -234,9 +235,9 @@ dependencies = [
[[package]]
name = "clap"
-version = "4.5.37"
+version = "4.5.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071"
+checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f"
dependencies = [
"clap_builder",
"clap_derive",
@@ -244,9 +245,9 @@ dependencies = [
[[package]]
name = "clap_builder"
-version = "4.5.37"
+version = "4.5.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2"
+checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e"
dependencies = [
"anstream",
"anstyle",
@@ -258,9 +259,9 @@ dependencies = [
[[package]]
name = "clap_derive"
-version = "4.5.32"
+version = "4.5.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7"
+checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce"
dependencies = [
"heck",
"proc-macro2",
@@ -270,15 +271,15 @@ dependencies = [
[[package]]
name = "clap_lex"
-version = "0.7.4"
+version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
+checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675"
[[package]]
name = "colorchoice"
-version = "1.0.3"
+version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
+checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
[[package]]
name = "compact_str"
@@ -349,7 +350,7 @@ dependencies = [
"document-features",
"mio",
"parking_lot",
- "rustix 1.0.5",
+ "rustix 1.0.7",
"signal-hook",
"signal-hook-mio",
"winapi",
@@ -448,7 +449,7 @@ dependencies = [
"libc",
"option-ext",
"redox_users",
- "windows-sys 0.59.0",
+ "windows-sys 0.60.2",
]
[[package]]
@@ -471,6 +472,12 @@ dependencies = [
"litrs",
]
+[[package]]
+name = "dyn-clone"
+version = "1.0.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005"
+
[[package]]
name = "either"
version = "1.15.0"
@@ -491,9 +498,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "errno"
-version = "0.3.11"
+version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e"
+checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18"
dependencies = [
"libc",
"windows-sys 0.59.0",
@@ -574,20 +581,20 @@ dependencies = [
[[package]]
name = "getrandom"
-version = "0.2.15"
+version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
+checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
dependencies = [
"cfg-if",
"libc",
- "wasi 0.11.0+wasi-snapshot-preview1",
+ "wasi 0.11.1+wasi-snapshot-preview1",
]
[[package]]
name = "getrandom"
-version = "0.3.2"
+version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0"
+checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
dependencies = [
"cfg-if",
"libc",
@@ -609,9 +616,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "hashbrown"
-version = "0.15.2"
+version = "0.15.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
+checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5"
dependencies = [
"allocator-api2",
"equivalent",
@@ -713,12 +720,13 @@ dependencies = [
[[package]]
name = "hyper-util"
-version = "0.1.11"
+version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2"
+checksum = "dc2fdfdbff08affe55bb779f33b053aa1fe5dd5b54c257343c17edfa55711bdb"
dependencies = [
"bytes",
"futures-channel",
+ "futures-core",
"futures-util",
"http",
"http-body",
@@ -772,21 +780,22 @@ dependencies = [
[[package]]
name = "icu_collections"
-version = "1.5.0"
+version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526"
+checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47"
dependencies = [
"displaydoc",
+ "potential_utf",
"yoke",
"zerofrom",
"zerovec",
]
[[package]]
-name = "icu_locid"
-version = "1.5.0"
+name = "icu_locale_core"
+version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637"
+checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a"
dependencies = [
"displaydoc",
"litemap",
@@ -795,31 +804,11 @@ dependencies = [
"zerovec",
]
-[[package]]
-name = "icu_locid_transform"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e"
-dependencies = [
- "displaydoc",
- "icu_locid",
- "icu_locid_transform_data",
- "icu_provider",
- "tinystr",
- "zerovec",
-]
-
-[[package]]
-name = "icu_locid_transform_data"
-version = "1.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d"
-
[[package]]
name = "icu_normalizer"
-version = "1.5.0"
+version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f"
+checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979"
dependencies = [
"displaydoc",
"icu_collections",
@@ -827,67 +816,54 @@ dependencies = [
"icu_properties",
"icu_provider",
"smallvec",
- "utf16_iter",
- "utf8_iter",
- "write16",
"zerovec",
]
[[package]]
name = "icu_normalizer_data"
-version = "1.5.1"
+version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7"
+checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3"
[[package]]
name = "icu_properties"
-version = "1.5.1"
+version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5"
+checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b"
dependencies = [
"displaydoc",
"icu_collections",
- "icu_locid_transform",
+ "icu_locale_core",
"icu_properties_data",
"icu_provider",
- "tinystr",
+ "potential_utf",
+ "zerotrie",
"zerovec",
]
[[package]]
name = "icu_properties_data"
-version = "1.5.1"
+version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2"
+checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632"
[[package]]
name = "icu_provider"
-version = "1.5.0"
+version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9"
+checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af"
dependencies = [
"displaydoc",
- "icu_locid",
- "icu_provider_macros",
+ "icu_locale_core",
"stable_deref_trait",
"tinystr",
"writeable",
"yoke",
"zerofrom",
+ "zerotrie",
"zerovec",
]
-[[package]]
-name = "icu_provider_macros"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
[[package]]
name = "ident_case"
version = "1.0.1"
@@ -907,9 +883,9 @@ dependencies = [
[[package]]
name = "idna_adapter"
-version = "1.2.0"
+version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71"
+checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344"
dependencies = [
"icu_normalizer",
"icu_properties",
@@ -933,7 +909,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
dependencies = [
"equivalent",
- "hashbrown 0.15.2",
+ "hashbrown 0.15.4",
"serde",
]
@@ -945,14 +921,12 @@ checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd"
[[package]]
name = "insta"
-version = "1.42.2"
+version = "1.43.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50259abbaa67d11d2bcafc7ba1d094ed7a0c70e3ce893f0d0997f73558cb3084"
+checksum = "154934ea70c58054b556dd430b99a98c2a7ff5309ac9891597e339b5c28f4371"
dependencies = [
"console",
- "linked-hash-map",
"once_cell",
- "pin-project",
"similar",
]
@@ -992,9 +966,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]]
name = "jiff"
-version = "0.2.10"
+version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a064218214dc6a10fbae5ec5fa888d80c45d611aba169222fc272072bf7aef6"
+checksum = "be1f93b8b1eb69c77f24bbb0afdf66f54b632ee39af40ca21c4365a1d7347e49"
dependencies = [
"jiff-static",
"jiff-tzdb",
@@ -1008,9 +982,9 @@ dependencies = [
[[package]]
name = "jiff-static"
-version = "0.2.10"
+version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "199b7932d97e325aff3a7030e141eafe7f2c6268e1d1b24859b753a627f45254"
+checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4"
dependencies = [
"proc-macro2",
"quote",
@@ -1050,9 +1024,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
-version = "0.2.172"
+version = "0.2.174"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
+checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
[[package]]
name = "libredox"
@@ -1064,12 +1038,6 @@ dependencies = [
"libc",
]
-[[package]]
-name = "linked-hash-map"
-version = "0.5.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
-
[[package]]
name = "linux-raw-sys"
version = "0.4.15"
@@ -1084,9 +1052,9 @@ checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
[[package]]
name = "litemap"
-version = "0.7.5"
+version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856"
+checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956"
[[package]]
name = "litrs"
@@ -1096,9 +1064,9 @@ checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5"
[[package]]
name = "lock_api"
-version = "0.4.12"
+version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
+checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765"
dependencies = [
"autocfg",
"scopeguard",
@@ -1116,34 +1084,34 @@ version = "0.12.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38"
dependencies = [
- "hashbrown 0.15.2",
+ "hashbrown 0.15.4",
]
[[package]]
name = "memchr"
-version = "2.7.4"
+version = "2.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
[[package]]
name = "miniz_oxide"
-version = "0.8.8"
+version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
+checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
dependencies = [
"adler2",
]
[[package]]
name = "mio"
-version = "1.0.3"
+version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
+checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
dependencies = [
"libc",
"log",
- "wasi 0.11.0+wasi-snapshot-preview1",
- "windows-sys 0.52.0",
+ "wasi 0.11.1+wasi-snapshot-preview1",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -1186,6 +1154,12 @@ version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
+[[package]]
+name = "once_cell_polyfill"
+version = "1.70.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad"
+
[[package]]
name = "option-ext"
version = "0.2.0"
@@ -1200,7 +1174,7 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]]
name = "oxker"
-version = "0.10.3"
+version = "0.10.4"
dependencies = [
"anyhow",
"bollard",
@@ -1226,9 +1200,9 @@ dependencies = [
[[package]]
name = "parking_lot"
-version = "0.12.3"
+version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
+checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13"
dependencies = [
"lock_api",
"parking_lot_core",
@@ -1236,15 +1210,15 @@ dependencies = [
[[package]]
name = "parking_lot_core"
-version = "0.9.10"
+version = "0.9.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
+checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
- "windows-targets",
+ "windows-targets 0.52.6",
]
[[package]]
@@ -1259,26 +1233,6 @@ version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
-[[package]]
-name = "pin-project"
-version = "1.1.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a"
-dependencies = [
- "pin-project-internal",
-]
-
-[[package]]
-name = "pin-project-internal"
-version = "1.1.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
[[package]]
name = "pin-project-lite"
version = "0.2.16"
@@ -1293,9 +1247,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "portable-atomic"
-version = "1.11.0"
+version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e"
+checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483"
[[package]]
name = "portable-atomic-util"
@@ -1306,6 +1260,15 @@ dependencies = [
"portable-atomic",
]
+[[package]]
+name = "potential_utf"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585"
+dependencies = [
+ "zerovec",
+]
+
[[package]]
name = "powerfmt"
version = "0.2.0"
@@ -1341,9 +1304,9 @@ dependencies = [
[[package]]
name = "r-efi"
-version = "5.2.0"
+version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
+checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]]
name = "rand"
@@ -1371,7 +1334,7 @@ version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
dependencies = [
- "getrandom 0.3.2",
+ "getrandom 0.3.3",
]
[[package]]
@@ -1397,9 +1360,9 @@ dependencies = [
[[package]]
name = "redox_syscall"
-version = "0.5.11"
+version = "0.5.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3"
+checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6"
dependencies = [
"bitflags",
]
@@ -1410,16 +1373,36 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b"
dependencies = [
- "getrandom 0.2.15",
+ "getrandom 0.2.16",
"libredox",
"thiserror",
]
[[package]]
-name = "rustc-demangle"
-version = "0.1.24"
+name = "ref-cast"
+version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
+checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf"
+dependencies = [
+ "ref-cast-impl",
+]
+
+[[package]]
+name = "ref-cast-impl"
+version = "1.0.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f"
[[package]]
name = "rustix"
@@ -1436,9 +1419,9 @@ dependencies = [
[[package]]
name = "rustix"
-version = "1.0.5"
+version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf"
+checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266"
dependencies = [
"bitflags",
"errno",
@@ -1449,9 +1432,9 @@ dependencies = [
[[package]]
name = "rustversion"
-version = "1.0.20"
+version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
+checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d"
[[package]]
name = "ryu"
@@ -1459,6 +1442,18 @@ version = "1.0.20"
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 = "scopeguard"
version = "1.2.0"
@@ -1521,9 +1516,9 @@ dependencies = [
[[package]]
name = "serde_spanned"
-version = "0.6.8"
+version = "0.6.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
+checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3"
dependencies = [
"serde",
]
@@ -1542,15 +1537,16 @@ dependencies = [
[[package]]
name = "serde_with"
-version = "3.12.0"
+version = "3.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa"
+checksum = "bf65a400f8f66fb7b0552869ad70157166676db75ed8181f8104ea91cf9d0b42"
dependencies = [
"base64",
"chrono",
"hex",
"indexmap 1.9.3",
"indexmap 2.9.0",
+ "schemars",
"serde",
"serde_derive",
"serde_json",
@@ -1574,9 +1570,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "signal-hook"
-version = "0.3.17"
+version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801"
+checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2"
dependencies = [
"libc",
"signal-hook-registry",
@@ -1610,24 +1606,21 @@ checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa"
[[package]]
name = "slab"
-version = "0.4.9"
+version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
-dependencies = [
- "autocfg",
-]
+checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d"
[[package]]
name = "smallvec"
-version = "1.15.0"
+version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9"
+checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
name = "socket2"
-version = "0.5.9"
+version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef"
+checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678"
dependencies = [
"libc",
"windows-sys 0.52.0",
@@ -1675,9 +1668,9 @@ dependencies = [
[[package]]
name = "syn"
-version = "2.0.100"
+version = "2.0.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
+checksum = "e4307e30089d6fd6aff212f2da3a1f9e32f3223b1f010fb09b7c95f90f3ca1e8"
dependencies = [
"proc-macro2",
"quote",
@@ -1686,9 +1679,9 @@ dependencies = [
[[package]]
name = "synstructure"
-version = "0.13.1"
+version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
+checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
dependencies = [
"proc-macro2",
"quote",
@@ -1717,12 +1710,11 @@ dependencies = [
[[package]]
name = "thread_local"
-version = "1.1.8"
+version = "1.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
+checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
dependencies = [
"cfg-if",
- "once_cell",
]
[[package]]
@@ -1758,9 +1750,9 @@ dependencies = [
[[package]]
name = "tinystr"
-version = "0.7.6"
+version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f"
+checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b"
dependencies = [
"displaydoc",
"zerovec",
@@ -1768,9 +1760,9 @@ dependencies = [
[[package]]
name = "tokio"
-version = "1.44.2"
+version = "1.45.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48"
+checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779"
dependencies = [
"backtrace",
"bytes",
@@ -1797,9 +1789,9 @@ dependencies = [
[[package]]
name = "tokio-util"
-version = "0.7.14"
+version = "0.7.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6b9590b93e6fcc1739458317cccd391ad3955e2bde8913edf6f95f9e65a8f034"
+checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df"
dependencies = [
"bytes",
"futures-core",
@@ -1810,9 +1802,9 @@ dependencies = [
[[package]]
name = "toml"
-version = "0.8.20"
+version = "0.8.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148"
+checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362"
dependencies = [
"serde",
"serde_spanned",
@@ -1822,18 +1814,18 @@ dependencies = [
[[package]]
name = "toml_datetime"
-version = "0.6.8"
+version = "0.6.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
+checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c"
dependencies = [
"serde",
]
[[package]]
name = "toml_edit"
-version = "0.22.24"
+version = "0.22.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474"
+checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
dependencies = [
"indexmap 2.9.0",
"serde",
@@ -1861,9 +1853,9 @@ dependencies = [
[[package]]
name = "tracing-attributes"
-version = "0.1.28"
+version = "0.1.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
+checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903"
dependencies = [
"proc-macro2",
"quote",
@@ -1872,9 +1864,9 @@ dependencies = [
[[package]]
name = "tracing-core"
-version = "0.1.33"
+version = "0.1.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
+checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678"
dependencies = [
"once_cell",
"valuable",
@@ -1963,12 +1955,6 @@ dependencies = [
"percent-encoding",
]
-[[package]]
-name = "utf16_iter"
-version = "1.0.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246"
-
[[package]]
name = "utf8_iter"
version = "1.0.4"
@@ -1983,12 +1969,14 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "uuid"
-version = "1.16.0"
+version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9"
+checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d"
dependencies = [
- "getrandom 0.3.2",
+ "getrandom 0.3.3",
+ "js-sys",
"rand",
+ "wasm-bindgen",
]
[[package]]
@@ -2008,9 +1996,9 @@ dependencies = [
[[package]]
name = "wasi"
-version = "0.11.0+wasi-snapshot-preview1"
+version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
name = "wasi"
@@ -2103,9 +2091,9 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-core"
-version = "0.61.0"
+version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980"
+checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
dependencies = [
"windows-implement",
"windows-interface",
@@ -2138,24 +2126,24 @@ dependencies = [
[[package]]
name = "windows-link"
-version = "0.1.1"
+version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
+checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
[[package]]
name = "windows-result"
-version = "0.3.2"
+version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252"
+checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-strings"
-version = "0.4.0"
+version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97"
+checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
dependencies = [
"windows-link",
]
@@ -2166,7 +2154,7 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
- "windows-targets",
+ "windows-targets 0.52.6",
]
[[package]]
@@ -2175,7 +2163,16 @@ version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
- "windows-targets",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.60.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
+dependencies = [
+ "windows-targets 0.53.2",
]
[[package]]
@@ -2184,14 +2181,30 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
- "windows_aarch64_gnullvm",
- "windows_aarch64_msvc",
- "windows_i686_gnu",
- "windows_i686_gnullvm",
- "windows_i686_msvc",
- "windows_x86_64_gnu",
- "windows_x86_64_gnullvm",
- "windows_x86_64_msvc",
+ "windows_aarch64_gnullvm 0.52.6",
+ "windows_aarch64_msvc 0.52.6",
+ "windows_i686_gnu 0.52.6",
+ "windows_i686_gnullvm 0.52.6",
+ "windows_i686_msvc 0.52.6",
+ "windows_x86_64_gnu 0.52.6",
+ "windows_x86_64_gnullvm 0.52.6",
+ "windows_x86_64_msvc 0.52.6",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.53.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef"
+dependencies = [
+ "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",
]
[[package]]
@@ -2200,42 +2213,84 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
+
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
+
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+[[package]]
+name = "windows_i686_gnu"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
+
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
+
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+[[package]]
+name = "windows_i686_msvc"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
+
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
+
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
+
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
@@ -2243,10 +2298,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
-name = "winnow"
-version = "0.7.6"
+name = "windows_x86_64_msvc"
+version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10"
+checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
+
+[[package]]
+name = "winnow"
+version = "0.7.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd"
dependencies = [
"memchr",
]
@@ -2260,23 +2321,17 @@ dependencies = [
"bitflags",
]
-[[package]]
-name = "write16"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936"
-
[[package]]
name = "writeable"
-version = "0.5.5"
+version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
+checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb"
[[package]]
name = "yoke"
-version = "0.7.5"
+version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40"
+checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc"
dependencies = [
"serde",
"stable_deref_trait",
@@ -2286,9 +2341,9 @@ dependencies = [
[[package]]
name = "yoke-derive"
-version = "0.7.5"
+version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154"
+checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6"
dependencies = [
"proc-macro2",
"quote",
@@ -2298,18 +2353,18 @@ dependencies = [
[[package]]
name = "zerocopy"
-version = "0.8.24"
+version = "0.8.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879"
+checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
-version = "0.8.24"
+version = "0.8.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be"
+checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef"
dependencies = [
"proc-macro2",
"quote",
@@ -2338,10 +2393,21 @@ dependencies = [
]
[[package]]
-name = "zerovec"
-version = "0.10.4"
+name = "zerotrie"
+version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079"
+checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595"
+dependencies = [
+ "displaydoc",
+ "yoke",
+ "zerofrom",
+]
+
+[[package]]
+name = "zerovec"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428"
dependencies = [
"yoke",
"zerofrom",
@@ -2350,9 +2416,9 @@ dependencies = [
[[package]]
name = "zerovec-derive"
-version = "0.10.3"
+version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
+checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f"
dependencies = [
"proc-macro2",
"quote",
diff --git a/Cargo.toml b/Cargo.toml
index a2c42fa..07124b6 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "oxker"
-version = "0.10.3"
+version = "0.10.4"
edition = "2024"
authors = ["Jack Wills "]
description = "A simple tui to view & control docker containers"
@@ -27,7 +27,7 @@ similar_names = "allow"
[dependencies]
anyhow = "1.0"
-bollard = "0.18"
+bollard = "0.19"
cansi = "2.2"
clap = { version = "4.5", features = ["color", "derive", "unicode"] }
crossterm = "0.29"
@@ -39,12 +39,12 @@ ratatui = "0.29"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
serde_jsonc = "1.0"
-tokio = { version = "1.44", features = ["full"] }
+tokio = { version = "1.45", features = ["full"] }
tokio-util = "0.7"
toml = { version = "0.8", default-features = false, features = ["parse"] }
tracing = "0.1"
tracing-subscriber = "0.3"
-uuid = { version = "1.16", features = ["fast-rng", "v4"] }
+uuid = { version = "1.17", features = ["fast-rng", "v4"] }
[profile.release]
lto = true
diff --git a/README.md b/README.md
index 7a347ad..83837ae 100644
--- a/README.md
+++ b/README.md
@@ -110,6 +110,8 @@ In application controls, these, amongst many other settings, can be customized w
| ```( 1-9 )``` | Sort containers by heading, clicking on headings also sorts the selected column. |
| ```( 0 )``` | Stop sorting.|
| ```( F1 )``` or ```( / )``` | Enter filter mode. |
+| ```( - ) ``` 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.|
| ```( h )``` | Toggle help menu.|
| ```( m )``` | Toggle mouse capture - if disabled, text on screen can be selected.|
@@ -143,7 +145,10 @@ A config file enables the user to persist settings, create a custom keymap, set
Examples of the config file, alsong with explanations of each value, can be found in the [example_config](https://github.com/mrjackwills/oxker/tree/main/example_config) directory. `oxker` supports `.toml`,`.json`, and `.jsonc` file formats.
-If not config file is found, `oxker` will create a `config.toml` in the user's local config directory. Command line arguments will take priority over values from the config file.
+If no config file is found, a `config.toml` file will be created in an `oxker` directory in the user's local config directory, as found by the [directories crate](https://docs.rs/directories/6.0.0/directories/struct.BaseDirs.html#method.config_local_dir).
+
+
+Command line arguments will take priority over values from the config file.
If running an `oxker` container, the default config location will be `/` rather than the automatically detected platform-specific local config directory, and can be mounted as follows;
diff --git a/_typos.toml b/_typos.toml
index 54ddf6c..6fd8cd3 100644
--- a/_typos.toml
+++ b/_typos.toml
@@ -1,2 +1,5 @@
[default.extend-words]
-ratatui = "ratatui"
\ No newline at end of file
+ratatui = "ratatui"
+
+[default]
+extend-ignore-words-re = ["[(?:[0-9a-fA-F]{8}|[0-9a-fA-F]{40})]"]
diff --git a/create_release.sh b/create_release.sh
index 7eebeb3..71ad52b 100755
--- a/create_release.sh
+++ b/create_release.sh
@@ -230,6 +230,18 @@ cross_build_x86_windows() {
cross build --target x86_64-pc-windows-gnu --release
}
+# 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
+ 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"
+ fi
+
+}
+
# Build all releases that GitHub workflow would
# This will download GB's of docker images
cross_build_all() {
@@ -242,6 +254,8 @@ cross_build_all() {
ask_continue
cross_build_x86_windows
ask_continue
+ zig_build_aarch64_apple
+ ask_continue
}
# $1 text to colourise
@@ -371,17 +385,17 @@ release_flow() {
}
build_choice() {
- cmd=(dialog --backtitle "Choose option" --radiolist "choose" 14 80 16)
+ cmd=(dialog --backtitle "Choose option" --keep-tite --radiolist "choose" 14 80 16)
options=(
1 "x86 musl linux" off
2 "aarch64 musl linux" off
3 "armv6 musl linux" off
- 4 "x86 windows" off
- 5 "all" off
+ 4 "aarch64 apple" off
+ 5 "x86 windows" off
+ 6 "all" off
)
choices=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty)
exitStatus=$?
- clear
if [ $exitStatus -ne 0 ]; then
exit
fi
@@ -403,10 +417,14 @@ build_choice() {
exit
;;
4)
- cross_build_x86_windows
+ zig_build_aarch64_apple
exit
;;
5)
+ cross_build_x86_windows
+ exit
+ ;;
+ 6)
cross_build_all
exit
;;
@@ -415,7 +433,7 @@ build_choice() {
}
build_container_choice() {
- cmd=(dialog --backtitle "Choose option" --radiolist "choose" 14 80 16)
+ cmd=(dialog --backtitle "Choose option" --keep-tite --radiolist "choose" 14 80 16)
options=(
1 "x86 " off
2 "aarch64" off
@@ -424,7 +442,6 @@ build_container_choice() {
)
choices=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty)
exitStatus=$?
- clear
if [ $exitStatus -ne 0 ]; then
exit
fi
@@ -455,7 +472,7 @@ build_container_choice() {
}
main() {
- cmd=(dialog --backtitle "Choose option" --radiolist "choose" 14 80 16)
+ cmd=(dialog --backtitle "Choose option" --keep-tite --radiolist "choose" 14 80 16)
options=(
1 "test" off
2 "release" off
@@ -464,7 +481,6 @@ main() {
)
choices=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty)
exitStatus=$?
- clear
if [ $exitStatus -ne 0 ]; then
exit
fi
diff --git a/example_config/example.config.jsonc b/example_config/example.config.jsonc
index f62f643..230e982 100644
--- a/example_config/example.config.jsonc
+++ b/example_config/example.config.jsonc
@@ -29,12 +29,15 @@
// "save_dir": "$HOME",
// Force use of docker cli when execing into containers, honestly mostly pointless
"use_cli": false,
+ // Show the logs section - this can be changed during operation with the log_section_toggle key
+ "show_logs": true,
//////////////////
// Custom Keymap //
//////////////////
// Available keys are;
// 1) a-z and A-Z
// 2) 0-9
+ // WARNING if using the \ key, it needs to be escaped, e.g. "log_section_toggle": ["\\"]
// 3) / \ , . # ' [ ] ; = -
// 3) F1-F12
// 4) backspace, tab, backtab, delete, end, esc, home, insert, pagedown, pageup, left, right, up, down
@@ -144,6 +147,18 @@
// Toggle mouse capture
"toggle_mouse_capture": [
"m"
+ ],
+ // Reduce the height of the logs list section
+ "log_section_height_decrease": [
+ "-"
+ ],
+ // Increase the height of the logs list section
+ "log_section_height_increase": [
+ "+"
+ ],
+ // Toggle visibility of the log section
+ "log_section_toggle": [
+ "\\"
]
},
////////////////////
diff --git a/example_config/example.config.toml b/example_config/example.config.toml
index b8d4534..3c71386 100644
--- a/example_config/example.config.toml
+++ b/example_config/example.config.toml
@@ -32,7 +32,7 @@ timezone = "Etc/UTC"
# 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.012345678Z
# *Should* accept any valid strftime string up to 32 chars, see https://strftime.org/
-timestamp_format="%Y-%m-%dT%H:%M:%S.%8f"
+timestamp_format = "%Y-%m-%dT%H:%M:%S.%8f"
# Directory for saving exported logs, defaults to `$HOME`, this is automatically *correctly* calculated for Linux, Mac, and Windows
# save_dir = "$HOME"
@@ -40,6 +40,9 @@ timestamp_format="%Y-%m-%dT%H:%M:%S.%8f"
# Force use of docker cli when execing into containers, honestly mostly pointless
use_cli = false
+# Show the logs section - this can be changed during operation with the log_section_toggle key
+show_logs = true
+
#################
# Custom Keymap #
#################
@@ -47,61 +50,68 @@ use_cli = false
# Available keys are;
# 1) a-z and A-Z
# 2) 0-9
+# WARNING if using the \ key, it needs to be escaped, e.g. log_section_toggle = ["\\"]
# 3) / \ , . # ' [ ] ; = -
# 3) F1-F12
# 4) backspace, tab, backtab, delete, end, esc, home, insert, pagedown, pageup, left, right, up, down
+
# Each definition can have two keys associated with it
# If any key clashes are found, oxker will revert to it's default keymap
[keymap]
# Clear any popup boxes, filter panel, or help panel
-clear=["c", "esc"]
+clear = ["c", "esc"]
# Cancel delete - clear also works here
-delete_deny=["n"]
+delete_deny = ["n"]
# Confirm Delete
-delete_confirm=["y"]
+delete_confirm = ["y"]
# Exec into the selected container
-exec=["e"]
+exec = ["e"]
# Enter filter mode
-filter_mode=["/", "F1"]
+filter_mode = ["/", "F1"]
# Quit at anytime
quit = ["q"]
# Save logs of selected container to file on disk
-save_logs=["s"]
+save_logs = ["s"]
# scroll down a list by many
-scroll_down_many=["pagedown"]
+scroll_down_many = ["pagedown"]
# scroll down a list by one item
-scroll_down_one=["down", "j"]
+scroll_down_one = ["down", "j"]
# scroll down to the end of a list
-scroll_end=["end"]
+scroll_end = ["end"]
# scroll up to the start of a list
-scroll_start=["home"]
+scroll_start = ["home"]
# scroll up a list by many
-scroll_up_many=["pageup"]
+scroll_up_many = ["pageup"]
# scroll up a list by one item
-scroll_up_one=["up", "k"]
+scroll_up_one = ["up", "k"]
# Select next panel
-select_next_panel=["tab"]
+select_next_panel = ["tab"]
# Select previous panel
-select_previous_panel=["backtab"]
+select_previous_panel = ["backtab"]
# Sort the containers based on specific column
-sort_by_name=["1"]
-sort_by_state=["2"]
-sort_by_status=["3"]
-sort_by_cpu=["4"]
-sort_by_memory=["5"]
-sort_by_id=["6"]
-sort_by_image=["7"]
-sort_by_rx=["8"]
-sort_by_tx=["9"]
+sort_by_name = ["1"]
+sort_by_state = ["2"]
+sort_by_status = ["3"]
+sort_by_cpu = ["4"]
+sort_by_memory = ["5"]
+sort_by_id = ["6"]
+sort_by_image = ["7"]
+sort_by_rx = ["8"]
+sort_by_tx = ["9"]
# Reset the sorted containers
-sort_reset=["0"]
+sort_reset = ["0"]
# Toggle the help panel
-toggle_help=["h"]
+toggle_help = ["h"]
# Toggle mouse capture
-toggle_mouse_capture=["m"]
-
+toggle_mouse_capture = ["m"]
+# Reduce the height of the logs list section
+log_section_height_decrease = ["-"]
+# Increase the height of the logs list section
+log_section_height_increase = ["+"]
+# Toggle visibility of the log section
+log_section_toggle = ["\\"]
#################
# Custom Colors #
@@ -119,7 +129,7 @@ toggle_mouse_capture=["m"]
# Background color of the entire line
background = "magenta"
# Animated loading icon at the start of the bar
-loading_spinner="white"
+loading_spinner = "white"
# Text color
text = "black"
# Text color of a selected header
@@ -128,137 +138,136 @@ text_selected = "gray"
# The borders around the selectable panels - Containers, Commands, Logs
[colors.borders]
# Border when selected
-selected="lightcyan"
+selected = "lightcyan"
# Border when not selected
-unselected="grey"
+unselected = "grey"
# The containers sections, in the future more color customization options should be made available in this section
[colors.containers]
# The icon use to illustrate which container is currently selected - at the moment the TUI library, ratatui, doesn't seem allow changing the color of the highlight symbol
-icon="white"
+icon = "white"
# Background color of panel
background = "reset"
# At the moment, this will only change the color of the name, id, and image columns
-text="blue"
+text = "blue"
# Text color of the RX column
-text_rx="#FFE9C1"
+text_rx = "#FFE9C1"
# Text color of the TX column
-text_tx="#CD8C8C"
+text_tx = "#CD8C8C"
# The logs panel, will only be applied if color_logs is false
[colors.logs]
# Background color of panel
background = "reset"
# text color
-text="reset"
+text = "reset"
# Each state of a container has a color, which is used in multiple places, i.e. chart titles, state/status/cpu/memory columns in the container section
[colors.container_state]
-dead="red"
-exited="red"
+dead = "red"
+exited = "red"
paused = "yellow"
-removing ="lightred"
-restarting ="lightgreen"
-running_healthy ="green"
-running_unhealthy="#FFB224"
-unknown="red"
+removing = "lightred"
+restarting = "lightgreen"
+running_healthy = "green"
+running_unhealthy = "#FFB224"
+unknown = "red"
# The filter panel
[colors.filter]
# Background color of panel
background = "reset"
# color of text
-text="gray"
+text = "gray"
# background color of the selected filter by item (Name/Image/Status/All)
-selected_filter_background="gray"
+selected_filter_background = "gray"
# text color of the selected filter by item (Name/Image/Status/All)
-selected_filter_text="black"
+selected_filter_text = "black"
# Highlighted text color
-highlight="magenta"
-
+highlight = "magenta"
# The color the of Docker commands available for each container
[colors.commands]
# Background color of panel
background = "reset"
-pause="yellow"
-restart="magenta"
+pause = "yellow"
+restart = "magenta"
stop = "red"
-delete ="gray"
-resume ="blue"
-start ="green"
+delete = "gray"
+resume = "blue"
+start = "green"
# The cpu chart
[colors.chart_cpu]
# Background color of panel
background = "reset"
# Border color
-border="white"
+border = "white"
# Chart title - only whilst container is running, paused & stopped colors not yet customizable - or could just use state color?
-title="green"
+title = "green"
# Maximum CPU percentage - again paused & stopped colors not yet customizable
-max="#FFB224"
+max = "#FFB224"
# Points on the chart - again paused & stopped colors not yet customizable
-points="magenta"
+points = "magenta"
# The charts y-axis
-y_axis="white"
+y_axis = "white"
# The memory chart
[colors.chart_memory]
# Background color of panel
background = "reset"
# Border color
-border="white"
+border = "white"
# Chart title - only whilst container is running, paused & stopped will use colors.container_state
-title="green"
+title = "green"
# Maximum memory use - again paused & stopped will use colors.container_state
-max="#FFB224"
+max = "#FFB224"
# Points on the chart - again paused & stopped will use colors.container_state
-points="cyan"
+points = "cyan"
# The charts y-axis
-y_axis="white"
+y_axis = "white"
# The ports chart
[colors.chart_ports]
# Background color of panel
background = "reset"
# Border color
-border="white"
+border = "white"
# Chart title - only whilst container is running, paused & stopped will use colors.container_state
-title="green"
+title = "green"
# Private/Public/IP headings
-headings="yellow"
+headings = "yellow"
# Ports & IP listing text
-text="white"
+text = "white"
# The help popup
[colors.popup_help]
# Background color
-background="magenta"
+background = "magenta"
# Text color
-text="black"
+text = "black"
# Highlighted text color
-text_highlight="white"
+text_highlight = "white"
# The info popup - used to display small messages - such as saving logs to disk, or change of mouse capture settings
[colors.popup_info]
# Background color
-background="blue"
+background = "blue"
# Text color
-text="white"
+text = "white"
# The delete popup - used to display a confirmation warning when about to delete a container
[colors.popup_delete]
# Background color
-background="white"
+background = "white"
# Text color
-text="black"
+text = "black"
# Highlighted text color
-text_highlight="red"
+text_highlight = "red"
# The error popup - hopefully you'll never have to see this
[colors.popup_error]
# Background color
-background="red"
+background = "red"
# Text color
-text="white"
\ No newline at end of file
+text = "white"
diff --git a/src/app_data/container_state.rs b/src/app_data/container_state.rs
index fa9587f..15f4cd8 100644
--- a/src/app_data/container_state.rs
+++ b/src/app_data/container_state.rs
@@ -30,10 +30,18 @@ impl From<&str> for ContainerId {
}
impl ContainerId {
+ // TODO remove this once zigbuild uses Rust v1.87.0
+ #[cfg(target_os = "macos")]
+ #[allow(clippy::missing_const_for_fn)]
pub fn get(&self) -> &str {
self.0.as_str()
}
+ #[cfg(not(target_os = "macos"))]
+ pub const fn get(&self) -> &str {
+ self.0.as_str()
+ }
+
/// Only return first 8 chars of id, is usually more than enough for uniqueness
/// need to update tests to use real ids, or atleast strings of the correct-ish length
pub fn get_short(&self) -> String {
@@ -76,10 +84,18 @@ macro_rules! unit_struct {
}
impl $name {
+ #[cfg(target_os = "macos")]
+ #[allow(clippy::missing_const_for_fn)]
+ // TODO remove this once zigbuild uses Rust v1.87.0
pub fn get(&self) -> &str {
self.0.as_str()
}
+ #[cfg(not(target_os = "macos"))]
+ pub const fn get(&self) -> &str {
+ self.0.as_str()
+ }
+
pub fn set(&mut self, value: String) {
self.0 = value;
}
@@ -326,6 +342,54 @@ impl From<(&str, &ContainerStatus)> for State {
}
}
+/// Need status, to check if container is unhealthy or not
+impl
+ From<(
+ &bollard::secret::ContainerSummaryStateEnum,
+ &ContainerStatus,
+ )> for State
+{
+ fn from(
+ (input, status): (
+ &bollard::secret::ContainerSummaryStateEnum,
+ &ContainerStatus,
+ ),
+ ) -> Self {
+ match input {
+ bollard::secret::ContainerSummaryStateEnum::DEAD => Self::Dead,
+ bollard::secret::ContainerSummaryStateEnum::EXITED => Self::Exited,
+ bollard::secret::ContainerSummaryStateEnum::PAUSED => Self::Paused,
+ bollard::secret::ContainerSummaryStateEnum::REMOVING => Self::Removing,
+ bollard::secret::ContainerSummaryStateEnum::RESTARTING => Self::Restarting,
+ bollard::secret::ContainerSummaryStateEnum::RUNNING => {
+ if status.unhealthy() {
+ Self::Running(RunningState::Unhealthy)
+ } else {
+ Self::Running(RunningState::Healthy)
+ }
+ }
+ _ => Self::Unknown,
+ }
+ }
+}
+
+/// Again, need status, to check if container is unhealthy or not
+impl
+ From<(
+ Option<&bollard::secret::ContainerSummaryStateEnum>,
+ &ContainerStatus,
+ )> for State
+{
+ fn from(
+ (input, status): (
+ Option<&bollard::secret::ContainerSummaryStateEnum>,
+ &ContainerStatus,
+ ),
+ ) -> Self {
+ input.map_or(Self::Unknown, |input| Self::from((input, status)))
+ }
+}
+
/// Again, need status, to check if container is unhealthy or not
impl From<(Option, &ContainerStatus)> for State {
fn from((input, status): (Option, &ContainerStatus)) -> Self {
@@ -544,7 +608,7 @@ impl LogsTz {
/// Store the logs alongside a HashSet, each log *should* generate a unique timestamp,
/// so if we store the timestamp separately in a HashSet, we can then check if we should insert a log line into the
-/// stateful list dependent on whethere the timestamp is in the HashSet or not
+/// stateful list dependent on whether the timestamp is in the HashSet or not
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Logs {
logs: StatefulList>,
@@ -570,10 +634,24 @@ impl Logs {
}
}
- pub fn to_vec(&self) -> Vec> {
- self.logs.items.clone()
+ /// Get the logs vec, but instead of cloning to whole vec, only clone items with x of the currently selected index
+ /// Where x is the abs different of the index plus the panel height & a padding
+ /// The rest can be just empty list items
+ pub fn to_vec(&self, height: usize, padding: usize) -> Vec> {
+ let current_index = self.logs.state.selected().unwrap_or_default();
+ self.logs
+ .items
+ .iter()
+ .enumerate()
+ .map(|(index, item)| {
+ if current_index.abs_diff(index) <= height + padding {
+ item.clone()
+ } else {
+ ListItem::from("")
+ }
+ })
+ .collect()
}
-
/// The rest of the methods are basically forwarding from the underlying StatefulList
pub fn get_state_title(&self) -> String {
self.logs.get_state_title()
@@ -594,10 +672,18 @@ impl Logs {
self.logs.start();
}
+ // TODO remove this once zigbuild uses Rust v1.87.0
+ #[cfg(target_os = "macos")]
+ #[allow(clippy::missing_const_for_fn)]
pub fn len(&self) -> usize {
self.logs.items.len()
}
+ #[cfg(not(target_os = "macos"))]
+ pub const fn len(&self) -> usize {
+ self.logs.items.len()
+ }
+
pub const fn state(&mut self) -> &mut ListState {
&mut self.logs.state
}
diff --git a/src/app_data/mod.rs b/src/app_data/mod.rs
index cbbc0e3..5fc9f7a 100644
--- a/src/app_data/mod.rs
+++ b/src/app_data/mod.rs
@@ -14,7 +14,7 @@ use crate::{
ENTRY_POINT,
app_error::AppError,
config::Config,
- ui::{GuiState, Redraw, Status, log_sanitizer},
+ ui::{GuiState, Rerender, Status, log_sanitizer},
};
pub use container_state::*;
@@ -122,7 +122,7 @@ pub struct AppData {
error: Option,
filter: Filter,
hidden_containers: Vec,
- redraw: Arc,
+ redraw: Arc,
sorted_by: Option<(Header, SortedOrder)>,
current_sorted_id: Vec,
pub config: Config,
@@ -137,13 +137,13 @@ pub struct AppData {
pub filter: Filter,
pub hidden_containers: Vec,
pub current_sorted_id: Vec,
- pub redraw: Arc,
+ pub redraw: Arc,
pub sorted_by: Option<(Header, SortedOrder)>,
}
impl AppData {
/// Generate a default app_state
- pub fn new(config: Config, redraw: &Arc) -> Self {
+ pub fn new(config: Config, redraw: &Arc) -> Self {
Self {
config,
containers: StatefulList::new(vec![]),
@@ -192,7 +192,7 @@ impl AppData {
/// sets the state to start if any filtering has occurred
/// Also search in the "hidden" vec for items and insert back into the main containers vec
fn filter_containers(&mut self) {
- self.redraw.set_true();
+ self.redraw.update();
let pre_len = self.get_container_len();
if !self.hidden_containers.is_empty() {
@@ -296,7 +296,7 @@ impl AppData {
/// Remove the sorted header & order, and sort by default - created datetime
pub fn reset_sorted(&mut self) {
self.set_sorted(None);
- self.redraw.set_true();
+ self.redraw.update();
}
/// Sort containers based on a given header, if headings match, and already ascending, remove sorting
@@ -392,7 +392,7 @@ impl AppData {
self.containers.items.sort_by(sort_closure);
if pre_order != self.get_current_ids() {
- self.redraw.set_true();
+ self.redraw.update();
}
} else if self.current_sorted_id != self.get_current_ids() {
self.containers.items.sort_by(|a, b| {
@@ -400,17 +400,24 @@ impl AppData {
.cmp(&b.created)
.then_with(|| a.name.get().cmp(b.name.get()))
});
- self.redraw.set_true();
+ self.redraw.update();
self.current_sorted_id = self.get_current_ids();
}
}
/// Container state methods
/// Get the total number of none "hidden" containers
+ // TODO remove this once zigbuild uses Rust v1.87.0
+ #[cfg(target_os = "macos")]
pub fn get_container_len(&self) -> usize {
self.containers.items.len()
}
+ #[cfg(not(target_os = "macos"))]
+ pub const fn get_container_len(&self) -> usize {
+ self.containers.items.len()
+ }
+
pub fn get_all_id_state(&self) -> Vec<(State, ContainerId)> {
self.containers
.items
@@ -439,25 +446,25 @@ impl AppData {
/// Select the first container
pub fn containers_start(&mut self) {
self.containers.start();
- self.redraw.set_true();
+ self.redraw.update();
}
/// select the last container
pub fn containers_end(&mut self) {
self.containers.end();
- self.redraw.set_true();
+ self.redraw.update();
}
/// Select the next container
pub fn containers_next(&mut self) {
self.containers.next();
- self.redraw.set_true();
+ self.redraw.update();
}
/// select the previous container
pub fn containers_previous(&mut self) {
self.containers.previous();
- self.redraw.set_true();
+ self.redraw.update();
}
/// Get ListState of containers
@@ -579,7 +586,7 @@ impl AppData {
pub fn docker_controls_next(&mut self) {
if let Some(i) = self.get_mut_selected_container() {
i.docker_controls.next();
- self.redraw.set_true();
+ self.redraw.update();
}
}
@@ -587,7 +594,7 @@ impl AppData {
pub fn docker_controls_previous(&mut self) {
if let Some(i) = self.get_mut_selected_container() {
i.docker_controls.previous();
- self.redraw.set_true();
+ self.redraw.update();
}
}
@@ -595,7 +602,7 @@ impl AppData {
pub fn docker_controls_start(&mut self) {
if let Some(i) = self.get_mut_selected_container() {
i.docker_controls.start();
- self.redraw.set_true();
+ self.redraw.update();
}
}
@@ -603,7 +610,7 @@ impl AppData {
pub fn docker_controls_end(&mut self) {
if let Some(i) = self.get_mut_selected_container() {
i.docker_controls.end();
- self.redraw.set_true();
+ self.redraw.update();
}
}
@@ -641,7 +648,7 @@ impl AppData {
pub fn log_next(&mut self) {
if let Some(i) = self.get_mut_selected_container() {
i.logs.next();
- self.redraw.set_true();
+ self.redraw.update();
}
}
@@ -649,7 +656,7 @@ impl AppData {
pub fn log_previous(&mut self) {
if let Some(i) = self.get_mut_selected_container() {
i.logs.previous();
- self.redraw.set_true();
+ self.redraw.update();
}
}
@@ -657,7 +664,7 @@ impl AppData {
pub fn log_end(&mut self) {
if let Some(i) = self.get_mut_selected_container() {
i.logs.end();
- self.redraw.set_true();
+ self.redraw.update();
}
}
@@ -665,17 +672,17 @@ impl AppData {
pub fn log_start(&mut self) {
if let Some(i) = self.get_mut_selected_container() {
i.logs.start();
- self.redraw.set_true();
+ self.redraw.update();
}
}
/// Get mutable Vec of current containers logs
- pub fn get_logs(&self) -> Vec> {
+ pub fn get_logs(&self, height: u16, padding: usize) -> Vec> {
self.containers
.state
.selected()
.and_then(|i| self.containers.items.get(i))
- .map_or(vec![], |i| i.logs.to_vec())
+ .map_or(vec![], |i| i.logs.to_vec(height.into(), padding))
}
/// Get mutable Option of the currently selected container Logs state
@@ -706,14 +713,14 @@ impl AppData {
/// Remove single app_state error
pub fn remove_error(&mut self) {
self.error = None;
- self.redraw.set_true();
+ self.redraw.update();
}
/// Insert single app_state error
pub fn set_error(&mut self, error: AppError, gui_state: &Arc>, status: Status) {
gui_state.lock().status_push(status);
self.error = Some(error);
- self.redraw.set_true();
+ self.redraw.update();
}
/// Check if the selected container is a dockerised version of oxker
@@ -803,7 +810,7 @@ impl AppData {
container.mem_limit.update(mem_limit);
}
if self.is_selected_container(id) {
- self.redraw.set_true();
+ self.redraw.update();
}
self.sort_containers();
}
@@ -841,7 +848,7 @@ impl AppData {
if self.containers.items.get(index).is_some() {
self.containers.items.remove(index);
if self.is_selected_container(id) {
- self.redraw.set_true();
+ self.redraw.update();
}
}
}
@@ -875,7 +882,12 @@ impl AppData {
.map_or(String::new(), std::clone::Clone::clone),
);
- let state = State::from((i.state.as_ref().map_or("dead", |z| z), &status));
+ let state = State::from((
+ i.state
+ .as_ref()
+ .map_or(&bollard::secret::ContainerSummaryStateEnum::DEAD, |z| z),
+ &status,
+ ));
let image = i
.image
.as_ref()
@@ -971,7 +983,7 @@ impl AppData {
}
}
if self.is_selected_container(id) {
- self.redraw.set_true();
+ self.redraw.update();
}
}
}
@@ -1946,7 +1958,7 @@ mod tests {
assert_eq!(result.as_ref().unwrap().selected(), Some(2));
assert_eq!(result.unwrap().offset(), 0);
- let result = app_data.get_logs();
+ let result = app_data.get_logs(4, 1);
assert_eq!(result.len(), 3);
let result = app_data.get_log_title();
@@ -2317,4 +2329,62 @@ mod tests {
let result = app_data.get_log_state();
assert!(result.is_none());
}
+
+ // *************** //
+ // Get logs method //
+ // *************** //
+
+ #[test]
+ /// get_logs() returns vec of item, but the items are empty unless they are in the *visible" zone, based on height, index, and padding
+ fn test_app_data_update_get_logs() {
+ let (ids, containers) = gen_containers();
+
+ let mut app_data = gen_appdata(&containers);
+
+ app_data.containers_start();
+ let logs = (0..=999).map(|i| format!("{i} {i}")).collect::>();
+
+ app_data.update_log_by_id(logs, &ids[0]);
+
+ let result = app_data.get_logs(10, 10);
+ for (index, item) in result.iter().enumerate() {
+ if index < 979 {
+ assert_eq!(item, &ListItem::new(""));
+ } else {
+ assert_eq!(item, &ListItem::new(format!("{index}")));
+ }
+ }
+
+ let result = app_data.get_logs(100, 20);
+ for (index, item) in result.iter().enumerate() {
+ if index < 879 {
+ assert_eq!(item, &ListItem::new(""));
+ } else {
+ assert_eq!(item, &ListItem::new(format!("{index}")));
+ }
+ }
+
+ app_data.log_start();
+ let result = app_data.get_logs(10, 10);
+ for (index, item) in result.iter().enumerate() {
+ if index > 20 {
+ assert_eq!(item, &ListItem::new(""));
+ } else {
+ assert_eq!(item, &ListItem::new(format!("{index}")));
+ }
+ }
+
+ for _ in 0..=500 {
+ app_data.log_next();
+ }
+
+ let result = app_data.get_logs(10, 10);
+ for (index, item) in result.iter().enumerate() {
+ if (481..=521).contains(&index) {
+ assert_eq!(item, &ListItem::new(format!("{index}")));
+ } else {
+ assert_eq!(item, &ListItem::new(""));
+ }
+ }
+ }
}
diff --git a/src/config/config.toml b/src/config/config.toml
index b8d4534..457064a 100644
--- a/src/config/config.toml
+++ b/src/config/config.toml
@@ -32,7 +32,7 @@ timezone = "Etc/UTC"
# 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.012345678Z
# *Should* accept any valid strftime string up to 32 chars, see https://strftime.org/
-timestamp_format="%Y-%m-%dT%H:%M:%S.%8f"
+timestamp_format = "%Y-%m-%dT%H:%M:%S.%8f"
# Directory for saving exported logs, defaults to `$HOME`, this is automatically *correctly* calculated for Linux, Mac, and Windows
# save_dir = "$HOME"
@@ -40,6 +40,9 @@ timestamp_format="%Y-%m-%dT%H:%M:%S.%8f"
# Force use of docker cli when execing into containers, honestly mostly pointless
use_cli = false
+# Show the logs section - this can be changed during operation with the log_section_toggle key
+show_logs = true
+
#################
# Custom Keymap #
#################
@@ -47,6 +50,7 @@ use_cli = false
# Available keys are;
# 1) a-z and A-Z
# 2) 0-9
+# WARNING if using the \ key, it needs to be escaped, e.g. log_section_toggle = ["\\"]
# 3) / \ , . # ' [ ] ; = -
# 3) F1-F12
# 4) backspace, tab, backtab, delete, end, esc, home, insert, pagedown, pageup, left, right, up, down
@@ -56,52 +60,56 @@ use_cli = false
[keymap]
# Clear any popup boxes, filter panel, or help panel
-clear=["c", "esc"]
+clear = ["c", "esc"]
# Cancel delete - clear also works here
-delete_deny=["n"]
+delete_deny = ["n"]
# Confirm Delete
-delete_confirm=["y"]
+delete_confirm = ["y"]
# Exec into the selected container
-exec=["e"]
+exec = ["e"]
# Enter filter mode
-filter_mode=["/", "F1"]
+filter_mode = ["/", "F1"]
# Quit at anytime
quit = ["q"]
# Save logs of selected container to file on disk
-save_logs=["s"]
+save_logs = ["s"]
# scroll down a list by many
-scroll_down_many=["pagedown"]
+scroll_down_many = ["pagedown"]
# scroll down a list by one item
-scroll_down_one=["down", "j"]
+scroll_down_one = ["down", "j"]
# scroll down to the end of a list
-scroll_end=["end"]
+scroll_end = ["end"]
# scroll up to the start of a list
-scroll_start=["home"]
+scroll_start = ["home"]
# scroll up a list by many
-scroll_up_many=["pageup"]
+scroll_up_many = ["pageup"]
# scroll up a list by one item
-scroll_up_one=["up", "k"]
+scroll_up_one = ["up", "k"]
# Select next panel
-select_next_panel=["tab"]
+select_next_panel = ["tab"]
# Select previous panel
-select_previous_panel=["backtab"]
+select_previous_panel = ["backtab"]
# Sort the containers based on specific column
-sort_by_name=["1"]
-sort_by_state=["2"]
-sort_by_status=["3"]
-sort_by_cpu=["4"]
-sort_by_memory=["5"]
-sort_by_id=["6"]
-sort_by_image=["7"]
-sort_by_rx=["8"]
-sort_by_tx=["9"]
+sort_by_name = ["1"]
+sort_by_state = ["2"]
+sort_by_status = ["3"]
+sort_by_cpu = ["4"]
+sort_by_memory = ["5"]
+sort_by_id = ["6"]
+sort_by_image = ["7"]
+sort_by_rx = ["8"]
+sort_by_tx = ["9"]
# Reset the sorted containers
-sort_reset=["0"]
+sort_reset = ["0"]
# Toggle the help panel
-toggle_help=["h"]
+toggle_help = ["h"]
# Toggle mouse capture
-toggle_mouse_capture=["m"]
-
+toggle_mouse_capture = ["m"]
+# Reduce the height of the logs list section
+log_section_height_decrease = ["-"]
+log_section_height_increase = ["+"]
+# Toggle visibility of the log section
+log_section_toggle = ["\\"]
#################
# Custom Colors #
@@ -119,7 +127,7 @@ toggle_mouse_capture=["m"]
# Background color of the entire line
background = "magenta"
# Animated loading icon at the start of the bar
-loading_spinner="white"
+loading_spinner = "white"
# Text color
text = "black"
# Text color of a selected header
@@ -128,137 +136,137 @@ text_selected = "gray"
# The borders around the selectable panels - Containers, Commands, Logs
[colors.borders]
# Border when selected
-selected="lightcyan"
+selected = "lightcyan"
# Border when not selected
-unselected="grey"
+unselected = "grey"
# The containers sections, in the future more color customization options should be made available in this section
[colors.containers]
# The icon use to illustrate which container is currently selected - at the moment the TUI library, ratatui, doesn't seem allow changing the color of the highlight symbol
-icon="white"
+icon = "white"
# Background color of panel
background = "reset"
# At the moment, this will only change the color of the name, id, and image columns
-text="blue"
+text = "blue"
# Text color of the RX column
-text_rx="#FFE9C1"
+text_rx = "#FFE9C1"
# Text color of the TX column
-text_tx="#CD8C8C"
+text_tx = "#CD8C8C"
# The logs panel, will only be applied if color_logs is false
[colors.logs]
# Background color of panel
background = "reset"
# text color
-text="reset"
+text = "reset"
# Each state of a container has a color, which is used in multiple places, i.e. chart titles, state/status/cpu/memory columns in the container section
[colors.container_state]
-dead="red"
-exited="red"
+dead = "red"
+exited = "red"
paused = "yellow"
-removing ="lightred"
-restarting ="lightgreen"
-running_healthy ="green"
-running_unhealthy="#FFB224"
-unknown="red"
+removing = "lightred"
+restarting = "lightgreen"
+running_healthy = "green"
+running_unhealthy = "#FFB224"
+unknown = "red"
# The filter panel
[colors.filter]
# Background color of panel
background = "reset"
# color of text
-text="gray"
+text = "gray"
# background color of the selected filter by item (Name/Image/Status/All)
-selected_filter_background="gray"
+selected_filter_background = "gray"
# text color of the selected filter by item (Name/Image/Status/All)
-selected_filter_text="black"
+selected_filter_text = "black"
# Highlighted text color
-highlight="magenta"
+highlight = "magenta"
# The color the of Docker commands available for each container
[colors.commands]
# Background color of panel
background = "reset"
-pause="yellow"
-restart="magenta"
+pause = "yellow"
+restart = "magenta"
stop = "red"
-delete ="gray"
-resume ="blue"
-start ="green"
+delete = "gray"
+resume = "blue"
+start = "green"
# The cpu chart
[colors.chart_cpu]
# Background color of panel
background = "reset"
# Border color
-border="white"
+border = "white"
# Chart title - only whilst container is running, paused & stopped colors not yet customizable - or could just use state color?
-title="green"
+title = "green"
# Maximum CPU percentage - again paused & stopped colors not yet customizable
-max="#FFB224"
+max = "#FFB224"
# Points on the chart - again paused & stopped colors not yet customizable
-points="magenta"
+points = "magenta"
# The charts y-axis
-y_axis="white"
+y_axis = "white"
# The memory chart
[colors.chart_memory]
# Background color of panel
background = "reset"
# Border color
-border="white"
+border = "white"
# Chart title - only whilst container is running, paused & stopped will use colors.container_state
-title="green"
+title = "green"
# Maximum memory use - again paused & stopped will use colors.container_state
-max="#FFB224"
+max = "#FFB224"
# Points on the chart - again paused & stopped will use colors.container_state
-points="cyan"
+points = "cyan"
# The charts y-axis
-y_axis="white"
+y_axis = "white"
# The ports chart
[colors.chart_ports]
# Background color of panel
background = "reset"
# Border color
-border="white"
+border = "white"
# Chart title - only whilst container is running, paused & stopped will use colors.container_state
-title="green"
+title = "green"
# Private/Public/IP headings
-headings="yellow"
+headings = "yellow"
# Ports & IP listing text
-text="white"
+text = "white"
# The help popup
[colors.popup_help]
# Background color
-background="magenta"
+background = "magenta"
# Text color
-text="black"
+text = "black"
# Highlighted text color
-text_highlight="white"
+text_highlight = "white"
# The info popup - used to display small messages - such as saving logs to disk, or change of mouse capture settings
[colors.popup_info]
# Background color
-background="blue"
+background = "blue"
# Text color
-text="white"
+text = "white"
# The delete popup - used to display a confirmation warning when about to delete a container
[colors.popup_delete]
# Background color
-background="white"
+background = "white"
# Text color
-text="black"
+text = "black"
# Highlighted text color
-text_highlight="red"
+text_highlight = "red"
# The error popup - hopefully you'll never have to see this
[colors.popup_error]
# Background color
-background="red"
+background = "red"
# Text color
-text="white"
\ No newline at end of file
+text = "white"
diff --git a/src/config/keymap_parser.rs b/src/config/keymap_parser.rs
index 0de79ec..9847800 100644
--- a/src/config/keymap_parser.rs
+++ b/src/config/keymap_parser.rs
@@ -39,6 +39,9 @@ optional_config_struct!(
delete_confirm,
exec,
filter_mode,
+ log_section_height_increase,
+ log_section_height_decrease,
+ log_section_toggle,
quit,
save_logs,
scroll_down_many,
@@ -70,6 +73,9 @@ config_struct!(
delete_confirm,
exec,
filter_mode,
+ log_section_height_increase,
+ log_section_height_decrease,
+ log_section_toggle,
quit,
save_logs,
scroll_down_many,
@@ -98,10 +104,13 @@ impl Keymap {
pub const fn new() -> Self {
Self {
clear: (KeyCode::Char('c'), Some(KeyCode::Esc)),
- delete_deny: (KeyCode::Char('n'), None),
delete_confirm: (KeyCode::Char('y'), None),
+ delete_deny: (KeyCode::Char('n'), None),
exec: (KeyCode::Char('e'), None),
filter_mode: (KeyCode::Char('/'), Some(KeyCode::F(1))),
+ log_section_height_decrease: (KeyCode::Char('-'), None),
+ log_section_height_increase: (KeyCode::Char('='), None),
+ log_section_toggle: (KeyCode::Char('\\'), None),
quit: (KeyCode::Char('q'), None),
save_logs: (KeyCode::Char('s'), None),
scroll_down_many: (KeyCode::PageDown, None),
@@ -112,14 +121,14 @@ impl Keymap {
scroll_up_one: (KeyCode::Up, Some(KeyCode::Char('k'))),
select_next_panel: (KeyCode::Tab, None),
select_previous_panel: (KeyCode::BackTab, None),
- sort_by_name: (KeyCode::Char('1'), None),
- sort_by_state: (KeyCode::Char('2'), None),
- sort_by_status: (KeyCode::Char('3'), None),
sort_by_cpu: (KeyCode::Char('4'), None),
- sort_by_memory: (KeyCode::Char('5'), None),
sort_by_id: (KeyCode::Char('6'), None),
sort_by_image: (KeyCode::Char('7'), None),
+ sort_by_memory: (KeyCode::Char('5'), None),
+ sort_by_name: (KeyCode::Char('1'), None),
sort_by_rx: (KeyCode::Char('8'), None),
+ sort_by_state: (KeyCode::Char('2'), None),
+ sort_by_status: (KeyCode::Char('3'), None),
sort_by_tx: (KeyCode::Char('9'), None),
sort_reset: (KeyCode::Char('0'), None),
toggle_help: (KeyCode::Char('h'), None),
@@ -162,6 +171,22 @@ impl From> for Keymap {
update_keymap(ck.clear, &mut keymap.clear, &mut clash);
update_keymap(ck.delete_deny, &mut keymap.delete_deny, &mut clash);
update_keymap(ck.delete_confirm, &mut keymap.delete_confirm, &mut clash);
+ update_keymap(
+ ck.log_section_height_decrease,
+ &mut keymap.log_section_height_decrease,
+ &mut clash,
+ );
+ update_keymap(
+ ck.log_section_height_increase,
+ &mut keymap.log_section_height_increase,
+ &mut clash,
+ );
+ update_keymap(
+ ck.log_section_toggle,
+ &mut keymap.log_section_toggle,
+ &mut clash,
+ );
+
update_keymap(ck.exec, &mut keymap.exec, &mut clash);
update_keymap(ck.filter_mode, &mut keymap.filter_mode, &mut clash);
update_keymap(ck.quit, &mut keymap.quit, &mut clash);
@@ -339,6 +364,8 @@ mod tests {
delete_deny: Some(vec!["s".to_owned()]),
delete_confirm: None,
exec: None,
+ log_section_height_decrease: None,
+ log_section_height_increase: None,
filter_mode: None,
quit: None,
save_logs: None,
@@ -349,6 +376,7 @@ mod tests {
scroll_up_many: None,
scroll_up_one: None,
select_next_panel: None,
+ log_section_toggle: None,
select_previous_panel: None,
sort_by_name: None,
sort_by_state: None,
@@ -376,10 +404,13 @@ mod tests {
let input = ConfigKeymap {
clear: gen_v(("a", "b")),
- delete_deny: gen_v(("c", "d")),
delete_confirm: gen_v(("e", "f")),
+ delete_deny: gen_v(("c", "d")),
exec: gen_v(("g", "h")),
filter_mode: gen_v(("i", "j")),
+ log_section_height_decrease: gen_v(("-", "Z")),
+ log_section_height_increase: gen_v(("=", "X")),
+ log_section_toggle: gen_v(("Y", "W")),
quit: gen_v(("k", "l")),
save_logs: gen_v(("m", "n")),
scroll_down_many: gen_v(("o", "p")),
@@ -390,14 +421,14 @@ mod tests {
scroll_up_one: gen_v(("y", "z")),
select_next_panel: gen_v(("0", "1")),
select_previous_panel: gen_v(("2", "3")),
- sort_by_name: gen_v(("4", "5")),
- sort_by_state: gen_v(("6", "7")),
- sort_by_status: gen_v(("8", "9")),
sort_by_cpu: gen_v(("F1", "F12")),
- sort_by_memory: gen_v(("/", "\\")),
sort_by_id: gen_v(("[", "]")),
sort_by_image: gen_v(("A", "B")),
+ sort_by_memory: gen_v(("/", "\\")),
+ sort_by_name: gen_v(("4", "5")),
sort_by_rx: gen_v(("C", "D")),
+ sort_by_state: gen_v(("6", "7")),
+ sort_by_status: gen_v(("8", "9")),
sort_by_tx: gen_v(("insert", "TAB")),
sort_reset: gen_v(("up", "down")),
toggle_help: gen_v(("home", "end")),
@@ -410,6 +441,9 @@ mod tests {
clear: (KeyCode::Char('a'), Some(KeyCode::Char('b'))),
delete_deny: (KeyCode::Char('c'), Some(KeyCode::Char('d'))),
delete_confirm: (KeyCode::Char('e'), Some(KeyCode::Char('f'))),
+ log_section_height_decrease: (KeyCode::Char('-'), Some(KeyCode::Char('Z'))),
+ log_section_height_increase: (KeyCode::Char('='), Some(KeyCode::Char('X'))),
+ log_section_toggle: (KeyCode::Char('Y'), Some(KeyCode::Char('W'))),
exec: (KeyCode::Char('g'), Some(KeyCode::Char('h'))),
filter_mode: (KeyCode::Char('i'), Some(KeyCode::Char('j'))),
quit: (KeyCode::Char('k'), Some(KeyCode::Char('l'))),
diff --git a/src/config/mod.rs b/src/config/mod.rs
index 9a31823..24e6877 100644
--- a/src/config/mod.rs
+++ b/src/config/mod.rs
@@ -30,6 +30,7 @@ pub struct Config {
pub show_timestamp: bool,
pub timezone: Option,
pub timestamp_format: String,
+ pub show_logs: bool,
pub use_cli: bool,
}
@@ -51,6 +52,7 @@ impl From<&Args> for Config {
timezone: Self::parse_timezone(args.timezone.clone()),
timestamp_format: Self::parse_timestamp_format(None),
use_cli: args.use_cli,
+ show_logs: true,
}
}
}
@@ -73,6 +75,7 @@ impl From for Config {
timezone: Self::parse_timezone(config_file.timezone),
timestamp_format: Self::parse_timestamp_format(config_file.timestamp_format),
use_cli: config_file.use_cli.unwrap_or(false),
+ show_logs: config_file.show_logs.unwrap_or(true),
}
}
}
@@ -132,6 +135,12 @@ impl Config {
if config_from_cli.color_logs != default_args.color {
self.color_logs = config_from_cli.color_logs;
+ self.raw_logs = !self.color_logs;
+ }
+
+ if config_from_cli.raw_logs != default_args.raw {
+ self.raw_logs = config_from_cli.raw_logs;
+ self.color_logs = !self.raw_logs;
}
if config_from_cli.gui != default_args.gui {
diff --git a/src/config/parse_config_file.rs b/src/config/parse_config_file.rs
index b198f1b..ead111a 100644
--- a/src/config/parse_config_file.rs
+++ b/src/config/parse_config_file.rs
@@ -75,6 +75,7 @@ pub struct ConfigFile {
pub timestamp_format: Option,
pub timezone: Option,
pub use_cli: Option,
+ pub show_logs: Option,
}
impl ConfigFile {
diff --git a/src/docker_data/mod.rs b/src/docker_data/mod.rs
index 499a647..18025f9 100644
--- a/src/docker_data/mod.rs
+++ b/src/docker_data/mod.rs
@@ -1,9 +1,7 @@
use bollard::{
Docker,
- container::{
- ListContainersOptions, LogsOptions, MemoryStatsStats, RemoveContainerOptions,
- StartContainerOptions, Stats, StatsOptions,
- },
+ query_parameters::{ListContainersOptions, LogsOptions, RemoveContainerOptions, StatsOptions},
+ secret::ContainerStatsResponse,
service::ContainerSummary,
};
use futures_util::StreamExt;
@@ -75,31 +73,44 @@ pub struct DockerData {
impl DockerData {
/// Use docker stats to calculate current cpu usage
#[allow(clippy::cast_precision_loss)]
- fn calculate_usage(stats: &Stats) -> f64 {
+ fn calculate_usage(stats: &ContainerStatsResponse) -> f64 {
let mut cpu_percentage = 0.0;
- let cpu_delta = stats
- .cpu_stats
- .cpu_usage
- .total_usage
- .saturating_sub(stats.precpu_stats.cpu_usage.total_usage)
- as f64;
- if let (Some(cpu_stats_usage), Some(precpu_stats_usage)) = (
- stats.cpu_stats.system_cpu_usage,
- stats.precpu_stats.system_cpu_usage,
+ let total_usage = stats.precpu_stats.as_ref().map_or(0, |i| {
+ i.cpu_usage
+ .as_ref()
+ .map_or(0, |i| i.total_usage.unwrap_or_default())
+ });
+
+ let cpu_delta = stats.cpu_stats.as_ref().map_or(0, |i| {
+ i.cpu_usage.as_ref().map_or(0, |i| {
+ i.total_usage
+ .unwrap_or_default()
+ .saturating_sub(total_usage)
+ })
+ }) as f64;
+
+ if let (Some(Some(cpu_stats_usage)), Some(Some(precpu_stats_usage))) = (
+ stats.cpu_stats.as_ref().map(|i| i.system_cpu_usage),
+ stats.precpu_stats.as_ref().map(|i| i.system_cpu_usage),
) {
let system_delta = cpu_stats_usage.saturating_sub(precpu_stats_usage) as f64;
- let online_cpus = stats.cpu_stats.online_cpus.unwrap_or_else(|| {
- u64::try_from(
- stats
- .cpu_stats
- .cpu_usage
- .percpu_usage
- .as_ref()
- .map_or(0, std::vec::Vec::len),
- )
- .unwrap_or_default()
- }) as f64;
+ let online_cpus = f64::from(stats.cpu_stats.as_ref().map_or(0, |i| {
+ i.online_cpus.unwrap_or_else(|| {
+ u32::try_from(
+ stats
+ .cpu_stats
+ .clone()
+ .unwrap_or_default()
+ .cpu_usage
+ .unwrap_or_default()
+ .percpu_usage
+ .as_ref()
+ .map_or(0, std::vec::Vec::len),
+ )
+ .unwrap_or_default()
+ })
+ }));
if system_delta > 0.0 && cpu_delta > 0.0 {
cpu_percentage = (cpu_delta / system_delta) * online_cpus * 100.0;
}
@@ -107,6 +118,9 @@ impl DockerData {
cpu_percentage
}
+ /// Get a single docker stat in order to update mem and cpu usage
+ /// don't take &self, so that can tokio::spawn into it's own thread
+ /// remove if from spawns hashmap when complete
/// Get a single docker stat in order to update mem and cpu usage
/// don't take &self, so that can tokio::spawn into it's own thread
/// remove if from spawns hashmap when complete
@@ -128,20 +142,23 @@ impl DockerData {
)
.take(1);
+ // some err here
while let Some(Ok(stats)) = stream.next().await {
// Memory stats are only collected if the container is alive - is this the behaviour we want?
+
let (mem_stat, cpu_stats) = if state.is_alive() {
- let mem_cache = stats.memory_stats.stats.map_or(0, |i| match i {
- MemoryStatsStats::V1(x) => x.inactive_file,
- MemoryStatsStats::V2(x) => x.inactive_file,
+ let mem_cache = stats.memory_stats.as_ref().map_or(&0, |i| {
+ i.stats
+ .as_ref()
+ .map_or(&0, |i| i.get("inactive_file").unwrap_or(&0))
});
(
Some(
stats
.memory_stats
- .usage
- .unwrap_or_default()
- .saturating_sub(mem_cache),
+ .as_ref()
+ .map_or(0, |i| i.usage.unwrap_or_default())
+ .saturating_sub(*mem_cache),
),
Some(Self::calculate_usage(&stats)),
)
@@ -149,26 +166,22 @@ impl DockerData {
(None, None)
};
- let op_key = stats
- .networks
- .as_ref()
- .and_then(|networks| networks.keys().next().cloned());
-
- let (rx, tx) = if let Some(key) = op_key {
- stats
- .networks
- .unwrap_or_default()
- .get(&key)
- .map_or((0, 0), |f| (f.rx_bytes, f.tx_bytes))
- } else {
- (0, 0)
- };
+ let (rx, tx) = stats.networks.as_ref().map_or((0, 0), |i| {
+ (
+ i.rx_bytes.unwrap_or_default(),
+ i.tx_bytes.unwrap_or_default(),
+ )
+ });
app_data.lock().update_stats_by_id(
id,
cpu_stats,
mem_stat,
- stats.memory_stats.limit.unwrap_or_default(),
+ stats
+ .memory_stats
+ .unwrap_or_default()
+ .limit
+ .unwrap_or_default(),
rx,
tx,
);
@@ -203,7 +216,7 @@ impl DockerData {
async fn update_all_containers(&self) {
let containers = self
.docker
- .list_containers(Some(ListContainersOptions:: {
+ .list_containers(Some(ListContainersOptions {
all: true,
..Default::default()
}))
@@ -242,11 +255,11 @@ impl DockerData {
spawns: Arc>>>,
stderr: bool,
) {
- let options = Some(LogsOptions:: {
+ let options = Some(LogsOptions {
stdout: true,
stderr,
timestamps: true,
- since: i64::try_from(since).unwrap_or_default(),
+ since: i32::try_from(since).unwrap_or_default(),
..Default::default()
});
@@ -363,14 +376,31 @@ impl DockerData {
.await
}
DockerCommand::Pause => docker.pause_container(id.get()).await,
- DockerCommand::Restart => docker.restart_container(id.get(), None).await,
+ DockerCommand::Restart => {
+ docker
+ .restart_container(
+ id.get(),
+ None::,
+ )
+ .await
+ }
DockerCommand::Resume => docker.unpause_container(id.get()).await,
DockerCommand::Start => {
docker
- .start_container(id.get(), None::>)
+ .start_container(
+ id.get(),
+ None::,
+ )
+ .await
+ }
+ DockerCommand::Stop => {
+ docker
+ .stop_container(
+ id.get(),
+ None::,
+ )
.await
}
- DockerCommand::Stop => docker.stop_container(id.get(), None).await,
}
.is_err()
{
@@ -445,119 +475,73 @@ impl DockerData {
#[cfg(test)]
#[allow(clippy::float_cmp)]
mod tests {
- use bollard::container::{
- BlkioStats, CPUStats, CPUUsage, MemoryStats, PidsStats, Stats, StorageStats, ThrottlingData,
- };
+
+ use bollard::secret::{ContainerCpuStats, ContainerCpuUsage};
use super::*;
- fn gen_stats() -> Stats {
- Stats {
- read: String::new(),
- preread: String::new(),
- num_procs: 1,
- pids_stats: PidsStats {
- current: None,
- limit: None,
- },
- network: None,
+ fn gen_stats() -> ContainerStatsResponse {
+ ContainerStatsResponse {
+ read: None,
+ preread: None,
+ num_procs: Some(1),
+ pids_stats: None,
networks: None,
- memory_stats: MemoryStats {
- stats: None,
- max_usage: None,
- usage: None,
- failcnt: None,
- limit: None,
- commit: None,
- commit_peak: None,
- commitbytes: None,
- commitpeakbytes: None,
- privateworkingset: None,
- },
- blkio_stats: BlkioStats {
- io_service_bytes_recursive: None,
- io_serviced_recursive: None,
- io_queue_recursive: None,
- io_service_time_recursive: None,
- io_wait_time_recursive: None,
- io_merged_recursive: None,
- io_time_recursive: None,
- sectors_recursive: None,
- },
- cpu_stats: CPUStats {
- cpu_usage: CPUUsage {
+ memory_stats: None,
+ blkio_stats: None,
+ cpu_stats: Some(ContainerCpuStats {
+ cpu_usage: Some(ContainerCpuUsage {
percpu_usage: Some(vec![50]),
- usage_in_usermode: 10,
- total_usage: 100,
- usage_in_kernelmode: 20,
- },
+ usage_in_usermode: Some(10),
+ total_usage: Some(100),
+ usage_in_kernelmode: Some(20),
+ }),
system_cpu_usage: Some(400),
online_cpus: Some(1),
- throttling_data: ThrottlingData {
- periods: 0,
- throttled_periods: 0,
- throttled_time: 0,
- },
- },
- precpu_stats: CPUStats {
- cpu_usage: CPUUsage {
+ throttling_data: None,
+ }),
+ precpu_stats: Some(ContainerCpuStats {
+ cpu_usage: Some(ContainerCpuUsage {
percpu_usage: Some(vec![50]),
- usage_in_usermode: 10,
- total_usage: 100,
- usage_in_kernelmode: 20,
- },
+ usage_in_usermode: Some(10),
+ total_usage: Some(100),
+ usage_in_kernelmode: Some(20),
+ }),
system_cpu_usage: Some(400),
online_cpus: Some(1),
- throttling_data: ThrottlingData {
- periods: 0,
- throttled_periods: 0,
- throttled_time: 0,
- },
- },
- storage_stats: StorageStats {
- read_count_normalized: None,
- read_size_bytes: None,
- write_count_normalized: None,
- write_size_bytes: None,
- },
- name: String::new(),
- id: String::new(),
+ throttling_data: None,
+ }),
+ storage_stats: None,
+ name: None,
+ id: None,
}
}
#[test]
fn test_calculate_usage_50() {
let mut stats = gen_stats();
- stats.precpu_stats = CPUStats {
- cpu_usage: CPUUsage {
+ stats.precpu_stats = Some(ContainerCpuStats {
+ cpu_usage: Some(ContainerCpuUsage {
percpu_usage: Some(vec![50]),
- usage_in_usermode: 10,
- total_usage: 100,
- usage_in_kernelmode: 20,
- },
+ usage_in_usermode: Some(10),
+ total_usage: Some(100),
+ usage_in_kernelmode: Some(20),
+ }),
system_cpu_usage: Some(400),
online_cpus: Some(1),
- throttling_data: ThrottlingData {
- periods: 0,
- throttled_periods: 0,
- throttled_time: 0,
- },
- };
- stats.cpu_stats = CPUStats {
- cpu_usage: CPUUsage {
+ throttling_data: None,
+ });
+ stats.cpu_stats = Some(ContainerCpuStats {
+ cpu_usage: Some(ContainerCpuUsage {
percpu_usage: Some(vec![150]),
- usage_in_usermode: 20,
- total_usage: 150,
- usage_in_kernelmode: 30,
- },
+ usage_in_usermode: Some(20),
+ total_usage: Some(150),
+ usage_in_kernelmode: Some(30),
+ }),
system_cpu_usage: Some(500),
online_cpus: Some(1),
- throttling_data: ThrottlingData {
- periods: 0,
- throttled_periods: 0,
- throttled_time: 0,
- },
- };
+ throttling_data: None,
+ });
let cpu_percentage = DockerData::calculate_usage(&stats);
assert_eq!(50.0, cpu_percentage);
}
@@ -565,37 +549,28 @@ mod tests {
#[test]
fn test_calculate_usage_25() {
let mut stats = gen_stats();
- stats.precpu_stats = CPUStats {
- cpu_usage: CPUUsage {
+ stats.precpu_stats = Some(ContainerCpuStats {
+ cpu_usage: Some(ContainerCpuUsage {
percpu_usage: Some(vec![50]),
- usage_in_usermode: 10,
- total_usage: 100,
- usage_in_kernelmode: 20,
- },
+ usage_in_usermode: Some(10),
+ total_usage: Some(100),
+ usage_in_kernelmode: Some(20),
+ }),
system_cpu_usage: Some(400),
online_cpus: Some(1),
- throttling_data: ThrottlingData {
- periods: 0,
- throttled_periods: 0,
- throttled_time: 0,
- },
- };
- stats.cpu_stats = CPUStats {
- cpu_usage: CPUUsage {
+ throttling_data: None,
+ });
+ stats.cpu_stats = Some(ContainerCpuStats {
+ cpu_usage: Some(ContainerCpuUsage {
percpu_usage: Some(vec![75]),
- usage_in_usermode: 20,
- total_usage: 125,
- usage_in_kernelmode: 30,
- },
+ usage_in_usermode: Some(20),
+ total_usage: Some(125),
+ usage_in_kernelmode: Some(30),
+ }),
system_cpu_usage: Some(500),
online_cpus: Some(1),
- throttling_data: ThrottlingData {
- periods: 0,
- throttled_periods: 0,
- throttled_time: 0,
- },
- };
-
+ throttling_data: None,
+ });
let cpu_percentage = DockerData::calculate_usage(&stats);
assert_eq!(25.0, cpu_percentage);
}
@@ -603,38 +578,28 @@ mod tests {
#[test]
fn test_calculate_usage_75() {
let mut stats = gen_stats();
- stats.precpu_stats = CPUStats {
- cpu_usage: CPUUsage {
+ stats.precpu_stats = Some(ContainerCpuStats {
+ cpu_usage: Some(ContainerCpuUsage {
percpu_usage: Some(vec![50]),
- usage_in_usermode: 10,
- total_usage: 100,
- usage_in_kernelmode: 20,
- },
+ usage_in_usermode: Some(10),
+ total_usage: Some(100),
+ usage_in_kernelmode: Some(20),
+ }),
system_cpu_usage: Some(400),
online_cpus: Some(1),
- throttling_data: ThrottlingData {
- periods: 0,
- throttled_periods: 0,
- throttled_time: 0,
- },
- };
-
- stats.cpu_stats = CPUStats {
- cpu_usage: CPUUsage {
+ throttling_data: None,
+ });
+ stats.cpu_stats = Some(ContainerCpuStats {
+ cpu_usage: Some(ContainerCpuUsage {
percpu_usage: Some(vec![175]),
- usage_in_usermode: 20,
- total_usage: 175,
- usage_in_kernelmode: 30,
- },
+ usage_in_usermode: Some(20),
+ total_usage: Some(175),
+ usage_in_kernelmode: Some(30),
+ }),
system_cpu_usage: Some(500),
online_cpus: Some(1),
- throttling_data: ThrottlingData {
- periods: 0,
- throttled_periods: 0,
- throttled_time: 0,
- },
- };
-
+ throttling_data: None,
+ });
let cpu_percentage = DockerData::calculate_usage(&stats);
assert_eq!(75.0, cpu_percentage);
}
@@ -642,36 +607,28 @@ mod tests {
#[test]
fn test_calculate_usage_100() {
let mut stats = gen_stats();
- stats.precpu_stats = CPUStats {
- cpu_usage: CPUUsage {
+ stats.precpu_stats = Some(ContainerCpuStats {
+ cpu_usage: Some(ContainerCpuUsage {
percpu_usage: Some(vec![50]),
- usage_in_usermode: 10,
- total_usage: 100,
- usage_in_kernelmode: 20,
- },
+ usage_in_usermode: Some(10),
+ total_usage: Some(100),
+ usage_in_kernelmode: Some(20),
+ }),
system_cpu_usage: Some(400),
online_cpus: Some(1),
- throttling_data: ThrottlingData {
- periods: 0,
- throttled_periods: 0,
- throttled_time: 0,
- },
- };
- stats.cpu_stats = CPUStats {
- cpu_usage: CPUUsage {
+ throttling_data: None,
+ });
+ stats.cpu_stats = Some(ContainerCpuStats {
+ cpu_usage: Some(ContainerCpuUsage {
percpu_usage: Some(vec![200]),
- usage_in_usermode: 20,
- total_usage: 200,
- usage_in_kernelmode: 30,
- },
+ usage_in_usermode: Some(20),
+ total_usage: Some(200),
+ usage_in_kernelmode: Some(30),
+ }),
system_cpu_usage: Some(500),
online_cpus: Some(1),
- throttling_data: ThrottlingData {
- periods: 0,
- throttled_periods: 0,
- throttled_time: 0,
- },
- };
+ throttling_data: None,
+ });
let cpu_percentage = DockerData::calculate_usage(&stats);
assert_eq!(100.0, cpu_percentage);
}
@@ -679,38 +636,28 @@ mod tests {
#[test]
fn test_calculate_usage_175() {
let mut stats = gen_stats();
- stats.precpu_stats = CPUStats {
- cpu_usage: CPUUsage {
+ stats.precpu_stats = Some(ContainerCpuStats {
+ cpu_usage: Some(ContainerCpuUsage {
percpu_usage: Some(vec![50]),
- usage_in_usermode: 10,
- total_usage: 100,
- usage_in_kernelmode: 20,
- },
+ usage_in_usermode: Some(10),
+ total_usage: Some(100),
+ usage_in_kernelmode: Some(20),
+ }),
system_cpu_usage: Some(400),
online_cpus: Some(1),
- throttling_data: ThrottlingData {
- periods: 0,
- throttled_periods: 0,
- throttled_time: 0,
- },
- };
-
- stats.cpu_stats = CPUStats {
- cpu_usage: CPUUsage {
+ throttling_data: None,
+ });
+ stats.cpu_stats = Some(ContainerCpuStats {
+ cpu_usage: Some(ContainerCpuUsage {
percpu_usage: Some(vec![275]),
- usage_in_usermode: 20,
- total_usage: 275,
- usage_in_kernelmode: 30,
- },
+ usage_in_usermode: Some(20),
+ total_usage: Some(275),
+ usage_in_kernelmode: Some(30),
+ }),
system_cpu_usage: Some(500),
online_cpus: Some(1),
- throttling_data: ThrottlingData {
- periods: 0,
- throttled_periods: 0,
- throttled_time: 0,
- },
- };
-
+ throttling_data: None,
+ });
let cpu_percentage = DockerData::calculate_usage(&stats);
assert_eq!(175.0, cpu_percentage);
}
diff --git a/src/input_handler/mod.rs b/src/input_handler/mod.rs
index a52c8dd..4c17a9a 100644
--- a/src/input_handler/mod.rs
+++ b/src/input_handler/mod.rs
@@ -5,7 +5,8 @@ use std::{
time::SystemTime,
};
-use bollard::container::LogsOptions;
+use bollard::query_parameters::LogsOptions;
+// use bollard::container::LogsOptions;
use cansi::v3::categorise_text;
use crossterm::{
event::{DisableMouseCapture, KeyCode, KeyModifiers, MouseButton, MouseEvent, MouseEventKind},
@@ -187,7 +188,7 @@ impl InputHandler {
let path = log_path.join(format!("{name}_{now}.log"));
- let options = Some(LogsOptions:: {
+ let options = Some(LogsOptions {
stderr: true,
stdout: true,
timestamps: args.show_timestamp,
@@ -288,23 +289,15 @@ impl InputHandler {
/// Change the the "next" selectable panel
/// If no containers, and on Commands panel, skip to next panel, as Commands panel isn't visible in this state
fn next_panel_key(&self) {
- self.gui_state.lock().next_panel();
- if self.app_data.lock().get_container_len() == 0
- && self.gui_state.lock().get_selected_panel() == SelectablePanel::Commands
- {
- self.gui_state.lock().next_panel();
- }
+ self.gui_state.lock().selectable_panel_next(&self.app_data);
}
/// Change to previously selected panel
/// Need to skip the commands planel if there no are current containers running
fn previous_panel_key(&self) {
- self.gui_state.lock().previous_panel();
- if self.app_data.lock().get_container_len() == 0
- && self.gui_state.lock().get_selected_panel() == SelectablePanel::Commands
- {
- self.gui_state.lock().previous_panel();
- }
+ self.gui_state
+ .lock()
+ .selectable_panel_previous(&self.app_data);
}
fn scroll_start_key(&self) {
@@ -458,9 +451,25 @@ impl InputHandler {
}
}
+ // Increase the log panel height
+ fn log_panel_height_increase(&self) {
+ self.gui_state.lock().log_height_increase();
+ }
+
+ // Decrease the log panel height
+ fn log_panel_height_decrease(&self) {
+ self.gui_state.lock().log_height_decrease();
+ }
+
+ // Toggle visibility of the log panel
+ fn log_panel_toggle(&self) {
+ self.gui_state.lock().toggle_show_logs();
+ }
+
/// Handle button presses in all other scenarios
async fn handle_others(&mut self, key_code: KeyCode) {
self.handle_sort(key_code);
+ // shift key plus arrows
match key_code {
_ if self.keymap.exec.0 == key_code || self.keymap.exec.1 == Some(key_code) => {
self.exec_key().await;
@@ -477,6 +486,23 @@ impl InputHandler {
{
self.mouse_capture_key();
}
+ _ if self.keymap.log_section_height_decrease.0 == key_code
+ || self.keymap.log_section_height_decrease.1 == Some(key_code) =>
+ {
+ self.log_panel_height_decrease();
+ }
+
+ _ if self.keymap.log_section_height_increase.0 == key_code
+ || self.keymap.log_section_height_increase.1 == Some(key_code) =>
+ {
+ self.log_panel_height_increase();
+ }
+
+ _ if self.keymap.log_section_toggle.0 == key_code
+ || self.keymap.log_section_toggle.1 == Some(key_code) =>
+ {
+ self.log_panel_toggle();
+ }
_ if self.keymap.save_logs.0 == key_code
|| self.keymap.save_logs.1 == Some(key_code) =>
diff --git a/src/main.rs b/src/main.rs
index 6a0142d..64ed988 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -23,7 +23,7 @@ mod exec;
mod input_handler;
mod ui;
-use ui::{GuiState, Redraw, Status, Ui};
+use ui::{GuiState, Rerender, Status, Ui};
use crate::docker_data::DockerMessage;
@@ -98,10 +98,10 @@ fn handler_init(
async fn main() {
setup_tracing();
let config = config::Config::new();
- let redraw = Arc::new(Redraw::new());
+ let redraw = Arc::new(Rerender::new());
let app_data = Arc::new(Mutex::new(AppData::new(config.clone(), &redraw)));
- let gui_state = Arc::new(Mutex::new(GuiState::new(&redraw)));
+ let gui_state = Arc::new(Mutex::new(GuiState::new(&redraw, config.show_logs)));
let is_running = Arc::new(AtomicBool::new(true));
let (docker_tx, docker_rx) = tokio::sync::mpsc::channel(32);
@@ -149,7 +149,7 @@ async fn main() {
#[allow(clippy::unwrap_used)]
mod tests {
- use std::sync::Arc;
+ use std::{str::FromStr, sync::Arc};
use bollard::service::{ContainerSummary, Port};
@@ -159,7 +159,7 @@ mod tests {
RunningState, State, StatefulList,
},
config::{AppColors, Config, Keymap},
- ui::Redraw,
+ ui::Rerender,
};
/// Default test config, has timestamps turned off
@@ -179,6 +179,7 @@ mod tests {
timestamp_format: "HH:MM:SS.NNNNN dd-mm-yyyy".to_owned(),
show_timestamp: false,
use_cli: false,
+ show_logs: true,
timezone: None,
}
}
@@ -207,7 +208,7 @@ mod tests {
current_sorted_id: vec![],
error: None,
sorted_by: None,
- redraw: Arc::new(Redraw::new()),
+ redraw: Arc::new(Rerender::new()),
filter: Filter::new(),
config: gen_config(),
}
@@ -227,6 +228,7 @@ mod tests {
pub fn gen_container_summary(index: usize, state: &str) -> ContainerSummary {
ContainerSummary {
+ image_manifest_descriptor: None,
id: Some(format!("{index}")),
names: Some(vec![format!("container_{}", index)]),
image: Some(format!("image_{index}")),
@@ -242,7 +244,7 @@ mod tests {
size_rw: None,
size_root_fs: None,
labels: None,
- state: Some(state.to_owned()),
+ state: Some(bollard::secret::ContainerSummaryStateEnum::from_str(state).unwrap()),
status: Some(format!("Up {index} hour")),
host_config: None,
network_settings: None,
diff --git a/src/ui/draw_blocks/commands.rs b/src/ui/draw_blocks/commands.rs
index 146313d..805ae0f 100644
--- a/src/ui/draw_blocks/commands.rs
+++ b/src/ui/draw_blocks/commands.rs
@@ -241,7 +241,10 @@ mod tests {
}
// Control panel now selected, should have a blue border
- setup.gui_state.lock().next_panel();
+ setup
+ .gui_state
+ .lock()
+ .selectable_panel_next(&setup.app_data);
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
setup
.terminal
diff --git a/src/ui/draw_blocks/containers.rs b/src/ui/draw_blocks/containers.rs
index a5c7395..a63283c 100644
--- a/src/ui/draw_blocks/containers.rs
+++ b/src/ui/draw_blocks/containers.rs
@@ -157,7 +157,10 @@ mod tests {
let mut setup = test_setup(40, 6, true, true);
setup.app_data.lock().containers = StatefulList::new(vec![]);
- setup.gui_state.lock().next_panel();
+ setup
+ .gui_state
+ .lock()
+ .selectable_panel_next(&setup.app_data);
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
let colors = setup.app_data.lock().config.app_colors;
@@ -184,7 +187,10 @@ mod tests {
}
}
- setup.gui_state.lock().previous_panel();
+ setup
+ .gui_state
+ .lock()
+ .selectable_panel_previous(&setup.app_data);
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
setup
@@ -255,7 +261,10 @@ mod tests {
}
// Change selected panel, border is now no longer blue
- setup.gui_state.lock().next_panel();
+ setup
+ .gui_state
+ .lock()
+ .selectable_panel_next(&setup.app_data);
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
setup
.terminal
diff --git a/src/ui/draw_blocks/headers.rs b/src/ui/draw_blocks/headers.rs
index 792ccf4..0e24e22 100644
--- a/src/ui/draw_blocks/headers.rs
+++ b/src/ui/draw_blocks/headers.rs
@@ -154,10 +154,9 @@ fn draw_columns(
})
.collect::>();
- let container_splits = header_data.iter().map(|i| i.2).collect::>();
let headers_section = Layout::default()
.direction(Direction::Horizontal)
- .constraints(container_splits)
+ .constraints(header_data.iter().map(|i| i.2))
.split(split_bar[1]);
for (index, (paragraph, header, _)) in header_data.into_iter().enumerate() {
@@ -196,19 +195,18 @@ pub fn draw(
let column_width = usize::from(area.width).saturating_sub(help_width);
let column_width = if column_width > 0 { column_width } else { 1 };
- let splits = if fd.has_containers {
- vec![
- Constraint::Max(4),
- Constraint::Max(column_width.try_into().unwrap_or_default()),
- Constraint::Max(help_width.try_into().unwrap_or_default()),
- ]
- } else {
- CONSTRAINT_100.to_vec()
- };
let split_bar = Layout::default()
.direction(Direction::Horizontal)
- .constraints(splits)
+ .constraints(if fd.has_containers {
+ vec![
+ Constraint::Max(4),
+ Constraint::Max(column_width.try_into().unwrap_or_default()),
+ Constraint::Max(help_width.try_into().unwrap_or_default()),
+ ]
+ } else {
+ CONSTRAINT_100.to_vec()
+ })
.split(area);
draw_loading_spinner(colors, f, fd, split_bar[0]);
diff --git a/src/ui/draw_blocks/help.rs b/src/ui/draw_blocks/help.rs
index 35ede46..a28cbbe 100644
--- a/src/ui/draw_blocks/help.rs
+++ b/src/ui/draw_blocks/help.rs
@@ -85,6 +85,7 @@ impl HelpInfo {
}
/// Generate the button information span + metadata
+ #[allow(clippy::too_many_lines)]
fn gen_keymap_info(colors: AppColors, 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);
@@ -152,6 +153,16 @@ impl HelpInfo {
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"),
@@ -212,15 +223,6 @@ impl HelpInfo {
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);
-
- // if keymap != &Keymap::new() {
- // op.push(highlighted("customised keymap, "));
- // }
-
- // if colors != AppColors::new() {
- // op.push(highlighted("customised app colors, "));
- // };
-
let zone = zone.and_then(|i| i.iana_name()).unwrap_or("Etc/UTC");
Line::from(Vec::from([text("logs timezone: "), highlighted(zone)])).centered()
}
@@ -295,6 +297,15 @@ impl HelpInfo {
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"),
];
@@ -426,7 +437,7 @@ mod tests {
#[test]
/// This will cause issues once the version has more than the current 5 chars (0.5.0)
fn test_draw_blocks_help() {
- let mut setup = test_setup(87, 33, true, true);
+ let mut setup = test_setup(87, 35, true, true);
let tz = setup.app_data.lock().config.timezone.clone();
setup
@@ -448,29 +459,29 @@ mod tests {
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 | 32, _) | (0..=33, 0 | 86) => {
+ (0 | 34, _) | (0..=33, 0 | 86) => {
assert_eq!(result_cell.bg, Color::Reset);
assert_eq!(result_cell.fg, Color::Reset);
}
// border is black on magenta
- (1 | 31, _) | (1..=31, 1 | 85) => {
+ (1 | 32, _) | (1..=31, 1 | 85) => {
assert_eq!(result_cell.bg, Color::Magenta);
assert_eq!(result_cell.fg, Color::Black);
}
- // oxker logo && description
- (2..=10, 2..=85) | (12, 19..=66)
- // button in the brackets
+ // oxker logo && description
+ (2..=10, 2..=85)
+ | (12, 19..=66)
| (14, 2..=10 | 13..=27)
| (15, 2..=10 | 13..=21 | 24..=40 | 43..=56)
| (16 | 23, 2..=12)
- | (17..=20 | 22 | 25, 2..=8)
+ | (17..=20 | 22 | 25 | 27, 2..=8)
| (21, 2..=9 | 12..=18)
- | (24, 2..=10) => {
+ | (24 | 26, 2..=10) => {
assert_eq!(result_cell.bg, Color::Magenta);
assert_eq!(result_cell.fg, Color::White);
}
// The URL is white and underlined
- (28, 25..=60) => {
+ (30, 25..=60) => {
assert_eq!(result_cell.bg, Color::Magenta);
assert_eq!(result_cell.fg, Color::White);
assert_eq!(result_cell.modifier, Modifier::UNDERLINED);
@@ -488,7 +499,7 @@ mod tests {
#[test]
/// Test that the help panel gets drawn with custom colors
fn test_draw_blocks_help_custom_colors() {
- let mut setup = test_setup(87, 33, true, true);
+ let mut setup = test_setup(87, 35, true, true);
let mut colors = AppColors::new();
let tz = setup.app_data.lock().config.timezone.clone();
@@ -515,29 +526,29 @@ mod tests {
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 | 32, _) | (0..=33, 0 | 86) => {
+ (0 | 34, _) | (0..=33, 0 | 86) => {
assert_eq!(result_cell.bg, Color::Reset);
assert_eq!(result_cell.fg, Color::Reset);
}
- // border is black on magenta
- (1 | 31, _) | (1..=31, 1 | 85) => {
+ // border is red on black
+ (1 | 32, _) | (1..=31, 1 | 85) => {
assert_eq!(result_cell.bg, Color::Black);
assert_eq!(result_cell.fg, Color::Red);
}
- // oxker logo && description
- (2..=10, 2..=85) | (12, 19..=66)
- // button in the brackets
+ // oxker logo && description
+ (2..=10, 2..=85)
+ | (12, 19..=66)
| (14, 2..=10 | 13..=27)
| (15, 2..=10 | 13..=21 | 24..=40 | 43..=56)
| (16 | 23, 2..=12)
- | (17..=20 | 22 | 25, 2..=8)
+ | (17..=20 | 22 | 25 | 27, 2..=8)
| (21, 2..=9 | 12..=18)
- | (24, 2..=10) => {
+ | (24 | 26, 2..=10) => {
assert_eq!(result_cell.bg, Color::Black);
assert_eq!(result_cell.fg, Color::Yellow);
}
// The URL is yellow and underlined
- (28, 25..=60) => {
+ (30, 25..=60) => {
assert_eq!(result_cell.bg, Color::Black);
assert_eq!(result_cell.fg, Color::Yellow);
assert_eq!(result_cell.modifier, Modifier::UNDERLINED);
@@ -562,6 +573,9 @@ mod tests {
delete_deny: (KeyCode::Char('c'), None),
delete_confirm: (KeyCode::Char('e'), None),
exec: (KeyCode::Char('g'), None),
+ log_section_height_decrease: (KeyCode::Char('z'), None),
+ log_section_height_increase: (KeyCode::Char('x'), None),
+ log_section_toggle: (KeyCode::Char('W'), None),
filter_mode: (KeyCode::Char('i'), None),
quit: (KeyCode::Char('k'), None),
save_logs: (KeyCode::Char('m'), None),
@@ -607,6 +621,9 @@ mod tests {
delete_deny: (KeyCode::Char('c'), Some(KeyCode::Char('d'))),
delete_confirm: (KeyCode::Char('e'), Some(KeyCode::Char('f'))),
exec: (KeyCode::Char('g'), Some(KeyCode::Char('h'))),
+ log_section_height_decrease: (KeyCode::Char('A'), Some(KeyCode::Char('Z'))),
+ log_section_height_increase: (KeyCode::Char('B'), Some(KeyCode::Char('X'))),
+ log_section_toggle: (KeyCode::Char('C'), Some(KeyCode::Char('W'))),
filter_mode: (KeyCode::Char('i'), Some(KeyCode::Char('j'))),
quit: (KeyCode::Char('k'), Some(KeyCode::Char('l'))),
save_logs: (KeyCode::Char('m'), Some(KeyCode::Char('n'))),
@@ -653,6 +670,9 @@ mod tests {
delete_confirm: (KeyCode::Char('e'), Some(KeyCode::Char('f'))),
exec: (KeyCode::Char('g'), None),
filter_mode: (KeyCode::Char('i'), Some(KeyCode::Char('j'))),
+ log_section_height_decrease: (KeyCode::Char('A'), Some(KeyCode::Char('Z'))),
+ log_section_height_increase: (KeyCode::Char('B'), Some(KeyCode::Char('X'))),
+ log_section_toggle: (KeyCode::Char('C'), Some(KeyCode::Char('W'))),
quit: (KeyCode::Char('k'), None),
save_logs: (KeyCode::Char('m'), Some(KeyCode::Char('n'))),
scroll_down_many: (KeyCode::Char('o'), None),
@@ -691,7 +711,7 @@ mod tests {
#[test]
fn test_draw_blocks_help_show_timezone() {
- let mut setup = test_setup(87, 35, true, true);
+ let mut setup = test_setup(87, 37, true, true);
setup
.terminal
diff --git a/src/ui/draw_blocks/logs.rs b/src/ui/draw_blocks/logs.rs
index 835b01a..6381e5b 100644
--- a/src/ui/draw_blocks/logs.rs
+++ b/src/ui/draw_blocks/logs.rs
@@ -39,7 +39,8 @@ pub fn draw(
}
f.render_widget(paragraph, area);
} else {
- let logs = app_data.lock().get_logs();
+ let padding = usize::from(area.height / 5);
+ let logs = app_data.lock().get_logs(area.height, padding);
if logs.is_empty() {
let mut paragraph = Paragraph::new("no logs found")
.block(block)
@@ -52,6 +53,7 @@ pub fn draw(
let items = List::new(logs)
.block(block)
.highlight_symbol(RIGHT_ARROW)
+ .scroll_padding(padding)
.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() {
@@ -124,8 +126,14 @@ mod tests {
}
}
- setup.gui_state.lock().next_panel();
- setup.gui_state.lock().next_panel();
+ setup
+ .gui_state
+ .lock()
+ .selectable_panel_next(&setup.app_data);
+ setup
+ .gui_state
+ .lock()
+ .selectable_panel_next(&setup.app_data);
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
// When selected, has a blue border
diff --git a/src/ui/draw_blocks/mod.rs b/src/ui/draw_blocks/mod.rs
index f729c90..ec06c61 100644
--- a/src/ui/draw_blocks/mod.rs
+++ b/src/ui/draw_blocks/mod.rs
@@ -127,8 +127,9 @@ pub mod tests {
use crate::{
app_data::{AppData, ContainerId, ContainerImage, ContainerName, ContainerPorts},
+ app_error::AppError,
tests::{gen_appdata, gen_containers},
- ui::{GuiState, Redraw, draw_frame},
+ ui::{GuiState, Rerender, Status, draw_frame},
};
use super::FrameData;
@@ -152,32 +153,33 @@ pub mod tests {
fn from(data: (&Arc>, &Arc>)) -> Self {
let (app_data, gui_data) = (data.0.lock(), data.1.lock());
- // set max height for container section, needs +5 to deal with docker commands list and borders
- let height = app_data.get_container_len();
- let height = if height < 12 {
- u16::try_from(height + 5).unwrap_or_default()
- } else {
- 12
- };
+ // let container_section_height = app_data.get_container_len();
+ // let container_section_height = if container_section_height < 12 {
+ // u16::try_from(container_section_height + 5).unwrap_or_default()
+ // } else {
+ // 12
+ // };
let (filter_by, filter_term) = app_data.get_filter();
Self {
chart_data: app_data.get_chart_data(),
- columns: app_data.get_width(),
color_logs: app_data.config.color_logs,
+ columns: app_data.get_width(),
+ // container_section_height,
container_title: app_data.get_container_title(),
delete_confirm: gui_data.get_delete_container(),
filter_by,
filter_term: filter_term.cloned(),
has_containers: app_data.get_container_len() > 0,
has_error: app_data.get_error(),
- height,
- ports: app_data.get_selected_ports(),
- port_max_lens: app_data.get_longest_port(),
+ show_logs: gui_data.get_show_logs(),
info_text: gui_data.info_box_text.clone(),
is_loading: gui_data.is_loading(),
loading_icon: gui_data.get_loading().to_string(),
+ log_height: gui_data.get_log_height(),
log_title: app_data.get_log_title(),
+ port_max_lens: app_data.get_longest_port(),
+ ports: app_data.get_selected_ports(),
selected_panel: gui_data.get_selected_panel(),
sorted_by: app_data.get_sorted(),
status: gui_data.get_status(),
@@ -199,8 +201,8 @@ pub mod tests {
app_data.containers_start();
}
- let redraw = Arc::new(Redraw::new());
- let gui_state = GuiState::new(&redraw);
+ let redraw = Arc::new(Rerender::new());
+ let gui_state = GuiState::new(&redraw, app_data.config.show_logs);
let app_data = Arc::new(Mutex::new(app_data));
let gui_state = Arc::new(Mutex::new(gui_state));
@@ -360,4 +362,189 @@ pub mod tests {
assert_snapshot!(setup.terminal.backend());
}
+
+ #[test]
+ /// Check that the whole layout is drawn correctly when the logs panel is removed
+ fn test_draw_blocks_whole_layout_no_logs() {
+ let mut setup = test_setup(160, 30, true, true);
+
+ insert_chart_data(&setup);
+ insert_logs(&setup);
+ setup.app_data.lock().containers.items[0]
+ .ports
+ .push(ContainerPorts {
+ ip: Some(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))),
+ private: 8003,
+ public: Some(8003),
+ });
+ let colors = setup.app_data.lock().config.app_colors;
+ let keymap = setup.app_data.lock().config.keymap.clone();
+ setup.gui_state.lock().log_height_zero();
+
+ let fd = FrameData::from((&setup.app_data, &setup.gui_state));
+ setup
+ .terminal
+ .draw(|f| {
+ draw_frame(&setup.app_data, colors, &keymap, f, &fd, &setup.gui_state);
+ })
+ .unwrap();
+
+ assert_snapshot!(setup.terminal.backend());
+ }
+
+ #[test]
+ /// Check that the whole layout is drawn correctly when the logs panel height is ~4
+ fn test_draw_blocks_whole_layout_short_height_logs() {
+ let mut setup = test_setup(160, 30, true, true);
+
+ insert_chart_data(&setup);
+ insert_logs(&setup);
+ setup.app_data.lock().containers.items[0]
+ .ports
+ .push(ContainerPorts {
+ ip: Some(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))),
+ private: 8003,
+ public: Some(8003),
+ });
+ let colors = setup.app_data.lock().config.app_colors;
+ let keymap = setup.app_data.lock().config.keymap.clone();
+ setup.gui_state.lock().log_height_zero();
+
+ for _ in 0..=3 {
+ setup.gui_state.lock().log_height_increase();
+ }
+ let fd = FrameData::from((&setup.app_data, &setup.gui_state));
+ setup
+ .terminal
+ .draw(|f| {
+ draw_frame(&setup.app_data, colors, &keymap, f, &fd, &setup.gui_state);
+ })
+ .unwrap();
+
+ assert_snapshot!(setup.terminal.backend());
+ }
+
+ #[test]
+ /// Check that the whole layout is drawn with the help panel visible
+ fn test_draw_blocks_whole_layout_help_panel() {
+ let mut setup = test_setup(160, 40, true, true);
+
+ insert_chart_data(&setup);
+ insert_logs(&setup);
+ setup.app_data.lock().containers.items[0]
+ .ports
+ .push(ContainerPorts {
+ ip: Some(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))),
+ private: 8003,
+ public: Some(8003),
+ });
+ let colors = setup.app_data.lock().config.app_colors;
+ let keymap = setup.app_data.lock().config.keymap.clone();
+
+ setup.gui_state.lock().status_push(Status::Help);
+
+ let fd = FrameData::from((&setup.app_data, &setup.gui_state));
+ setup
+ .terminal
+ .draw(|f| {
+ draw_frame(&setup.app_data, colors, &keymap, f, &fd, &setup.gui_state);
+ })
+ .unwrap();
+
+ assert_snapshot!(setup.terminal.backend());
+ }
+
+ #[test]
+ /// Check that the whole layout is drawn with the error box is visible
+ fn test_draw_blocks_whole_layout_error() {
+ let mut setup = test_setup(160, 40, true, true);
+
+ insert_chart_data(&setup);
+ insert_logs(&setup);
+ setup.app_data.lock().containers.items[0]
+ .ports
+ .push(ContainerPorts {
+ ip: Some(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))),
+ private: 8003,
+ public: Some(8003),
+ });
+ let colors = setup.app_data.lock().config.app_colors;
+ let keymap = setup.app_data.lock().config.keymap.clone();
+
+ setup.app_data.lock().set_error(
+ AppError::DockerCommand(crate::app_data::DockerCommand::Pause),
+ &setup.gui_state,
+ Status::Error,
+ );
+
+ let fd = FrameData::from((&setup.app_data, &setup.gui_state));
+ setup
+ .terminal
+ .draw(|f| {
+ draw_frame(&setup.app_data, colors, &keymap, f, &fd, &setup.gui_state);
+ })
+ .unwrap();
+
+ assert_snapshot!(setup.terminal.backend());
+ }
+
+ #[test]
+ /// Check that the whole layout is drawn with the delete box is visible
+ fn test_draw_blocks_whole_layout_delete() {
+ let mut setup = test_setup(160, 40, true, true);
+
+ insert_chart_data(&setup);
+ insert_logs(&setup);
+ setup.app_data.lock().containers.items[0]
+ .ports
+ .push(ContainerPorts {
+ ip: Some(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))),
+ private: 8003,
+ public: Some(8003),
+ });
+ let colors = setup.app_data.lock().config.app_colors;
+ let keymap = setup.app_data.lock().config.keymap.clone();
+ setup
+ .gui_state
+ .lock()
+ .set_delete_container(setup.app_data.lock().get_selected_container_id());
+
+ let fd = FrameData::from((&setup.app_data, &setup.gui_state));
+ setup
+ .terminal
+ .draw(|f| {
+ draw_frame(&setup.app_data, colors, &keymap, f, &fd, &setup.gui_state);
+ })
+ .unwrap();
+
+ assert_snapshot!(setup.terminal.backend());
+ }
+
+ #[test]
+ /// Check that the whole layout is drawn with the info box is visible
+ fn test_draw_blocks_whole_layout_info_box() {
+ let mut setup = test_setup(160, 40, true, true);
+
+ insert_chart_data(&setup);
+ insert_logs(&setup);
+ setup.app_data.lock().containers.items[0]
+ .ports
+ .push(ContainerPorts {
+ ip: Some(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))),
+ private: 8003,
+ public: Some(8003),
+ });
+ let colors = setup.app_data.lock().config.app_colors;
+ let keymap = setup.app_data.lock().config.keymap.clone();
+ setup.gui_state.lock().set_info_box("This is a test");
+ let fd = FrameData::from((&setup.app_data, &setup.gui_state));
+ setup
+ .terminal
+ .draw(|f| {
+ draw_frame(&setup.app_data, colors, &keymap, f, &fd, &setup.gui_state);
+ })
+ .unwrap();
+
+ assert_snapshot!(setup.terminal.backend());
+ }
}
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 01c97c2..747eec0 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
@@ -1,5 +1,6 @@
---
source: src/ui/draw_blocks/help.rs
+assertion_line: 456
expression: setup.terminal.backend()
---
" "
@@ -26,6 +27,8 @@ expression: setup.terminal.backend()
" │ ( F1 ) or ( / ) enter filter 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 │ "
" │ │ "
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
index 01c97c2..a0f9ea1 100644
--- 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
@@ -26,6 +26,8 @@ expression: setup.terminal.backend()
" │ ( F1 ) or ( / ) enter filter 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 │ "
" │ │ "
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 384ea0a..8cebda0 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,7 +2,6 @@
source: src/ui/draw_blocks/help.rs
expression: setup.terminal.backend()
---
-" "
" ╭ 0.00.000 ──────────────────────────────────────────────────────────────────────────────────╮ "
" │ │ "
" │ 88 │ "
@@ -40,12 +39,13 @@ expression: setup.terminal.backend()
" │ ( , ) sort containers by image │ "
" │ ( . ) sort containers by rx │ "
" │ ( Insert ) sort containers by tx │ "
+" │ ( z ) decrease log section height │ "
+" │ ( x ) increase log section height │ "
+" │ ( W ) toggle log section visibility │ "
" │ ( a ) close dialog │ "
" │ ( 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_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
index 03504ba..818fc99 100644
--- 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
@@ -2,7 +2,6 @@
source: src/ui/draw_blocks/help.rs
expression: setup.terminal.backend()
---
-" "
" ╭ 0.00.000 ────────────────────────────────────────────────────────────────────────────────────────────────╮ "
" │ │ "
" │ 88 │ "
@@ -40,12 +39,13 @@ expression: setup.terminal.backend()
" │ ( , ) or ( \ ) sort containers by image │ "
" │ ( . ) or ( ] ) sort containers by rx │ "
" │ ( Insert ) or ( Back Tab ) sort containers by tx │ "
+" │ ( A ) or ( Z ) decrease log section height │ "
+" │ ( B ) or ( X ) increase log section height │ "
+" │ ( C ) or ( W ) toggle log section visibility │ "
" │ ( a ) or ( b ) close dialog │ "
" │ ( k ) or ( 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_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
index f6dfd9d..1778328 100644
--- 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
@@ -2,7 +2,6 @@
source: src/ui/draw_blocks/help.rs
expression: setup.terminal.backend()
---
-" "
" ╭ 0.00.000 ────────────────────────────────────────────────────────────────────────────────────────────────╮ "
" │ │ "
" │ 88 │ "
@@ -40,12 +39,13 @@ expression: setup.terminal.backend()
" │ ( , ) sort containers by image │ "
" │ ( . ) or ( ] ) sort containers by rx │ "
" │ ( Insert ) sort containers by tx │ "
+" │ ( A ) or ( Z ) decrease log section height │ "
+" │ ( B ) or ( X ) increase log section height │ "
+" │ ( C ) or ( W ) toggle log section visibility │ "
" │ ( a ) or ( b ) close dialog │ "
" │ ( 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_show_timezone.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_show_timezone.snap
index ea369da..291cc9f 100644
--- 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
@@ -28,6 +28,8 @@ expression: setup.terminal.backend()
" │ ( F1 ) or ( / ) enter filter 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 │ "
" │ │ "
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 f90c683..df648f6 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
@@ -8,8 +8,6 @@ expression: setup.terminal.backend()
"│ 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_1 - image_1 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮"
"│ line 1 │"
@@ -25,10 +23,12 @@ 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│"
+"│10.00%│ ••• ││100.00 kB│ •• ││ ip private public│"
+"│ │ •• • ││ │ •• • ││ 8001 │"
+"│ │ ••• • • ││ │ ••• • • ││127.0.0.1 8003 8003│"
+"│ │• •• ││ │• •• ││ │"
"│ │ ││ │ ││ │"
"╰───────────────────────────────────────────────────────────────╯╰───────────────────────────────────────────────────────────────╯╰────────────────────────────╯"
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
new file mode 100644
index 0000000..37af4fd
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_delete.snap
@@ -0,0 +1,44 @@
+---
+source: src/ui/draw_blocks/mod.rs
+expression: setup.terminal.backend()
+---
+" name state status cpu memory/limit id image ↓ rx ↑ tx ( h ) show help "
+"╭ Containers 1/3 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮╭──────────────╮"
+"│⚪ 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_1 - image_1 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮"
+"│ line 1 │"
+"│ line 2 │"
+"│▶ line 3 │"
+"│ │"
+"│ │"
+"│ │"
+"│ │"
+"│ ╭──────────────────────── Confirm Delete ────────────────────────╮ │"
+"│ │ │ │"
+"│ │ Are you sure you want to delete container: container_1 │ │"
+"│ │ │ │"
+"│ │ ╭─────────────────────╮ ╭─────────────────────╮ │ │"
+"│ │ │ ( n ) no │ │ ( y ) yes │ │ │"
+"│ │ ╰─────────────────────╯ ╰─────────────────────╯ │ │"
+"│ ╰────────────────────────────────────────────────────────────────╯ │"
+"│ │"
+"│ │"
+"│ │"
+"│ │"
+"│ │"
+"╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯"
+"╭───────────────────────── cpu 03.00% ──────────────────────────╮╭─────────────────────── memory 30.00 kB ───────────────────────╮╭────────── ports ───────────╮"
+"│10.00%│ •• ││100.00 kB│ •• ││ ip private public│"
+"│ │ • • ││ │ •• ││ 8001 │"
+"│ │ •• • ││ │ •• • ││127.0.0.1 8003 8003│"
+"│ │ • • ││ │ • • ││ │"
+"│ │ •• • • ││ │ •• • • ││ │"
+"│ │• •• ││ │• •• ││ │"
+"│ │• • ││ │• • ││ │"
+"│ │ ││ │ ││ │"
+"╰───────────────────────────────────────────────────────────────╯╰───────────────────────────────────────────────────────────────╯╰────────────────────────────╯"
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
new file mode 100644
index 0000000..49919e8
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_error.snap
@@ -0,0 +1,44 @@
+---
+source: src/ui/draw_blocks/mod.rs
+expression: setup.terminal.backend()
+---
+" name state status cpu memory/limit id image ↓ rx ↑ tx ( h ) show help "
+"╭ Containers 1/3 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮╭──────────────╮"
+"│⚪ 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_1 - image_1 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮"
+"│ line 1 │"
+"│ line 2 │"
+"│▶ line 3 │"
+"│ │"
+"│ │"
+"│ │"
+"│ ╭──────────── Error ─────────────╮ │"
+"│ │ │ │"
+"│ │ Unable to pause container │ │"
+"│ │ │ │"
+"│ │ ( c ) clear error │ │"
+"│ │ │ │"
+"│ │ ( q ) quit oxker │ │"
+"│ │ │ │"
+"│ │ │ │"
+"│ ╰────────────────────────────────╯ │"
+"│ │"
+"│ │"
+"│ │"
+"│ │"
+"╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯"
+"╭───────────────────────── cpu 03.00% ──────────────────────────╮╭─────────────────────── memory 30.00 kB ───────────────────────╮╭────────── ports ───────────╮"
+"│10.00%│ •• ││100.00 kB│ •• ││ ip private public│"
+"│ │ • • ││ │ •• ││ 8001 │"
+"│ │ •• • ││ │ •• • ││127.0.0.1 8003 8003│"
+"│ │ • • ││ │ • • ││ │"
+"│ │ •• • • ││ │ •• • • ││ │"
+"│ │• •• ││ │• •• ││ │"
+"│ │• • ││ │• • ││ │"
+"│ │ ││ │ ││ │"
+"╰───────────────────────────────────────────────────────────────╯╰───────────────────────────────────────────────────────────────╯╰────────────────────────────╯"
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
new file mode 100644
index 0000000..ed89388
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_help_panel.snap
@@ -0,0 +1,44 @@
+---
+source: src/ui/draw_blocks/mod.rs
+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 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 ho╭ 0.00.000 ──────────────────────────────────────────────────────────────────────────╮ ││ stop │"
+"│ │ │ ││ delete │"
+"│ │ 88 │ ││ │"
+"╰────────────────────────────────────│ 88 │────────────────────╯╰──────────────╯"
+"╭ Logs 3/3 - container_1 - image_1 ──│ 88 │────────────────────────────────────╮"
+"│ line 1 │ ,adPPYba, 8b, ,d8 88 ,d8 ,adPPYba, 8b,dPPYba, │ │"
+"│ line 2 │ a8" "8a `Y8, ,8P' 88 ,a8" a8P_____88 88P' "Y8 │ │"
+"│▶ line 3 │ 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 ( PgUp PgDown ) or ( Home End ) change selected line │ │"
+"│ │ ( enter ) send docker container command │ │"
+"│ │ ( e ) exec into a container │ │"
+"│ │ ( 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 │ │"
+"│ │ ( 0 ) stop sort │ │"
+"│ │ ( 1 - 9 ) sort by header - or click header │ │"
+"│ │ ( - = ) change log section height │ │"
+"│ │ ( \ ) toggle log section visibility │ │"
+"╰────────────────────────────────────│ ( esc ) close dialog │────────────────────────────────────╯"
+"╭───────────────────────── cpu 03.00%│ ( q ) quit at any time │──────╮╭────────── ports ───────────╮"
+"│10.00%│ •• │ │ ││ ip private public│"
+"│ │ • • │ currently an early work in progress, all and any input appreciated │ ││ 8001 │"
+"│ │ •• • │ https://github.com/mrjackwills/oxker │ ││127.0.0.1 8003 8003│"
+"│ │ • • │ │ ││ │"
+"│ │ •• • • ╰────────────────────────────────────────────────────────────────────────────────────╯ ││ │"
+"│ │• •• ││ │• •• ││ │"
+"│ │• • ││ │• • ││ │"
+"│ │ ││ │ ││ │"
+"╰───────────────────────────────────────────────────────────────╯╰───────────────────────────────────────────────────────────────╯╰────────────────────────────╯"
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
new file mode 100644
index 0000000..a9abbf8
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_info_box.snap
@@ -0,0 +1,44 @@
+---
+source: src/ui/draw_blocks/mod.rs
+expression: setup.terminal.backend()
+---
+" name state status cpu memory/limit id image ↓ rx ↑ tx ( h ) show help "
+"╭ Containers 1/3 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮╭──────────────╮"
+"│⚪ 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_1 - image_1 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮"
+"│ line 1 │"
+"│ 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│"
+"│ │ • • ││ │ • • ││ │"
+"│ │ •• • • ││ │ •• • • ││ │"
+"│ │• •• ││ │• •• ││ "
+"│ │• • ││ │• • ││ 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 27fe0a0..e0a34fb 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
@@ -8,8 +8,6 @@ expression: setup.terminal.backend()
"│ 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 - a_long_container_name_for_the_purposes_of_this_test - a_long_image_name_for_the_purposes_of_this_test ──────────────────────────────────────────────────────────────────────────╮"
"│ line 1 │"
@@ -25,10 +23,12 @@ 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│"
+"│10.00%│ ••• ││100.00 kB│ •••• ││ ip private public│"
+"│ │ ••• • ││ │ •• • ││ 8001 │"
+"│ │ ••• • • ││ │ ••• • • ││127.0.0.1 8003 8003│"
+"│ │•• ••• ││ │•• •• ││ │"
"│ │ ││ │ ││ │"
"╰──────────────────────────────────────────────────────────────────────────────╯╰──────────────────────────────────────────────────────────────────────────────╯╰────────────────────────────╯"
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
new file mode 100644
index 0000000..fd83747
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_no_logs.snap
@@ -0,0 +1,34 @@
+---
+source: src/ui/draw_blocks/mod.rs
+expression: setup.terminal.backend()
+---
+" name state status cpu memory/limit id image ↓ rx ↑ tx ( h ) show help "
+"╭ Containers 1/3 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮╭──────────────╮"
+"│⚪ 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 │"
+"│ ││ │"
+"│ ││ │"
+"│ ││ │"
+"│ ││ │"
+"│ ││ │"
+"│ ││ │"
+"│ ││ │"
+"│ ││ │"
+"│ ││ │"
+"│ ││ │"
+"│ ││ │"
+"│ ││ │"
+"│ ││ │"
+"│ ││ │"
+"│ ││ │"
+"│ ││ │"
+"╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯╰──────────────╯"
+"╭───────────────────────── cpu 03.00% ──────────────────────────╮╭─────────────────────── memory 30.00 kB ───────────────────────╮╭────────── ports ───────────╮"
+"│10.00%│ ••• ││100.00 kB│ •• ││ ip private public│"
+"│ │ •• • ││ │ •• • ││ 8001 │"
+"│ │ ••• • • ││ │ ••• • • ││127.0.0.1 8003 8003│"
+"│ │• •• ││ │• •• ││ │"
+"│ │ ││ │ ││ │"
+"╰───────────────────────────────────────────────────────────────╯╰───────────────────────────────────────────────────────────────╯╰────────────────────────────╯"
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
new file mode 100644
index 0000000..046a12e
--- /dev/null
+++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_short_height_logs.snap
@@ -0,0 +1,34 @@
+---
+source: src/ui/draw_blocks/mod.rs
+expression: setup.terminal.backend()
+---
+" name state status cpu memory/limit id image ↓ rx ↑ tx ( h ) show help "
+"╭ Containers 1/3 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮╭──────────────╮"
+"│⚪ 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_1 - image_1 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮"
+"│ 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│"
+"│ │• •• ││ │• •• ││ │"
+"│ │ ││ │ ││ │"
+"╰───────────────────────────────────────────────────────────────╯╰───────────────────────────────────────────────────────────────╯╰────────────────────────────╯"
diff --git a/src/ui/gui_state.rs b/src/ui/gui_state.rs
index 0c9d57a..e16e720 100644
--- a/src/ui/gui_state.rs
+++ b/src/ui/gui_state.rs
@@ -9,11 +9,11 @@ use tokio::task::JoinHandle;
use uuid::Uuid;
use crate::{
- app_data::{ContainerId, Header},
+ app_data::{AppData, ContainerId, Header},
exec::ExecMode,
};
-use super::Redraw;
+use super::Rerender;
#[derive(Debug, Default, Clone, Copy, Eq, Hash, PartialEq)]
pub enum SelectablePanel {
@@ -175,7 +175,7 @@ pub enum Status {
/// Global gui_state, stored in an Arc
#[derive(Debug)]
pub struct GuiState {
- delete_container: Option,
+ delete_container_id: Option,
exec_mode: Option,
intersect_delete: HashMap,
intersect_heading: HashMap,
@@ -184,15 +184,17 @@ pub struct GuiState {
loading_handle: Option>,
loading_index: u8,
loading_set: HashSet,
- redraw: Arc,
+ log_height: u16,
+ rerender: Arc,
selected_panel: SelectablePanel,
+ show_logs: bool,
status: HashSet,
pub info_box_text: Option<(String, Instant)>,
}
impl GuiState {
- pub fn new(redraw: &Arc) -> Self {
+ pub fn new(redraw: &Arc, show_logs: bool) -> Self {
Self {
- delete_container: None,
+ delete_container_id: None,
exec_mode: None,
info_box_text: None,
intersect_delete: HashMap::new(),
@@ -202,11 +204,57 @@ impl GuiState {
loading_handle: None,
loading_index: 0,
loading_set: HashSet::new(),
- redraw: Arc::clone(redraw),
+ log_height: 75,
+ rerender: Arc::clone(redraw),
selected_panel: SelectablePanel::default(),
+ show_logs,
status: HashSet::new(),
}
}
+ /// Increase the height of the log panel, then rerender
+ pub fn log_height_increase(&mut self) {
+ if self.show_logs && self.log_height <= 75 {
+ self.log_height = self.log_height.saturating_add(5);
+ self.rerender.update();
+ }
+ }
+
+ /// Reduce the height of the logs panel, then rerender
+ /// Unselect logs panel if currently selected
+ pub fn log_height_decrease(&mut self) {
+ if self.show_logs {
+ self.log_height = self.log_height.saturating_sub(5);
+ if self.log_height == 0 && self.selected_panel == SelectablePanel::Logs {
+ self.show_logs = false;
+ self.selected_panel = SelectablePanel::Containers;
+ }
+ self.rerender.update();
+ }
+ }
+
+ pub const fn get_show_logs(&self) -> bool {
+ self.show_logs
+ }
+
+ pub fn toggle_show_logs(&mut self) {
+ self.show_logs = !self.show_logs;
+ if !self.show_logs && self.selected_panel == SelectablePanel::Logs {
+ self.selected_panel = SelectablePanel::Containers;
+ }
+ self.rerender.update();
+ }
+
+ /// Set the log_height to zero, for now only used by tests
+ #[cfg(test)]
+ pub const fn log_height_zero(&mut self) {
+ self.log_height = 0;
+ }
+
+ /// Get the log height, *should* be a u8 between 0 and 80, essentially a percentage
+ pub const fn get_log_height(&self) -> u16 {
+ self.log_height
+ }
+
/// Clear panels hash map, so on resize can fix the sizes for mouse clicks
pub fn clear_area_map(&mut self) {
self.intersect_panel.clear();
@@ -227,7 +275,7 @@ impl GuiState {
.first()
{
self.selected_panel = *data.0;
- self.redraw.set_true();
+ self.rerender.update();
}
}
@@ -287,7 +335,7 @@ impl GuiState {
/// Check if an ContainerId is set in the delete_container field
pub fn get_delete_container(&self) -> Option {
- self.delete_container.clone()
+ self.delete_container_id.clone()
}
/// Set either a ContainerId, or None, to the delete_container field
@@ -299,8 +347,8 @@ impl GuiState {
self.intersect_delete.clear();
self.status_del(Status::DeleteConfirm);
}
- self.delete_container = id;
- self.redraw.set_true();
+ self.delete_container_id = id;
+ self.rerender.update();
}
/// Return a copy of the Status HashSet
@@ -321,7 +369,7 @@ impl GuiState {
}
_ => (),
}
- self.redraw.set_true();
+ self.rerender.update();
}
/// Inset the ExecMode into self, and set the Status as exec
@@ -330,7 +378,7 @@ impl GuiState {
pub fn set_exec_mode(&mut self, mode: ExecMode) {
self.exec_mode = Some(mode);
self.status.insert(Status::Exec);
- self.redraw.set_true();
+ self.rerender.update();
}
pub fn get_exec_mode(&self) -> Option {
@@ -342,20 +390,32 @@ impl GuiState {
pub fn status_push(&mut self, status: Status) {
if status != Status::Exec {
self.status.insert(status);
- self.redraw.set_true();
+ self.rerender.update();
}
}
/// Change to next selectable panel
- pub fn next_panel(&mut self) {
+ pub fn selectable_panel_next(&mut self, app_data: &Arc>) {
self.selected_panel = self.selected_panel.next();
- self.redraw.set_true();
+ if (app_data.lock().get_container_len() == 0
+ && self.get_selected_panel() == SelectablePanel::Commands)
+ || (self.log_height == 0 && self.get_selected_panel() == SelectablePanel::Logs)
+ {
+ self.selected_panel = self.selected_panel.next();
+ }
+ self.rerender.update();
}
/// Change to previous selectable panel
- pub fn previous_panel(&mut self) {
+ pub fn selectable_panel_previous(&mut self, app_data: &Arc>) {
self.selected_panel = self.selected_panel.prev();
- self.redraw.set_true();
+ if (app_data.lock().get_container_len() == 0
+ && self.get_selected_panel() == SelectablePanel::Commands)
+ || (self.log_height == 0 && self.get_selected_panel() == SelectablePanel::Logs)
+ {
+ self.selected_panel = self.selected_panel.prev();
+ }
+ self.rerender.update();
}
/// Insert a new loading_uuid into HashSet, and advance the loading_index by one frame, or reset to 0 if at end of array
@@ -366,7 +426,7 @@ impl GuiState {
self.loading_index += 1;
}
self.loading_set.insert(uuid);
- self.redraw.set_true();
+ self.rerender.update();
}
pub fn is_loading(&self) -> bool {
@@ -399,7 +459,7 @@ impl GuiState {
/// Stop the loading_spin function, and reset gui loading status
pub fn stop_loading_animation(&mut self, loading_uuid: Uuid) {
self.loading_set.remove(&loading_uuid);
- self.redraw.set_true();
+ self.rerender.update();
if self.loading_set.is_empty() {
self.loading_index = 0;
if let Some(h) = &self.loading_handle {
@@ -412,12 +472,12 @@ impl GuiState {
/// Set info box content
pub fn set_info_box(&mut self, text: &str) {
self.info_box_text = Some((text.to_owned(), std::time::Instant::now()));
- self.redraw.set_true();
+ self.rerender.update();
}
/// Remove info box content
pub fn reset_info_box(&mut self) {
self.info_box_text = None;
- self.redraw.set_true();
+ self.rerender.update();
}
}
diff --git a/src/ui/mod.rs b/src/ui/mod.rs
index cc64199..de55e09 100644
--- a/src/ui/mod.rs
+++ b/src/ui/mod.rs
@@ -24,7 +24,7 @@ mod color_match;
mod draw_blocks;
mod gui_state;
mod redraw;
-pub use redraw::Redraw;
+pub use redraw::Rerender;
pub use self::color_match::*;
pub use self::gui_state::{DeleteButton, GuiState, SelectablePanel, Status};
@@ -50,7 +50,7 @@ pub struct Ui {
input_tx: Sender,
is_running: Arc,
now: Instant,
- redraw: Arc,
+ redraw: Arc,
terminal: Terminal>,
}
@@ -73,7 +73,7 @@ impl Ui {
gui_state: Arc>,
input_tx: Sender,
is_running: Arc,
- redraw: Arc,
+ redraw: Arc,
) {
match Self::setup_terminal() {
Ok(mut terminal) => {
@@ -245,6 +245,8 @@ impl Ui {
}
} else if let Event::Resize(_, _) = event {
self.gui_state.lock().clear_area_map();
+ // self.gui_state.lock().set_window_height(row);
+
self.terminal.autoresize().ok();
}
}
@@ -267,6 +269,7 @@ impl Ui {
/// Frequent data required by multiple frame drawing functions, can reduce mutex reads by placing it all in here
#[derive(Debug, Clone)]
+#[allow(clippy::struct_excessive_bools)]
pub struct FrameData {
chart_data: Option<(CpuTuple, MemTuple)>,
color_logs: bool,
@@ -276,8 +279,10 @@ pub struct FrameData {
filter_by: FilterBy,
filter_term: Option,
has_containers: bool,
+ // container_section_height: u16,
+ log_height: u16,
+ show_logs: bool,
has_error: Option,
- height: u16,
info_text: Option<(String, Instant)>,
is_loading: bool,
loading_icon: String,
@@ -293,14 +298,6 @@ impl From<&Ui> for FrameData {
fn from(ui: &Ui) -> Self {
let (app_data, gui_data) = (ui.app_data.lock(), ui.gui_state.lock());
- // set max height for container section, needs +5 to deal with docker commands list and borders
- let height = app_data.get_container_len();
- let height = if height < 12 {
- u16::try_from(height + 5).unwrap_or_default()
- } else {
- 12
- };
-
let (filter_by, filter_term) = app_data.get_filter();
Self {
chart_data: app_data.get_chart_data(),
@@ -312,10 +309,11 @@ impl From<&Ui> for FrameData {
filter_term: filter_term.cloned(),
has_containers: app_data.get_container_len() > 0,
has_error: app_data.get_error(),
- height,
info_text: gui_data.info_box_text.clone(),
is_loading: gui_data.is_loading(),
+ show_logs: gui_data.get_show_logs(),
loading_icon: gui_data.get_loading().to_string(),
+ log_height: gui_data.get_log_height(),
log_title: app_data.get_log_title(),
port_max_lens: app_data.get_longest_port(),
ports: app_data.get_selected_ports(),
@@ -335,57 +333,63 @@ fn draw_frame(
fd: &FrameData,
gui_state: &Arc>,
) {
- let whole_constraints = if fd.status.contains(&Status::Filter) {
- vec![Constraint::Max(1), Constraint::Min(1), Constraint::Max(1)]
- } else {
- vec![Constraint::Max(1), Constraint::Min(1)]
- };
-
let whole_layout = Layout::default()
.direction(Direction::Vertical)
- .constraints(whole_constraints)
+ .constraints(if fd.status.contains(&Status::Filter) {
+ vec![Constraint::Max(1), Constraint::Min(1), Constraint::Max(1)]
+ } else {
+ vec![Constraint::Max(1), Constraint::Min(1)]
+ })
.split(f.area());
- // Split into 3, containers+controls, logs, then graphs
- let upper_main = Layout::default()
- .direction(Direction::Vertical)
- .constraints([Constraint::Max(fd.height), Constraint::Min(1)].as_ref())
- .split(whole_layout[1]);
-
- let top_split = if fd.has_containers {
- vec![Constraint::Percentage(90), Constraint::Percentage(10)]
- } else {
- vec![Constraint::Percentage(100)]
- };
- // Containers + docker commands
- let top_panel = Layout::default()
- .direction(Direction::Horizontal)
- .constraints(top_split)
- .split(upper_main[0]);
-
- let lower_split = if fd.has_containers {
- vec![Constraint::Percentage(70), Constraint::Percentage(30)]
- } else {
- vec![Constraint::Percentage(100)]
- };
-
- // Split into 2, logs and charts
- let lower_main = Layout::default()
- .direction(Direction::Vertical)
- .constraints(lower_split)
- .split(upper_main[1]);
-
- draw_blocks::containers::draw(app_data, top_panel[0], colors, f, fd, gui_state);
-
- draw_blocks::logs::draw(app_data, lower_main[0], colors, f, fd, gui_state);
-
draw_blocks::headers::draw(whole_layout[0], colors, f, fd, gui_state, keymap);
- // Draw filter bar
+ // If required, draw filter bar
if let Some(rect) = whole_layout.get(2) {
draw_blocks::filter::draw(*rect, colors, f, fd);
}
+ 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]);
+
+ // 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(
|| {
@@ -400,7 +404,7 @@ fn draw_frame(
}
// only draw commands + charts if there are containers
- if let Some(rect) = top_panel.get(1) {
+ 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)
@@ -411,7 +415,7 @@ fn draw_frame(
let lower = Layout::default()
.direction(Direction::Horizontal)
.constraints([Constraint::Min(1), Constraint::Max(ports_len)])
- .split(lower_main[1]);
+ .split(upper_main[1]);
draw_blocks::charts::draw(lower[0], colors, f, fd);
draw_blocks::ports::draw(lower[1], colors, f, fd);
diff --git a/src/ui/redraw.rs b/src/ui/redraw.rs
index 32e21a1..4fd40d2 100644
--- a/src/ui/redraw.rs
+++ b/src/ui/redraw.rs
@@ -1,14 +1,14 @@
use std::sync::atomic::{AtomicBool, Ordering};
#[derive(Debug)]
-pub struct Redraw(AtomicBool);
+pub struct Rerender(AtomicBool);
-impl Redraw {
+impl Rerender {
pub const fn new() -> Self {
Self(AtomicBool::new(true))
}
- pub fn set_true(&self) {
+ pub fn update(&self) {
self.0.store(true, Ordering::SeqCst);
}