proses 0.1.1

Proses – Professional Secure Execution System
mod app;
mod ui;

use std::time::Duration;

use anyhow::Result;
use crossterm::event::{self, Event, KeyCode, KeyEventKind};

pub use app::App;

/// Launch the live process dashboard.
///
/// Initialises the Ratatui terminal, runs the event loop, and restores
/// the terminal on exit (including on panic, via Ratatui's built-in hook).
pub fn run_dashboard() -> Result<()> {
    let mut terminal = ratatui::init();
    let result = event_loop(&mut terminal);
    ratatui::restore();
    result
}

// ── Main event loop ───────────────────────────────────────────────────────────

fn event_loop(terminal: &mut ratatui::DefaultTerminal) -> Result<()> {
    let mut app = App::new();

    // Populate with live data before the first draw.
    app.refresh();

    loop {
        // Draw a frame.
        terminal.draw(|frame| ui::render(frame, &mut app))?;

        // Poll for keyboard / resize events with a short timeout so the
        // 2-second refresh timer can fire promptly.
        if event::poll(Duration::from_millis(100))? {
            match event::read()? {
                Event::Key(key) if key.kind == KeyEventKind::Press => {
                    handle_key(&mut app, key.code);
                }
                Event::Resize(_, _) => {
                    // Terminal resize is handled automatically by ratatui on
                    // the next draw; we just need to trigger a redraw.
                }
                _ => {}
            }
        }

        // Periodic refresh: pull a fresh snapshot from the daemon every 2 s.
        if app.last_refresh.elapsed() >= Duration::from_secs(2) {
            app.refresh();
        }

        if app.should_quit {
            break;
        }
    }

    Ok(())
}

// ── Keyboard handler ──────────────────────────────────────────────────────────

fn handle_key(app: &mut App, code: KeyCode) {
    // Esc always cancels any pending confirmation and, if nothing is pending,
    // quits the dashboard.
    if code == KeyCode::Esc {
        if app.confirm_delete {
            app.confirm_delete = false;
        } else {
            app.should_quit = true;
        }
        return;
    }

    match code {
        // ── Navigation ────────────────────────────────────────────────────
        KeyCode::Down | KeyCode::Char('j') => {
            app.confirm_delete = false;
            app.select_next();
        }
        KeyCode::Up | KeyCode::Char('k') => {
            app.confirm_delete = false;
            app.select_prev();
        }

        // ── Process actions ───────────────────────────────────────────────
        KeyCode::Char('r') => {
            app.confirm_delete = false;
            app.restart_selected();
        }
        KeyCode::Char('s') => {
            app.confirm_delete = false;
            app.toggle_stop();
        }
        KeyCode::Char('d') => {
            // First press → arms confirmation; second press → executes.
            app.delete_selected();
        }

        // ── Log pane ──────────────────────────────────────────────────────
        KeyCode::Char('e') => {
            app.confirm_delete = false;
            app.toggle_log_source();
        }

        // ── Quit ──────────────────────────────────────────────────────────
        KeyCode::Char('q') => {
            if app.confirm_delete {
                app.confirm_delete = false;
            } else {
                app.should_quit = true;
            }
        }

        _ => {}
    }
}