Add About modal with sock ASCII art

This commit is contained in:
jasonwitty 2025-10-26 02:16:42 -07:00
parent e51cdb0c50
commit 25632f3427
4 changed files with 114 additions and 2 deletions

View File

@ -614,7 +614,8 @@ impl App {
{ {
self.clear_process_details(); self.clear_process_details();
} }
// Modal was dismissed, continue to normal processing // Modal was dismissed, skip normal key processing
continue;
} }
ModalAction::Confirm => { ModalAction::Confirm => {
// Handle confirmation action here if needed in the future // Handle confirmation action here if needed in the future
@ -654,6 +655,12 @@ impl App {
) { ) {
self.should_quit = true; self.should_quit = true;
} }
// Show About modal on 'a' or 'A'
if matches!(k.code, KeyCode::Char('a') | KeyCode::Char('A')) {
self.modal_manager.push_modal(ModalType::About);
}
// Per-core scroll via keys (Up/Down/PageUp/PageDown/Home/End) // Per-core scroll via keys (Up/Down/PageUp/PageDown/Home/End)
let sz = terminal.size()?; let sz = terminal.size()?;
let area = Rect::new(0, 0, sz.width, sz.height); let area = Rect::new(0, 0, sz.width, sz.height);

View File

@ -46,7 +46,7 @@ pub fn draw_header(
parts.push(tok_txt.into()); parts.push(tok_txt.into());
} }
parts.push(intervals); parts.push(intervals);
parts.push("(q to quit)".into()); parts.push("(a: about, q: quit)".into());
let title = parts.join(" | "); let title = parts.join(" | ");
f.render_widget(Block::default().title(title).borders(Borders::BOTTOM), area); f.render_widget(Block::default().title(title).borders(Borders::BOTTOM), area);
} }

View File

@ -55,6 +55,7 @@ impl ModalManager {
self.journal_scroll_max = 0; self.journal_scroll_max = 0;
ModalButton::Ok ModalButton::Ok
} }
Some(ModalType::About) => ModalButton::Ok,
Some(ModalType::Confirmation { .. }) => ModalButton::Confirm, Some(ModalType::Confirmation { .. }) => ModalButton::Confirm,
Some(ModalType::Info { .. }) => ModalButton::Ok, Some(ModalType::Info { .. }) => ModalButton::Ok,
None => ModalButton::Ok, None => ModalButton::Ok,
@ -66,6 +67,7 @@ impl ModalManager {
self.active_button = match next { self.active_button = match next {
ModalType::ConnectionError { .. } => ModalButton::Retry, ModalType::ConnectionError { .. } => ModalButton::Retry,
ModalType::ProcessDetails { .. } => ModalButton::Ok, ModalType::ProcessDetails { .. } => ModalButton::Ok,
ModalType::About => ModalButton::Ok,
ModalType::Confirmation { .. } => ModalButton::Confirm, ModalType::Confirmation { .. } => ModalButton::Confirm,
ModalType::Info { .. } => ModalButton::Ok, ModalType::Info { .. } => ModalButton::Ok,
}; };
@ -205,6 +207,10 @@ impl ModalManager {
self.pop_modal(); self.pop_modal();
ModalAction::Dismiss ModalAction::Dismiss
} }
(Some(ModalType::About), ModalButton::Ok) => {
self.pop_modal();
ModalAction::Dismiss
}
(Some(ModalType::Confirmation { .. }), ModalButton::Confirm) => ModalAction::Confirm, (Some(ModalType::Confirmation { .. }), ModalButton::Confirm) => ModalAction::Confirm,
(Some(ModalType::Confirmation { .. }), ModalButton::Cancel) => ModalAction::Cancel, (Some(ModalType::Confirmation { .. }), ModalButton::Cancel) => ModalAction::Cancel,
(Some(ModalType::Info { .. }), ModalButton::Ok) => { (Some(ModalType::Info { .. }), ModalButton::Ok) => {
@ -253,6 +259,10 @@ impl ModalManager {
// Process details modal uses almost full screen (95% width, 90% height) // Process details modal uses almost full screen (95% width, 90% height)
self.centered_rect(95, 90, area) self.centered_rect(95, 90, area)
} }
ModalType::About => {
// About modal uses medium size
self.centered_rect(60, 60, area)
}
_ => { _ => {
// Other modals use smaller size // Other modals use smaller size
self.centered_rect(70, 50, area) self.centered_rect(70, 50, area)
@ -276,6 +286,7 @@ impl ModalManager {
ModalType::ProcessDetails { pid } => { ModalType::ProcessDetails { pid } => {
self.render_process_details(f, modal_area, *pid, data) self.render_process_details(f, modal_area, *pid, data)
} }
ModalType::About => self.render_about(f, modal_area),
ModalType::Confirmation { ModalType::Confirmation {
title, title,
message, message,
@ -378,6 +389,99 @@ impl ModalManager {
); );
} }
fn render_about(&self, f: &mut Frame, area: Rect) {
const ASCII_ART: &str = r#"
"#;
let version = env!("CARGO_PKG_VERSION");
let about_text = format!(
"{}\n\
Version {}\n\
\n\
A terminal first remote monitoring tool\n\
\n\
Website: https://socktop.io\n\
GitHub: https://github.com/jasonwitty/socktop\n\
\n\
License: MIT License\n\
\n\
Created by Jason Witty\n\
jasonpwitty+socktop@proton.me",
ASCII_ART, version
);
// Render the border block
let block = Block::default()
.title(" About socktop ")
.borders(Borders::ALL)
.style(Style::default().bg(Color::Black).fg(Color::Green));
f.render_widget(block, area);
// Calculate inner area manually to avoid any parent styling
let inner_area = Rect {
x: area.x + 1,
y: area.y + 1,
width: area.width.saturating_sub(2),
height: area.height.saturating_sub(3), // Leave room for button at bottom
};
// Render content area with explicit black background
f.render_widget(
Paragraph::new(about_text)
.style(Style::default().fg(Color::Cyan).bg(Color::Black))
.alignment(Alignment::Center)
.wrap(Wrap { trim: false }),
inner_area,
);
// Button area
let button_area = Rect {
x: area.x + 1,
y: area.y + area.height.saturating_sub(3),
width: area.width.saturating_sub(2),
height: 1,
};
let ok_style = if self.active_button == ModalButton::Ok {
Style::default()
.bg(Color::Blue)
.fg(Color::White)
.add_modifier(Modifier::BOLD)
} else {
Style::default().fg(Color::Blue).bg(Color::Black)
};
f.render_widget(
Paragraph::new("[ Enter ] Close")
.style(ok_style)
.alignment(Alignment::Center),
button_area,
);
}
fn centered_rect(&self, percent_x: u16, percent_y: u16, r: Rect) -> Rect { fn centered_rect(&self, percent_x: u16, percent_y: u16, r: Rect) -> Rect {
let vert = Layout::default() let vert = Layout::default()
.direction(Direction::Vertical) .direction(Direction::Vertical)

View File

@ -38,6 +38,7 @@ pub enum ModalType {
ProcessDetails { ProcessDetails {
pid: u32, pid: u32,
}, },
About,
#[allow(dead_code)] #[allow(dead_code)]
Confirmation { Confirmation {
title: String, title: String,