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