changxi 0.3.0

TUI EPUB Reader
pub mod components;
pub mod views;

use crate::app::App;
use crate::config::Config;
use crate::core::ContentElement;
use ratatui::widgets::BorderType;
use ratatui::{
    Frame,
    layout::{Constraint, Direction, Layout, Rect},
};
use ratatui_image::picker::Picker;

pub trait Component {
    fn render(&self, f: &mut Frame, area: Rect, app: &mut App, picker: &mut Picker);
}

pub trait View {
    fn render(&self, f: &mut Frame, app: &mut App, picker: &mut Picker);
}

pub use views::{BookView, ContinuousView, DefaultView};

// Helper function to get border type from config
pub fn get_border_type(config: &Config) -> BorderType {
    match config.border.as_str() {
        "plain" => BorderType::Plain,
        "rounded" => BorderType::Rounded,
        "double" => BorderType::Double,
        "thick" => BorderType::Thick,
        "quadrant_inside" => BorderType::QuadrantInside,
        "quadrant_outside" => BorderType::QuadrantOutside,
        "light_double_dashed" => BorderType::LightDoubleDashed,
        "heavy_double_dashed" => BorderType::HeavyDoubleDashed,
        "light_triple_dashed" => BorderType::LightTripleDashed,
        "heavy_triple_dashed" => BorderType::HeavyTripleDashed,
        "light_quadruple_dashed" => BorderType::LightQuadrupleDashed,
        "heavy_quadruple_dashed" => BorderType::HeavyQuadrupleDashed,
        _ => BorderType::Plain,
    }
}

// Helper function to count lines for wrapping text
pub fn count_lines(element: &ContentElement, width: usize) -> usize {
    match element {
        ContentElement::Text(spans) => {
            let text: String = spans.iter().map(|s| s.text.as_str()).collect();
            let mut count = 0;
            for line in text.lines() {
                if line.trim().is_empty() {
                    count += 1;
                    continue;
                }
                let wrapped = (line.chars().count() as f64 / width as f64).ceil() as usize;
                count += wrapped.max(1);
            }
            count
        }
        ContentElement::Image(_) => 30,
        ContentElement::BlankLine => 1,
    }
}

// Helper function to create a centered rectangle
pub fn centered_rect(percent_x: u16, percent_y: u16, r: Rect) -> Rect {
    let popup_layout = Layout::default()
        .direction(Direction::Vertical)
        .constraints([
            Constraint::Percentage((100 - percent_y) / 2),
            Constraint::Percentage(percent_y),
            Constraint::Percentage((100 - percent_y) / 2),
        ])
        .split(r);

    Layout::default()
        .direction(Direction::Horizontal)
        .constraints([
            Constraint::Percentage((100 - percent_x) / 2),
            Constraint::Percentage(percent_x),
            Constraint::Percentage((100 - percent_x) / 2),
        ])
        .split(popup_layout[1])[1]
}