diff --git a/Cargo.lock b/Cargo.lock index 044c4a2..bf9d683 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -166,6 +166,7 @@ dependencies = [ "bytes", "crossterm", "log", + "open", "procfs", "thiserror", "tokio", @@ -314,6 +315,16 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" +[[package]] +name = "open" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4a3100141f1733ea40b53381b0ae3117330735ef22309a190ac57b9576ea716" +dependencies = [ + "pathdiff", + "windows-sys", +] + [[package]] name = "parking_lot" version = "0.12.1" @@ -337,6 +348,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "pathdiff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" + [[package]] name = "pin-project-lite" version = "0.2.9" diff --git a/Cargo.toml b/Cargo.toml index 67963ea..0fa113b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,10 +9,11 @@ edition = "2021" anyhow = "1.0" bytes = "1" crossterm = { version = "0.25", features = ["event-stream"] } +log = { version = "0.4", features = ["std"] } +open = "3" thiserror = "1.0" tokio = { version = "1", features = ["full"] } tokio-stream = "0.1" -log = { version = "0.4", features = ["std"] } [target.'cfg(target_os="linux")'.dependencies] procfs = "0.14.1" diff --git a/src/ui.rs b/src/ui.rs index 10afc53..32e2b4b 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -11,6 +11,7 @@ use crossterm::{ }, }; use log::{Level, Metadata, Record}; +use open; use std::collections::vec_deque::VecDeque; use std::io::{stdout, Write}; use tokio::sync::mpsc; @@ -62,20 +63,42 @@ async fn run_ui_core( execute!(stdout, EnterAlternateScreen, DisableLineWrap)?; let mut events = EventStream::new(); + let mut selection = 0; let mut show_logs = false; let mut lines: VecDeque = VecDeque::with_capacity(1024); - let mut ports = None; + let mut ports: Option> = None; loop { tokio::select! { ev = events.next() => { match ev { Some(Ok(Event::Key(ev))) => { match ev { - KeyEvent {code:KeyCode::Esc, ..} => { break; }, - KeyEvent {code:KeyCode::Char('q'), ..} => { break; }, + KeyEvent {code:KeyCode::Esc, ..} + | KeyEvent {code:KeyCode::Char('q'), ..} => { break; }, KeyEvent {code:KeyCode::Char('l'), ..} => { show_logs = !show_logs; } + KeyEvent { code:KeyCode::Up, ..} + | KeyEvent { code:KeyCode::Char('j'), ..} => { + if selection > 0 { + selection -= 1; + } + } + KeyEvent { code:KeyCode::Down, ..} + | KeyEvent { code:KeyCode::Char('k'), ..} => { + if let Some(p) = &ports { + if selection != p.len() - 1 { + selection += 1; + } + } + } + KeyEvent { code:KeyCode::Enter, ..} => { + if let Some(p) = &ports { + if selection < p.len() { + _ = open::that(format!("http://127.0.0.1:{}/", p[selection].port)); + } + } + } _ => () } }, @@ -86,7 +109,10 @@ async fn run_ui_core( } pr = port_receiver.recv() => { match pr { - Some(p) => { ports = Some(p); } + Some(mut p) => { + p.sort_by(|a, b| a.port.partial_cmp(&b.port).unwrap()); + ports = Some(p); + } None => break, } } @@ -112,28 +138,25 @@ async fn run_ui_core( let columns: usize = columns.into(); let padding = 1; let port_width = 5; // 5 characters for 16-bit number - let url_width = "http://127.0.0.1:/".len() + port_width; - let description_width = columns - (padding + port_width + padding + url_width + padding); + let description_width = columns - (padding + padding + port_width + padding); print!( "{}", format!( - " {port:>port_width$} {url:port_width$} {description: - browse" + ) + .negative() ); stdout.flush()?; }