ratatui-markdown 0.3.3

Markdown rendering, syntax highlighting, collapsible trees, and rich scroll widgets for ratatui
Documentation
use ratatui::{
    layout::Rect,
    style::Style,
    widgets::{Scrollbar, ScrollbarOrientation, ScrollbarState},
    Frame,
};

use crate::{
    constants::list_prefix::{ARROW_DOWN, ARROW_UP, BLOCK_FULL, VLINE},
    theme::RichTextTheme,
};

pub struct ArrowScrollbar {
    content_length: usize,
    viewport_length: usize,
    position: usize,
}

impl ArrowScrollbar {
    pub fn new(content_length: usize, viewport_length: usize) -> Self {
        Self {
            content_length,
            viewport_length,
            position: 0,
        }
    }

    pub fn position(mut self, position: usize) -> Self {
        self.position = position;
        self
    }

    pub fn render(self, f: &mut Frame, area: ratatui::layout::Rect, theme: &impl RichTextTheme) {
        if self.content_length == 0 || self.viewport_length == 0 {
            return;
        }

        if self.content_length <= self.viewport_length {
            return;
        }

        let max_offset = self.content_length.saturating_sub(self.viewport_length);
        let max_scrollbar_pos = self.content_length.saturating_sub(1);
        let remapped_position = if max_offset > 0 && max_scrollbar_pos > 0 {
            (self.position as u64 * max_scrollbar_pos as u64 / max_offset as u64) as usize
        } else {
            0
        };

        let scrollbar = Scrollbar::default()
            .orientation(ScrollbarOrientation::VerticalRight)
            .begin_symbol(Some(ARROW_UP))
            .end_symbol(Some(ARROW_DOWN))
            .track_symbol(Some(VLINE))
            .thumb_symbol(BLOCK_FULL)
            .begin_style(Style::default().fg(theme.get_muted_text_color()))
            .end_style(Style::default().fg(theme.get_muted_text_color()))
            .track_style(Style::default().fg(theme.get_muted_text_color()))
            .thumb_style(Style::default().fg(theme.get_text_color()));

        let mut state = ScrollbarState::default()
            .content_length(self.content_length)
            .viewport_content_length(self.viewport_length)
            .position(remapped_position);

        f.render_stateful_widget(scrollbar, area, &mut state);
    }
}

pub fn render_arrow_scrollbar(
    f: &mut Frame,
    area: ratatui::layout::Rect,
    content_length: usize,
    viewport_length: usize,
    position: usize,
    theme: &impl RichTextTheme,
) {
    ArrowScrollbar::new(content_length, viewport_length)
        .position(position)
        .render(f, area, theme);
}

pub fn anchored_panel_scrollbar_area(panel_area: Rect, content_area: Rect) -> Rect {
    Rect {
        x: panel_area.x + panel_area.width.saturating_sub(2),
        y: content_area.y,
        width: 2,
        height: content_area.height,
    }
}

pub fn border_scrollbar_area(panel_area: Rect, content_area: Rect) -> Rect {
    Rect {
        x: panel_area.x + panel_area.width.saturating_sub(1),
        y: content_area.y,
        width: 1,
        height: content_area.height,
    }
}