1pub mod app;
12pub mod events;
13pub mod features;
14pub mod layout;
15pub mod utils;
16pub mod widgets;
17
18use std::io;
19use std::panic;
20use std::path::PathBuf;
21use std::time::Duration;
22
23use anyhow::Result;
24use crossterm::event::{self, Event};
25use crossterm::execute;
26use crossterm::terminal::{
27 disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen,
28};
29use ratatui::backend::CrosstermBackend;
30use ratatui::Terminal;
31
32use crate::app::App;
33
34pub fn run(mut repo_path: Option<PathBuf>) -> Result<()> {
39 if repo_path.is_none() {
41 repo_path = gitkraft_core::features::persistence::get_last_repo()
42 .ok()
43 .flatten();
44 }
45
46 let default_hook = panic::take_hook();
50 panic::set_hook(Box::new(move |info| {
51 let _ = restore_terminal();
52 default_hook(info);
53 }));
54
55 enable_raw_mode()?;
56 let mut stdout = io::stdout();
57 execute!(stdout, EnterAlternateScreen)?;
58 let backend = CrosstermBackend::new(stdout);
59 let mut terminal = Terminal::new(backend)?;
60
61 let result = run_app(&mut terminal, repo_path);
62
63 restore_terminal()?;
64 terminal.show_cursor()?;
65 result
66}
67
68pub fn run_app<B: ratatui::backend::Backend>(
70 terminal: &mut Terminal<B>,
71 repo_path: Option<PathBuf>,
72) -> Result<()>
73where
74 B::Error: Send + Sync + 'static,
75{
76 let mut app = App::new();
77
78 if let Some(path) = repo_path {
79 app.open_repo(path);
80 }
81
82 loop {
83 app.tick_count = app.tick_count.wrapping_add(1);
84
85 app.poll_background();
89
90 terminal.draw(|frame| layout::render(&mut app, frame))?;
91
92 if event::poll(Duration::from_millis(33))? {
96 if let Event::Key(key) = event::read()? {
97 if key.kind == crossterm::event::KeyEventKind::Press {
99 events::handle_key(&mut app, key);
100 }
101 }
102 }
103
104 if app.should_quit {
105 break;
106 }
107 }
108
109 Ok(())
110}
111
112fn restore_terminal() -> Result<()> {
115 disable_raw_mode()?;
116 execute!(io::stdout(), LeaveAlternateScreen)?;
117 Ok(())
118}