Skip to main content

sl/
lib.rs

1pub mod config;
2pub mod debug;
3pub mod render;
4pub mod smoke;
5pub mod terminal;
6pub mod train;
7
8use crate::config::Config;
9use crate::render::render_frame;
10use crate::terminal::{InputAction, Terminal};
11use std::io;
12use std::thread;
13use std::time::Duration;
14
15const FRAME_TIME_MS: u64 = 40;
16
17/// Runs the SL animation with the given arguments.
18pub fn run<I, T>(args: I) -> io::Result<()>
19where
20    I: IntoIterator<Item = T>,
21    T: AsRef<str>,
22{
23    run_with_token(args, None)
24}
25
26/// Runs the SL animation with a cancellation token.
27pub fn run_with_token<I, T>(
28    args: I,
29    cancel_token: Option<wasibox_core::CancellationToken>,
30) -> io::Result<()>
31where
32    I: IntoIterator<Item = T>,
33    T: AsRef<str>,
34{
35    let config = Config::from_args(args);
36    let terminal = Terminal::new()?;
37
38    let width = terminal.width() as i32;
39    let max_length = if config.logo {
40        40
41    } else if config.c51 {
42        95
43    } else {
44        83
45    };
46
47    let mut pattern = 0usize;
48    let mut paused = false;
49
50    for x in (-(max_length + 10)..=(width + 10)).rev() {
51        if let Some(token) = &cancel_token {
52            if token.is_cancelled() {
53                break;
54            }
55        }
56        // Unified input handler
57        match terminal.check_input()? {
58            InputAction::Quit => break,
59            InputAction::Pause => {
60                paused = !paused;
61                if paused {
62                    eprintln!("⏸️  PAUSED - Press Space or P to resume, Ctrl+C to quit");
63                }
64            }
65            InputAction::None => {}
66        }
67
68        // If paused, wait for resume signal without advancing animation
69        while paused {
70            if let Some(token) = &cancel_token {
71                if token.is_cancelled() {
72                    return Ok(());
73                }
74            }
75            thread::sleep(Duration::from_millis(100));
76            match terminal.check_input()? {
77                InputAction::Pause => {
78                    paused = false;
79                    eprintln!("▶️  RESUMED");
80                }
81                InputAction::Quit => return Ok(()),
82                InputAction::None => {}
83            }
84        }
85
86        render_frame(&terminal, x, pattern, &config)?;
87
88        pattern = (pattern + 1) % 6;
89        thread::sleep(Duration::from_millis(FRAME_TIME_MS));
90    }
91
92    Ok(())
93}