use crossterm::event::KeyCode;
use ratatui::{
layout::{Constraint, Direction, Layout, Rect},
style::{Color, Modifier, Style},
widgets::{Block, Borders, Cell, Paragraph, Row, Table, TableState},
Frame,
};
use crate::sources::parser::AptSource;
pub struct SourcesPanel {
pub sources: Vec<AptSource>,
pub state: TableState,
}
impl SourcesPanel {
pub fn new(sources: Vec<AptSource>) -> Self {
let mut state = TableState::default();
if !sources.is_empty() {
state.select(Some(0));
}
Self { sources, state }
}
pub fn handle_key(&mut self, code: KeyCode) {
match code {
KeyCode::Char('j') | KeyCode::Down => self.next(1),
KeyCode::Char('k') | KeyCode::Up => self.prev(1),
KeyCode::PageDown => self.next(10),
KeyCode::PageUp => self.prev(10),
_ => {}
}
}
fn next(&mut self, step: usize) {
let len = self.sources.len();
if len == 0 { return; }
let i = self.state.selected().map_or(0, |i| (i + step).min(len - 1));
self.state.select(Some(i));
}
fn prev(&mut self, step: usize) {
let i = self.state.selected().map_or(0, |i| i.saturating_sub(step));
self.state.select(Some(i));
}
pub fn render(&self, f: &mut Frame, area: Rect) {
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([Constraint::Min(0), Constraint::Length(3)])
.split(area);
let rows: Vec<Row> = self
.sources
.iter()
.map(|s| {
let enabled_marker = if s.enabled { "✓" } else { "✗" };
let components = s.components.join(" ");
let file = s
.file
.file_name()
.and_then(|n| n.to_str())
.unwrap_or("?");
let style = if s.enabled {
Style::default().fg(Color::White)
} else {
Style::default().fg(Color::DarkGray)
};
Row::new(vec![
Cell::from(enabled_marker),
Cell::from(s.source_type.clone()),
Cell::from(s.uri.clone()),
Cell::from(s.suite.clone()),
Cell::from(components),
Cell::from(file),
])
.style(style)
})
.collect();
let widths = [
Constraint::Length(3),
Constraint::Length(8),
Constraint::Min(35),
Constraint::Length(15),
Constraint::Length(25),
Constraint::Length(20),
];
let table = Table::new(rows, widths)
.header(
Row::new(vec!["", "Type", "URI", "Suite", "Components", "File"])
.style(Style::default().fg(Color::Cyan).add_modifier(Modifier::BOLD)),
)
.block(Block::default().borders(Borders::ALL).title(" APT Sources "))
.row_highlight_style(Style::default().bg(Color::DarkGray).add_modifier(Modifier::BOLD))
.highlight_symbol("▶ ");
let mut state = self.state.clone();
f.render_stateful_widget(table, chunks[0], &mut state);
let actions = Paragraph::new(" a: add d: delete Space: enable/disable e: edit in $EDITOR [?] Help")
.block(Block::default().borders(Borders::ALL).title(" Actions "));
f.render_widget(actions, chunks[1]);
}
}