eazygit 0.5.1

A fast TUI for Git with staging, conflicts, rebase, and palette-first UX
Documentation
use crate::app::{actions::Action, state::{AppState, DiffMode, FocusPane}};

pub fn handle(state: &mut AppState, action: &Action) -> bool {
    match action {
        Action::SetSelectedPath(path) => {
            if state.status_selected_path != *path {
                state.last_diff.clear();
                state.last_diff_path.clear();
                state.last_diff_truncated = false;
                state.last_diff_total = 0;
            }
            state.status_selected_path = path.clone();
        }
        Action::SetCurrentBranch(branch) => {
            state.current_branch = branch.clone();
        }
        Action::ToggleDiffMode => {
            state.diff_mode = match state.diff_mode {
                DiffMode::Working => DiffMode::Staged,
                DiffMode::Staged => DiffMode::Working,
            };
        }
        Action::SetLastRefreshed(ts) => {
            state.last_refreshed = ts.clone();
        }
        Action::SetRefreshing(on) => {
            state.refreshing = *on;
            if *on {
                // Track when refresh starts (for potential future analytics, not rate limiting)
                state.last_refresh_time = Some(std::time::Instant::now());
            }
        }
        Action::RequestRefreshStatus => {
            // Show immediate visual feedback
            state.refreshing = true;
            state.last_refresh_time = Some(std::time::Instant::now());
        }
        Action::SetDetailScrollX(x) => {
            state.detail_scroll_x = *x;
        }
        Action::ToggleFileSelect(path) => {
            if state.selected_files.contains(path) {
                state.selected_files.remove(path);
            } else {
                state.selected_files.insert(path.clone());
            }
        }
        Action::ClearFileSelects => {
            state.selected_files.clear();
        }
        Action::SetFileScroll(path, offs) => {
            state.file_scrolls.insert(path.clone(), *offs);
        }
        Action::ResetFileScroll(path) => {
            state.file_scrolls.remove(path);
        }
        Action::SetBranchAheadBehind(name, ahead, behind) => {
            for b in state.branches.iter_mut() {
                if b.name == *name {
                    b.name = if *ahead != 0 || *behind != 0 {
                        format!("{} (+{} / -{})", name, ahead, behind)
                    } else {
                        name.clone()
                    };
                    b.ahead = Some(*ahead);
                    b.behind = Some(*behind);
                }
            }
        }
        Action::FocusNext => {
            state.focus = match state.focus {
                FocusPane::Status => FocusPane::Branches,
                FocusPane::Branches => FocusPane::Log,
                FocusPane::Log => FocusPane::Stash,
                FocusPane::Stash => FocusPane::Tags,
                FocusPane::Tags => FocusPane::Reflog,
                FocusPane::Reflog => FocusPane::Status,
            };
        }
        Action::FocusPrev => {
            state.focus = match state.focus {
                FocusPane::Status => FocusPane::Reflog,
                FocusPane::Branches => FocusPane::Status,
                FocusPane::Log => FocusPane::Branches,
                FocusPane::Stash => FocusPane::Log,
                FocusPane::Tags => FocusPane::Stash,
                FocusPane::Reflog => FocusPane::Tags,
            };
        }
        _ => return false,
    }
    true
}