diff --git a/src/lib.rs b/src/lib.rs index 7bbef62..4b69de3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,6 +17,12 @@ mod inbox; const POLL_INTERVAL: Duration = Duration::from_secs(30); +#[derive(PartialEq)] +enum Focus { + Inbox, + Preview, +} + pub(crate) struct Email { pub seq: u32, pub subject: String, @@ -89,6 +95,8 @@ pub fn main(config: &Config, terminal: &mut Terminal>) let mut preview_body: String = String::new(); let mut preview_seq: Option = None; let mut body_loading = false; + let mut focus = Focus::Inbox; + let mut preview_scroll: u16 = 0; // --- Main loop --- loop { @@ -131,6 +139,7 @@ pub fn main(config: &Config, terminal: &mut Terminal>) WorkerResult::Body { seq, result } => { body_loading = false; if preview_seq == Some(seq) { + preview_scroll = 0; match result { Ok(body) => preview_body = body, Err(e) => preview_body = format!("Error loading body: {}", e), @@ -159,19 +168,30 @@ pub fn main(config: &Config, terminal: &mut Terminal>) .split(area); // --- Top: Inbox list --- + let inbox_border = if focus == Focus::Inbox { + Style::default().fg(Color::Cyan) + } else { + Style::default().fg(Color::DarkGray) + }; + let preview_border = if focus == Focus::Preview { + Style::default().fg(Color::Cyan) + } else { + Style::default().fg(Color::DarkGray) + }; + if let Some(e) = &error { let p = Paragraph::new(format!("IMAP error: {}", e)) - .block(Block::default().title("Inbox").borders(Borders::ALL)) + .block(Block::default().title("Inbox").borders(Borders::ALL).border_style(inbox_border)) .style(Style::default().fg(Color::Red)); frame.render_widget(p, layout[0]); } else if emails.is_empty() && loading { let p = Paragraph::new("Loading...") - .block(Block::default().title("Inbox").borders(Borders::ALL)) + .block(Block::default().title("Inbox").borders(Borders::ALL).border_style(inbox_border)) .style(Style::default().fg(Color::Yellow)); frame.render_widget(p, layout[0]); } else if emails.is_empty() { let p = Paragraph::new("No messages in inbox.") - .block(Block::default().title("Inbox").borders(Borders::ALL)) + .block(Block::default().title("Inbox").borders(Borders::ALL).border_style(inbox_border)) .style(Style::default().fg(Color::Yellow)); frame.render_widget(p, layout[0]); } else { @@ -192,7 +212,8 @@ pub fn main(config: &Config, terminal: &mut Terminal>) .block( Block::default() .title(title) - .borders(Borders::ALL), + .borders(Borders::ALL) + .border_style(inbox_border), ) .style(Style::default().fg(Color::White)) .highlight_style( @@ -212,12 +233,13 @@ pub fn main(config: &Config, terminal: &mut Terminal>) preview_body.clone() }; let preview = Paragraph::new(preview_text) - .block(Block::default().title("Preview").borders(Borders::ALL)) + .block(Block::default().title("Preview").borders(Borders::ALL).border_style(preview_border)) .style(Style::default().fg(Color::White)) - .wrap(ratatui::widgets::Wrap { trim: false }); + .wrap(ratatui::widgets::Wrap { trim: false }) + .scroll((preview_scroll, 0)); frame.render_widget(preview, layout[1]); - let status = Paragraph::new(" 'q' quit | 'r' refresh | ↑/↓ navigate") + let status = Paragraph::new(" 'q' quit | 'r' refresh | ↑/↓ navigate | Tab switch pane") .style(Style::default().fg(Color::DarkGray)); frame.render_widget(status, layout[2]); })?; @@ -227,6 +249,12 @@ pub fn main(config: &Config, terminal: &mut Terminal>) if let Event::Key(key) = event::read()? { match key.code { KeyCode::Char('q') | KeyCode::Esc => break, + KeyCode::Tab => { + focus = match focus { + Focus::Inbox => Focus::Preview, + Focus::Preview => Focus::Inbox, + }; + } KeyCode::Char('r') => { if !loading { let _ = cmd_tx.send(WorkerCmd::Refresh); @@ -234,39 +262,51 @@ pub fn main(config: &Config, terminal: &mut Terminal>) last_fetch = Instant::now(); } } - KeyCode::Down | KeyCode::Char('j') => { - let len = emails.len(); - if len > 0 { - let i = list_state.selected().map_or(0, |i| i + 1); - if i >= len && has_older && !loading { - if let Some(seq) = oldest_seq { - let _ = cmd_tx.send(WorkerCmd::FetchMore { oldest_seq: seq }); - loading = true; + KeyCode::Down | KeyCode::Char('j') => match focus { + Focus::Inbox => { + let len = emails.len(); + if len > 0 { + let i = list_state.selected().map_or(0, |i| i + 1); + if i >= len && has_older && !loading { + if let Some(seq) = oldest_seq { + let _ = cmd_tx.send(WorkerCmd::FetchMore { oldest_seq: seq }); + loading = true; + } + } + let new_idx = i.min(len.saturating_sub(1)); + list_state.select(Some(new_idx)); + let seq = emails[new_idx].seq; + if preview_seq != Some(seq) { + preview_seq = Some(seq); + preview_body.clear(); + preview_scroll = 0; + body_loading = true; + let _ = cmd_tx.send(WorkerCmd::FetchBody { seq }); } } - let new_idx = i.min(len.saturating_sub(1)); - list_state.select(Some(new_idx)); - let seq = emails[new_idx].seq; - if preview_seq != Some(seq) { - preview_seq = Some(seq); - preview_body.clear(); - body_loading = true; - let _ = cmd_tx.send(WorkerCmd::FetchBody { seq }); - } + } + Focus::Preview => { + preview_scroll = preview_scroll.saturating_add(1); } } - KeyCode::Up | KeyCode::Char('k') => { - let i = list_state.selected().map_or(0, |i| i.saturating_sub(1)); - list_state.select(Some(i)); - if !emails.is_empty() { - let seq = emails[i].seq; - if preview_seq != Some(seq) { - preview_seq = Some(seq); - preview_body.clear(); - body_loading = true; - let _ = cmd_tx.send(WorkerCmd::FetchBody { seq }); + KeyCode::Up | KeyCode::Char('k') => match focus { + Focus::Inbox => { + let i = list_state.selected().map_or(0, |i| i.saturating_sub(1)); + list_state.select(Some(i)); + if !emails.is_empty() { + let seq = emails[i].seq; + if preview_seq != Some(seq) { + preview_seq = Some(seq); + preview_body.clear(); + preview_scroll = 0; + body_loading = true; + let _ = cmd_tx.send(WorkerCmd::FetchBody { seq }); + } } } + Focus::Preview => { + preview_scroll = preview_scroll.saturating_sub(1); + } } _ => {} }