diff --git a/Cargo.lock b/Cargo.lock index 45123faa..bb5dc00c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -193,6 +193,26 @@ version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" +[[package]] +name = "arboard" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6041616acea41d67c4a984709ddab1587fd0b10efe5cc563fee954d2f011854" +dependencies = [ + "clipboard-win", + "core-graphics", + "image", + "log", + "objc", + "objc-foundation", + "objc_id", + "once_cell", + "parking_lot 0.12.1", + "thiserror", + "winapi 0.3.9", + "x11rb", +] + [[package]] name = "argon2" version = "0.5.0" @@ -727,6 +747,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + [[package]] name = "block-buffer" version = "0.9.0" @@ -827,6 +853,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "bytemuck" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" + [[package]] name = "byteorder" version = "1.4.3" @@ -1081,6 +1113,17 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" +[[package]] +name = "clipboard-win" +version = "4.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7191c27c2357d9b7ef96baac1773290d4ca63b24205b82a3fd8a0637afcf0362" +dependencies = [ + "error-code", + "str-buf", + "winapi 0.3.9", +] + [[package]] name = "cmake" version = "0.1.50" @@ -1103,6 +1146,12 @@ dependencies = [ "owo-colors", ] +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + [[package]] name = "combine" version = "4.6.6" @@ -1241,6 +1290,30 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +[[package]] +name = "core-graphics" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" +dependencies = [ + "bitflags 1.3.2", + "core-foundation 0.9.3", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bb142d41022986c1d8ff29103a1411c8a3dfad3552f87a4f8dc50d61d4f4e33" +dependencies = [ + "bitflags 1.3.2", + "core-foundation 0.9.3", + "libc", +] + [[package]] name = "cpufeatures" version = "0.2.8" @@ -1413,8 +1486,6 @@ dependencies = [ "lazy_static", "libc", "log", - "maplit", - "ncurses", "signal-hook", "tokio 1.28.2", "unicode-segmentation", @@ -1826,6 +1897,16 @@ dependencies = [ "libc", ] +[[package]] +name = "error-code" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64f18991e7bf11e7ffee451b5318b5c1a73c52d0d0ada6e5a3017c8c1ced6a21" +dependencies = [ + "libc", + "str-buf", +] + [[package]] name = "ethbloom" version = "0.13.0" @@ -1912,6 +1993,15 @@ dependencies = [ "instant", ] +[[package]] +name = "fdeflate" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d329bdeac514ee06249dabc27877490f17f5d371ec693360768b838e19f3ae10" +dependencies = [ + "simd-adler32", +] + [[package]] name = "ffi-support" version = "0.4.4" @@ -1987,6 +2077,21 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.2.0" @@ -2176,6 +2281,16 @@ dependencies = [ "version_check 0.9.4", ] +[[package]] +name = "gethostname" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1ebd34e35c46e00bb73e81363248d627782724609fe1b6396f553f68fe3862e" +dependencies = [ + "libc", + "winapi 0.3.9", +] + [[package]] name = "getrandom" version = "0.1.16" @@ -2593,6 +2708,21 @@ dependencies = [ "xmltree", ] +[[package]] +name = "image" +version = "0.24.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527909aa81e20ac3a44803521443a765550f09b5130c2c2fa1ea59c2f8f50a3a" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "num-rational", + "num-traits", + "png", + "tiff", +] + [[package]] name = "impl-codec" version = "0.6.0" @@ -2778,6 +2908,12 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" +[[package]] +name = "jpeg-decoder" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e" + [[package]] name = "js-sys" version = "0.3.64" @@ -3029,6 +3165,15 @@ dependencies = [ "linked-hash-map", ] +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + [[package]] name = "maplit" version = "1.0.2" @@ -3129,6 +3274,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" dependencies = [ "adler", + "simd-adler32", ] [[package]] @@ -3232,17 +3378,6 @@ dependencies = [ "socket2 0.4.9", ] -[[package]] -name = "ncurses" -version = "5.101.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e2c5d34d72657dc4b638a1c25d40aae81e4f1c699062f72f467237920752032" -dependencies = [ - "cc", - "libc", - "pkg-config", -] - [[package]] name = "ndk" version = "0.7.0" @@ -3402,6 +3537,18 @@ dependencies = [ "memoffset 0.6.5", ] +[[package]] +name = "nix" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" +dependencies = [ + "bitflags 1.3.2", + "cfg-if 1.0.0", + "libc", + "memoffset 0.6.5", +] + [[package]] name = "nix" version = "0.26.2" @@ -3590,6 +3737,35 @@ dependencies = [ "libc", ] +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", +] + +[[package]] +name = "objc-foundation" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" +dependencies = [ + "block", + "objc", + "objc_id", +] + +[[package]] +name = "objc_id" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" +dependencies = [ + "objc", +] + [[package]] name = "object" version = "0.30.4" @@ -4044,6 +4220,19 @@ dependencies = [ "plotters-backend", ] +[[package]] +name = "png" +version = "0.17.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59871cc5b6cce7eaccca5a802b4173377a1c2ba90654246789a8fa2334426d11" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide 0.7.1", +] + [[package]] name = "polling" version = "2.8.0" @@ -5091,6 +5280,12 @@ version = "1.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +[[package]] +name = "simd-adler32" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "238abfbb77c1915110ad968465608b68e869e0772622c9656714e73e5a1a522f" + [[package]] name = "simdutf8" version = "0.1.4" @@ -5261,6 +5456,12 @@ dependencies = [ "pin-project-lite 0.2.9", ] +[[package]] +name = "str-buf" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0" + [[package]] name = "strsim" version = "0.8.0" @@ -5422,6 +5623,17 @@ dependencies = [ "once_cell", ] +[[package]] +name = "tiff" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7449334f9ff2baf290d55d73983a7d6fa15e01198faef72af07e2a8db851e471" +dependencies = [ + "flate2", + "jpeg-decoder", + "weezl", +] + [[package]] name = "time" version = "0.1.45" @@ -6164,6 +6376,7 @@ checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" name = "veilid-cli" version = "0.1.0" dependencies = [ + "arboard", "async-std", "async-tungstenite 0.8.0", "bugsalot", @@ -6683,6 +6896,12 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "weezl" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" + [[package]] name = "wg" version = "0.3.2" @@ -6754,6 +6973,15 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "winapi-wsapoll" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c17110f57155602a80dca10be03852116403c9ff3cd25b079d666f2aa3df6e" +dependencies = [ + "winapi 0.3.9", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -7022,6 +7250,28 @@ dependencies = [ "tap", ] +[[package]] +name = "x11rb" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "592b4883219f345e712b3209c62654ebda0bb50887f330cbd018d0f654bfd507" +dependencies = [ + "gethostname", + "nix 0.24.3", + "winapi 0.3.9", + "winapi-wsapoll", + "x11rb-protocol", +] + +[[package]] +name = "x11rb-protocol" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56b245751c0ac9db0e006dc812031482784e434630205a93c73cfefcaabeac67" +dependencies = [ + "nix 0.24.3", +] + [[package]] name = "x25519-dalek" version = "1.2.0" diff --git a/veilid-cli/Cargo.toml b/veilid-cli/Cargo.toml index 1701aa1e..b2d2d5dd 100644 --- a/veilid-cli/Cargo.toml +++ b/veilid-cli/Cargo.toml @@ -11,7 +11,6 @@ path = "src/main.rs" [features] default = [ "rt-tokio" ] -macos = [ "cursive/ncurses-backend" ] rt-async-std = [ "async-std", "veilid-tools/rt-async-std", "cursive/rt-async-std" ] rt-tokio = [ "tokio", "tokio-util", "veilid-tools/rt-tokio", "cursive/rt-tokio" ] @@ -26,6 +25,7 @@ cursive_buffered_backend = { path = "../external/cursive_buffered_backend" } # cursive-multiplex = "0.6.0" # cursive_tree_view = "0.6.0" cursive_table_view = "0.14.0" +arboard = "3.2.0" # cursive-tabs = "0.5.0" clap = "^3" directories = "^4" diff --git a/veilid-cli/src/client_api_connection.rs b/veilid-cli/src/client_api_connection.rs index e790dcd9..0f5e20b2 100644 --- a/veilid-cli/src/client_api_connection.rs +++ b/veilid-cli/src/client_api_connection.rs @@ -83,7 +83,7 @@ impl ClientApiConnection { async fn process_veilid_update(&self, update: json::JsonValue) { let comproc = self.inner.lock().comproc.clone(); let Some(kind) = update["kind"].as_str() else { - comproc.log_message(format!("missing update kind: {}", update)); + comproc.log_message(Level::Error, format!("missing update kind: {}", update)); return; }; match kind { @@ -113,7 +113,7 @@ impl ClientApiConnection { comproc.update_value_change(&update); } _ => { - comproc.log_message(format!("unknown update kind: {}", update)); + comproc.log_message(Level::Error, format!("unknown update kind: {}", update)); } } } diff --git a/veilid-cli/src/command_processor.rs b/veilid-cli/src/command_processor.rs index dcf0a8cb..cab0aaa2 100644 --- a/veilid-cli/src/command_processor.rs +++ b/veilid-cli/src/command_processor.rs @@ -105,6 +105,7 @@ impl CommandProcessor { pub fn cmd_help(&self, _rest: Option, callback: UICallback) -> Result<(), String> { trace!("CommandProcessor::cmd_help"); self.ui_sender().add_node_event( + Level::Info, r#"Commands: exit/quit - exit the client disconnect - disconnect the client from the Veilid node @@ -206,7 +207,7 @@ reply - reply to an AppCall not handled directly by the server let log_level = match convert_loglevel(&rest.unwrap_or_default()) { Ok(v) => v, Err(e) => { - ui.add_node_event(format!("Failed to change log level: {}", e)); + ui.add_node_event(Level::Error, format!("Failed to change log level: {}", e)); ui.send_callback(callback); return; } @@ -239,7 +240,7 @@ reply - reply to an AppCall not handled directly by the server let (id, msg) = if let Some(second) = second { let id = match u64::from_str(&first) { Err(e) => { - ui.add_node_event(format!("invalid appcall id: {}", e)); + ui.add_node_event(Level::Error, format!("invalid appcall id: {}", e)); ui.send_callback(callback); return; } @@ -249,7 +250,7 @@ reply - reply to an AppCall not handled directly by the server } else { let id = match some_last_id { None => { - ui.add_node_event("must specify last call id".to_owned()); + ui.add_node_event(Level::Error, "must specify last call id".to_owned()); ui.send_callback(callback); return; } @@ -260,7 +261,7 @@ reply - reply to an AppCall not handled directly by the server let msg = if msg[0..1] == "#".to_owned() { match hex::decode(msg[1..].as_bytes().to_vec()) { Err(e) => { - ui.add_node_event(format!("invalid hex message: {}", e)); + ui.add_node_event(Level::Error, format!("invalid hex message: {}", e)); ui.send_callback(callback); return; } @@ -272,7 +273,10 @@ reply - reply to an AppCall not handled directly by the server let msglen = msg.len(); match capi.server_appcall_reply(id, msg).await { Ok(()) => { - ui.add_node_event(format!("reply sent to {} : {} bytes", id, msglen)); + ui.add_node_event( + Level::Info, + format!("reply sent to {} : {} bytes", id, msglen), + ); ui.send_callback(callback); return; } @@ -383,8 +387,8 @@ reply - reply to an AppCall not handled directly by the server // calls into ui //////////////////////////////////////////// - pub fn log_message(&self, message: String) { - self.inner().ui_sender.add_node_event(message); + pub fn log_message(&self, log_level: Level, message: String) { + self.inner().ui_sender.add_node_event(log_level, message); } pub fn update_attachment(&self, attachment: &json::JsonValue) { @@ -428,25 +432,30 @@ reply - reply to an AppCall not handled directly by the server )); } if !out.is_empty() { - self.inner().ui_sender.add_node_event(out); + self.inner().ui_sender.add_node_event(Level::Info, out); } } pub fn update_value_change(&self, value_change: &json::JsonValue) { let out = format!("Value change: {:?}", value_change.as_str().unwrap_or("???")); - self.inner().ui_sender.add_node_event(out); + self.inner().ui_sender.add_node_event(Level::Info, out); } pub fn update_log(&self, log: &json::JsonValue) { - self.inner().ui_sender.add_node_event(format!( - "{}: {}{}", - log["log_level"].as_str().unwrap_or("???"), - log["message"].as_str().unwrap_or("???"), - if let Some(bt) = log["backtrace"].as_str() { - format!("\nBacktrace:\n{}", bt) - } else { - "".to_owned() - } - )); + let log_level = + Level::from_str(log["log_level"].as_str().unwrap_or("error")).unwrap_or(Level::Error); + self.inner().ui_sender.add_node_event( + log_level, + format!( + "{}: {}{}", + log["log_level"].as_str().unwrap_or("???"), + log["message"].as_str().unwrap_or("???"), + if let Some(bt) = log["backtrace"].as_str() { + format!("\nBacktrace:\n{}", bt) + } else { + "".to_owned() + } + ), + ); } pub fn update_app_message(&self, msg: &json::JsonValue) { @@ -466,9 +475,10 @@ reply - reply to an AppCall not handled directly by the server hex::encode(message) }; - self.inner() - .ui_sender - .add_node_event(format!("AppMessage ({:?}): {}", msg["sender"], strmsg)); + self.inner().ui_sender.add_node_event( + Level::Info, + format!("AppMessage ({:?}): {}", msg["sender"], strmsg), + ); } pub fn update_app_call(&self, call: &json::JsonValue) { @@ -490,10 +500,13 @@ reply - reply to an AppCall not handled directly by the server let id = json_str_u64(&call["call_id"]); - self.inner().ui_sender.add_node_event(format!( - "AppCall ({:?}) id = {:016x} : {}", - call["sender"], id, strmsg - )); + self.inner().ui_sender.add_node_event( + Level::Info, + format!( + "AppCall ({:?}) id = {:016x} : {}", + call["sender"], id, strmsg + ), + ); self.inner_mut().last_call_id = Some(id); } diff --git a/veilid-cli/src/peers_table_view.rs b/veilid-cli/src/peers_table_view.rs index 2d158de6..6ba9639a 100644 --- a/veilid-cli/src/peers_table_view.rs +++ b/veilid-cli/src/peers_table_view.rs @@ -74,8 +74,14 @@ impl TableViewItem for json::JsonValue { Self: Sized, { match column { - PeerTableColumn::NodeId => self.to_column(column).cmp(&other.to_column(column)), - PeerTableColumn::Address => self.to_column(column).cmp(&other.to_column(column)), + PeerTableColumn::NodeId => self + .to_column(column) + .to_ascii_lowercase() + .cmp(&other.to_column(column).to_ascii_lowercase()), + PeerTableColumn::Address => self + .to_column(column) + .to_ascii_lowercase() + .cmp(&other.to_column(column).to_ascii_lowercase()), PeerTableColumn::LatencyAvg => json_str_u64(&self["peer_stats"]["latency"]["average"]) .cmp(&json_str_u64(&other["peer_stats"]["latency"]["average"])), PeerTableColumn::TransferDownAvg => { diff --git a/veilid-cli/src/settings.rs b/veilid-cli/src/settings.rs index a5382ea0..5cc9268f 100644 --- a/veilid-cli/src/settings.rs +++ b/veilid-cli/src/settings.rs @@ -27,23 +27,23 @@ interface: shadow: false borders: "simple" colors: - background : "#333D3D" - shadow : "#000000" - view : "#1c2323" - primary : "#a6d8d3" - secondary : "#8cb4b7" - tertiary : "#eeeeee" - title_primary : "#f93fbd" - title_secondary : "#ff0000" - highlight : "#f93fbd" - highlight_inactive : "#a6d8d3" - highlight_text : "#333333" + background : "black" + shadow : "black" + view : "black" + primary : "light cyan" + secondary : "cyan" + tertiary : "green" + title_primary : "light magenta" + title_secondary : "magenta" + highlight : "light white" + highlight_inactive : "white" + highlight_text : "black" log_colors: - trace : "#707070" - debug : "#a0a0a0" - info : "#5cd3c6" - warn : "#fedc50" - error : "#ff4a15" + trace : "light blue" + debug : "light green" + info : "white" + warn : "light yellow" + error : "light red" "### .replace( "%LOGGING_FILE_DIRECTORY%", diff --git a/veilid-cli/src/ui.rs b/veilid-cli/src/ui.rs index a38ff6dc..5d7e1e4d 100644 --- a/veilid-cli/src/ui.rs +++ b/veilid-cli/src/ui.rs @@ -12,7 +12,7 @@ use cursive::views::*; use cursive::Cursive; use cursive::CursiveRunnable; use cursive_flexi_logger_view::{CursiveLogWriter, FlexiLoggerView}; -//use cursive_multiplex::*; +// use cursive_multiplex::*; use std::collections::{HashMap, VecDeque}; use thiserror::Error; @@ -311,8 +311,7 @@ impl UI { .button("Close", move |s| { s.pop_layer(); close_cb(s); - }), //.wrap_with(CircularFocus::new) - //.wrap_tab(), + }), ); s.set_global_callback(cursive::event::Event::Key(Key::Esc), move |s| { s.set_global_callback(cursive::event::Event::Key(Key::Esc), UI::quit_handler); @@ -455,6 +454,23 @@ impl UI { Self::command_processor(s).start_connection(); } + fn on_submit_peers_table_view(s: &mut Cursive, _row: usize, index: usize) { + let peers_table_view = UI::peers(s); + let node_id = peers_table_view + .borrow_item(index) + .map(|j| j["node_ids"][0].to_string()); + if let Some(node_id) = node_id { + let mut clipboard = arboard::Clipboard::new().unwrap(); + clipboard.set_text(node_id.clone()).unwrap(); + + let color = *Self::inner_mut(s).log_colors.get(&Level::Info).unwrap(); + cursive_flexi_logger_view::push_to_log(StyledString::styled( + format!(">> NodeId Copied: {}", node_id), + color, + )); + } + } + fn show_connection_dialog(s: &mut Cursive, state: ConnectionState) -> bool { let mut inner = Self::inner_mut(s); @@ -644,7 +660,28 @@ impl UI { fn refresh_peers(s: &mut Cursive) { let mut peers = UI::peers(s); let inner = Self::inner_mut(s); + let sel_item = peers.item(); + let sel_item_text = peers + .item() + .map(|x| peers.borrow_items()[x]["node_ids"][0].clone()); + peers.set_items_stable(inner.ui_state.peers_state.get().clone()); + + let mut selected = false; + if let Some(sel_item_text) = sel_item_text { + // First select by name + for n in 0..peers.borrow_items().len() { + if peers.borrow_items()[n]["node_ids"][0] == sel_item_text { + peers.set_selected_item(n); + selected = true; + } + } + } + if !selected { + if let Some(sel_item) = sel_item { + peers.set_selected_item(sel_item); + } + } } fn update_cb(s: &mut Cursive) { @@ -707,9 +744,6 @@ impl UI { // Instantiate the cursive runnable let runnable = CursiveRunnable::new( || -> Result, Box> { - #[cfg(feature = "macos")] - let backend = cursive::backends::curses::n::Backend::init().unwrap(); - #[cfg(not(feature = "macos"))] let backend = cursive::backends::crossterm::Backend::init().unwrap(); let buffered_backend = cursive_buffered_backend::BufferedBackend::new(backend); Ok(Box::new(buffered_backend)) @@ -737,6 +771,11 @@ impl UI { })), }; + let ui_sender = UISender { + inner: this.inner.clone(), + cb_sink, + }; + let mut inner = this.inner.lock(); // Make the inner object accessible in callbacks easily @@ -750,12 +789,14 @@ impl UI { .with_name("node-events-panel") .full_screen(); - let peers_table_view = PeersTableView::new() + let mut peers_table_view = PeersTableView::new() .column(PeerTableColumn::NodeId, "Node Id", |c| c.width(48)) .column(PeerTableColumn::Address, "Address", |c| c) .column(PeerTableColumn::LatencyAvg, "Ping", |c| c.width(8)) .column(PeerTableColumn::TransferDownAvg, "Down", |c| c.width(8)) - .column(PeerTableColumn::TransferUpAvg, "Up", |c| c.width(8)) + .column(PeerTableColumn::TransferUpAvg, "Up", |c| c.width(8)); + peers_table_view.set_on_submit(UI::on_submit_peers_table_view); + let peers_table_view = peers_table_view .with_name("peers") .full_width() .min_height(8); @@ -832,8 +873,7 @@ impl UI { drop(inner); - let inner = this.inner.clone(); - (this, UISender { inner, cb_sink }) + (this, ui_sender) } pub fn cursive_flexi_logger(&self) -> Box { let mut flv = cursive_flexi_logger_view::cursive_flexi_logger(self.siv.cb_sink().clone()); @@ -906,7 +946,7 @@ impl UISender { started: bool, bps_down: u64, bps_up: u64, - peers: Vec, + mut peers: Vec, ) { { let mut inner = self.inner.lock(); @@ -915,6 +955,11 @@ impl UISender { ((bps_down as f64) / 1000.0f64) as f32, ((bps_up as f64) / 1000.0f64) as f32, )); + peers.sort_by(|a, b| { + a["node_ids"][0] + .to_string() + .cmp(&b["node_ids"][0].to_string()) + }); inner.ui_state.peers_state.set(peers); } let _ = self.cb_sink.send(Box::new(UI::update_cb)); @@ -935,10 +980,10 @@ impl UISender { let _ = self.cb_sink.send(Box::new(UI::update_cb)); } - pub fn add_node_event(&self, event: String) { + pub fn add_node_event(&self, log_color: Level, event: String) { { let inner = self.inner.lock(); - let color = *inner.log_colors.get(&Level::Info).unwrap(); + let color = *inner.log_colors.get(&log_color).unwrap(); let mut starting_style: Style = color.into(); for line in event.lines() { let (spanned_string, end_style) =