diff --git a/src/main.rs b/src/main.rs index 17dc718..9317a3f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ use clap::Command; -use colored::{Color, Colorize}; +use colored::*; use serde::{Deserialize, Serialize}; use std::error::Error; use std::io::{stdout, Write}; @@ -8,13 +8,6 @@ use std::sync::{Arc, Mutex}; use termion::{clear, cursor, event::Key, input::TermRead, raw::IntoRawMode, terminal_size}; use tokio::time::{sleep, Duration}; -const BORDER_COLOR: Color = Color::TrueColor { r: 117, g: 142, b: 205 }; // #758ECD -const REF_COLOR: Color = Color::TrueColor { r: 113, g: 137, b: 255 }; // #7189FF -const TEXT_COLOR: Color = Color::TrueColor { r: 193, g: 206, b: 254 }; // #C1CEFE -const ATTR_COLOR: Color = Color::TrueColor { r: 160, g: 221, b: 255 }; // #A0DDFF -const NEXT_BTN_COLOR: Color = Color::TrueColor { r: 98, g: 76, b: 171 }; // #624CAB -const QUIT_BTN_COLOR: Color = Color::TrueColor { r: 160, g: 221, b: 255 }; // #A0DDFF - #[derive(Debug, Serialize, Deserialize)] struct BibleVerse { translation: Translation, @@ -70,102 +63,97 @@ fn word_wrap(text: &str, max_width: usize) -> Vec { fn display_verse(verse: &BibleVerse, term_width: u16, term_height: u16) { let max_width = std::cmp::min(80, term_width as usize - 10); - let box_width = max_width + 8; + let box_inner_width = max_width; + let box_width = box_inner_width + 2; let reference = format!("{} {}:{}", verse.random_verse.book, verse.random_verse.chapter, verse.random_verse.verse); - let wrapped_text = word_wrap(&verse.random_verse.text, max_width - 4); + let wrapped_text = word_wrap(&verse.random_verse.text, box_inner_width); let attribution = format!("{} ({})", verse.translation.name, verse.translation.identifier); - let box_height = wrapped_text.len() + 7; + let box_height = wrapped_text.len() + 6; let start_y = (term_height as usize / 2).saturating_sub(box_height / 2); let start_x = (term_width as usize / 2).saturating_sub(box_width / 2); print!("{}{}", clear::All, cursor::Goto(1, 1)); + // Top border print!("{}", cursor::Goto(start_x as u16, start_y as u16)); println!( "{}{}{}", - "╔".color(BORDER_COLOR).bold(), - "═".repeat(box_width - 2).color(BORDER_COLOR).bold(), - "╗".color(BORDER_COLOR).bold() + "╔".truecolor(117, 142, 205).bold(), + "═".repeat(box_inner_width).truecolor(117, 142, 205).bold(), + "╗".truecolor(117, 142, 205).bold() ); + // Reference + let centered_ref = format!("{:^width$}", reference, width = box_inner_width); print!("{}", cursor::Goto(start_x as u16, (start_y + 1) as u16)); println!( "{}{}{}", - "║".color(BORDER_COLOR).bold(), - " ".repeat(box_width - 2), - "║".color(BORDER_COLOR).bold() + "║".truecolor(117, 142, 205).bold(), + centered_ref.truecolor(113, 137, 255).bold(), + "║".truecolor(117, 142, 205).bold() ); + // Empty line print!("{}", cursor::Goto(start_x as u16, (start_y + 2) as u16)); - let centered_ref = format!("{:^width$}", reference, width = box_width - 2); println!( "{}{}{}", - "║".color(BORDER_COLOR).bold(), - centered_ref.color(REF_COLOR).bold(), - "║".color(BORDER_COLOR).bold() - ); - - print!("{}", cursor::Goto(start_x as u16, (start_y + 3) as u16)); - println!( - "{}{}{}", - "║".color(BORDER_COLOR).bold(), - " ".repeat(box_width - 2), - "║".color(BORDER_COLOR).bold() + "║".truecolor(117, 142, 205).bold(), + " ".repeat(box_inner_width), + "║".truecolor(117, 142, 205).bold() ); + // Verse text for (i, line) in wrapped_text.iter().enumerate() { - print!("{}", cursor::Goto(start_x as u16, (start_y + 4 + i) as u16)); - let padding = (box_width - 2 - line.len()) / 2; - let left_padding = " ".repeat(padding); - let right_padding = " ".repeat(box_width - 2 - padding - line.len()); + let centered = format!("{:^width$}", line, width = box_inner_width); + print!("{}", cursor::Goto(start_x as u16, (start_y + 3 + i) as u16)); println!( - "{}{}{}{}", - "║".color(BORDER_COLOR).bold(), - left_padding, - line.color(TEXT_COLOR), - format!("{}{}", right_padding, "║").color(BORDER_COLOR).bold() + "{}{}{}", + "║".truecolor(117, 142, 205).bold(), + centered.truecolor(160, 221, 255), + "║".truecolor(117, 142, 205).bold() ); } + // Empty line + print!( + "{}{}{}", + cursor::Goto(start_x as u16, (start_y + 3 + wrapped_text.len()) as u16), + "║".truecolor(117, 142, 205).bold(), + " ".repeat(box_inner_width), + ); + println!("{}", "║".truecolor(117, 142, 205).bold()); + + // Attribution + let centered_attr = format!("{:^width$}", attribution, width = box_inner_width); print!("{}", cursor::Goto(start_x as u16, (start_y + 4 + wrapped_text.len()) as u16)); println!( "{}{}{}", - "║".color(BORDER_COLOR).bold(), - " ".repeat(box_width - 2), - "║".color(BORDER_COLOR).bold() + "║".truecolor(117, 142, 205).bold(), + centered_attr.truecolor(193, 206, 254).italic(), + "║".truecolor(117, 142, 205).bold() ); + // Bottom border print!("{}", cursor::Goto(start_x as u16, (start_y + 5 + wrapped_text.len()) as u16)); - let centered_attr = format!("{:^width$}", attribution, width = box_width - 2); println!( "{}{}{}", - "║".color(BORDER_COLOR).bold(), - centered_attr.color(ATTR_COLOR).italic(), - "║".color(BORDER_COLOR).bold() + "╚".truecolor(117, 142, 205).bold(), + "═".repeat(box_inner_width).truecolor(117, 142, 205).bold(), + "╝".truecolor(117, 142, 205).bold() ); - print!("{}", cursor::Goto(start_x as u16, (start_y + 6 + wrapped_text.len()) as u16)); - println!( - "{}{}{}", - "╚".color(BORDER_COLOR).bold(), - "═".repeat(box_width - 2).color(BORDER_COLOR).bold(), - "╝".color(BORDER_COLOR).bold() - ); + // Buttons + print!("{}", cursor::Goto(start_x as u16 + 2, (start_y + 7 + wrapped_text.len()) as u16)); + print!("{}", "[N]ext Verse".truecolor(98, 76, 171).bold()); - print!("{}", cursor::Goto(start_x as u16 + 4, (start_y + 8 + wrapped_text.len()) as u16)); - print!("{}", "[N]ext Verse".color(NEXT_BTN_COLOR).bold()); - - print!("{}", cursor::Goto(start_x as u16 + box_width as u16 - 15, (start_y + 8 + wrapped_text.len()) as u16)); - print!("{}", "[Q]uit".color(QUIT_BTN_COLOR).bold()); - - print!("{}", cursor::Goto(start_x as u16 + (box_width / 2) as u16 - 15, (start_y + 10 + wrapped_text.len()) as u16)); - print!("{}", "Press N for next verse or Q to quit".color(REF_COLOR)); + print!("{}", cursor::Goto(start_x as u16 + box_width as u16 - 14, (start_y + 7 + wrapped_text.len()) as u16)); + print!("{}", "[Q]uit".truecolor(98, 76, 171).bold()); stdout().flush().unwrap(); } @@ -173,16 +161,18 @@ fn display_verse(verse: &BibleVerse, term_width: u16, term_height: u16) { async fn display_loading_animation(loading: Arc>, term_width: u16, term_height: u16) { let spinner_frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]; let mut frame_index = 0; + let message = "Loading Bible verse"; let message_len = message.len() + 2; while *loading.lock().unwrap() { - let spinner = spinner_frames[frame_index].color(NEXT_BTN_COLOR).bold(); + let spinner = spinner_frames[frame_index].cyan().bold(); + let x_pos = (term_width as usize / 2).saturating_sub(message_len / 2); let y_pos = term_height as usize / 2; print!("{}", cursor::Goto(x_pos as u16, y_pos as u16)); - print!("{} {}", spinner, message.color(REF_COLOR).bold()); + print!("{} {}", spinner, message.cyan().bold()); stdout().flush().unwrap(); frame_index = (frame_index + 1) % spinner_frames.len(); @@ -203,6 +193,7 @@ async fn main() -> Result<(), Box> { stdout.flush()?; let mut stdin = termion::async_stdin().keys(); + let (term_width, term_height) = terminal_size()?; let loading = Arc::new(Mutex::new(true)); let loading_clone = loading.clone(); @@ -225,14 +216,17 @@ async fn main() -> Result<(), Box> { if let Some(Ok(key)) = stdin.next() { match key { Key::Char('q') | Key::Char('Q') | Key::Esc => { + // Proper cleanup write!(stdout, "{}{}", clear::All, cursor::Show)?; stdout.flush()?; return Ok(()); - } + }, Key::Char('n') | Key::Char('N') | Key::Char(' ') => { key_pressed = true; + let loading = Arc::new(Mutex::new(true)); let loading_clone = loading.clone(); + write!(stdout, "{}", clear::All)?; stdout.flush()?; @@ -245,17 +239,18 @@ async fn main() -> Result<(), Box> { *loading.lock().unwrap() = false; animation_task.await?; current_verse = verse; - } + }, Err(e) => { *loading.lock().unwrap() = false; animation_task.await?; + write!(stdout, "{}{}", clear::All, cursor::Show)?; stdout.flush()?; - eprintln!("{} {}", "Error fetching verse:".bold().color(QUIT_BTN_COLOR), e); + eprintln!("{} {}", "Error fetching verse:".bold().red(), e); return Err(e); } } - } + }, _ => {} } }