pub mod app;
pub mod includes_view;
pub mod inline_form;
pub mod sandbox_view;
pub mod settings_view;
pub mod tea;
pub mod test_panel;
pub mod theme;
pub mod tool_registry;
pub mod tree_view;
pub mod walkthrough;
pub mod widgets;
use std::path::Path;
use anyhow::{Context, Result, anyhow};
use crossterm::cursor::Show;
use crossterm::event::{DisableMouseCapture, EnableMouseCapture};
use crossterm::execute;
use crossterm::terminal::{
EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode,
};
use ratatui::Terminal;
use ratatui::backend::CrosstermBackend;
use clash_starlark::codegen::StarDocument;
use crate::policy_loader::legacy_json_error;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TuiOutcome {
Completed,
Aborted,
}
pub(crate) fn restore_terminal() {
let _ = disable_raw_mode();
let _ = execute!(
std::io::stdout(),
LeaveAlternateScreen,
DisableMouseCapture,
Show
);
}
pub fn run(path: &Path) -> Result<TuiOutcome> {
run_with_options(path, false, false)
}
pub fn run_with_options(
path: &Path,
show_test_panel: bool,
onboarding: bool,
) -> Result<TuiOutcome> {
let is_star = path.extension().is_some_and(|ext| ext == "star");
if !is_star {
if path.file_name().and_then(|n| n.to_str()) == Some("policy.json") {
return Err(anyhow!(legacy_json_error(path)));
}
return Err(anyhow!(
"TUI only supports `.star` policies; got `{}`",
path.display()
));
}
let doc =
StarDocument::open(path).with_context(|| format!("failed to parse {}", path.display()))?;
let mut app = app::App::new_star(doc)?;
if show_test_panel {
app.show_test_panel();
}
if onboarding {
app.start_walkthrough();
}
let original_hook = std::panic::take_hook();
std::panic::set_hook(Box::new(move |info| {
restore_terminal();
original_hook(info);
}));
let result = setup_and_run(&mut app);
let _ = std::panic::take_hook();
result
}
fn setup_and_run(app: &mut app::App) -> Result<TuiOutcome> {
enable_raw_mode()?;
let result = (|| {
let mut stdout = std::io::stdout();
execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::new(backend)?;
app.run(&mut terminal)
})();
restore_terminal();
result
}