Radish alpha
r
Radicle terminal user interface
Radicle
Git (anonymous pull)
Log in to clone via SSH
lib: Introduce new flux app implementation
Erik Kundt committed 2 years ago
commit ce675b03b5e7395525582dd244866c1767fb3ec7
parent 78e18113112aac2b267a86aad4a315b25a9acebc
8 files changed +736 -5
modified Cargo.lock
@@ -3,6 +3,15 @@
version = 3

[[package]]
+
name = "addr2line"
+
version = "0.21.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
+
dependencies = [
+
 "gimli",
+
]
+

+
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -44,6 +53,18 @@ dependencies = [
]

[[package]]
+
name = "ahash"
+
version = "0.8.8"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "42cd52102d3df161c77a887b608d7a4897d7cc112886a9537b738a887a03aaff"
+
dependencies = [
+
 "cfg-if",
+
 "once_cell",
+
 "version_check",
+
 "zerocopy",
+
]
+

+
[[package]]
name = "aho-corasick"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -53,6 +74,12 @@ dependencies = [
]

[[package]]
+
name = "allocator-api2"
+
version = "0.2.16"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
+

+
[[package]]
name = "amplify"
version = "4.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -139,6 +166,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"

[[package]]
+
name = "backtrace"
+
version = "0.3.69"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
+
dependencies = [
+
 "addr2line",
+
 "cc",
+
 "cfg-if",
+
 "libc",
+
 "miniz_oxide",
+
 "object",
+
 "rustc-demangle",
+
]
+

+
[[package]]
name = "base-x"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -238,12 +280,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"

[[package]]
+
name = "bytes"
+
version = "1.5.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
+

+
[[package]]
name = "cassowary"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"

[[package]]
+
name = "castaway"
+
version = "0.2.2"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "8a17ed5635fc8536268e5d4de1e22e81ac34419e5f052d4d51f4e01dcc263fcc"
+
dependencies = [
+
 "rustversion",
+
]
+

+
[[package]]
name = "cbc"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -289,7 +346,7 @@ dependencies = [
 "iana-time-zone",
 "js-sys",
 "num-traits",
-
 "time",
+
 "time 0.1.45",
 "wasm-bindgen",
 "winapi",
]
@@ -305,6 +362,19 @@ dependencies = [
]

[[package]]
+
name = "compact_str"
+
version = "0.7.1"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f"
+
dependencies = [
+
 "castaway",
+
 "cfg-if",
+
 "itoa",
+
 "ryu",
+
 "static_assertions",
+
]
+

+
[[package]]
name = "const-oid"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -354,6 +424,31 @@ dependencies = [
]

[[package]]
+
name = "crossterm"
+
version = "0.27.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df"
+
dependencies = [
+
 "bitflags 2.4.1",
+
 "crossterm_winapi",
+
 "libc",
+
 "mio",
+
 "parking_lot",
+
 "signal-hook",
+
 "signal-hook-mio",
+
 "winapi",
+
]
+

+
[[package]]
+
name = "crossterm_winapi"
+
version = "0.9.1"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b"
+
dependencies = [
+
 "winapi",
+
]
+

+
[[package]]
name = "crypto-bigint"
version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -460,6 +555,15 @@ dependencies = [
]

[[package]]
+
name = "deranged"
+
version = "0.3.11"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
+
dependencies = [
+
 "powerfmt",
+
]
+

+
[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -611,6 +715,12 @@ dependencies = [
]

[[package]]
+
name = "futures-core"
+
version = "0.3.30"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
+

+
[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -643,6 +753,12 @@ dependencies = [
]

[[package]]
+
name = "gimli"
+
version = "0.28.1"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
+

+
[[package]]
name = "git-ref-format"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -703,6 +819,10 @@ name = "hashbrown"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
+
dependencies = [
+
 "ahash",
+
 "allocator-api2",
+
]

[[package]]
name = "heck"
@@ -711,6 +831,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"

[[package]]
+
name = "hermit-abi"
+
version = "0.3.5"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "d0c62115964e08cb8039170eb33c1d0e2388a256930279edca206fff675f82c3"
+

+
[[package]]
name = "hmac"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -814,6 +940,15 @@ dependencies = [
]

[[package]]
+
name = "itertools"
+
version = "0.12.1"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
+
dependencies = [
+
 "either",
+
]
+

+
[[package]]
name = "itoa"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -947,12 +1082,31 @@ dependencies = [
]

[[package]]
+
name = "lock_api"
+
version = "0.4.11"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
+
dependencies = [
+
 "autocfg",
+
 "scopeguard",
+
]
+

+
[[package]]
name = "log"
version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"

[[package]]
+
name = "lru"
+
version = "0.12.2"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "db2c024b41519440580066ba82aab04092b333e09066a5eb86c7c4890df31f22"
+
dependencies = [
+
 "hashbrown",
+
]
+

+
[[package]]
name = "memchr"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -968,6 +1122,18 @@ dependencies = [
]

[[package]]
+
name = "mio"
+
version = "0.8.10"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09"
+
dependencies = [
+
 "libc",
+
 "log",
+
 "wasi 0.11.0+wasi-snapshot-preview1",
+
 "windows-sys 0.48.0",
+
]
+

+
[[package]]
name = "multibase"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1020,6 +1186,12 @@ dependencies = [
]

[[package]]
+
name = "num-conv"
+
version = "0.1.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
+

+
[[package]]
name = "num-integer"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1051,12 +1223,40 @@ dependencies = [
]

[[package]]
+
name = "num_cpus"
+
version = "1.16.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
+
dependencies = [
+
 "hermit-abi",
+
 "libc",
+
]
+

+
[[package]]
+
name = "num_threads"
+
version = "0.1.6"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44"
+
dependencies = [
+
 "libc",
+
]
+

+
[[package]]
name = "numtoa"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"

[[package]]
+
name = "object"
+
version = "0.32.2"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
+
dependencies = [
+
 "memchr",
+
]
+

+
[[package]]
name = "once_cell"
version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1107,6 +1307,29 @@ dependencies = [
]

[[package]]
+
name = "parking_lot"
+
version = "0.12.1"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
+
dependencies = [
+
 "lock_api",
+
 "parking_lot_core",
+
]
+

+
[[package]]
+
name = "parking_lot_core"
+
version = "0.9.9"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
+
dependencies = [
+
 "cfg-if",
+
 "libc",
+
 "redox_syscall 0.4.1",
+
 "smallvec",
+
 "windows-targets 0.48.1",
+
]
+

+
[[package]]
name = "paste"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1155,6 +1378,12 @@ dependencies = [
]

[[package]]
+
name = "pin-project-lite"
+
version = "0.2.13"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
+

+
[[package]]
name = "pkcs1"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1205,6 +1434,12 @@ dependencies = [
]

[[package]]
+
name = "powerfmt"
+
version = "0.2.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
+

+
[[package]]
name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1412,6 +1647,7 @@ dependencies = [
 "radicle",
 "radicle-surf",
 "radicle-term",
+
 "ratatui 0.26.1",
 "serde",
 "serde_json",
 "simple-logging",
@@ -1419,6 +1655,8 @@ dependencies = [
 "textwrap",
 "thiserror",
 "timeago",
+
 "tokio",
+
 "tokio-stream",
 "tui-realm-stdlib",
 "tuirealm",
]
@@ -1461,15 +1699,37 @@ dependencies = [
 "bitflags 2.4.1",
 "cassowary",
 "indoc",
-
 "itertools",
+
 "itertools 0.11.0",
 "paste",
-
 "strum",
+
 "strum 0.25.0",
 "termion 2.0.1",
 "unicode-segmentation",
 "unicode-width",
]

[[package]]
+
name = "ratatui"
+
version = "0.26.1"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "bcb12f8fbf6c62614b0d56eb352af54f6a22410c3b079eb53ee93c7b97dd31d8"
+
dependencies = [
+
 "bitflags 2.4.1",
+
 "cassowary",
+
 "compact_str",
+
 "crossterm",
+
 "indoc",
+
 "itertools 0.12.1",
+
 "lru",
+
 "paste",
+
 "stability",
+
 "strum 0.26.1",
+
 "termion 3.0.0",
+
 "time 0.3.34",
+
 "unicode-segmentation",
+
 "unicode-width",
+
]
+

+
[[package]]
name = "redox_syscall"
version = "0.1.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1569,6 +1829,12 @@ dependencies = [
]

[[package]]
+
name = "rustc-demangle"
+
version = "0.1.23"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
+

+
[[package]]
name = "rustix"
version = "0.38.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1594,6 +1860,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"

[[package]]
+
name = "scopeguard"
+
version = "1.2.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+

+
[[package]]
name = "sec1"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1661,6 +1933,36 @@ dependencies = [
]

[[package]]
+
name = "signal-hook"
+
version = "0.3.17"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801"
+
dependencies = [
+
 "libc",
+
 "signal-hook-registry",
+
]
+

+
[[package]]
+
name = "signal-hook-mio"
+
version = "0.2.3"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af"
+
dependencies = [
+
 "libc",
+
 "mio",
+
 "signal-hook",
+
]
+

+
[[package]]
+
name = "signal-hook-registry"
+
version = "1.4.1"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
+
dependencies = [
+
 "libc",
+
]
+

+
[[package]]
name = "signature"
version = "1.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1712,6 +2014,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f67ad224767faa3c7d8b6d91985b78e70a1324408abcb1cfcc2be4c06bc06043"

[[package]]
+
name = "socket2"
+
version = "0.5.5"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9"
+
dependencies = [
+
 "libc",
+
 "windows-sys 0.48.0",
+
]
+

+
[[package]]
name = "socks5-client"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1817,12 +2129,37 @@ dependencies = [
]

[[package]]
+
name = "stability"
+
version = "0.1.1"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "ebd1b177894da2a2d9120208c3386066af06a488255caabc5de8ddca22dbc3ce"
+
dependencies = [
+
 "quote",
+
 "syn 1.0.109",
+
]
+

+
[[package]]
+
name = "static_assertions"
+
version = "1.1.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+

+
[[package]]
name = "strum"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125"
dependencies = [
-
 "strum_macros",
+
 "strum_macros 0.25.3",
+
]
+

+
[[package]]
+
name = "strum"
+
version = "0.26.1"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "723b93e8addf9aa965ebe2d11da6d7540fa2283fcea14b3371ff055f7ba13f5f"
+
dependencies = [
+
 "strum_macros 0.26.1",
]

[[package]]
@@ -1839,6 +2176,19 @@ dependencies = [
]

[[package]]
+
name = "strum_macros"
+
version = "0.26.1"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "7a3417fc93d76740d974a01654a09777cb500428cc874ca9f45edfe0c4d4cd18"
+
dependencies = [
+
 "heck",
+
 "proc-macro2",
+
 "quote",
+
 "rustversion",
+
 "syn 2.0.48",
+
]
+

+
[[package]]
name = "subtle"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1980,6 +2330,27 @@ dependencies = [
]

[[package]]
+
name = "time"
+
version = "0.3.34"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749"
+
dependencies = [
+
 "deranged",
+
 "libc",
+
 "num-conv",
+
 "num_threads",
+
 "powerfmt",
+
 "serde",
+
 "time-core",
+
]
+

+
[[package]]
+
name = "time-core"
+
version = "0.1.2"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
+

+
[[package]]
name = "timeago"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2005,6 +2376,47 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"

[[package]]
+
name = "tokio"
+
version = "1.36.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931"
+
dependencies = [
+
 "backtrace",
+
 "bytes",
+
 "libc",
+
 "mio",
+
 "num_cpus",
+
 "parking_lot",
+
 "pin-project-lite",
+
 "signal-hook-registry",
+
 "socket2",
+
 "tokio-macros",
+
 "windows-sys 0.48.0",
+
]
+

+
[[package]]
+
name = "tokio-macros"
+
version = "2.2.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
+
dependencies = [
+
 "proc-macro2",
+
 "quote",
+
 "syn 2.0.48",
+
]
+

+
[[package]]
+
name = "tokio-stream"
+
version = "0.1.14"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842"
+
dependencies = [
+
 "futures-core",
+
 "pin-project-lite",
+
 "tokio",
+
]
+

+
[[package]]
name = "tui"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2036,7 +2448,7 @@ checksum = "412447298ad477c25ff50c4a894ff5077b6ee3e25b913d42db30021d81b1af53"
dependencies = [
 "bitflags 2.4.1",
 "lazy-regex",
-
 "ratatui",
+
 "ratatui 0.23.0",
 "termion 2.0.1",
 "thiserror",
 "tui",
@@ -2380,6 +2792,26 @@ dependencies = [
]

[[package]]
+
name = "zerocopy"
+
version = "0.7.32"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be"
+
dependencies = [
+
 "zerocopy-derive",
+
]
+

+
[[package]]
+
name = "zerocopy-derive"
+
version = "0.7.32"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
+
dependencies = [
+
 "proc-macro2",
+
 "quote",
+
 "syn 2.0.48",
+
]
+

+
[[package]]
name = "zeroize"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
modified Cargo.toml
@@ -18,6 +18,7 @@ log = { version = "0.4.19" }
radicle = { git = "https://github.com/radicle-dev/heartwood" }
radicle-term = { git = "https://github.com/radicle-dev/heartwood", package = "radicle-term" }
radicle-surf = { version = "0.18.0" }
+
ratatui = { version = "0.26.0", features = ["all-widgets", "termion"] }
simple-logging = { version = "2.0.2" }
serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0" }
@@ -25,5 +26,7 @@ timeago = { version = "0.4.1" }
termion = { version = "3" }
textwrap = { version = "0.16.0" }
thiserror = { version = "1" }
+
tokio = { version = "1.32.0", features = ["full"] }
+
tokio-stream = { version = "0.1.14" }
tuirealm = { version = "^1.9.0", default-features = false, features = ["termion", "ratatui", "derive"] }
tui-realm-stdlib = { version = "1.3.1", default-features = false, features = ["termion", "ratatui"] }
added src/flux.rs
@@ -0,0 +1,3 @@
+
pub mod store;
+
pub mod termination;
+
pub mod ui;
added src/flux/store.rs
@@ -0,0 +1,83 @@
+
use std::marker::PhantomData;
+
use std::time::Duration;
+

+
use tokio::sync::broadcast;
+
use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender};
+

+
use super::termination::{Interrupted, Terminator};
+

+
pub trait State<A> {
+
    fn tick(&self);
+

+
    fn handle_action(self, action: A) -> anyhow::Result<bool>;
+
}
+

+
pub struct Store<A, S>
+
where
+
    S: State<A> + Clone + Send + Sync,
+
{
+
    state_tx: UnboundedSender<S>,
+
    _phantom: PhantomData<A>,
+
}
+

+
impl<A, S> Store<A, S>
+
where
+
    S: State<A> + Clone + Send + Sync,
+
{
+
    pub fn new() -> (Self, UnboundedReceiver<S>) {
+
        let (state_tx, state_rx) = mpsc::unbounded_channel::<S>();
+

+
        (
+
            Store {
+
                state_tx,
+
                _phantom: PhantomData,
+
            },
+
            state_rx,
+
        )
+
    }
+
}
+

+
impl<A, S> Store<A, S>
+
where
+
    S: State<A> + Clone + Send + Sync + 'static,
+
{
+
    pub async fn main_loop(
+
        self,
+
        state: S,
+
        mut terminator: Terminator,
+
        mut action_rx: UnboundedReceiver<A>,
+
        mut interrupt_rx: broadcast::Receiver<Interrupted>,
+
    ) -> anyhow::Result<Interrupted> {
+
        // the initial state once
+
        self.state_tx.send(state.clone())?;
+

+
        let mut ticker = tokio::time::interval(Duration::from_secs(1));
+

+
        let result = loop {
+
            tokio::select! {
+
                // Handle the actions coming from the UI
+
                // and process them to do async operations
+
                Some(action) = action_rx.recv() => {
+
                    let exit = state.clone().handle_action(action)?;
+
                    if exit {
+
                        let _ = terminator.terminate(Interrupted::UserInt);
+

+
                        break Interrupted::UserInt;
+
                    }
+
                },
+
                // Tick to terminate the select every N milliseconds
+
                _ = ticker.tick() => {
+
                    state.tick();
+
                },
+
                // Catch and handle interrupt signal to gracefully shutdown
+
                Ok(interrupted) = interrupt_rx.recv() => {
+
                    break interrupted;
+
                }
+
            }
+

+
            self.state_tx.send(state.clone())?;
+
        };
+

+
        Ok(result)
+
    }
+
}
added src/flux/termination.rs
@@ -0,0 +1,49 @@
+
#[cfg(unix)]
+
use tokio::signal::unix::signal;
+
use tokio::sync::broadcast;
+

+
#[derive(Debug, Clone)]
+
pub enum Interrupted {
+
    OsSigInt,
+
    UserInt,
+
}
+

+
#[derive(Debug, Clone)]
+
pub struct Terminator {
+
    interrupt_tx: broadcast::Sender<Interrupted>,
+
}
+

+
impl Terminator {
+
    pub fn new(interrupt_tx: broadcast::Sender<Interrupted>) -> Self {
+
        Self { interrupt_tx }
+
    }
+

+
    pub fn terminate(&mut self, interrupted: Interrupted) -> anyhow::Result<()> {
+
        self.interrupt_tx.send(interrupted)?;
+

+
        Ok(())
+
    }
+
}
+

+
#[cfg(unix)]
+
async fn terminate_by_unix_signal(mut terminator: Terminator) {
+
    let mut interrupt_signal = signal(tokio::signal::unix::SignalKind::interrupt())
+
        .expect("failed to create interrupt signal stream");
+

+
    interrupt_signal.recv().await;
+

+
    terminator
+
        .terminate(Interrupted::OsSigInt)
+
        .expect("failed to send interrupt signal");
+
}
+

+
// create a broadcast channel for retrieving the application kill signal
+
pub fn create_termination() -> (Terminator, broadcast::Receiver<Interrupted>) {
+
    let (tx, rx) = broadcast::channel(1);
+
    let terminator = Terminator::new(tx);
+

+
    #[cfg(unix)]
+
    tokio::spawn(terminate_by_unix_signal(terminator.clone()));
+

+
    (terminator, rx)
+
}
added src/flux/ui.rs
@@ -0,0 +1,126 @@
+
pub mod layout;
+

+
use std::io::{self};
+
use std::thread;
+
use std::time::Duration;
+

+
use termion::event::{Event, Key};
+
use termion::input::TermRead;
+
use termion::raw::{IntoRawMode, RawTerminal};
+

+
use ratatui::prelude::*;
+

+
use tokio::sync::broadcast;
+
use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender};
+

+
use super::termination::Interrupted;
+

+
use super::store::State;
+

+
type Backend = TermionBackend<RawTerminal<io::Stdout>>;
+

+
const RENDERING_TICK_RATE: Duration = Duration::from_millis(250);
+

+
pub trait Widget<S, A> {
+
    fn new(state: &S, action_tx: UnboundedSender<A>) -> Self
+
    where
+
        Self: Sized;
+

+
    fn move_with_state(self, state: &S) -> Self
+
    where
+
        Self: Sized;
+

+
    fn name(&self) -> &str;
+

+
    fn handle_key_event(&mut self, key: Key);
+
}
+

+
pub trait Render<P> {
+
    fn render<B: ratatui::backend::Backend>(&self, frame: &mut Frame, props: P);
+
}
+

+
pub struct Frontend<A> {
+
    action_tx: mpsc::UnboundedSender<A>,
+
}
+

+
impl<A> Frontend<A> {
+
    pub fn new() -> (Self, UnboundedReceiver<A>) {
+
        let (action_tx, action_rx) = mpsc::unbounded_channel();
+

+
        (Self { action_tx }, action_rx)
+
    }
+

+
    pub async fn main_loop<S: State<A>, W: Widget<S, A> + Render<()>>(
+
        self,
+
        mut state_rx: UnboundedReceiver<S>,
+
        mut interrupt_rx: broadcast::Receiver<Interrupted>,
+
    ) -> anyhow::Result<Interrupted> {
+
        let mut terminal = setup_terminal()?;
+
        let mut ticker = tokio::time::interval(RENDERING_TICK_RATE);
+
        let mut events_rx = events();
+

+
        let mut root = {
+
            let state = state_rx.recv().await.unwrap();
+

+
            W::new(&state, self.action_tx.clone())
+
        };
+

+
        let result: anyhow::Result<Interrupted> = loop {
+
            tokio::select! {
+
                // Tick to terminate the select every N milliseconds
+
                _ = ticker.tick() => (),
+
                Some(event) = events_rx.recv() => match event {
+
                    Event::Key(key) => {
+
                        root.handle_key_event(key)
+
                    }
+
                    _ => (),
+
                },
+
                // Handle state updates
+
                Some(state) = state_rx.recv() => {
+
                    root = root.move_with_state(&state);
+
                },
+
                // Catch and handle interrupt signal to gracefully shutdown
+
                Ok(interrupted) = interrupt_rx.recv() => {
+
                    break Ok(interrupted);
+
                }
+
            }
+

+
            terminal.draw(|frame| root.render::<Backend>(frame, ()))?;
+
        };
+

+
        restore_terminal(&mut terminal)?;
+

+
        result
+
    }
+
}
+

+
fn setup_terminal() -> anyhow::Result<Terminal<Backend>> {
+
    let stdout = io::stdout().into_raw_mode()?;
+
    let options = TerminalOptions {
+
        viewport: Viewport::Inline(10),
+
    };
+

+
    Ok(Terminal::with_options(
+
        TermionBackend::new(stdout),
+
        options,
+
    )?)
+
}
+

+
fn restore_terminal(terminal: &mut Terminal<Backend>) -> anyhow::Result<()> {
+
    Ok(terminal.show_cursor()?)
+
}
+

+
fn events() -> mpsc::UnboundedReceiver<Event> {
+
    let (tx, rx) = mpsc::unbounded_channel();
+
    let keys_tx = tx.clone();
+
    thread::spawn(move || {
+
        let stdin = io::stdin();
+
        for key in stdin.keys().flatten() {
+
            if let Err(err) = keys_tx.send(Event::Key(key)) {
+
                eprintln!("{err}");
+
                return;
+
            }
+
        }
+
    });
+
    rx
+
}
added src/flux/ui/layout.rs
@@ -0,0 +1,33 @@
+
use ratatui::layout::{Constraint, Direction, Layout, Rect};
+

+
pub struct DefaultPage {
+
    pub component: Rect,
+
    pub context: Rect,
+
    pub shortcuts: Rect,
+
}
+

+
pub fn default_page(area: Rect, context_h: u16, shortcuts_h: u16) -> DefaultPage {
+
    let margin_h = 1u16;
+
    let component_h = area
+
        .height
+
        .saturating_sub(context_h.saturating_add(shortcuts_h));
+

+
    let layout = Layout::default()
+
        .direction(Direction::Vertical)
+
        .horizontal_margin(margin_h)
+
        .constraints(
+
            [
+
                Constraint::Length(component_h),
+
                Constraint::Length(context_h),
+
                Constraint::Length(shortcuts_h),
+
            ]
+
            .as_ref(),
+
        )
+
        .split(area);
+

+
    DefaultPage {
+
        component: layout[0],
+
        context: layout[1],
+
        shortcuts: layout[2],
+
    }
+
}
modified src/lib.rs
@@ -18,6 +18,8 @@ pub mod context;
pub mod log;
pub mod ui;

+
pub mod flux;
+

use context::Context;
use ui::theme::Theme;