From d27a56a1d8af962502c3da97afb2bafd78b0b437 Mon Sep 17 00:00:00 2001 From: Francesco Date: Mon, 9 Mar 2026 17:01:25 +0400 Subject: [PATCH 1/7] Add info page and status bar --- src/app.rs | 2 ++ src/components/file_explorer.rs | 4 +-- src/main.rs | 2 +- src/ui.rs | 62 ++++++++++++++++++++++++++++++++- 4 files changed, 66 insertions(+), 4 deletions(-) diff --git a/src/app.rs b/src/app.rs index dec9374..362ab18 100644 --- a/src/app.rs +++ b/src/app.rs @@ -12,6 +12,7 @@ pub enum CurrentScreen { Home, BitcoinConfig, P2PoolConfig, + Info, FileExplorer, Exiting, } @@ -63,6 +64,7 @@ impl App { 0 => self.current_screen = CurrentScreen::Home, 1 => self.current_screen = CurrentScreen::BitcoinConfig, 2 => self.current_screen = CurrentScreen::P2PoolConfig, + 3 => self.current_screen = CurrentScreen::Info, _ => {} } } diff --git a/src/components/file_explorer.rs b/src/components/file_explorer.rs index fee01b0..0bdcbf5 100644 --- a/src/components/file_explorer.rs +++ b/src/components/file_explorer.rs @@ -143,8 +143,8 @@ impl FileExplorer { #[cfg(test)] mod tests { use super::*; - use std::env::temp_dir; - use std::fs::{File, create_dir}; + //use std::env::temp_dir; + use std::fs::File; //{ , create_dir}; fn setup_temp_fs() -> PathBuf { use std::time::{SystemTime, UNIX_EPOCH}; diff --git a/src/main.rs b/src/main.rs index 355f2e9..10c2766 100644 --- a/src/main.rs +++ b/src/main.rs @@ -73,7 +73,7 @@ fn run_app(terminal: &mut Terminal, app: &mut App) -> Result<()> } KeyCode::Down => { - if app.sidebar_index < 2 { + if app.sidebar_index < 3 { app.sidebar_index += 1; AppAction::ToggleMenu } else { diff --git a/src/ui.rs b/src/ui.rs index c83c3ea..c97f0da 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -9,19 +9,31 @@ use ratatui::{ }; pub fn ui(f: &mut Frame, app: &mut App) { + let outer = Layout::default() + .direction(Direction::Vertical) + .constraints([ + Constraint::Min(0), // Main area + Constraint::Length(1), // Status bar + ]) + .split(f.area()); + + let main_row = outer[0]; + let status_bar_area = outer[1]; + let chunks = Layout::default() .direction(Direction::Horizontal) .constraints([ Constraint::Length(25), // Sidebar Constraint::Min(0), // Main Content ]) - .split(f.area()); + .split(main_row); // Sidebar let items = vec![ ListItem::new("Home"), ListItem::new("Bitcoin Config"), ListItem::new("P2Pool Config"), + ListItem::new("Info"), ]; // Highlight the active one @@ -69,11 +81,59 @@ pub fn ui(f: &mut Frame, app: &mut App) { f.render_widget(p, main_area); } } + CurrentScreen::Info => { + let p = Paragraph::new("PDM — P2Pool & Bitcoin Config Manager\n\nTemporary text.") + .block(Block::default().borders(Borders::ALL).title(" Info ")) + .wrap(Wrap { trim: true }); + f.render_widget(p, main_area); + } CurrentScreen::FileExplorer => { render_file_explorer(f, app, main_area); } _ => {} } + + render_status_bar(f, app, status_bar_area); +} + +fn hint(key: &str, desc: &str) -> Vec> { + vec![ + Span::styled( + format!(" {key} "), + Style::default().bg(Color::DarkGray).fg(Color::White), + ), + Span::styled(format!(" {desc} "), Style::default().fg(Color::DarkGray)), + ] +} + +fn render_status_bar(f: &mut Frame, app: &App, area: Rect) { + let mut spans: Vec = Vec::new(); + + match app.current_screen { + CurrentScreen::FileExplorer => { + spans.extend(hint("↑↓", "Navigate")); + spans.extend(hint("Enter", "Select")); + spans.extend(hint("Esc", "Cancel")); + } + CurrentScreen::BitcoinConfig if app.bitcoin_conf_path.is_some() => { + spans.extend(hint("↑↓", "Navigate")); + spans.extend(hint("Enter", "Open file")); + spans.extend(hint("q", "Quit")); + } + CurrentScreen::P2PoolConfig if app.p2pool_conf_path.is_some() => { + spans.extend(hint("↑↓", "Navigate")); + spans.extend(hint("Enter", "Open file")); + spans.extend(hint("q", "Quit")); + } + _ => { + spans.extend(hint("↑↓", "Navigate sidebar")); + spans.extend(hint("Enter", "Select")); + spans.extend(hint("q", "Quit")); + } + } + + let bar = Paragraph::new(Line::from(spans)).style(Style::default().bg(Color::Black)); + f.render_widget(bar, area); } fn render_file_explorer(f: &mut Frame, app: &mut App, area: Rect) { From 12fc70a11282aab34a3a748b28e25d02fe0bbc77 Mon Sep 17 00:00:00 2001 From: Francesco Date: Tue, 10 Mar 2026 11:53:39 +0400 Subject: [PATCH 2/7] Add Bitcoin Status with Chain info, System, Logs and Peers tabs, P2PoolConfig, P2PoolStatus, LNConfig, LNStatus, SharesMarket screens --- .gitignore | 1 + src/app.rs | 16 +- src/main.rs | 34 ++- src/snapshots/pdm__tests__home_screen.snap | 21 +- src/snapshots/pdm__tests__menu_toggled.snap | 19 +- ...__tests__bitcoin_config_screen_render.snap | 55 +++++ ...ui__tests__bitcoin_screen_render.snap.new} | 20 +- ...__tests__bitcoin_status_screen_render.snap | 57 +++++ ...tests__bitcoin_status_tab_logs_render.snap | 57 +++++ ...ests__bitcoin_status_tab_peers_render.snap | 57 +++++ ...sts__bitcoin_status_tab_system_render.snap | 57 +++++ .../pdm__ui__tests__home_screen_render.snap | 19 +- ...m__ui__tests__ln_config_screen_render.snap | 55 +++++ ...m__ui__tests__ln_status_screen_render.snap | 55 +++++ ...__tests__p2pool_config_screen_render.snap} | 21 +- ...__ui__tests__p2pool_screen_render.snap.new | 54 +++++ ...i__tests__p2pool_status_screen_render.snap | 55 +++++ ...i__tests__shares_market_screen_render.snap | 55 +++++ src/ui.rs | 209 ++++++++++++++++-- .../ui_snapshots__config_screen_render.snap | 19 +- .../ui_snapshots__home_screen_render.snap | 21 +- 21 files changed, 890 insertions(+), 67 deletions(-) create mode 100644 src/snapshots/pdm__ui__tests__bitcoin_config_screen_render.snap rename src/snapshots/{pdm__ui__tests__bitcoin_screen_render.snap => pdm__ui__tests__bitcoin_screen_render.snap.new} (71%) create mode 100644 src/snapshots/pdm__ui__tests__bitcoin_status_screen_render.snap create mode 100644 src/snapshots/pdm__ui__tests__bitcoin_status_tab_logs_render.snap create mode 100644 src/snapshots/pdm__ui__tests__bitcoin_status_tab_peers_render.snap create mode 100644 src/snapshots/pdm__ui__tests__bitcoin_status_tab_system_render.snap create mode 100644 src/snapshots/pdm__ui__tests__ln_config_screen_render.snap create mode 100644 src/snapshots/pdm__ui__tests__ln_status_screen_render.snap rename src/snapshots/{pdm__ui__tests__p2pool_screen_render.snap => pdm__ui__tests__p2pool_config_screen_render.snap} (66%) create mode 100644 src/snapshots/pdm__ui__tests__p2pool_screen_render.snap.new create mode 100644 src/snapshots/pdm__ui__tests__p2pool_status_screen_render.snap create mode 100644 src/snapshots/pdm__ui__tests__shares_market_screen_render.snap diff --git a/.gitignore b/.gitignore index ea8c4bf..68942e3 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +./src/snapshots diff --git a/src/app.rs b/src/app.rs index 362ab18..f94b501 100644 --- a/src/app.rs +++ b/src/app.rs @@ -11,8 +11,12 @@ use std::path::PathBuf; pub enum CurrentScreen { Home, BitcoinConfig, + BitcoinStatus, P2PoolConfig, - Info, + P2PoolStatus, + LNConfig, + LNStatus, + SharesMarket, FileExplorer, Exiting, } @@ -42,6 +46,7 @@ pub struct App { pub explorer: FileExplorer, pub p2pool_config: Option, pub bitcoin_data: Vec, + pub bitcoin_status_tab: usize, } impl App { @@ -55,6 +60,7 @@ impl App { explorer: FileExplorer::new(), p2pool_config: None, bitcoin_data: Vec::new(), + bitcoin_status_tab: 0, } } @@ -63,8 +69,12 @@ impl App { match self.sidebar_index { 0 => self.current_screen = CurrentScreen::Home, 1 => self.current_screen = CurrentScreen::BitcoinConfig, - 2 => self.current_screen = CurrentScreen::P2PoolConfig, - 3 => self.current_screen = CurrentScreen::Info, + 2 => self.current_screen = CurrentScreen::BitcoinStatus, + 3 => self.current_screen = CurrentScreen::P2PoolConfig, + 4 => self.current_screen = CurrentScreen::P2PoolStatus, + 5 => self.current_screen = CurrentScreen::LNConfig, + 6 => self.current_screen = CurrentScreen::LNStatus, + 7 => self.current_screen = CurrentScreen::SharesMarket, _ => {} } } diff --git a/src/main.rs b/src/main.rs index 10c2766..d53ce48 100644 --- a/src/main.rs +++ b/src/main.rs @@ -60,6 +60,38 @@ fn run_app(terminal: &mut Terminal, app: &mut App) -> Result<()> let action = match app.current_screen { CurrentScreen::FileExplorer => app.explorer.handle_input(key), + CurrentScreen::BitcoinStatus => match key.code { + KeyCode::Left => { + if app.bitcoin_status_tab > 0 { + app.bitcoin_status_tab -= 1; + } + AppAction::None + } + KeyCode::Right => { + if app.bitcoin_status_tab < 3 { + app.bitcoin_status_tab += 1; + } + AppAction::None + } + KeyCode::Up => { + if app.sidebar_index > 0 { + app.sidebar_index -= 1; + AppAction::ToggleMenu + } else { + AppAction::None + } + } + KeyCode::Down => { + if app.sidebar_index < 3 { + app.sidebar_index += 1; + AppAction::ToggleMenu + } else { + AppAction::None + } + } + _ => AppAction::None, + }, + _ => match key.code { KeyCode::Enter => { if matches!( @@ -73,7 +105,7 @@ fn run_app(terminal: &mut Terminal, app: &mut App) -> Result<()> } KeyCode::Down => { - if app.sidebar_index < 3 { + if app.sidebar_index < 7 { app.sidebar_index += 1; AppAction::ToggleMenu } else { diff --git a/src/snapshots/pdm__tests__home_screen.snap b/src/snapshots/pdm__tests__home_screen.snap index bf9b5ea..1c88ef1 100644 --- a/src/snapshots/pdm__tests__home_screen.snap +++ b/src/snapshots/pdm__tests__home_screen.snap @@ -9,13 +9,12 @@ TestBackend { "┌ PDM ──────────────────┐┌ Home ───────────────────────────────────────────────┐", "│Home ││Welcome to PDM. │", "│Bitcoin Config ││ │", - "│P2Pool Config ││Select a config from the sidebar to edit. │", - "│ ││ │", - "│ ││ │", - "│ ││ │", - "│ ││ │", - "│ ││ │", - "│ ││ │", + "│Bitcoin Status ││Select a config from the sidebar to edit. │", + "│P2Pool Config ││ │", + "│P2Pool Status ││ │", + "│LN Config ││ │", + "│LN Status ││ │", + "│Shares Market ││ │", "│ ││ │", "│ ││ │", "│ ││ │", @@ -31,11 +30,19 @@ TestBackend { "│ ││ │", "│ ││ │", "└───────────────────────┘└─────────────────────────────────────────────────────┘", + " ↑↓ Navigate sidebar Enter Select q Quit ", ], styles: [ x: 0, y: 0, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, x: 1, y: 1, fg: Black, bg: Gray, underline: Reset, modifier: NONE, x: 24, y: 1, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, + x: 0, y: 24, fg: White, bg: DarkGray, underline: Reset, modifier: NONE, + x: 4, y: 24, fg: DarkGray, bg: Black, underline: Reset, modifier: NONE, + x: 23, y: 24, fg: White, bg: DarkGray, underline: Reset, modifier: NONE, + x: 30, y: 24, fg: DarkGray, bg: Black, underline: Reset, modifier: NONE, + x: 39, y: 24, fg: White, bg: DarkGray, underline: Reset, modifier: NONE, + x: 42, y: 24, fg: DarkGray, bg: Black, underline: Reset, modifier: NONE, + x: 49, y: 24, fg: Reset, bg: Black, underline: Reset, modifier: NONE, ] }, scrollback: Buffer { diff --git a/src/snapshots/pdm__tests__menu_toggled.snap b/src/snapshots/pdm__tests__menu_toggled.snap index bc9e283..85b75d4 100644 --- a/src/snapshots/pdm__tests__menu_toggled.snap +++ b/src/snapshots/pdm__tests__menu_toggled.snap @@ -9,13 +9,12 @@ TestBackend { "┌ PDM ──────────────────┐┌ Bitcoin Config ─────────────────────────────────────┐", "│Home ││Press [Enter] to select a bitcoin.conf file │", "│Bitcoin Config ││ │", + "│Bitcoin Status ││ │", "│P2Pool Config ││ │", - "│ ││ │", - "│ ││ │", - "│ ││ │", - "│ ││ │", - "│ ││ │", - "│ ││ │", + "│P2Pool Status ││ │", + "│LN Config ││ │", + "│LN Status ││ │", + "│Shares Market ││ │", "│ ││ │", "│ ││ │", "│ ││ │", @@ -31,11 +30,19 @@ TestBackend { "│ ││ │", "│ ││ │", "└───────────────────────┘└─────────────────────────────────────────────────────┘", + " ↑↓ Navigate sidebar Enter Select q Quit ", ], styles: [ x: 0, y: 0, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, x: 1, y: 2, fg: Black, bg: Gray, underline: Reset, modifier: NONE, x: 24, y: 2, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, + x: 0, y: 24, fg: White, bg: DarkGray, underline: Reset, modifier: NONE, + x: 4, y: 24, fg: DarkGray, bg: Black, underline: Reset, modifier: NONE, + x: 23, y: 24, fg: White, bg: DarkGray, underline: Reset, modifier: NONE, + x: 30, y: 24, fg: DarkGray, bg: Black, underline: Reset, modifier: NONE, + x: 39, y: 24, fg: White, bg: DarkGray, underline: Reset, modifier: NONE, + x: 42, y: 24, fg: DarkGray, bg: Black, underline: Reset, modifier: NONE, + x: 49, y: 24, fg: Reset, bg: Black, underline: Reset, modifier: NONE, ] }, scrollback: Buffer { diff --git a/src/snapshots/pdm__ui__tests__bitcoin_config_screen_render.snap b/src/snapshots/pdm__ui__tests__bitcoin_config_screen_render.snap new file mode 100644 index 0000000..7dc809c --- /dev/null +++ b/src/snapshots/pdm__ui__tests__bitcoin_config_screen_render.snap @@ -0,0 +1,55 @@ +--- +source: src/ui.rs +expression: terminal.backend() +--- +TestBackend { + buffer: Buffer { + area: Rect { x: 0, y: 0, width: 80, height: 24 }, + content: [ + "┌ PDM ──────────────────┐┌ Bitcoin Config ─────────────────────────────────────┐", + "│Home ││Press [Enter] to select a bitcoin.conf file │", + "│Bitcoin Config ││ │", + "│Bitcoin Status ││ │", + "│P2Pool Config ││ │", + "│P2Pool Status ││ │", + "│LN Config ││ │", + "│LN Status ││ │", + "│Shares Market ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "└───────────────────────┘└─────────────────────────────────────────────────────┘", + " ↑↓ Navigate sidebar Enter Select q Quit ", + ], + styles: [ + x: 0, y: 0, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, + x: 1, y: 2, fg: Black, bg: Gray, underline: Reset, modifier: NONE, + x: 24, y: 2, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, + x: 0, y: 23, fg: White, bg: DarkGray, underline: Reset, modifier: NONE, + x: 4, y: 23, fg: DarkGray, bg: Black, underline: Reset, modifier: NONE, + x: 23, y: 23, fg: White, bg: DarkGray, underline: Reset, modifier: NONE, + x: 30, y: 23, fg: DarkGray, bg: Black, underline: Reset, modifier: NONE, + x: 39, y: 23, fg: White, bg: DarkGray, underline: Reset, modifier: NONE, + x: 42, y: 23, fg: DarkGray, bg: Black, underline: Reset, modifier: NONE, + x: 49, y: 23, fg: Reset, bg: Black, underline: Reset, modifier: NONE, + ] + }, + scrollback: Buffer { + area: Rect { x: 0, y: 0, width: 80, height: 0 } + }, + cursor: false, + pos: ( + 0, + 0, + ), +} diff --git a/src/snapshots/pdm__ui__tests__bitcoin_screen_render.snap b/src/snapshots/pdm__ui__tests__bitcoin_screen_render.snap.new similarity index 71% rename from src/snapshots/pdm__ui__tests__bitcoin_screen_render.snap rename to src/snapshots/pdm__ui__tests__bitcoin_screen_render.snap.new index 0223c7e..ea4c542 100644 --- a/src/snapshots/pdm__ui__tests__bitcoin_screen_render.snap +++ b/src/snapshots/pdm__ui__tests__bitcoin_screen_render.snap.new @@ -1,5 +1,6 @@ --- source: src/ui.rs +assertion_line: 409 expression: terminal.backend() --- TestBackend { @@ -9,13 +10,12 @@ TestBackend { "┌ PDM ──────────────────┐┌ Bitcoin Config ─────────────────────────────────────┐", "│Home ││Press [Enter] to select a bitcoin.conf file │", "│Bitcoin Config ││ │", + "│Bitcoin Status ││ │", "│P2Pool Config ││ │", - "│ ││ │", - "│ ││ │", - "│ ││ │", - "│ ││ │", - "│ ││ │", - "│ ││ │", + "│P2Pool Status ││ │", + "│LN Config ││ │", + "│LN Status ││ │", + "│Shares Market ││ │", "│ ││ │", "│ ││ │", "│ ││ │", @@ -26,11 +26,19 @@ TestBackend { "│ ││ │", "│ ││ │", "└───────────────────────┘└─────────────────────────────────────────────────────┘", + " ↑↓ Navigate sidebar Enter Select q Quit ", ], styles: [ x: 0, y: 0, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, x: 1, y: 2, fg: Black, bg: Gray, underline: Reset, modifier: NONE, x: 24, y: 2, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, + x: 0, y: 19, fg: White, bg: DarkGray, underline: Reset, modifier: NONE, + x: 4, y: 19, fg: DarkGray, bg: Black, underline: Reset, modifier: NONE, + x: 23, y: 19, fg: White, bg: DarkGray, underline: Reset, modifier: NONE, + x: 30, y: 19, fg: DarkGray, bg: Black, underline: Reset, modifier: NONE, + x: 39, y: 19, fg: White, bg: DarkGray, underline: Reset, modifier: NONE, + x: 42, y: 19, fg: DarkGray, bg: Black, underline: Reset, modifier: NONE, + x: 49, y: 19, fg: Reset, bg: Black, underline: Reset, modifier: NONE, ] }, scrollback: Buffer { diff --git a/src/snapshots/pdm__ui__tests__bitcoin_status_screen_render.snap b/src/snapshots/pdm__ui__tests__bitcoin_status_screen_render.snap new file mode 100644 index 0000000..004c272 --- /dev/null +++ b/src/snapshots/pdm__ui__tests__bitcoin_status_screen_render.snap @@ -0,0 +1,57 @@ +--- +source: src/ui.rs +expression: terminal.backend() +--- +TestBackend { + buffer: Buffer { + area: Rect { x: 0, y: 0, width: 80, height: 24 }, + content: [ + "┌ PDM ──────────────────┐┌ Info ───────────────────────────────────────────────┐", + "│Home ││ Chain Info │ System │ Logs │ Peers │", + "│Bitcoin Config ││ │", + "│Bitcoin Status │└─────────────────────────────────────────────────────┘", + "│P2Pool Config │┌─────────────────────────────────────────────────────┐", + "│P2Pool Status ││Chain Info │", + "│LN Config ││ │", + "│LN Status ││ │", + "│Shares Market ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "└───────────────────────┘└─────────────────────────────────────────────────────┘", + " ↑↓ Navigate sidebar ←→ Switch tab q Quit ", + ], + styles: [ + x: 0, y: 0, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, + x: 27, y: 1, fg: Black, bg: Gray, underline: Reset, modifier: NONE, + x: 37, y: 1, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, + x: 1, y: 3, fg: Black, bg: Gray, underline: Reset, modifier: NONE, + x: 24, y: 3, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, + x: 0, y: 23, fg: White, bg: DarkGray, underline: Reset, modifier: NONE, + x: 4, y: 23, fg: DarkGray, bg: Black, underline: Reset, modifier: NONE, + x: 23, y: 23, fg: White, bg: DarkGray, underline: Reset, modifier: NONE, + x: 27, y: 23, fg: DarkGray, bg: Black, underline: Reset, modifier: NONE, + x: 40, y: 23, fg: White, bg: DarkGray, underline: Reset, modifier: NONE, + x: 43, y: 23, fg: DarkGray, bg: Black, underline: Reset, modifier: NONE, + x: 50, y: 23, fg: Reset, bg: Black, underline: Reset, modifier: NONE, + ] + }, + scrollback: Buffer { + area: Rect { x: 0, y: 0, width: 80, height: 0 } + }, + cursor: false, + pos: ( + 0, + 0, + ), +} diff --git a/src/snapshots/pdm__ui__tests__bitcoin_status_tab_logs_render.snap b/src/snapshots/pdm__ui__tests__bitcoin_status_tab_logs_render.snap new file mode 100644 index 0000000..374cd78 --- /dev/null +++ b/src/snapshots/pdm__ui__tests__bitcoin_status_tab_logs_render.snap @@ -0,0 +1,57 @@ +--- +source: src/ui.rs +expression: terminal.backend() +--- +TestBackend { + buffer: Buffer { + area: Rect { x: 0, y: 0, width: 80, height: 24 }, + content: [ + "┌ PDM ──────────────────┐┌ Info ───────────────────────────────────────────────┐", + "│Home ││ Chain Info │ System │ Logs │ Peers │", + "│Bitcoin Config ││ │", + "│Bitcoin Status │└─────────────────────────────────────────────────────┘", + "│P2Pool Config │┌─────────────────────────────────────────────────────┐", + "│P2Pool Status ││Logs │", + "│LN Config ││ │", + "│LN Status ││ │", + "│Shares Market ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "└───────────────────────┘└─────────────────────────────────────────────────────┘", + " ↑↓ Navigate sidebar ←→ Switch tab q Quit ", + ], + styles: [ + x: 0, y: 0, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, + x: 49, y: 1, fg: Black, bg: Gray, underline: Reset, modifier: NONE, + x: 53, y: 1, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, + x: 1, y: 3, fg: Black, bg: Gray, underline: Reset, modifier: NONE, + x: 24, y: 3, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, + x: 0, y: 23, fg: White, bg: DarkGray, underline: Reset, modifier: NONE, + x: 4, y: 23, fg: DarkGray, bg: Black, underline: Reset, modifier: NONE, + x: 23, y: 23, fg: White, bg: DarkGray, underline: Reset, modifier: NONE, + x: 27, y: 23, fg: DarkGray, bg: Black, underline: Reset, modifier: NONE, + x: 40, y: 23, fg: White, bg: DarkGray, underline: Reset, modifier: NONE, + x: 43, y: 23, fg: DarkGray, bg: Black, underline: Reset, modifier: NONE, + x: 50, y: 23, fg: Reset, bg: Black, underline: Reset, modifier: NONE, + ] + }, + scrollback: Buffer { + area: Rect { x: 0, y: 0, width: 80, height: 0 } + }, + cursor: false, + pos: ( + 0, + 0, + ), +} diff --git a/src/snapshots/pdm__ui__tests__bitcoin_status_tab_peers_render.snap b/src/snapshots/pdm__ui__tests__bitcoin_status_tab_peers_render.snap new file mode 100644 index 0000000..72ec916 --- /dev/null +++ b/src/snapshots/pdm__ui__tests__bitcoin_status_tab_peers_render.snap @@ -0,0 +1,57 @@ +--- +source: src/ui.rs +expression: terminal.backend() +--- +TestBackend { + buffer: Buffer { + area: Rect { x: 0, y: 0, width: 80, height: 24 }, + content: [ + "┌ PDM ──────────────────┐┌ Info ───────────────────────────────────────────────┐", + "│Home ││ Chain Info │ System │ Logs │ Peers │", + "│Bitcoin Config ││ │", + "│Bitcoin Status │└─────────────────────────────────────────────────────┘", + "│P2Pool Config │┌─────────────────────────────────────────────────────┐", + "│P2Pool Status ││Peers │", + "│LN Config ││ │", + "│LN Status ││ │", + "│Shares Market ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "└───────────────────────┘└─────────────────────────────────────────────────────┘", + " ↑↓ Navigate sidebar ←→ Switch tab q Quit ", + ], + styles: [ + x: 0, y: 0, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, + x: 56, y: 1, fg: Black, bg: Gray, underline: Reset, modifier: NONE, + x: 61, y: 1, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, + x: 1, y: 3, fg: Black, bg: Gray, underline: Reset, modifier: NONE, + x: 24, y: 3, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, + x: 0, y: 23, fg: White, bg: DarkGray, underline: Reset, modifier: NONE, + x: 4, y: 23, fg: DarkGray, bg: Black, underline: Reset, modifier: NONE, + x: 23, y: 23, fg: White, bg: DarkGray, underline: Reset, modifier: NONE, + x: 27, y: 23, fg: DarkGray, bg: Black, underline: Reset, modifier: NONE, + x: 40, y: 23, fg: White, bg: DarkGray, underline: Reset, modifier: NONE, + x: 43, y: 23, fg: DarkGray, bg: Black, underline: Reset, modifier: NONE, + x: 50, y: 23, fg: Reset, bg: Black, underline: Reset, modifier: NONE, + ] + }, + scrollback: Buffer { + area: Rect { x: 0, y: 0, width: 80, height: 0 } + }, + cursor: false, + pos: ( + 0, + 0, + ), +} diff --git a/src/snapshots/pdm__ui__tests__bitcoin_status_tab_system_render.snap b/src/snapshots/pdm__ui__tests__bitcoin_status_tab_system_render.snap new file mode 100644 index 0000000..34e5465 --- /dev/null +++ b/src/snapshots/pdm__ui__tests__bitcoin_status_tab_system_render.snap @@ -0,0 +1,57 @@ +--- +source: src/ui.rs +expression: terminal.backend() +--- +TestBackend { + buffer: Buffer { + area: Rect { x: 0, y: 0, width: 80, height: 24 }, + content: [ + "┌ PDM ──────────────────┐┌ Info ───────────────────────────────────────────────┐", + "│Home ││ Chain Info │ System │ Logs │ Peers │", + "│Bitcoin Config ││ │", + "│Bitcoin Status │└─────────────────────────────────────────────────────┘", + "│P2Pool Config │┌─────────────────────────────────────────────────────┐", + "│P2Pool Status ││System │", + "│LN Config ││ │", + "│LN Status ││ │", + "│Shares Market ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "└───────────────────────┘└─────────────────────────────────────────────────────┘", + " ↑↓ Navigate sidebar ←→ Switch tab q Quit ", + ], + styles: [ + x: 0, y: 0, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, + x: 40, y: 1, fg: Black, bg: Gray, underline: Reset, modifier: NONE, + x: 46, y: 1, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, + x: 1, y: 3, fg: Black, bg: Gray, underline: Reset, modifier: NONE, + x: 24, y: 3, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, + x: 0, y: 23, fg: White, bg: DarkGray, underline: Reset, modifier: NONE, + x: 4, y: 23, fg: DarkGray, bg: Black, underline: Reset, modifier: NONE, + x: 23, y: 23, fg: White, bg: DarkGray, underline: Reset, modifier: NONE, + x: 27, y: 23, fg: DarkGray, bg: Black, underline: Reset, modifier: NONE, + x: 40, y: 23, fg: White, bg: DarkGray, underline: Reset, modifier: NONE, + x: 43, y: 23, fg: DarkGray, bg: Black, underline: Reset, modifier: NONE, + x: 50, y: 23, fg: Reset, bg: Black, underline: Reset, modifier: NONE, + ] + }, + scrollback: Buffer { + area: Rect { x: 0, y: 0, width: 80, height: 0 } + }, + cursor: false, + pos: ( + 0, + 0, + ), +} diff --git a/src/snapshots/pdm__ui__tests__home_screen_render.snap b/src/snapshots/pdm__ui__tests__home_screen_render.snap index 7e664ba..1557c37 100644 --- a/src/snapshots/pdm__ui__tests__home_screen_render.snap +++ b/src/snapshots/pdm__ui__tests__home_screen_render.snap @@ -4,14 +4,17 @@ expression: terminal.backend() --- TestBackend { buffer: Buffer { - area: Rect { x: 0, y: 0, width: 80, height: 20 }, + area: Rect { x: 0, y: 0, width: 80, height: 24 }, content: [ "┌ PDM ──────────────────┐┌ Home ───────────────────────────────────────────────┐", "│Home ││Welcome to PDM. │", "│Bitcoin Config ││ │", - "│P2Pool Config ││Select a config from the sidebar to edit. │", - "│ ││ │", - "│ ││ │", + "│Bitcoin Status ││Select a config from the sidebar to edit. │", + "│P2Pool Config ││ │", + "│P2Pool Status ││ │", + "│LN Config ││ │", + "│LN Status ││ │", + "│Shares Market ││ │", "│ ││ │", "│ ││ │", "│ ││ │", @@ -26,11 +29,19 @@ TestBackend { "│ ││ │", "│ ││ │", "└───────────────────────┘└─────────────────────────────────────────────────────┘", + " ↑↓ Navigate sidebar Enter Select q Quit ", ], styles: [ x: 0, y: 0, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, x: 1, y: 1, fg: Black, bg: Gray, underline: Reset, modifier: NONE, x: 24, y: 1, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, + x: 0, y: 23, fg: White, bg: DarkGray, underline: Reset, modifier: NONE, + x: 4, y: 23, fg: DarkGray, bg: Black, underline: Reset, modifier: NONE, + x: 23, y: 23, fg: White, bg: DarkGray, underline: Reset, modifier: NONE, + x: 30, y: 23, fg: DarkGray, bg: Black, underline: Reset, modifier: NONE, + x: 39, y: 23, fg: White, bg: DarkGray, underline: Reset, modifier: NONE, + x: 42, y: 23, fg: DarkGray, bg: Black, underline: Reset, modifier: NONE, + x: 49, y: 23, fg: Reset, bg: Black, underline: Reset, modifier: NONE, ] }, scrollback: Buffer { diff --git a/src/snapshots/pdm__ui__tests__ln_config_screen_render.snap b/src/snapshots/pdm__ui__tests__ln_config_screen_render.snap new file mode 100644 index 0000000..d0b44a5 --- /dev/null +++ b/src/snapshots/pdm__ui__tests__ln_config_screen_render.snap @@ -0,0 +1,55 @@ +--- +source: src/ui.rs +expression: terminal.backend() +--- +TestBackend { + buffer: Buffer { + area: Rect { x: 0, y: 0, width: 80, height: 24 }, + content: [ + "┌ PDM ──────────────────┐┌ LN Config ──────────────────────────────────────────┐", + "│Home ││LN Config │", + "│Bitcoin Config ││ │", + "│Bitcoin Status ││ │", + "│P2Pool Config ││ │", + "│P2Pool Status ││ │", + "│LN Config ││ │", + "│LN Status ││ │", + "│Shares Market ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "└───────────────────────┘└─────────────────────────────────────────────────────┘", + " ↑↓ Navigate sidebar Enter Select q Quit ", + ], + styles: [ + x: 0, y: 0, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, + x: 1, y: 6, fg: Black, bg: Gray, underline: Reset, modifier: NONE, + x: 24, y: 6, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, + x: 0, y: 23, fg: White, bg: DarkGray, underline: Reset, modifier: NONE, + x: 4, y: 23, fg: DarkGray, bg: Black, underline: Reset, modifier: NONE, + x: 23, y: 23, fg: White, bg: DarkGray, underline: Reset, modifier: NONE, + x: 30, y: 23, fg: DarkGray, bg: Black, underline: Reset, modifier: NONE, + x: 39, y: 23, fg: White, bg: DarkGray, underline: Reset, modifier: NONE, + x: 42, y: 23, fg: DarkGray, bg: Black, underline: Reset, modifier: NONE, + x: 49, y: 23, fg: Reset, bg: Black, underline: Reset, modifier: NONE, + ] + }, + scrollback: Buffer { + area: Rect { x: 0, y: 0, width: 80, height: 0 } + }, + cursor: false, + pos: ( + 0, + 0, + ), +} diff --git a/src/snapshots/pdm__ui__tests__ln_status_screen_render.snap b/src/snapshots/pdm__ui__tests__ln_status_screen_render.snap new file mode 100644 index 0000000..63c2683 --- /dev/null +++ b/src/snapshots/pdm__ui__tests__ln_status_screen_render.snap @@ -0,0 +1,55 @@ +--- +source: src/ui.rs +expression: terminal.backend() +--- +TestBackend { + buffer: Buffer { + area: Rect { x: 0, y: 0, width: 80, height: 24 }, + content: [ + "┌ PDM ──────────────────┐┌ LN Status ──────────────────────────────────────────┐", + "│Home ││LN Status │", + "│Bitcoin Config ││ │", + "│Bitcoin Status ││ │", + "│P2Pool Config ││ │", + "│P2Pool Status ││ │", + "│LN Config ││ │", + "│LN Status ││ │", + "│Shares Market ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "└───────────────────────┘└─────────────────────────────────────────────────────┘", + " ↑↓ Navigate sidebar Enter Select q Quit ", + ], + styles: [ + x: 0, y: 0, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, + x: 1, y: 7, fg: Black, bg: Gray, underline: Reset, modifier: NONE, + x: 24, y: 7, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, + x: 0, y: 23, fg: White, bg: DarkGray, underline: Reset, modifier: NONE, + x: 4, y: 23, fg: DarkGray, bg: Black, underline: Reset, modifier: NONE, + x: 23, y: 23, fg: White, bg: DarkGray, underline: Reset, modifier: NONE, + x: 30, y: 23, fg: DarkGray, bg: Black, underline: Reset, modifier: NONE, + x: 39, y: 23, fg: White, bg: DarkGray, underline: Reset, modifier: NONE, + x: 42, y: 23, fg: DarkGray, bg: Black, underline: Reset, modifier: NONE, + x: 49, y: 23, fg: Reset, bg: Black, underline: Reset, modifier: NONE, + ] + }, + scrollback: Buffer { + area: Rect { x: 0, y: 0, width: 80, height: 0 } + }, + cursor: false, + pos: ( + 0, + 0, + ), +} diff --git a/src/snapshots/pdm__ui__tests__p2pool_screen_render.snap b/src/snapshots/pdm__ui__tests__p2pool_config_screen_render.snap similarity index 66% rename from src/snapshots/pdm__ui__tests__p2pool_screen_render.snap rename to src/snapshots/pdm__ui__tests__p2pool_config_screen_render.snap index af40537..1e183bf 100644 --- a/src/snapshots/pdm__ui__tests__p2pool_screen_render.snap +++ b/src/snapshots/pdm__ui__tests__p2pool_config_screen_render.snap @@ -4,14 +4,17 @@ expression: terminal.backend() --- TestBackend { buffer: Buffer { - area: Rect { x: 0, y: 0, width: 80, height: 20 }, + area: Rect { x: 0, y: 0, width: 80, height: 24 }, content: [ "┌ PDM ──────────────────┐┌ P2Pool Config ──────────────────────────────────────┐", "│Home ││Press [Enter] to select a p2poolv2 config file │", "│Bitcoin Config ││ │", + "│Bitcoin Status ││ │", "│P2Pool Config ││ │", - "│ ││ │", - "│ ││ │", + "│P2Pool Status ││ │", + "│LN Config ││ │", + "│LN Status ││ │", + "│Shares Market ││ │", "│ ││ │", "│ ││ │", "│ ││ │", @@ -26,11 +29,19 @@ TestBackend { "│ ││ │", "│ ││ │", "└───────────────────────┘└─────────────────────────────────────────────────────┘", + " ↑↓ Navigate sidebar Enter Select q Quit ", ], styles: [ x: 0, y: 0, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, - x: 1, y: 3, fg: Black, bg: Gray, underline: Reset, modifier: NONE, - x: 24, y: 3, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, + x: 1, y: 4, fg: Black, bg: Gray, underline: Reset, modifier: NONE, + x: 24, y: 4, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, + x: 0, y: 23, fg: White, bg: DarkGray, underline: Reset, modifier: NONE, + x: 4, y: 23, fg: DarkGray, bg: Black, underline: Reset, modifier: NONE, + x: 23, y: 23, fg: White, bg: DarkGray, underline: Reset, modifier: NONE, + x: 30, y: 23, fg: DarkGray, bg: Black, underline: Reset, modifier: NONE, + x: 39, y: 23, fg: White, bg: DarkGray, underline: Reset, modifier: NONE, + x: 42, y: 23, fg: DarkGray, bg: Black, underline: Reset, modifier: NONE, + x: 49, y: 23, fg: Reset, bg: Black, underline: Reset, modifier: NONE, ] }, scrollback: Buffer { diff --git a/src/snapshots/pdm__ui__tests__p2pool_screen_render.snap.new b/src/snapshots/pdm__ui__tests__p2pool_screen_render.snap.new new file mode 100644 index 0000000..cea0888 --- /dev/null +++ b/src/snapshots/pdm__ui__tests__p2pool_screen_render.snap.new @@ -0,0 +1,54 @@ +--- +source: src/ui.rs +assertion_line: 423 +expression: terminal.backend() +--- +TestBackend { + buffer: Buffer { + area: Rect { x: 0, y: 0, width: 80, height: 20 }, + content: [ + "┌ PDM ──────────────────┐┌ Info ───────────────────────────────────────────────┐", + "│Home ││ Chain Info │ System │ Logs │ Peers │", + "│Bitcoin Config ││ │", + "│Bitcoin Status │└─────────────────────────────────────────────────────┘", + "│P2Pool Config │┌─────────────────────────────────────────────────────┐", + "│P2Pool Status ││Chain Info │", + "│LN Config ││ │", + "│LN Status ││ │", + "│Shares Market ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "└───────────────────────┘└─────────────────────────────────────────────────────┘", + " ↑↓ Navigate sidebar ←→ Switch tab q Quit ", + ], + styles: [ + x: 0, y: 0, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, + x: 27, y: 1, fg: Black, bg: Gray, underline: Reset, modifier: NONE, + x: 37, y: 1, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, + x: 1, y: 3, fg: Black, bg: Gray, underline: Reset, modifier: NONE, + x: 24, y: 3, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, + x: 0, y: 19, fg: White, bg: DarkGray, underline: Reset, modifier: NONE, + x: 4, y: 19, fg: DarkGray, bg: Black, underline: Reset, modifier: NONE, + x: 23, y: 19, fg: White, bg: DarkGray, underline: Reset, modifier: NONE, + x: 27, y: 19, fg: DarkGray, bg: Black, underline: Reset, modifier: NONE, + x: 40, y: 19, fg: White, bg: DarkGray, underline: Reset, modifier: NONE, + x: 43, y: 19, fg: DarkGray, bg: Black, underline: Reset, modifier: NONE, + x: 50, y: 19, fg: Reset, bg: Black, underline: Reset, modifier: NONE, + ] + }, + scrollback: Buffer { + area: Rect { x: 0, y: 0, width: 80, height: 0 } + }, + cursor: false, + pos: ( + 0, + 0, + ), +} diff --git a/src/snapshots/pdm__ui__tests__p2pool_status_screen_render.snap b/src/snapshots/pdm__ui__tests__p2pool_status_screen_render.snap new file mode 100644 index 0000000..f11c111 --- /dev/null +++ b/src/snapshots/pdm__ui__tests__p2pool_status_screen_render.snap @@ -0,0 +1,55 @@ +--- +source: src/ui.rs +expression: terminal.backend() +--- +TestBackend { + buffer: Buffer { + area: Rect { x: 0, y: 0, width: 80, height: 24 }, + content: [ + "┌ PDM ──────────────────┐┌ P2Pool Status ──────────────────────────────────────┐", + "│Home ││P2Pool Status │", + "│Bitcoin Config ││ │", + "│Bitcoin Status ││ │", + "│P2Pool Config ││ │", + "│P2Pool Status ││ │", + "│LN Config ││ │", + "│LN Status ││ │", + "│Shares Market ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "└───────────────────────┘└─────────────────────────────────────────────────────┘", + " ↑↓ Navigate sidebar Enter Select q Quit ", + ], + styles: [ + x: 0, y: 0, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, + x: 1, y: 5, fg: Black, bg: Gray, underline: Reset, modifier: NONE, + x: 24, y: 5, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, + x: 0, y: 23, fg: White, bg: DarkGray, underline: Reset, modifier: NONE, + x: 4, y: 23, fg: DarkGray, bg: Black, underline: Reset, modifier: NONE, + x: 23, y: 23, fg: White, bg: DarkGray, underline: Reset, modifier: NONE, + x: 30, y: 23, fg: DarkGray, bg: Black, underline: Reset, modifier: NONE, + x: 39, y: 23, fg: White, bg: DarkGray, underline: Reset, modifier: NONE, + x: 42, y: 23, fg: DarkGray, bg: Black, underline: Reset, modifier: NONE, + x: 49, y: 23, fg: Reset, bg: Black, underline: Reset, modifier: NONE, + ] + }, + scrollback: Buffer { + area: Rect { x: 0, y: 0, width: 80, height: 0 } + }, + cursor: false, + pos: ( + 0, + 0, + ), +} diff --git a/src/snapshots/pdm__ui__tests__shares_market_screen_render.snap b/src/snapshots/pdm__ui__tests__shares_market_screen_render.snap new file mode 100644 index 0000000..d6a49bd --- /dev/null +++ b/src/snapshots/pdm__ui__tests__shares_market_screen_render.snap @@ -0,0 +1,55 @@ +--- +source: src/ui.rs +expression: terminal.backend() +--- +TestBackend { + buffer: Buffer { + area: Rect { x: 0, y: 0, width: 80, height: 24 }, + content: [ + "┌ PDM ──────────────────┐┌ Share Market ───────────────────────────────────────┐", + "│Home ││Share Market │", + "│Bitcoin Config ││ │", + "│Bitcoin Status ││ │", + "│P2Pool Config ││ │", + "│P2Pool Status ││ │", + "│LN Config ││ │", + "│LN Status ││ │", + "│Shares Market ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "│ ││ │", + "└───────────────────────┘└─────────────────────────────────────────────────────┘", + " ↑↓ Navigate sidebar Enter Select q Quit ", + ], + styles: [ + x: 0, y: 0, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, + x: 1, y: 8, fg: Black, bg: Gray, underline: Reset, modifier: NONE, + x: 24, y: 8, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, + x: 0, y: 23, fg: White, bg: DarkGray, underline: Reset, modifier: NONE, + x: 4, y: 23, fg: DarkGray, bg: Black, underline: Reset, modifier: NONE, + x: 23, y: 23, fg: White, bg: DarkGray, underline: Reset, modifier: NONE, + x: 30, y: 23, fg: DarkGray, bg: Black, underline: Reset, modifier: NONE, + x: 39, y: 23, fg: White, bg: DarkGray, underline: Reset, modifier: NONE, + x: 42, y: 23, fg: DarkGray, bg: Black, underline: Reset, modifier: NONE, + x: 49, y: 23, fg: Reset, bg: Black, underline: Reset, modifier: NONE, + ] + }, + scrollback: Buffer { + area: Rect { x: 0, y: 0, width: 80, height: 0 } + }, + cursor: false, + pos: ( + 0, + 0, + ), +} diff --git a/src/ui.rs b/src/ui.rs index c97f0da..9c445bb 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -5,7 +5,7 @@ use crate::app::{App, CurrentScreen}; use ratatui::{ prelude::*, - widgets::{Block, Borders, List, ListItem, ListState, Paragraph, Wrap}, + widgets::{Block, Borders, List, ListItem, ListState, Paragraph, Tabs, Wrap}, }; pub fn ui(f: &mut Frame, app: &mut App) { @@ -32,8 +32,12 @@ pub fn ui(f: &mut Frame, app: &mut App) { let items = vec![ ListItem::new("Home"), ListItem::new("Bitcoin Config"), + ListItem::new("Bitcoin Status"), ListItem::new("P2Pool Config"), - ListItem::new("Info"), + ListItem::new("P2Pool Status"), + ListItem::new("LN Config"), + ListItem::new("LN Status"), + ListItem::new("Shares Market"), ]; // Highlight the active one @@ -68,7 +72,9 @@ pub fn ui(f: &mut Frame, app: &mut App) { f.render_widget(p, main_area); } } - + CurrentScreen::BitcoinStatus => { + render_bitcoin_status_view(f, app, main_area); + } CurrentScreen::P2PoolConfig => { if app.p2pool_conf_path.is_some() { render_p2pool_view(f, app, main_area); @@ -81,10 +87,30 @@ pub fn ui(f: &mut Frame, app: &mut App) { f.render_widget(p, main_area); } } - CurrentScreen::Info => { - let p = Paragraph::new("PDM — P2Pool & Bitcoin Config Manager\n\nTemporary text.") - .block(Block::default().borders(Borders::ALL).title(" Info ")) - .wrap(Wrap { trim: true }); + CurrentScreen::P2PoolStatus => { + let p = Paragraph::new("P2Pool Status").block( + Block::default() + .borders(Borders::ALL) + .title(" P2Pool Status "), + ); + f.render_widget(p, main_area); + } + CurrentScreen::LNConfig => { + let p = Paragraph::new("LN Config") + .block(Block::default().borders(Borders::ALL).title(" LN Config ")); + f.render_widget(p, main_area); + } + CurrentScreen::LNStatus => { + let p = Paragraph::new("LN Status") + .block(Block::default().borders(Borders::ALL).title(" LN Status ")); + f.render_widget(p, main_area); + } + CurrentScreen::SharesMarket => { + let p = Paragraph::new("Share Market").block( + Block::default() + .borders(Borders::ALL) + .title(" Share Market "), + ); f.render_widget(p, main_area); } CurrentScreen::FileExplorer => { @@ -106,6 +132,7 @@ fn hint(key: &str, desc: &str) -> Vec> { ] } +// Status bar fn render_status_bar(f: &mut Frame, app: &App, area: Rect) { let mut spans: Vec = Vec::new(); @@ -125,6 +152,11 @@ fn render_status_bar(f: &mut Frame, app: &App, area: Rect) { spans.extend(hint("Enter", "Open file")); spans.extend(hint("q", "Quit")); } + CurrentScreen::BitcoinStatus => { + spans.extend(hint("↑↓", "Navigate sidebar")); + spans.extend(hint("←→", "Switch tab")); + spans.extend(hint("q", "Quit")); + } _ => { spans.extend(hint("↑↓", "Navigate sidebar")); spans.extend(hint("Enter", "Select")); @@ -136,17 +168,75 @@ fn render_status_bar(f: &mut Frame, app: &App, area: Rect) { f.render_widget(bar, area); } +fn render_bitcoin_status_view(f: &mut Frame, app: &App, area: Rect) { + let outer = Layout::default() + .direction(Direction::Vertical) + .constraints([ + Constraint::Length(4), // Tabs bar + Constraint::Min(0), // Tab content + ]) + .split(area); + + let tabs = Tabs::new(vec!["Chain Info", "System", "Logs", "Peers"]) + .block(Block::default().borders(Borders::ALL).title(" Info ")) + .select(app.bitcoin_status_tab) + .highlight_style(Style::default().bg(Color::Gray).fg(Color::Black)); + + f.render_widget(tabs, outer[0]); + + let content_area = outer[1]; + match app.bitcoin_status_tab { + // Chain Info + 0 => { + let text = "Chain Info"; + let p = Paragraph::new(text) + .block(Block::default().borders(Borders::ALL)) + .wrap(Wrap { trim: true }); + f.render_widget(p, content_area); + } + // System + 1 => { + let text = "System"; + let p = Paragraph::new(text) + .block(Block::default().borders(Borders::ALL)) + .wrap(Wrap { trim: true }); + f.render_widget(p, content_area); + } + // Logs + 2 => { + let text = "Logs"; + let p = Paragraph::new(text) + .block(Block::default().borders(Borders::ALL)) + .wrap(Wrap { trim: true }); + f.render_widget(p, content_area); + } + // Peers + 3 => { + let text = "Peers"; + let p = Paragraph::new(text) + .block(Block::default().borders(Borders::ALL)) + .wrap(Wrap { trim: true }); + f.render_widget(p, content_area); + } + _ => {} + } +} + fn render_file_explorer(f: &mut Frame, app: &mut App, area: Rect) { let files: Vec = app .explorer .files .iter() .map(|path| { - let name = path.file_name().unwrap_or_default().to_string_lossy(); - let display_name = if path.is_dir() { - format!("📁 {}", name) + let display_name = if path.ends_with("..") { + "📁 ..".to_string() } else { - format!("📄 {}", name) + let name = path.file_name().unwrap_or_default().to_string_lossy(); + if path.is_dir() { + format!("📁 {}", name) + } else { + format!("📄 {}", name) + } }; ListItem::new(display_name) }) @@ -293,43 +383,118 @@ mod tests { use ratatui::Terminal; use ratatui::backend::TestBackend; + fn make_terminal() -> Terminal { + Terminal::new(TestBackend::new(80, 24)).unwrap() + } + #[test] fn test_home_screen_render() { - let backend = TestBackend::new(80, 20); - let mut terminal = Terminal::new(backend).unwrap(); - + let mut terminal = make_terminal(); let mut app = App::new(); + terminal.draw(|f| ui(f, &mut app)).unwrap(); + insta::assert_debug_snapshot!(terminal.backend()); + } + #[test] + fn test_bitcoin_config_screen_render() { + let mut terminal = make_terminal(); + let mut app = App::new(); + app.sidebar_index = 1; + app.toggle_menu(); terminal.draw(|f| ui(f, &mut app)).unwrap(); + insta::assert_debug_snapshot!(terminal.backend()); + } + #[test] + fn test_bitcoin_status_screen_render() { + let mut terminal = make_terminal(); + let mut app = App::new(); + app.sidebar_index = 2; + app.toggle_menu(); + terminal.draw(|f| ui(f, &mut app)).unwrap(); insta::assert_debug_snapshot!(terminal.backend()); } #[test] - fn test_bitcoin_screen_render() { - let backend = TestBackend::new(80, 20); - let mut terminal = Terminal::new(backend).unwrap(); + fn test_bitcoin_status_tab_system_render() { + let mut terminal = make_terminal(); + let mut app = App::new(); + app.sidebar_index = 2; + app.toggle_menu(); + app.bitcoin_status_tab = 1; + terminal.draw(|f| ui(f, &mut app)).unwrap(); + insta::assert_debug_snapshot!(terminal.backend()); + } + #[test] + fn test_bitcoin_status_tab_logs_render() { + let mut terminal = make_terminal(); let mut app = App::new(); - app.sidebar_index = 1; + app.sidebar_index = 2; app.toggle_menu(); + app.bitcoin_status_tab = 2; + terminal.draw(|f| ui(f, &mut app)).unwrap(); + insta::assert_debug_snapshot!(terminal.backend()); + } + #[test] + fn test_bitcoin_status_tab_peers_render() { + let mut terminal = make_terminal(); + let mut app = App::new(); + app.sidebar_index = 2; + app.toggle_menu(); + app.bitcoin_status_tab = 3; terminal.draw(|f| ui(f, &mut app)).unwrap(); + insta::assert_debug_snapshot!(terminal.backend()); + } + #[test] + fn test_p2pool_config_screen_render() { + let mut terminal = make_terminal(); + let mut app = App::new(); + app.sidebar_index = 3; + app.toggle_menu(); + terminal.draw(|f| ui(f, &mut app)).unwrap(); insta::assert_debug_snapshot!(terminal.backend()); } #[test] - fn test_p2pool_screen_render() { - let backend = TestBackend::new(80, 20); - let mut terminal = Terminal::new(backend).unwrap(); + fn test_p2pool_status_screen_render() { + let mut terminal = make_terminal(); + let mut app = App::new(); + app.sidebar_index = 4; + app.toggle_menu(); + terminal.draw(|f| ui(f, &mut app)).unwrap(); + insta::assert_debug_snapshot!(terminal.backend()); + } + #[test] + fn test_ln_config_screen_render() { + let mut terminal = make_terminal(); let mut app = App::new(); - app.sidebar_index = 2; + app.sidebar_index = 5; app.toggle_menu(); + terminal.draw(|f| ui(f, &mut app)).unwrap(); + insta::assert_debug_snapshot!(terminal.backend()); + } + #[test] + fn test_ln_status_screen_render() { + let mut terminal = make_terminal(); + let mut app = App::new(); + app.sidebar_index = 6; + app.toggle_menu(); terminal.draw(|f| ui(f, &mut app)).unwrap(); + insta::assert_debug_snapshot!(terminal.backend()); + } + #[test] + fn test_shares_market_screen_render() { + let mut terminal = make_terminal(); + let mut app = App::new(); + app.sidebar_index = 7; + app.toggle_menu(); + terminal.draw(|f| ui(f, &mut app)).unwrap(); insta::assert_debug_snapshot!(terminal.backend()); } } diff --git a/tests/snapshots/ui_snapshots__config_screen_render.snap b/tests/snapshots/ui_snapshots__config_screen_render.snap index 32f7690..5445ad4 100644 --- a/tests/snapshots/ui_snapshots__config_screen_render.snap +++ b/tests/snapshots/ui_snapshots__config_screen_render.snap @@ -9,13 +9,12 @@ TestBackend { "┌ PDM ──────────────────┐┌ Bitcoin Config ─────────────────────────────────────┐", "│Home ││Press [Enter] to select a bitcoin.conf file │", "│Bitcoin Config ││ │", + "│Bitcoin Status ││ │", "│P2Pool Config ││ │", - "│ ││ │", - "│ ││ │", - "│ ││ │", - "│ ││ │", - "│ ││ │", - "│ ││ │", + "│P2Pool Status ││ │", + "│LN Config ││ │", + "│LN Status ││ │", + "│Shares Market ││ │", "│ ││ │", "│ ││ │", "│ ││ │", @@ -31,11 +30,19 @@ TestBackend { "│ ││ │", "│ ││ │", "└───────────────────────┘└─────────────────────────────────────────────────────┘", + " ↑↓ Navigate sidebar Enter Select q Quit ", ], styles: [ x: 0, y: 0, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, x: 1, y: 2, fg: Black, bg: Gray, underline: Reset, modifier: NONE, x: 24, y: 2, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, + x: 0, y: 24, fg: White, bg: DarkGray, underline: Reset, modifier: NONE, + x: 4, y: 24, fg: DarkGray, bg: Black, underline: Reset, modifier: NONE, + x: 23, y: 24, fg: White, bg: DarkGray, underline: Reset, modifier: NONE, + x: 30, y: 24, fg: DarkGray, bg: Black, underline: Reset, modifier: NONE, + x: 39, y: 24, fg: White, bg: DarkGray, underline: Reset, modifier: NONE, + x: 42, y: 24, fg: DarkGray, bg: Black, underline: Reset, modifier: NONE, + x: 49, y: 24, fg: Reset, bg: Black, underline: Reset, modifier: NONE, ] }, scrollback: Buffer { diff --git a/tests/snapshots/ui_snapshots__home_screen_render.snap b/tests/snapshots/ui_snapshots__home_screen_render.snap index df0f123..c3e52fa 100644 --- a/tests/snapshots/ui_snapshots__home_screen_render.snap +++ b/tests/snapshots/ui_snapshots__home_screen_render.snap @@ -9,13 +9,12 @@ TestBackend { "┌ PDM ──────────────────┐┌ Home ───────────────────────────────────────────────┐", "│Home ││Welcome to PDM. │", "│Bitcoin Config ││ │", - "│P2Pool Config ││Select a config from the sidebar to edit. │", - "│ ││ │", - "│ ││ │", - "│ ││ │", - "│ ││ │", - "│ ││ │", - "│ ││ │", + "│Bitcoin Status ││Select a config from the sidebar to edit. │", + "│P2Pool Config ││ │", + "│P2Pool Status ││ │", + "│LN Config ││ │", + "│LN Status ││ │", + "│Shares Market ││ │", "│ ││ │", "│ ││ │", "│ ││ │", @@ -31,11 +30,19 @@ TestBackend { "│ ││ │", "│ ││ │", "└───────────────────────┘└─────────────────────────────────────────────────────┘", + " ↑↓ Navigate sidebar Enter Select q Quit ", ], styles: [ x: 0, y: 0, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, x: 1, y: 1, fg: Black, bg: Gray, underline: Reset, modifier: NONE, x: 24, y: 1, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, + x: 0, y: 24, fg: White, bg: DarkGray, underline: Reset, modifier: NONE, + x: 4, y: 24, fg: DarkGray, bg: Black, underline: Reset, modifier: NONE, + x: 23, y: 24, fg: White, bg: DarkGray, underline: Reset, modifier: NONE, + x: 30, y: 24, fg: DarkGray, bg: Black, underline: Reset, modifier: NONE, + x: 39, y: 24, fg: White, bg: DarkGray, underline: Reset, modifier: NONE, + x: 42, y: 24, fg: DarkGray, bg: Black, underline: Reset, modifier: NONE, + x: 49, y: 24, fg: Reset, bg: Black, underline: Reset, modifier: NONE, ] }, scrollback: Buffer { From 5f6dc1f1d4564cc85ed33b8535f562a60856c398 Mon Sep 17 00:00:00 2001 From: Francesco Date: Wed, 11 Mar 2026 15:17:54 +0400 Subject: [PATCH 3/7] Update UI --- src/ui.rs | 355 +++++++++++++++++++++++++++++------------------------- 1 file changed, 192 insertions(+), 163 deletions(-) diff --git a/src/ui.rs b/src/ui.rs index 9c445bb..a23bc89 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -55,63 +55,28 @@ pub fn ui(f: &mut Frame, app: &mut App) { match app.current_screen { CurrentScreen::Home => { - let p = Paragraph::new("Welcome to PDM.\n\nSelect a config from the sidebar to edit.") - .block(Block::default().borders(Borders::ALL).title(" Home ")) - .wrap(Wrap { trim: true }); - f.render_widget(p, main_area); + render_home_view(f, app, main_area); } CurrentScreen::BitcoinConfig => { - if app.bitcoin_conf_path.is_some() { - render_bitcoin_view(f, app, main_area); - } else { - let p = Paragraph::new("Press [Enter] to select a bitcoin.conf file").block( - Block::default() - .borders(Borders::ALL) - .title(" Bitcoin Config "), - ); - f.render_widget(p, main_area); - } + render_bitcoin_view(f, app, main_area); } CurrentScreen::BitcoinStatus => { render_bitcoin_status_view(f, app, main_area); } CurrentScreen::P2PoolConfig => { - if app.p2pool_conf_path.is_some() { - render_p2pool_view(f, app, main_area); - } else { - let p = Paragraph::new("Press [Enter] to select a p2poolv2 config file").block( - Block::default() - .borders(Borders::ALL) - .title(" P2Pool Config "), - ); - f.render_widget(p, main_area); - } + render_p2pool_view(f, app, main_area); } CurrentScreen::P2PoolStatus => { - let p = Paragraph::new("P2Pool Status").block( - Block::default() - .borders(Borders::ALL) - .title(" P2Pool Status "), - ); - f.render_widget(p, main_area); + render_p2pool_status_view(f, app, main_area); } CurrentScreen::LNConfig => { - let p = Paragraph::new("LN Config") - .block(Block::default().borders(Borders::ALL).title(" LN Config ")); - f.render_widget(p, main_area); + render_ln_config_view(f, app, main_area); } CurrentScreen::LNStatus => { - let p = Paragraph::new("LN Status") - .block(Block::default().borders(Borders::ALL).title(" LN Status ")); - f.render_widget(p, main_area); + render_ln_status_view(f, app, main_area); } CurrentScreen::SharesMarket => { - let p = Paragraph::new("Share Market").block( - Block::default() - .borders(Borders::ALL) - .title(" Share Market "), - ); - f.render_widget(p, main_area); + render_shares_market_view(f, app, main_area); } CurrentScreen::FileExplorer => { render_file_explorer(f, app, main_area); @@ -168,6 +133,63 @@ fn render_status_bar(f: &mut Frame, app: &App, area: Rect) { f.render_widget(bar, area); } +// Home +fn render_home_view(f: &mut Frame, app: &mut App, area: Rect) { + let p = Paragraph::new("Welcome to PDM.\n\nSelect a config from the sidebar to edit.") + .block(Block::default().borders(Borders::ALL).title(" Home ")) + .wrap(Wrap { trim: true }); + f.render_widget(p, area); +} + +// Bitcoin Config +fn render_bitcoin_view(f: &mut Frame, app: &mut App, area: Rect) { + if app.bitcoin_conf_path.is_some() { + let items: Vec = app + .bitcoin_data + .iter() + .map(|entry| { + let style = if entry.enabled { + Style::default() + .fg(Color::White) + .add_modifier(Modifier::BOLD) + } else { + Style::default().fg(Color::DarkGray) + }; + + let content = Line::from(vec![ + Span::styled(format!("{} = ", entry.key), style), + Span::styled(&entry.value, style), + if !entry.enabled { + Span::styled(" (disabled)", style) + } else { + Span::raw("") + }, + ]); + + ListItem::new(content) + }) + .collect(); + + let list = List::new(items) + .block( + Block::default() + .borders(Borders::ALL) + .title(" Bitcoin Configuration "), + ) + .highlight_style(Style::default().bg(Color::Yellow)); + + f.render_widget(list, area); + } else { + let p = Paragraph::new("Press [Enter] to select a bitcoin.conf file").block( + Block::default() + .borders(Borders::ALL) + .title(" Bitcoin Config "), + ); + f.render_widget(p, area); + } +} + +// Bitcoin Status fn render_bitcoin_status_view(f: &mut Frame, app: &App, area: Rect) { let outer = Layout::default() .direction(Direction::Vertical) @@ -222,6 +244,134 @@ fn render_bitcoin_status_view(f: &mut Frame, app: &App, area: Rect) { } } +// P2Pool Config +fn render_p2pool_view(f: &mut Frame, app: &mut App, area: Rect) { + if app.p2pool_conf_path.is_some() { + let mut items: Vec = Vec::new(); + + if let Some(cfg) = &app.p2pool_config { + // STRATUM + items.push(ListItem::new(Line::from(vec![ + Span::styled("[stratum] ", Style::default().fg(Color::Blue)), + Span::raw(format!("hostname = {}", cfg.stratum.hostname)), + ]))); + + items.push(ListItem::new(Line::from(vec![ + Span::styled("[stratum] ", Style::default().fg(Color::Blue)), + Span::raw(format!("port = {}", cfg.stratum.port)), + ]))); + + items.push(ListItem::new(Line::from(vec![ + Span::styled("[stratum] ", Style::default().fg(Color::Blue)), + Span::raw(format!( + "start_difficulty = {}", + cfg.stratum.start_difficulty + )), + ]))); + + items.push(ListItem::new(Line::from(vec![ + Span::styled("[stratum] ", Style::default().fg(Color::Blue)), + Span::raw(format!( + "minimum_difficulty = {}", + cfg.stratum.minimum_difficulty + )), + ]))); + + // BITCOIN RPC + items.push(ListItem::new(Line::from(vec![ + Span::styled("[bitcoinrpc] ", Style::default().fg(Color::Blue)), + Span::raw(format!("url = {}", cfg.bitcoinrpc.url)), + ]))); + + items.push(ListItem::new(Line::from(vec![ + Span::styled("[bitcoinrpc] ", Style::default().fg(Color::Blue)), + Span::raw(format!("username = {}", cfg.bitcoinrpc.username)), + ]))); + + // NETWORK + items.push(ListItem::new(Line::from(vec![ + Span::styled("[network] ", Style::default().fg(Color::Blue)), + Span::raw(format!("listen_address = {}", cfg.network.listen_address)), + ]))); + + items.push(ListItem::new(Line::from(vec![ + Span::styled("[network] ", Style::default().fg(Color::Blue)), + Span::raw(format!( + "max_established_incoming = {}", + cfg.network.max_established_incoming + )), + ]))); + + // STORE + items.push(ListItem::new(Line::from(vec![ + Span::styled("[store] ", Style::default().fg(Color::Blue)), + Span::raw(format!("path = {}", cfg.store.path)), + ]))); + + // API + items.push(ListItem::new(Line::from(vec![ + Span::styled("[api] ", Style::default().fg(Color::Blue)), + Span::raw(format!("hostname = {}", cfg.api.hostname)), + ]))); + + items.push(ListItem::new(Line::from(vec![ + Span::styled("[api] ", Style::default().fg(Color::Blue)), + Span::raw(format!("port = {}", cfg.api.port)), + ]))); + } + + let list = List::new(items).block( + Block::default() + .borders(Borders::ALL) + .title(" P2Pool Configuration "), + ); + + f.render_widget(list, area); + } else { + let p = Paragraph::new("Press [Enter] to select a p2poolv2 config file").block( + Block::default() + .borders(Borders::ALL) + .title(" P2Pool Config "), + ); + f.render_widget(p, area); + } +} + +// P2Pool Status +fn render_p2pool_status_view(f: &mut Frame, app: &mut App, area: Rect) { + let p = Paragraph::new("P2Pool Status").block( + Block::default() + .borders(Borders::ALL) + .title(" P2Pool Status "), + ); + f.render_widget(p, area); +} + +// LN Config +fn render_ln_config_view(f: &mut Frame, app: &mut App, area: Rect) { + let p = Paragraph::new("LN Config") + .block(Block::default().borders(Borders::ALL).title(" LN Config ")); + f.render_widget(p, area); +} + +// LN Status +fn render_ln_status_view(f: &mut Frame, app: &mut App, area: Rect) { + let p = Paragraph::new("LN Status") + .block(Block::default().borders(Borders::ALL).title(" LN Status ")); + f.render_widget(p, area); +} + +// Shares Market +fn render_shares_market_view(f: &mut Frame, app: &mut App, area: Rect) { + let p = Paragraph::new("Share Market").block( + Block::default() + .borders(Borders::ALL) + .title(" Share Market "), + ); + f.render_widget(p, area); +} + +// File Explorer fn render_file_explorer(f: &mut Frame, app: &mut App, area: Rect) { let files: Vec = app .explorer @@ -255,127 +405,6 @@ fn render_file_explorer(f: &mut Frame, app: &mut App, area: Rect) { f.render_stateful_widget(list, area, &mut state); } -fn render_p2pool_view(f: &mut Frame, app: &mut App, area: Rect) { - let mut items: Vec = Vec::new(); - - if let Some(cfg) = &app.p2pool_config { - // STRATUM - items.push(ListItem::new(Line::from(vec![ - Span::styled("[stratum] ", Style::default().fg(Color::Blue)), - Span::raw(format!("hostname = {}", cfg.stratum.hostname)), - ]))); - - items.push(ListItem::new(Line::from(vec![ - Span::styled("[stratum] ", Style::default().fg(Color::Blue)), - Span::raw(format!("port = {}", cfg.stratum.port)), - ]))); - - items.push(ListItem::new(Line::from(vec![ - Span::styled("[stratum] ", Style::default().fg(Color::Blue)), - Span::raw(format!( - "start_difficulty = {}", - cfg.stratum.start_difficulty - )), - ]))); - - items.push(ListItem::new(Line::from(vec![ - Span::styled("[stratum] ", Style::default().fg(Color::Blue)), - Span::raw(format!( - "minimum_difficulty = {}", - cfg.stratum.minimum_difficulty - )), - ]))); - - // BITCOIN RPC - items.push(ListItem::new(Line::from(vec![ - Span::styled("[bitcoinrpc] ", Style::default().fg(Color::Blue)), - Span::raw(format!("url = {}", cfg.bitcoinrpc.url)), - ]))); - - items.push(ListItem::new(Line::from(vec![ - Span::styled("[bitcoinrpc] ", Style::default().fg(Color::Blue)), - Span::raw(format!("username = {}", cfg.bitcoinrpc.username)), - ]))); - - // NETWORK - items.push(ListItem::new(Line::from(vec![ - Span::styled("[network] ", Style::default().fg(Color::Blue)), - Span::raw(format!("listen_address = {}", cfg.network.listen_address)), - ]))); - - items.push(ListItem::new(Line::from(vec![ - Span::styled("[network] ", Style::default().fg(Color::Blue)), - Span::raw(format!( - "max_established_incoming = {}", - cfg.network.max_established_incoming - )), - ]))); - - // STORE - items.push(ListItem::new(Line::from(vec![ - Span::styled("[store] ", Style::default().fg(Color::Blue)), - Span::raw(format!("path = {}", cfg.store.path)), - ]))); - - // API - items.push(ListItem::new(Line::from(vec![ - Span::styled("[api] ", Style::default().fg(Color::Blue)), - Span::raw(format!("hostname = {}", cfg.api.hostname)), - ]))); - - items.push(ListItem::new(Line::from(vec![ - Span::styled("[api] ", Style::default().fg(Color::Blue)), - Span::raw(format!("port = {}", cfg.api.port)), - ]))); - } - - let list = List::new(items).block( - Block::default() - .borders(Borders::ALL) - .title(" P2Pool Configuration "), - ); - - f.render_widget(list, area); -} - -fn render_bitcoin_view(f: &mut Frame, app: &mut App, area: Rect) { - let items: Vec = app - .bitcoin_data - .iter() - .map(|entry| { - let style = if entry.enabled { - Style::default() - .fg(Color::White) - .add_modifier(Modifier::BOLD) - } else { - Style::default().fg(Color::DarkGray) - }; - - let content = Line::from(vec![ - Span::styled(format!("{} = ", entry.key), style), - Span::styled(&entry.value, style), - if !entry.enabled { - Span::styled(" (disabled)", style) - } else { - Span::raw("") - }, - ]); - - ListItem::new(content) - }) - .collect(); - - let list = List::new(items) - .block( - Block::default() - .borders(Borders::ALL) - .title(" Bitcoin Configuration "), - ) - .highlight_style(Style::default().bg(Color::Yellow)); - - f.render_widget(list, area); -} - #[cfg(test)] mod tests { use super::*; From edbabdb9be2310e5cbaa51a710edff8d424b59de Mon Sep 17 00:00:00 2001 From: Francesco Date: Wed, 11 Mar 2026 15:51:44 +0400 Subject: [PATCH 4/7] Add clippy fix --- src/ui.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ui.rs b/src/ui.rs index a23bc89..3b509f6 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -134,7 +134,7 @@ fn render_status_bar(f: &mut Frame, app: &App, area: Rect) { } // Home -fn render_home_view(f: &mut Frame, app: &mut App, area: Rect) { +fn render_home_view(f: &mut Frame, _app: &mut App, area: Rect) { let p = Paragraph::new("Welcome to PDM.\n\nSelect a config from the sidebar to edit.") .block(Block::default().borders(Borders::ALL).title(" Home ")) .wrap(Wrap { trim: true }); @@ -338,7 +338,7 @@ fn render_p2pool_view(f: &mut Frame, app: &mut App, area: Rect) { } // P2Pool Status -fn render_p2pool_status_view(f: &mut Frame, app: &mut App, area: Rect) { +fn render_p2pool_status_view(f: &mut Frame, _app: &mut App, area: Rect) { let p = Paragraph::new("P2Pool Status").block( Block::default() .borders(Borders::ALL) @@ -348,21 +348,21 @@ fn render_p2pool_status_view(f: &mut Frame, app: &mut App, area: Rect) { } // LN Config -fn render_ln_config_view(f: &mut Frame, app: &mut App, area: Rect) { +fn render_ln_config_view(f: &mut Frame, _app: &mut App, area: Rect) { let p = Paragraph::new("LN Config") .block(Block::default().borders(Borders::ALL).title(" LN Config ")); f.render_widget(p, area); } // LN Status -fn render_ln_status_view(f: &mut Frame, app: &mut App, area: Rect) { +fn render_ln_status_view(f: &mut Frame, _app: &mut App, area: Rect) { let p = Paragraph::new("LN Status") .block(Block::default().borders(Borders::ALL).title(" LN Status ")); f.render_widget(p, area); } // Shares Market -fn render_shares_market_view(f: &mut Frame, app: &mut App, area: Rect) { +fn render_shares_market_view(f: &mut Frame, _app: &mut App, area: Rect) { let p = Paragraph::new("Share Market").block( Block::default() .borders(Borders::ALL) From 09f27ac93fb9ac274d00dbcc2a457079ae55f7e0 Mon Sep 17 00:00:00 2001 From: Francesco Date: Sun, 15 Mar 2026 00:07:52 +0400 Subject: [PATCH 5/7] Add the different views as separate components --- src/components/bitcoin_config_view.rs | 72 ++++++ src/components/bitcoin_status_view.rs | 78 ++++++ src/components/file_explorer.rs | 39 ++- src/components/home_view.rs | 31 +++ src/components/ln_config_view.rs | 31 +++ src/components/ln_status_view.rs | 31 +++ src/components/mod.rs | 9 + src/components/p2pool_config_view.rs | 117 +++++++++ src/components/p2pool_status_view.rs | 34 +++ src/components/shares_market_view.rs | 34 +++ src/components/status_bar.rs | 67 +++++ src/ui.rs | 347 ++------------------------ 12 files changed, 560 insertions(+), 330 deletions(-) create mode 100644 src/components/bitcoin_config_view.rs create mode 100644 src/components/bitcoin_status_view.rs create mode 100644 src/components/home_view.rs create mode 100644 src/components/ln_config_view.rs create mode 100644 src/components/ln_status_view.rs create mode 100644 src/components/p2pool_config_view.rs create mode 100644 src/components/p2pool_status_view.rs create mode 100644 src/components/shares_market_view.rs create mode 100644 src/components/status_bar.rs diff --git a/src/components/bitcoin_config_view.rs b/src/components/bitcoin_config_view.rs new file mode 100644 index 0000000..c107a18 --- /dev/null +++ b/src/components/bitcoin_config_view.rs @@ -0,0 +1,72 @@ +// SPDX-FileCopyrightText: 2024 PDM Authors +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +use crate::app::App; +use ratatui::{ + prelude::*, + widgets::{Block, Borders, List, ListItem, Paragraph}, +}; + +#[derive(Debug, Clone)] +pub struct BitcoinConfigView; + +impl BitcoinConfigView { + pub fn new() -> Self { + Self + } + + // Bitcoin Config + pub fn render(f: &mut Frame, app: &mut App, area: Rect) { + if app.bitcoin_conf_path.is_some() { + let items: Vec = app + .bitcoin_data + .iter() + .map(|entry| { + let style = if entry.enabled { + Style::default() + .fg(Color::White) + .add_modifier(Modifier::BOLD) + } else { + Style::default().fg(Color::DarkGray) + }; + + let content = Line::from(vec![ + Span::styled(format!("{} = ", entry.key), style), + Span::styled(&entry.value, style), + if !entry.enabled { + Span::styled(" (disabled)", style) + } else { + Span::raw("") + }, + ]); + + ListItem::new(content) + }) + .collect(); + + let list = List::new(items) + .block( + Block::default() + .borders(Borders::ALL) + .title(" Bitcoin Configuration "), + ) + .highlight_style(Style::default().bg(Color::Yellow)); + + f.render_widget(list, area); + } else { + let p = Paragraph::new("Press [Enter] to select a bitcoin.conf file").block( + Block::default() + .borders(Borders::ALL) + .title(" Bitcoin Config "), + ); + f.render_widget(p, area); + } + } +} + +impl Default for BitcoinConfigView { + fn default() -> Self { + Self::new() + } +} diff --git a/src/components/bitcoin_status_view.rs b/src/components/bitcoin_status_view.rs new file mode 100644 index 0000000..7c9d408 --- /dev/null +++ b/src/components/bitcoin_status_view.rs @@ -0,0 +1,78 @@ +// SPDX-FileCopyrightText: 2024 PDM Authors +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +use crate::app::App; +use ratatui::{ + prelude::*, + widgets::{Block, Borders, Paragraph, Tabs, Wrap}, +}; + +#[derive(Debug, Clone)] +pub struct BitcoinStatusView; + +impl BitcoinStatusView { + pub fn new() -> Self { + Self + } + + pub fn render(f: &mut Frame, app: &App, area: Rect) { + let outer = Layout::default() + .direction(Direction::Vertical) + .constraints([ + Constraint::Length(4), // Tabs bar + Constraint::Min(0), // Tab content + ]) + .split(area); + + let tabs = Tabs::new(vec!["Chain Info", "System", "Logs", "Peers"]) + .block(Block::default().borders(Borders::ALL).title(" Info ")) + .select(app.bitcoin_status_tab) + .highlight_style(Style::default().bg(Color::Gray).fg(Color::Black)); + + f.render_widget(tabs, outer[0]); + + let content_area = outer[1]; + match app.bitcoin_status_tab { + // Chain Info + 0 => { + let text = "Chain Info"; + let p = Paragraph::new(text) + .block(Block::default().borders(Borders::ALL)) + .wrap(Wrap { trim: true }); + f.render_widget(p, content_area); + } + // System + 1 => { + let text = "System"; + let p = Paragraph::new(text) + .block(Block::default().borders(Borders::ALL)) + .wrap(Wrap { trim: true }); + f.render_widget(p, content_area); + } + // Logs + 2 => { + let text = "Logs"; + let p = Paragraph::new(text) + .block(Block::default().borders(Borders::ALL)) + .wrap(Wrap { trim: true }); + f.render_widget(p, content_area); + } + // Peers + 3 => { + let text = "Peers"; + let p = Paragraph::new(text) + .block(Block::default().borders(Borders::ALL)) + .wrap(Wrap { trim: true }); + f.render_widget(p, content_area); + } + _ => {} + } + } +} + +impl Default for BitcoinStatusView { + fn default() -> Self { + Self::new() + } +} diff --git a/src/components/file_explorer.rs b/src/components/file_explorer.rs index 0bdcbf5..c0e7473 100644 --- a/src/components/file_explorer.rs +++ b/src/components/file_explorer.rs @@ -2,8 +2,12 @@ // // SPDX-License-Identifier: AGPL-3.0-or-later -use crate::app::AppAction; +use crate::app::{App, AppAction}; use crossterm::event::{KeyCode, KeyEvent}; +use ratatui::{ + prelude::*, + widgets::{Block, Borders, List, ListItem, ListState}, +}; use std::fs; use std::path::PathBuf; @@ -138,6 +142,39 @@ impl FileExplorer { _ => AppAction::None, } } + + pub fn render(f: &mut Frame, app: &mut App, area: Rect) { + let files: Vec = app + .explorer + .files + .iter() + .map(|path| { + let display_name = if path.ends_with("..") { + "📁 ..".to_string() + } else { + let name = path.file_name().unwrap_or_default().to_string_lossy(); + if path.is_dir() { + format!("📁 {}", name) + } else { + format!("📄 {}", name) + } + }; + ListItem::new(display_name) + }) + .collect(); + + let mut state = ListState::default(); + state.select(Some(app.explorer.selected_index)); + + let title = format!(" Select File (Current: {:?}) ", app.explorer.current_dir); + + let list = List::new(files) + .block(Block::default().borders(Borders::ALL).title(title)) + .highlight_style(Style::default().bg(Color::Blue).fg(Color::White)) + .highlight_symbol(">> "); + + f.render_stateful_widget(list, area, &mut state); + } } #[cfg(test)] diff --git a/src/components/home_view.rs b/src/components/home_view.rs new file mode 100644 index 0000000..0c012ba --- /dev/null +++ b/src/components/home_view.rs @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText: 2024 PDM Authors +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +use crate::app::App; +use ratatui::{ + prelude::*, + widgets::{Block, Borders, Paragraph, Wrap}, +}; + +#[derive(Debug, Clone)] +pub struct HomeView; + +impl HomeView { + pub fn new() -> Self { + Self + } + + pub fn render(f: &mut Frame, _app: &mut App, area: Rect) { + let p = Paragraph::new("Welcome to PDM.\n\nSelect a config from the sidebar to edit.") + .block(Block::default().borders(Borders::ALL).title(" Home ")) + .wrap(Wrap { trim: true }); + f.render_widget(p, area); + } +} + +impl Default for HomeView { + fn default() -> Self { + Self::new() + } +} diff --git a/src/components/ln_config_view.rs b/src/components/ln_config_view.rs new file mode 100644 index 0000000..4093ea8 --- /dev/null +++ b/src/components/ln_config_view.rs @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText: 2024 PDM Authors +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +use crate::app::App; +use ratatui::{ + prelude::*, + widgets::{Block, Borders, Paragraph}, +}; + +#[derive(Debug, Clone)] +pub struct LNConfigView; + +impl LNConfigView { + pub fn new() -> Self { + Self + } + + // LN Config + pub fn render(f: &mut Frame, _app: &mut App, area: Rect) { + let p = Paragraph::new("LN Config") + .block(Block::default().borders(Borders::ALL).title(" LN Config ")); + f.render_widget(p, area); + } +} + +impl Default for LNConfigView { + fn default() -> Self { + Self::new() + } +} diff --git a/src/components/ln_status_view.rs b/src/components/ln_status_view.rs new file mode 100644 index 0000000..8df3b0b --- /dev/null +++ b/src/components/ln_status_view.rs @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText: 2024 PDM Authors +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +use crate::app::App; +use ratatui::{ + prelude::*, + widgets::{Block, Borders, Paragraph}, +}; + +#[derive(Debug, Clone)] +pub struct LNStatusView; + +impl LNStatusView { + pub fn new() -> Self { + Self + } + + // LN Status + pub fn render(f: &mut Frame, _app: &mut App, area: Rect) { + let p = Paragraph::new("LN Status") + .block(Block::default().borders(Borders::ALL).title(" LN Status ")); + f.render_widget(p, area); + } +} + +impl Default for LNStatusView { + fn default() -> Self { + Self::new() + } +} diff --git a/src/components/mod.rs b/src/components/mod.rs index 6d3b562..b1fcb60 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -2,4 +2,13 @@ // // SPDX-License-Identifier: AGPL-3.0-or-later +pub mod bitcoin_config_view; +pub mod bitcoin_status_view; pub mod file_explorer; +pub mod home_view; +pub mod ln_config_view; +pub mod ln_status_view; +pub mod p2pool_config_view; +pub mod p2pool_status_view; +pub mod shares_market_view; +pub mod status_bar; diff --git a/src/components/p2pool_config_view.rs b/src/components/p2pool_config_view.rs new file mode 100644 index 0000000..77155f2 --- /dev/null +++ b/src/components/p2pool_config_view.rs @@ -0,0 +1,117 @@ +// SPDX-FileCopyrightText: 2024 PDM Authors +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +use crate::app::App; +use ratatui::{ + prelude::*, + widgets::{Block, Borders, List, ListItem, Paragraph}, +}; + +#[derive(Debug, Clone)] +pub struct P2PoolConfigView; + +impl P2PoolConfigView { + pub fn new() -> Self { + Self + } + + // P2Pool Config + pub fn render(f: &mut Frame, app: &mut App, area: Rect) { + if app.p2pool_conf_path.is_some() { + let mut items: Vec = Vec::new(); + + if let Some(cfg) = &app.p2pool_config { + // STRATUM + items.push(ListItem::new(Line::from(vec![ + Span::styled("[stratum] ", Style::default().fg(Color::Blue)), + Span::raw(format!("hostname = {}", cfg.stratum.hostname)), + ]))); + + items.push(ListItem::new(Line::from(vec![ + Span::styled("[stratum] ", Style::default().fg(Color::Blue)), + Span::raw(format!("port = {}", cfg.stratum.port)), + ]))); + + items.push(ListItem::new(Line::from(vec![ + Span::styled("[stratum] ", Style::default().fg(Color::Blue)), + Span::raw(format!( + "start_difficulty = {}", + cfg.stratum.start_difficulty + )), + ]))); + + items.push(ListItem::new(Line::from(vec![ + Span::styled("[stratum] ", Style::default().fg(Color::Blue)), + Span::raw(format!( + "minimum_difficulty = {}", + cfg.stratum.minimum_difficulty + )), + ]))); + + // BITCOIN RPC + items.push(ListItem::new(Line::from(vec![ + Span::styled("[bitcoinrpc] ", Style::default().fg(Color::Blue)), + Span::raw(format!("url = {}", cfg.bitcoinrpc.url)), + ]))); + + items.push(ListItem::new(Line::from(vec![ + Span::styled("[bitcoinrpc] ", Style::default().fg(Color::Blue)), + Span::raw(format!("username = {}", cfg.bitcoinrpc.username)), + ]))); + + // NETWORK + items.push(ListItem::new(Line::from(vec![ + Span::styled("[network] ", Style::default().fg(Color::Blue)), + Span::raw(format!("listen_address = {}", cfg.network.listen_address)), + ]))); + + items.push(ListItem::new(Line::from(vec![ + Span::styled("[network] ", Style::default().fg(Color::Blue)), + Span::raw(format!( + "max_established_incoming = {}", + cfg.network.max_established_incoming + )), + ]))); + + // STORE + items.push(ListItem::new(Line::from(vec![ + Span::styled("[store] ", Style::default().fg(Color::Blue)), + Span::raw(format!("path = {}", cfg.store.path)), + ]))); + + // API + items.push(ListItem::new(Line::from(vec![ + Span::styled("[api] ", Style::default().fg(Color::Blue)), + Span::raw(format!("hostname = {}", cfg.api.hostname)), + ]))); + + items.push(ListItem::new(Line::from(vec![ + Span::styled("[api] ", Style::default().fg(Color::Blue)), + Span::raw(format!("port = {}", cfg.api.port)), + ]))); + } + + let list = List::new(items).block( + Block::default() + .borders(Borders::ALL) + .title(" P2Pool Configuration "), + ); + + f.render_widget(list, area); + } else { + let p = Paragraph::new("Press [Enter] to select a p2poolv2 config file").block( + Block::default() + .borders(Borders::ALL) + .title(" P2Pool Config "), + ); + f.render_widget(p, area); + } + } +} + +impl Default for P2PoolConfigView { + fn default() -> Self { + Self::new() + } +} diff --git a/src/components/p2pool_status_view.rs b/src/components/p2pool_status_view.rs new file mode 100644 index 0000000..82d6f07 --- /dev/null +++ b/src/components/p2pool_status_view.rs @@ -0,0 +1,34 @@ +// SPDX-FileCopyrightText: 2024 PDM Authors +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +use crate::app::App; +use ratatui::{ + prelude::*, + widgets::{Block, Borders, Paragraph}, +}; + +#[derive(Debug, Clone)] +pub struct P2PoolStatusView; + +impl P2PoolStatusView { + pub fn new() -> Self { + Self + } + + // P2Pool Status + pub fn render(f: &mut Frame, _app: &mut App, area: Rect) { + let p = Paragraph::new("P2Pool Status").block( + Block::default() + .borders(Borders::ALL) + .title(" P2Pool Status "), + ); + f.render_widget(p, area); + } +} + +impl Default for P2PoolStatusView { + fn default() -> Self { + Self::new() + } +} diff --git a/src/components/shares_market_view.rs b/src/components/shares_market_view.rs new file mode 100644 index 0000000..a576eb5 --- /dev/null +++ b/src/components/shares_market_view.rs @@ -0,0 +1,34 @@ +// SPDX-FileCopyrightText: 2024 PDM Authors +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +use crate::app::App; +use ratatui::{ + prelude::*, + widgets::{Block, Borders, Paragraph}, +}; + +#[derive(Debug, Clone)] +pub struct SharesMarketView; + +impl SharesMarketView { + pub fn new() -> Self { + Self + } + + // LN Status + pub fn render(f: &mut Frame, _app: &mut App, area: Rect) { + let p = Paragraph::new("Share Market").block( + Block::default() + .borders(Borders::ALL) + .title(" Share Market "), + ); + f.render_widget(p, area); + } +} + +impl Default for SharesMarketView { + fn default() -> Self { + Self::new() + } +} diff --git a/src/components/status_bar.rs b/src/components/status_bar.rs new file mode 100644 index 0000000..f615bda --- /dev/null +++ b/src/components/status_bar.rs @@ -0,0 +1,67 @@ +// SPDX-FileCopyrightText: 2024 PDM Authors +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +use crate::app::{App, CurrentScreen}; +use ratatui::{prelude::*, widgets::Paragraph}; + +#[derive(Clone, Debug)] +pub struct StatusBar; + +fn hint(key: &str, desc: &str) -> Vec> { + vec![ + Span::styled( + format!(" {key} "), + Style::default().bg(Color::DarkGray).fg(Color::White), + ), + Span::styled(format!(" {desc} "), Style::default().fg(Color::DarkGray)), + ] +} + +impl StatusBar { + pub fn new() -> Self { + Self + } + + // Status bar + pub fn render(f: &mut Frame, app: &App, area: Rect) { + let mut spans: Vec = Vec::new(); + + match app.current_screen { + CurrentScreen::FileExplorer => { + spans.extend(hint("↑↓", "Navigate")); + spans.extend(hint("Enter", "Select")); + spans.extend(hint("Esc", "Cancel")); + } + CurrentScreen::BitcoinConfig if app.bitcoin_conf_path.is_some() => { + spans.extend(hint("↑↓", "Navigate")); + spans.extend(hint("Enter", "Open file")); + spans.extend(hint("q", "Quit")); + } + CurrentScreen::P2PoolConfig if app.p2pool_conf_path.is_some() => { + spans.extend(hint("↑↓", "Navigate")); + spans.extend(hint("Enter", "Open file")); + spans.extend(hint("q", "Quit")); + } + CurrentScreen::BitcoinStatus => { + spans.extend(hint("↑↓", "Navigate sidebar")); + spans.extend(hint("←→", "Switch tab")); + spans.extend(hint("q", "Quit")); + } + _ => { + spans.extend(hint("↑↓", "Navigate sidebar")); + spans.extend(hint("Enter", "Select")); + spans.extend(hint("q", "Quit")); + } + } + + let bar = Paragraph::new(Line::from(spans)).style(Style::default().bg(Color::Black)); + f.render_widget(bar, area); + } +} + +impl Default for StatusBar { + fn default() -> Self { + Self::new() + } +} diff --git a/src/ui.rs b/src/ui.rs index 3b509f6..f7740c1 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -3,9 +3,16 @@ // SPDX-License-Identifier: AGPL-3.0-or-later use crate::app::{App, CurrentScreen}; +use crate::components::{ + bitcoin_config_view::BitcoinConfigView, bitcoin_status_view::BitcoinStatusView, + file_explorer::FileExplorer, home_view::HomeView, ln_config_view::LNConfigView, + ln_status_view::LNStatusView, p2pool_config_view::P2PoolConfigView, + p2pool_status_view::P2PoolStatusView, shares_market_view::SharesMarketView, + status_bar::StatusBar, +}; use ratatui::{ prelude::*, - widgets::{Block, Borders, List, ListItem, ListState, Paragraph, Tabs, Wrap}, + widgets::{Block, Borders, List, ListItem, ListState}, }; pub fn ui(f: &mut Frame, app: &mut App) { @@ -55,354 +62,36 @@ pub fn ui(f: &mut Frame, app: &mut App) { match app.current_screen { CurrentScreen::Home => { - render_home_view(f, app, main_area); + HomeView::render(f, app, main_area); } CurrentScreen::BitcoinConfig => { - render_bitcoin_view(f, app, main_area); + BitcoinConfigView::render(f, app, main_area); } CurrentScreen::BitcoinStatus => { - render_bitcoin_status_view(f, app, main_area); + BitcoinStatusView::render(f, app, main_area); } CurrentScreen::P2PoolConfig => { - render_p2pool_view(f, app, main_area); + P2PoolConfigView::render(f, app, main_area); } CurrentScreen::P2PoolStatus => { - render_p2pool_status_view(f, app, main_area); + P2PoolStatusView::render(f, app, main_area); } CurrentScreen::LNConfig => { - render_ln_config_view(f, app, main_area); + LNConfigView::render(f, app, main_area); } CurrentScreen::LNStatus => { - render_ln_status_view(f, app, main_area); + LNStatusView::render(f, app, main_area); } CurrentScreen::SharesMarket => { - render_shares_market_view(f, app, main_area); + SharesMarketView::render(f, app, main_area); } CurrentScreen::FileExplorer => { - render_file_explorer(f, app, main_area); + FileExplorer::render(f, app, main_area); } _ => {} } - render_status_bar(f, app, status_bar_area); -} - -fn hint(key: &str, desc: &str) -> Vec> { - vec![ - Span::styled( - format!(" {key} "), - Style::default().bg(Color::DarkGray).fg(Color::White), - ), - Span::styled(format!(" {desc} "), Style::default().fg(Color::DarkGray)), - ] -} - -// Status bar -fn render_status_bar(f: &mut Frame, app: &App, area: Rect) { - let mut spans: Vec = Vec::new(); - - match app.current_screen { - CurrentScreen::FileExplorer => { - spans.extend(hint("↑↓", "Navigate")); - spans.extend(hint("Enter", "Select")); - spans.extend(hint("Esc", "Cancel")); - } - CurrentScreen::BitcoinConfig if app.bitcoin_conf_path.is_some() => { - spans.extend(hint("↑↓", "Navigate")); - spans.extend(hint("Enter", "Open file")); - spans.extend(hint("q", "Quit")); - } - CurrentScreen::P2PoolConfig if app.p2pool_conf_path.is_some() => { - spans.extend(hint("↑↓", "Navigate")); - spans.extend(hint("Enter", "Open file")); - spans.extend(hint("q", "Quit")); - } - CurrentScreen::BitcoinStatus => { - spans.extend(hint("↑↓", "Navigate sidebar")); - spans.extend(hint("←→", "Switch tab")); - spans.extend(hint("q", "Quit")); - } - _ => { - spans.extend(hint("↑↓", "Navigate sidebar")); - spans.extend(hint("Enter", "Select")); - spans.extend(hint("q", "Quit")); - } - } - - let bar = Paragraph::new(Line::from(spans)).style(Style::default().bg(Color::Black)); - f.render_widget(bar, area); -} - -// Home -fn render_home_view(f: &mut Frame, _app: &mut App, area: Rect) { - let p = Paragraph::new("Welcome to PDM.\n\nSelect a config from the sidebar to edit.") - .block(Block::default().borders(Borders::ALL).title(" Home ")) - .wrap(Wrap { trim: true }); - f.render_widget(p, area); -} - -// Bitcoin Config -fn render_bitcoin_view(f: &mut Frame, app: &mut App, area: Rect) { - if app.bitcoin_conf_path.is_some() { - let items: Vec = app - .bitcoin_data - .iter() - .map(|entry| { - let style = if entry.enabled { - Style::default() - .fg(Color::White) - .add_modifier(Modifier::BOLD) - } else { - Style::default().fg(Color::DarkGray) - }; - - let content = Line::from(vec![ - Span::styled(format!("{} = ", entry.key), style), - Span::styled(&entry.value, style), - if !entry.enabled { - Span::styled(" (disabled)", style) - } else { - Span::raw("") - }, - ]); - - ListItem::new(content) - }) - .collect(); - - let list = List::new(items) - .block( - Block::default() - .borders(Borders::ALL) - .title(" Bitcoin Configuration "), - ) - .highlight_style(Style::default().bg(Color::Yellow)); - - f.render_widget(list, area); - } else { - let p = Paragraph::new("Press [Enter] to select a bitcoin.conf file").block( - Block::default() - .borders(Borders::ALL) - .title(" Bitcoin Config "), - ); - f.render_widget(p, area); - } -} - -// Bitcoin Status -fn render_bitcoin_status_view(f: &mut Frame, app: &App, area: Rect) { - let outer = Layout::default() - .direction(Direction::Vertical) - .constraints([ - Constraint::Length(4), // Tabs bar - Constraint::Min(0), // Tab content - ]) - .split(area); - - let tabs = Tabs::new(vec!["Chain Info", "System", "Logs", "Peers"]) - .block(Block::default().borders(Borders::ALL).title(" Info ")) - .select(app.bitcoin_status_tab) - .highlight_style(Style::default().bg(Color::Gray).fg(Color::Black)); - - f.render_widget(tabs, outer[0]); - - let content_area = outer[1]; - match app.bitcoin_status_tab { - // Chain Info - 0 => { - let text = "Chain Info"; - let p = Paragraph::new(text) - .block(Block::default().borders(Borders::ALL)) - .wrap(Wrap { trim: true }); - f.render_widget(p, content_area); - } - // System - 1 => { - let text = "System"; - let p = Paragraph::new(text) - .block(Block::default().borders(Borders::ALL)) - .wrap(Wrap { trim: true }); - f.render_widget(p, content_area); - } - // Logs - 2 => { - let text = "Logs"; - let p = Paragraph::new(text) - .block(Block::default().borders(Borders::ALL)) - .wrap(Wrap { trim: true }); - f.render_widget(p, content_area); - } - // Peers - 3 => { - let text = "Peers"; - let p = Paragraph::new(text) - .block(Block::default().borders(Borders::ALL)) - .wrap(Wrap { trim: true }); - f.render_widget(p, content_area); - } - _ => {} - } -} - -// P2Pool Config -fn render_p2pool_view(f: &mut Frame, app: &mut App, area: Rect) { - if app.p2pool_conf_path.is_some() { - let mut items: Vec = Vec::new(); - - if let Some(cfg) = &app.p2pool_config { - // STRATUM - items.push(ListItem::new(Line::from(vec![ - Span::styled("[stratum] ", Style::default().fg(Color::Blue)), - Span::raw(format!("hostname = {}", cfg.stratum.hostname)), - ]))); - - items.push(ListItem::new(Line::from(vec![ - Span::styled("[stratum] ", Style::default().fg(Color::Blue)), - Span::raw(format!("port = {}", cfg.stratum.port)), - ]))); - - items.push(ListItem::new(Line::from(vec![ - Span::styled("[stratum] ", Style::default().fg(Color::Blue)), - Span::raw(format!( - "start_difficulty = {}", - cfg.stratum.start_difficulty - )), - ]))); - - items.push(ListItem::new(Line::from(vec![ - Span::styled("[stratum] ", Style::default().fg(Color::Blue)), - Span::raw(format!( - "minimum_difficulty = {}", - cfg.stratum.minimum_difficulty - )), - ]))); - - // BITCOIN RPC - items.push(ListItem::new(Line::from(vec![ - Span::styled("[bitcoinrpc] ", Style::default().fg(Color::Blue)), - Span::raw(format!("url = {}", cfg.bitcoinrpc.url)), - ]))); - - items.push(ListItem::new(Line::from(vec![ - Span::styled("[bitcoinrpc] ", Style::default().fg(Color::Blue)), - Span::raw(format!("username = {}", cfg.bitcoinrpc.username)), - ]))); - - // NETWORK - items.push(ListItem::new(Line::from(vec![ - Span::styled("[network] ", Style::default().fg(Color::Blue)), - Span::raw(format!("listen_address = {}", cfg.network.listen_address)), - ]))); - - items.push(ListItem::new(Line::from(vec![ - Span::styled("[network] ", Style::default().fg(Color::Blue)), - Span::raw(format!( - "max_established_incoming = {}", - cfg.network.max_established_incoming - )), - ]))); - - // STORE - items.push(ListItem::new(Line::from(vec![ - Span::styled("[store] ", Style::default().fg(Color::Blue)), - Span::raw(format!("path = {}", cfg.store.path)), - ]))); - - // API - items.push(ListItem::new(Line::from(vec![ - Span::styled("[api] ", Style::default().fg(Color::Blue)), - Span::raw(format!("hostname = {}", cfg.api.hostname)), - ]))); - - items.push(ListItem::new(Line::from(vec![ - Span::styled("[api] ", Style::default().fg(Color::Blue)), - Span::raw(format!("port = {}", cfg.api.port)), - ]))); - } - - let list = List::new(items).block( - Block::default() - .borders(Borders::ALL) - .title(" P2Pool Configuration "), - ); - - f.render_widget(list, area); - } else { - let p = Paragraph::new("Press [Enter] to select a p2poolv2 config file").block( - Block::default() - .borders(Borders::ALL) - .title(" P2Pool Config "), - ); - f.render_widget(p, area); - } -} - -// P2Pool Status -fn render_p2pool_status_view(f: &mut Frame, _app: &mut App, area: Rect) { - let p = Paragraph::new("P2Pool Status").block( - Block::default() - .borders(Borders::ALL) - .title(" P2Pool Status "), - ); - f.render_widget(p, area); -} - -// LN Config -fn render_ln_config_view(f: &mut Frame, _app: &mut App, area: Rect) { - let p = Paragraph::new("LN Config") - .block(Block::default().borders(Borders::ALL).title(" LN Config ")); - f.render_widget(p, area); -} - -// LN Status -fn render_ln_status_view(f: &mut Frame, _app: &mut App, area: Rect) { - let p = Paragraph::new("LN Status") - .block(Block::default().borders(Borders::ALL).title(" LN Status ")); - f.render_widget(p, area); -} - -// Shares Market -fn render_shares_market_view(f: &mut Frame, _app: &mut App, area: Rect) { - let p = Paragraph::new("Share Market").block( - Block::default() - .borders(Borders::ALL) - .title(" Share Market "), - ); - f.render_widget(p, area); -} - -// File Explorer -fn render_file_explorer(f: &mut Frame, app: &mut App, area: Rect) { - let files: Vec = app - .explorer - .files - .iter() - .map(|path| { - let display_name = if path.ends_with("..") { - "📁 ..".to_string() - } else { - let name = path.file_name().unwrap_or_default().to_string_lossy(); - if path.is_dir() { - format!("📁 {}", name) - } else { - format!("📄 {}", name) - } - }; - ListItem::new(display_name) - }) - .collect(); - - let mut state = ListState::default(); - state.select(Some(app.explorer.selected_index)); - - let title = format!(" Select File (Current: {:?}) ", app.explorer.current_dir); - - let list = List::new(files) - .block(Block::default().borders(Borders::ALL).title(title)) - .highlight_style(Style::default().bg(Color::Blue).fg(Color::White)) - .highlight_symbol(">> "); - - f.render_stateful_widget(list, area, &mut state); + StatusBar::render(f, app, status_bar_area); } #[cfg(test)] From 78e0fea81478b4ab366e930c21c1e0a4cc96ef18 Mon Sep 17 00:00:00 2001 From: Francesco Date: Fri, 20 Mar 2026 15:18:10 +0100 Subject: [PATCH 6/7] Remove commented imports --- src/components/file_explorer.rs | 3 +-- src/components/shares_market_view.rs | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/components/file_explorer.rs b/src/components/file_explorer.rs index c0e7473..19efd98 100644 --- a/src/components/file_explorer.rs +++ b/src/components/file_explorer.rs @@ -180,8 +180,7 @@ impl FileExplorer { #[cfg(test)] mod tests { use super::*; - //use std::env::temp_dir; - use std::fs::File; //{ , create_dir}; + use std::fs::File; fn setup_temp_fs() -> PathBuf { use std::time::{SystemTime, UNIX_EPOCH}; diff --git a/src/components/shares_market_view.rs b/src/components/shares_market_view.rs index a576eb5..2fe6950 100644 --- a/src/components/shares_market_view.rs +++ b/src/components/shares_market_view.rs @@ -18,10 +18,10 @@ impl SharesMarketView { // LN Status pub fn render(f: &mut Frame, _app: &mut App, area: Rect) { - let p = Paragraph::new("Share Market").block( + let p = Paragraph::new("Shares Market").block( Block::default() .borders(Borders::ALL) - .title(" Share Market "), + .title(" Shares Market "), ); f.render_widget(p, area); } From 22d24f5ae36542f9de2f43d49a90fcbd535ff705 Mon Sep 17 00:00:00 2001 From: Francesco Date: Mon, 23 Mar 2026 12:24:06 +0100 Subject: [PATCH 7/7] Update test snapshot --- .../pdm__ui__tests__shares_market_screen_render.snap | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/snapshots/pdm__ui__tests__shares_market_screen_render.snap b/src/snapshots/pdm__ui__tests__shares_market_screen_render.snap index d6a49bd..32cb5f8 100644 --- a/src/snapshots/pdm__ui__tests__shares_market_screen_render.snap +++ b/src/snapshots/pdm__ui__tests__shares_market_screen_render.snap @@ -6,8 +6,8 @@ TestBackend { buffer: Buffer { area: Rect { x: 0, y: 0, width: 80, height: 24 }, content: [ - "┌ PDM ──────────────────┐┌ Share Market ───────────────────────────────────────┐", - "│Home ││Share Market │", + "┌ PDM ──────────────────┐┌ Shares Market ──────────────────────────────────────┐", + "│Home ││Shares Market │", "│Bitcoin Config ││ │", "│Bitcoin Status ││ │", "│P2Pool Config ││ │",