ratatui-markdown 0.3.5

Markdown rendering, syntax highlighting, collapsible trees, and rich scroll widgets for ratatui
Documentation
#[derive(Debug, Clone, Default)]
pub struct FollowScrollState {
    pub manual_offset: Option<usize>,
    last_non_streaming_total: usize,
}

impl FollowScrollState {
    pub fn new() -> Self {
        Self {
            manual_offset: None,
            last_non_streaming_total: 0,
        }
    }

    pub fn resolve(&self, total_lines: usize, viewport_height: usize, is_streaming: bool) -> usize {
        let max = total_lines.saturating_sub(viewport_height);
        match self.manual_offset {
            Some(offset) => offset.min(max),
            None => {
                if is_streaming {
                    max
                } else {
                    max.max(
                        self.last_non_streaming_total
                            .saturating_sub(viewport_height),
                    )
                    .min(max)
                }
            }
        }
    }

    pub fn resolve_always_follow(&self, total_lines: usize, viewport_height: usize) -> usize {
        let max = total_lines.saturating_sub(viewport_height);
        match self.manual_offset {
            None => max,
            Some(offset) => offset.min(max),
        }
    }

    pub fn scroll_up(&mut self, step: usize, total_lines: usize, viewport_height: usize) {
        let current = self.resolve(total_lines, viewport_height, false);
        let new_offset = current.saturating_sub(step);
        self.manual_offset = Some(new_offset);
    }

    pub fn scroll_down(&mut self, step: usize, total_lines: usize, viewport_height: usize) {
        let max = total_lines.saturating_sub(viewport_height);
        let current = self.resolve(total_lines, viewport_height, false);
        let new_offset = (current + step).min(max);
        if new_offset >= max {
            self.manual_offset = None;
        } else {
            self.manual_offset = Some(new_offset);
        }
    }

    pub fn page_up(&mut self, total_lines: usize, viewport_height: usize) {
        let page = viewport_height.max(1);
        self.scroll_up(page, total_lines, viewport_height);
    }

    pub fn page_down(&mut self, total_lines: usize, viewport_height: usize) {
        let page = viewport_height.max(1);
        self.scroll_down(page, total_lines, viewport_height);
    }

    pub fn is_auto_following(&self) -> bool {
        self.manual_offset.is_none()
    }

    pub fn reset(&mut self) {
        self.manual_offset = None;
        self.last_non_streaming_total = 0;
    }

    pub fn update_non_streaming_baseline(&mut self, total_lines: usize) {
        if self.manual_offset.is_none() {
            self.last_non_streaming_total = total_lines;
        }
    }
}