Skip to main content

hjkl_splash_tui/
lib.rs

1//! Ratatui adapter for `hjkl-splash` — renders a [`hjkl_splash::StartScreen`]
2//! into a ratatui [`Frame`].
3
4use hjkl_splash::{
5    CellKind, Layout, Rgb, Splash, default_trail_color, presets, start_screen::StartScreen,
6};
7use ratatui::{
8    Frame,
9    layout::Rect,
10    style::{Color, Style},
11    text::{Line, Span},
12    widgets::Widget,
13};
14
15fn rgb_to_color(Rgb(r, g, b): Rgb) -> Color {
16    Color::Rgb(r, g, b)
17}
18
19/// Render the start screen into a ratatui `Frame`.
20///
21/// Paints the hjkl art animation centred in `area`, followed by a dim version
22/// line and a hint line below it.
23pub fn render(frame: &mut Frame, area: Rect, screen: &StartScreen) {
24    let splash = Splash::new(presets::hjkl::ART, presets::hjkl::PATH);
25    let layout = Layout::centered(
26        area.width,
27        area.height,
28        presets::hjkl::ROWS,
29        presets::hjkl::COLS,
30    );
31
32    let buf = frame.buffer_mut();
33
34    // Paint art + animation cells.
35    for cell in splash.cells(layout) {
36        let x = area.x + cell.x;
37        let y = area.y + cell.y;
38        if x >= area.x + area.width || y >= area.y + area.height {
39            continue;
40        }
41        let buf_cell = buf.cell_mut((x, y)).unwrap_or_else(|| {
42            // Safety: we bounds-checked above; if the cell is somehow missing
43            // just skip this iteration via a panic-free path.
44            panic!("start_screen: cell ({x},{y}) out of buffer bounds");
45        });
46        buf_cell.set_char(cell.ch);
47        let style = match cell.kind {
48            CellKind::Art => Style::default().fg(rgb_to_color(screen.palette.text_dim)),
49            CellKind::Trail { age } => {
50                let trail_rgb = default_trail_color(age);
51                Style::default().fg(rgb_to_color(trail_rgb))
52            }
53            CellKind::Cursor => Style::default()
54                .fg(rgb_to_color(screen.palette.text))
55                .bg(rgb_to_color(screen.palette.cursor_line_bg)),
56        };
57        buf_cell.set_style(style);
58    }
59
60    // Version line — two rows below the art block.
61    let ver_y = area.y + layout.origin_y + presets::hjkl::ROWS + 1;
62    if ver_y < area.y + area.height {
63        let ver_line = Line::from(vec![Span::styled(
64            format!("  hjkl v{}", screen.version),
65            Style::default().fg(rgb_to_color(screen.palette.text_dim)),
66        )]);
67        let ver_area = Rect {
68            x: area.x + layout.origin_x,
69            y: ver_y,
70            width: area.width.saturating_sub(layout.origin_x),
71            height: 1,
72        };
73        ver_line.render(ver_area, buf);
74    }
75
76    // Hint line — one row below the version.
77    let hint_y = ver_y + 1;
78    if hint_y < area.y + area.height {
79        let hint_line = Line::from(vec![Span::styled(
80            "  :e <file>  to open",
81            Style::default().fg(rgb_to_color(screen.palette.text_dim)),
82        )]);
83        let hint_area = Rect {
84            x: area.x + layout.origin_x,
85            y: hint_y,
86            width: area.width.saturating_sub(layout.origin_x),
87            height: 1,
88        };
89        hint_line.render(hint_area, buf);
90    }
91}