basalt-tui 0.9.0

Basalt TUI application for Obsidian notes.
Documentation
use std::marker::PhantomData;

use basalt_core::obsidian::Vault;
use ratatui::{
    buffer::Buffer,
    layout::{Constraint, Flex, Layout, Rect},
    style::Stylize,
    text::Text,
    widgets::{StatefulWidgetRef, Widget},
};

use crate::vault_selector::{VaultSelector, VaultSelectorState};

const TITLE: &str = "⋅𝕭𝖆𝖘𝖆𝖑𝖙⋅";

pub const LOGO: [&str; 25] = [
    "           ▒███▓░          ",
    "          ▒█████▒░         ",
    "        ▒███▒██▓▒▒░        ",
    "      ▒████░██▓▒░▒▒░       ",
    "     ▒███▒▒██▒▒░ ░▒▒░      ",
    "   ▒████▓▓██▒░▒░  ░▒▒▒░    ",
    " ▒█████▓▓▓██ ░▒░  ░░▒▒▒░   ",
    "░████▓▓▒░░██ ░░ ░░░░░░▒▒░  ",
    "▒██▓▓▒░░░▒██░░▒░░░    ░▒░  ",
    "░███▓░░░░██▓░░▒▒▒▒░   ░▒▒  ",
    " ▒███░░░░██░░░░▒▒▒▒▒░░░▒▒  ",
    " ▒▒██▒░░░██░░░░░░░▒▒▒░ ░▒  ",
    " ▓▒░██░░▒█▓░░ ░░▒▒▒▒░ ░░▒  ",
    " █▒▒██▒░▓█░░ ░▒▒▒▒▒▒░ ░░▒░ ",
    "▒█▒▓▒██░██░▒▒▒▒▒░░░░ ░░░▒▒░",
    "▓█▒▓▒▓██▓█░░░░░░░░░  ░ ░░▒▒",
    "██▓▓▒▒▓█▓▓ ░░░░░░░░░░░░░░▒▒",
    "▒█▓▒░░ ▒▒▒░░░░ ░▒░░ ░░░▒▒▒░",
    "░▒▒▒░░░ ░░░░░░░░░░░░░░░▒▒░ ",
    " ░░▒▒░ ░ ░░░░░░░░░░░░▒▒░   ",
    "   ░▒▒▒░ ░ ░░░░░░░░▒▒░░    ",
    "     ░▒▒░░  ░░░░░░▒▒░      ",
    "       ░▒▒░░░░░▒▒▒▒░       ",
    "        ░░▒▒▒▒▒▒▒░         ",
    "          ░░▒▒░            ",
];

#[derive(Debug, Default, Clone, PartialEq)]
pub struct SplashState<'a> {
    pub(crate) vault_selector_state: VaultSelectorState<'a>,
    pub(crate) version: &'a str,
}

impl<'a> SplashState<'a> {
    pub fn new(version: &'a str, items: Vec<&'a Vault>) -> Self {
        let vault_selector_state = VaultSelectorState::new(items);

        SplashState {
            version,
            vault_selector_state,
        }
    }

    pub fn select(&self) -> Self {
        Self {
            vault_selector_state: self.vault_selector_state.select(),
            ..self.clone()
        }
    }

    pub fn items(self) -> Vec<&'a Vault> {
        self.vault_selector_state.items
    }

    pub fn get_item(self, index: usize) -> Option<&'a Vault> {
        self.vault_selector_state.items.get(index).cloned()
    }

    pub fn selected(&self) -> Option<usize> {
        self.vault_selector_state.selected()
    }

    pub fn next(self) -> Self {
        Self {
            vault_selector_state: self.vault_selector_state.next(),
            ..self
        }
    }

    pub fn previous(self) -> Self {
        Self {
            vault_selector_state: self.vault_selector_state.previous(),
            ..self
        }
    }
}

#[derive(Default)]
pub struct Splash<'a> {
    _lifetime: PhantomData<&'a ()>,
}

impl<'a> StatefulWidgetRef for Splash<'a> {
    type State = SplashState<'a>;

    fn render_ref(&self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
        let [_, center, _] = Layout::horizontal([
            Constraint::Fill(1),
            Constraint::Length(79),
            Constraint::Fill(1),
        ])
        .areas(area);

        let [_, top, bottom, _, help] = Layout::vertical([
            Constraint::Fill(1),
            Constraint::Length(28),
            Constraint::Min(6),
            Constraint::Fill(1),
            Constraint::Length(1),
        ])
        .flex(Flex::Center)
        .margin(1)
        .areas(center);

        let [logo, title] =
            Layout::vertical([Constraint::Fill(1), Constraint::Length(3)]).areas(top);

        let [_, title, version] = Layout::horizontal([
            Constraint::Fill(1),
            Constraint::Fill(1),
            Constraint::Fill(1),
        ])
        .flex(Flex::SpaceBetween)
        .margin(1)
        .areas(title);

        let [bottom] = Layout::horizontal([Constraint::Length(60)])
            .flex(Flex::Center)
            .areas(bottom);

        Text::from_iter(LOGO)
            .dark_gray()
            .centered()
            .render(logo, buf);

        Text::from(TITLE).dark_gray().centered().render(title, buf);

        Text::from(state.version)
            .dark_gray()
            .italic()
            .centered()
            .render(version, buf);

        Text::from("Press (?) for help")
            .italic()
            .dark_gray()
            .centered()
            .render(help, buf);

        VaultSelector::default().render_ref(bottom, buf, &mut state.vault_selector_state);
    }
}