use anyhow::Result;
use ratatui::{
layout::Rect,
style::{Color, Modifier, Style},
text::{Line, Span},
widgets::{Block, Borders, Paragraph, Wrap},
Frame,
};
pub struct PasswordPrompt {
password_input: String,
error_message: Option<String>,
visible: bool,
authenticated: bool,
}
impl Default for PasswordPrompt {
fn default() -> Self {
Self::new()
}
}
impl PasswordPrompt {
pub fn new() -> Self {
Self {
password_input: String::new(),
error_message: None,
visible: false,
authenticated: false,
}
}
pub fn show(&mut self) {
self.visible = true;
self.password_input.clear();
self.error_message = None;
}
pub fn hide(&mut self) {
self.visible = false;
self.password_input.clear();
self.error_message = None;
}
pub fn is_visible(&self) -> bool {
self.visible
}
pub fn is_authenticated(&self) -> bool {
self.authenticated
}
pub fn add_char(&mut self, c: char) {
self.password_input.push(c);
}
pub fn remove_char(&mut self) {
self.password_input.pop();
}
pub fn verify_password(&mut self) -> Result<bool> {
let authenticated = cleansys_core::authenticate_sudo(&self.password_input)?;
if authenticated {
self.authenticated = true;
self.visible = false;
self.password_input.clear();
self.error_message = None;
Ok(true)
} else {
self.error_message = Some("Incorrect password. Please try again.".to_string());
self.password_input.clear();
Ok(false)
}
}
pub fn submit(&mut self) -> Result<bool> {
self.verify_password()
}
pub fn cancel(&mut self) {
self.hide();
}
pub fn render(&self, f: &mut Frame, area: Rect) {
if !self.visible {
return;
}
let popup_width = 60.min(area.width.saturating_sub(4));
let popup_height = 12.min(area.height.saturating_sub(4));
let popup_x = (area.width.saturating_sub(popup_width)) / 2;
let popup_y = (area.height.saturating_sub(popup_height)) / 2;
let popup_area = Rect {
x: popup_x,
y: popup_y,
width: popup_width,
height: popup_height,
};
let mut lines = vec![
Line::from(vec![Span::styled(
"🔒 System Cleaner Authentication",
Style::default()
.fg(Color::Yellow)
.add_modifier(Modifier::BOLD),
)]),
Line::from(vec![Span::raw("")]),
Line::from(vec![Span::raw(
"System cleaners require root privileges to clean system files.",
)]),
Line::from(vec![Span::raw("Please enter your password to continue:")]),
Line::from(vec![Span::raw("")]),
];
let password_display = "•".repeat(self.password_input.len());
lines.push(Line::from(vec![
Span::styled("Password: ", Style::default().fg(Color::Cyan)),
Span::styled(
password_display,
Style::default()
.fg(Color::White)
.add_modifier(Modifier::BOLD),
),
Span::styled("_", Style::default().fg(Color::Yellow)),
]));
lines.push(Line::from(vec![Span::raw("")]));
if let Some(error) = &self.error_message {
lines.push(Line::from(vec![Span::styled(
format!("❌ {}", error),
Style::default().fg(Color::Red),
)]));
lines.push(Line::from(vec![Span::raw("")]));
}
lines.push(Line::from(vec![Span::styled(
"Press Enter to authenticate | ESC to cancel",
Style::default()
.fg(Color::DarkGray)
.add_modifier(Modifier::ITALIC),
)]));
let popup = Paragraph::new(lines)
.block(
Block::default()
.title("Authentication Required")
.borders(Borders::ALL)
.border_style(Style::default().fg(Color::Yellow)),
)
.wrap(Wrap { trim: true });
let clear_block = Block::default().style(Style::default().bg(Color::Black));
f.render_widget(clear_block, popup_area);
f.render_widget(popup, popup_area);
}
}