Skip to main content

teamctl_ui/
splash.rs

1//! Splash screen widget — figlet-isometric4 logo, version + team line,
2//! help-hint footer. Shown for ~3 seconds at launch (or until a key
3//! press) before the Triptych takes over. The art is vendored as a
4//! static asset; regenerate with `figlet -f isometric4 teamctl` when
5//! the wordmark changes.
6
7use ratatui::buffer::Buffer;
8use ratatui::layout::{Alignment, Constraint, Direction, Layout, Rect};
9use ratatui::style::{Modifier, Style};
10use ratatui::widgets::{Paragraph, Widget};
11
12use crate::app::App;
13
14const SPLASH_ART: &str = include_str!("assets/splash.txt");
15
16pub fn draw(f: &mut ratatui::Frame<'_>, app: &App) {
17    Splash { app }.render(f.area(), f.buffer_mut());
18}
19
20/// Standalone widget for snapshot tests: rendering into a `Buffer`
21/// directly is enough to assert layout without a `Terminal`.
22pub struct Splash<'a> {
23    pub app: &'a App,
24}
25
26impl Widget for Splash<'_> {
27    fn render(self, area: Rect, buf: &mut Buffer) {
28        let chunks = Layout::default()
29            .direction(Direction::Vertical)
30            .constraints([
31                Constraint::Min(0),     // top spacer
32                Constraint::Length(11), // logo (10 lines of art + 1 padding)
33                Constraint::Length(1),  // gap between logo and version line
34                Constraint::Length(1),  // version + team line
35                Constraint::Length(1),  // hint line
36                Constraint::Min(0),     // bottom spacer
37            ])
38            .split(area);
39
40        let accent = Style::default()
41            .fg(self.app.capabilities.accent())
42            .add_modifier(Modifier::BOLD);
43        let muted = Style::default().fg(self.app.capabilities.muted());
44
45        Paragraph::new(SPLASH_ART)
46            .style(accent)
47            .alignment(Alignment::Center)
48            .render(chunks[1], buf);
49
50        // chunks[2] is the gap row (intentionally unrendered).
51
52        let count = self.app.team.agents.len();
53        let team_line = format!(
54            "v{}  ·  {}  ·  {} agent{}",
55            self.app.version,
56            self.app.team.team_name,
57            count,
58            if count == 1 { "" } else { "s" }
59        );
60        Paragraph::new(team_line)
61            .alignment(Alignment::Center)
62            .render(chunks[3], buf);
63
64        Paragraph::new("Press `?` for help · `t` for tutorial")
65            .style(muted)
66            .alignment(Alignment::Center)
67            .render(chunks[4], buf);
68    }
69}