82 lines
2.6 KiB
Rust
82 lines
2.6 KiB
Rust
mod app;
|
|
mod editor;
|
|
mod ui;
|
|
|
|
use std::{env, error::Error, io, time::Duration};
|
|
|
|
use crossterm::{
|
|
event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyEventKind},
|
|
execute,
|
|
};
|
|
use gotcha_gitea::Config;
|
|
|
|
const HELP: &str = "\
|
|
Gotcha TUI
|
|
|
|
Usage: gotcha-tui [--server SERVER]
|
|
|
|
Keyboard:
|
|
1..5 switch Home, Issues, Repos, PRs, Milestones
|
|
j/k, arrows move selection
|
|
n/p or ]/[ next/previous page
|
|
Enter open selected item
|
|
Backspace go back (edits text inside an editor)
|
|
a/e/c/x/d add, edit, comment, toggle state, delete
|
|
/, v, b, * filters, activity filter, branch, repository favorite
|
|
f browse files from a repository commit view
|
|
r reload
|
|
s choose or manage servers
|
|
, edit TUI preferences
|
|
q back, or quit from a root pane
|
|
|
|
Editors use Tab/Shift-Tab between fields, normal cursor/editing keys, Ctrl-S to
|
|
save, and Esc to cancel. Mouse clicks select items, double-click opens them,
|
|
and the scroll wheel moves through lists.";
|
|
|
|
#[tokio::main]
|
|
async fn main() -> Result<(), Box<dyn Error>> {
|
|
let requested_server = parse_args()?;
|
|
let config = Config::load()?;
|
|
let mut app = app::App::new(config, requested_server.as_deref()).await?;
|
|
|
|
let mut terminal = ratatui::init();
|
|
execute!(io::stdout(), EnableMouseCapture)?;
|
|
let result = run(&mut terminal, &mut app).await;
|
|
execute!(io::stdout(), DisableMouseCapture)?;
|
|
ratatui::restore();
|
|
result
|
|
}
|
|
|
|
async fn run(
|
|
terminal: &mut ratatui::DefaultTerminal,
|
|
app: &mut app::App,
|
|
) -> Result<(), Box<dyn Error>> {
|
|
while !app.should_quit {
|
|
terminal.draw(|frame| ui::draw(frame, app))?;
|
|
if event::poll(Duration::from_millis(100))? {
|
|
match event::read()? {
|
|
Event::Key(key) if key.kind == KeyEventKind::Press => app.handle_key(key).await,
|
|
Event::Mouse(mouse) => app.handle_mouse(mouse).await,
|
|
Event::Resize(_, _) => {}
|
|
_ => {}
|
|
}
|
|
} else if app.should_auto_reload() {
|
|
app.reload().await;
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn parse_args() -> Result<Option<String>, String> {
|
|
let args: Vec<_> = env::args().skip(1).collect();
|
|
match args.as_slice() {
|
|
[] => Ok(None),
|
|
[help] if matches!(help.as_str(), "-h" | "--help" | "help") => {
|
|
println!("{HELP}");
|
|
std::process::exit(0);
|
|
}
|
|
[option, server] if option == "--server" => Ok(Some(server.clone())),
|
|
_ => Err(format!("invalid arguments\n\n{HELP}")),
|
|
}
|
|
}
|