//! Terminal display helpers for the REPL. use super::conversation::StoredMessage; use super::session::SessionState; // ANSI color codes const RESET: &str = "\x1b[0m"; const BOLD: &str = "\x1b[1m"; const DIM: &str = "\x1b[2m"; const GREEN: &str = "\x1b[32m"; const CYAN: &str = "\x1b[36m"; const YELLOW: &str = "\x1b[33m"; /// Print the REPL prompt showing the active conversation and unread count. pub fn print_prompt(session: &SessionState) { use std::io::Write; let name = session .active_display_name() .unwrap_or_else(|| "no conversation".into()); let unread = session.total_unread(); if unread > 0 { print!("{DIM}[{RESET}{BOLD}{name}{RESET} {YELLOW}{unread} unread{RESET}{DIM}]{RESET} > "); } else { print!("{DIM}[{RESET}{BOLD}{name}{RESET}{DIM}]{RESET} > "); } let _ = std::io::stdout().flush(); } /// Print an incoming or outgoing message. pub fn print_message(msg: &StoredMessage) { let body = if msg.msg_type == "reaction" { format!("reacted {}", msg.body) } else { msg.body.clone() }; if msg.is_outgoing { println!("\r{GREEN}> {body}{RESET}"); } else { let fallback = hex::encode(&msg.sender_key[..4]); let sender = msg.sender_name.as_deref().unwrap_or(&fallback); println!("\r{CYAN}{BOLD}[{sender}]{RESET} {body}"); } } /// Print a message received in real-time (clears current line first). pub fn print_incoming(sender: &str, body: &str) { use std::io::Write; // Clear current line, print message, then re-show prompt context print!("\r\x1b[2K"); println!("{CYAN}{BOLD}[{sender}]{RESET} {body}"); let _ = std::io::stdout().flush(); } /// Print a system/status message. pub fn print_status(msg: &str) { println!("{DIM} {msg}{RESET}"); } /// Print a transient typing indicator (clears current line first). pub fn print_typing(sender: &str) { use std::io::Write; print!("\r\x1b[2K"); println!("{DIM} {sender} is typing...{RESET}"); let _ = std::io::stdout().flush(); } /// Print an error message. pub fn print_error(msg: &str) { println!("{YELLOW} error: {msg}{RESET}"); } /// Format a conversation list entry for `/list`. pub fn format_conv_line(display_name: &str, kind: &str, unread: u32, members: usize) -> String { let unread_str = if unread > 0 { format!(" {YELLOW}({unread} new){RESET}") } else { String::new() }; format!( " {BOLD}{display_name}{RESET} {DIM}[{kind}, {members} members]{RESET}{unread_str}" ) }