eazygit 0.5.1

A fast TUI for Git with staging, conflicts, rebase, and palette-first UX
Documentation
use crate::app::{AppState, Action};
use crate::input::InputEvent;
use crate::errors::ComponentError;
use crate::config;
use crossterm::event::KeyCode::*;
use crossterm::event::KeyModifiers;

pub fn handle_op_log(event: InputEvent) -> Result<Option<Action>, ComponentError> {
    if let InputEvent::Key(key) = &event {
        if key.kind == crossterm::event::KeyEventKind::Press {
            return Ok(match key.code {
                Esc | Enter | Char('q') => Some(Action::ToggleOpLog),
                _ => None,
            });
        }
    }
    Ok(None)
}

pub fn handle_merge_log(event: InputEvent) -> Result<Option<Action>, ComponentError> {
    if let InputEvent::Key(key) = &event {
        if key.kind == crossterm::event::KeyEventKind::Press {
            return Ok(match key.code {
                Esc | Enter | Char('q') => Some(Action::ToggleMergeLog),
                _ => None,
            });
        }
    }
    Ok(None)
}

pub fn handle_pr_helper(event: InputEvent, state: &AppState) -> Result<Option<Action>, ComponentError> {
    let prs = &state.pr_list;
    let max_idx = prs.len().saturating_sub(1);
    if let InputEvent::Key(key) = &event {
        if key.kind == crossterm::event::KeyEventKind::Press {
            return Ok(match key.code {
                Esc => Some(Action::HidePrHelper),
                Up | Char('k') => Some(Action::PrHelperUp),
                Down | Char('j') => Some(Action::PrHelperDown),
                Enter => prs
                    .get(state.pr_selected.min(max_idx))
                    .map(|pr| Action::CheckoutPr(pr.number.clone())),
                Char('o') => prs
                    .get(state.pr_selected.min(max_idx))
                    .map(|pr| Action::OpenPrInBrowser(pr.number.clone())),
                _ => None,
            });
        }
    }
    Ok(None)
}

pub fn handle_theme_picker(event: InputEvent, state: &AppState) -> Result<Option<Action>, ComponentError> {
    if let InputEvent::Key(key) = &event {
        if key.kind == crossterm::event::KeyEventKind::Press {
            match key.code {
                Esc => {
                    return Ok(Some(Action::ThemePreviewCancel));
                }
                Char('q') => {
                    return Ok(Some(Action::ThemePreviewCancel));
                }
                Up | Char('k') => {
                    if state.theme_picker_selected > 0 {
                        let target = state.theme_picker_selected - 1;
                        if let Some(name) = state.available_themes.get(target) {
                            let mut theme = config::load_theme_by_name(name);
                            // Preserve transparency setting
                            theme.enable_transparency = state.theme.enable_transparency;
                            return Ok(Some(Action::ThemePreviewSet(
                                target,
                                Box::new(theme),
                            )));
                        }
                    }
                }
                Down | Char('j') => {
                    let max_idx = state.available_themes.len().saturating_sub(1);
                    if state.theme_picker_selected < max_idx {
                        let target = state.theme_picker_selected + 1;
                        if let Some(name) = state.available_themes.get(target) {
                            let mut theme = config::load_theme_by_name(name);
                            // Preserve transparency setting
                            theme.enable_transparency = state.theme.enable_transparency;
                            return Ok(Some(Action::ThemePreviewSet(
                                target,
                                Box::new(theme),
                            )));
                        }
                    }
                }
                Enter => {
                    let max_idx = state.available_themes.len().saturating_sub(1);
                    let selected = state.theme_picker_selected.min(max_idx);
                    let name = state.available_themes.get(selected).cloned();
                    if let Some(n) = name {
                        return Ok(Some(Action::ApplyTheme(n)));
                    }
                    return Ok(Some(Action::ThemePreviewCancel));
                }
                _ => {}
            }
        }
    }
    Ok(None)
}

pub fn handle_merge_base_picker(event: InputEvent, state: &AppState) -> Result<Option<Action>, ComponentError> {
    if let InputEvent::Key(key) = &event {
        if key.kind == crossterm::event::KeyEventKind::Press {
            return Ok(match key.code {
                Esc => Some(Action::HideMergeBasePicker),
                Up | Char('k') => Some(Action::MergeBaseUp),
                Down | Char('j') => Some(Action::MergeBaseDown),
                Enter => {
                    let idx = state.merge_base_selected.min(state.merge_base_candidates.len().saturating_sub(1));
                    let name = state.merge_base_candidates.get(idx).cloned();
                    name.map(Action::ApplyMergeBase)
                }
                _ => None,
            });
        }
    }
    Ok(None)
}

pub fn handle_help(event: InputEvent, _state: &AppState) -> Result<Option<Action>, ComponentError> {
    if let InputEvent::Key(key) = &event {
        if key.kind == crossterm::event::KeyEventKind::Press {
            return Ok(match key.code {
                Char('q') => Some(Action::Quit),
                Char('c') if key.modifiers.contains(KeyModifiers::CONTROL) => Some(Action::Quit),
                Char(':') => Some(Action::ShowPalette),
                Up | Char('k') => Some(Action::HelpUp),
                Down | Char('j') => Some(Action::HelpDown),
                Esc | Char('h') => Some(Action::ToggleHelp),
                _ => None,
            });
        }
    }
    Ok(None)
}