thesa 4.1.30

Archive GitHub repositories, ML models, and websites with Scrin/Aisling TUI workflows
use std::io::{self, Write};

use scrin::{
    Frame, Rect, Terminal, TerminalOptions,
    widgets::{Block, List, ListState, Widget},
};

use crate::error::Result;
use crate::{normalize_bg, theme_surface, theme_white};

pub(crate) struct TerminalSession {
    pub(crate) terminal: Terminal,
}

impl TerminalSession {
    pub(crate) fn new() -> Result<Self> {
        let mut terminal = Terminal::init_with(TerminalOptions {
            mouse_capture: true,
            ..TerminalOptions::default()
        })?;
        terminal.clear();
        Ok(Self { terminal })
    }
}

impl Drop for TerminalSession {
    fn drop(&mut self) {
        let _ = self.terminal.restore();
    }
}

pub(crate) fn draw_full_frame<F>(terminal: &mut Terminal, f: F) -> io::Result<()>
where
    F: FnOnce(&mut Frame<'_>),
{
    terminal.draw(f)?;
    let frame = terminal.back_buffer().to_ansi_string();
    let mut stdout = io::stdout();
    write!(stdout, "\x1b[H{frame}")?;
    stdout.flush()?;
    Ok(())
}

pub(crate) trait FrameRenderExt {
    fn render_block_widget<'item, W: Widget>(&mut self, area: Rect, widget: W, block: Block<'item>);
    fn render_block_stateful_list<'item>(
        &mut self,
        area: Rect,
        list: &List<'item>,
        state: &mut ListState,
        block: Block<'item>,
    );
}

impl FrameRenderExt for Frame<'_> {
    fn render_block_widget<'item, W: Widget>(
        &mut self,
        area: Rect,
        widget: W,
        block: Block<'item>,
    ) {
        block.render(self.buffer(), area);
        let inner = block.inner(area);
        if inner.width > 0 && inner.height > 0 {
            self.buffer()
                .fill(inner, ' ', theme_white(), Some(theme_surface()));
            widget.render(self.buffer(), inner);
            normalize_bg(self.buffer(), inner, theme_surface());
        }
    }

    fn render_block_stateful_list<'b>(
        &mut self,
        area: Rect,
        list: &List<'b>,
        state: &mut ListState,
        block: Block<'b>,
    ) {
        block.render(self.buffer(), area);
        let inner = block.inner(area);
        if inner.width > 0 && inner.height > 0 {
            self.buffer()
                .fill(inner, ' ', theme_white(), Some(theme_surface()));
            list.render_stateful(self.buffer(), inner, state);
            normalize_bg(self.buffer(), inner, theme_surface());
        }
    }
}