eazygit 0.5.1

A fast TUI for Git with staging, conflicts, rebase, and palette-first UX
Documentation
use crate::app::{AppState, state::FocusPane};
use crate::errors::ComponentError;
use ratatui::Frame;
use ratatui::layout::{Constraint, Direction, Layout, Rect};
use crate::components::Component;
use super::{ComponentManager, renderers};

pub fn render(mgr: &mut ComponentManager, frame: &mut Frame, state: &AppState) -> Result<(), ComponentError> {
    let area = frame.size();
    
    // Debug: Rebase modal logging REMOVED - rebase feature removed
    
    // Render main UI only if no modal is open (production-grade: avoid overlapping)
    let modal_open = state.rebase_editor_open 
        // is_reword_modal_open/is_edit_modal_open REMOVED - rebase feature removed
        || state.commit_mode
        || state.branch_create_mode
        || state.stash_create_mode;
    
    if !modal_open {
        render_main(mgr, frame, area, state)?;
    }
    
    // Render modals (in order of priority)
    if state.show_help {
        mgr.help.render(frame, area, state);
    }
    if state.command_palette && !state.theme_picker {
        // Use new palette renderer (includes custom commands)
        use crate::palette::PaletteRenderer;
        let commands = mgr.palette_handler.filter_commands(&state.palette_input, state);
        PaletteRenderer::render(frame, area, state, &commands);
    } else if state.command_palette {
        // Fallback to old renderer for theme picker
        renderers::render_palette(mgr, frame, area, state);
    }
    if state.theme_picker {
        renderers::render_theme_picker(mgr, frame, area, state);
    }
    if state.conflicts_popup {
        renderers::render_conflicts_popup(mgr, frame, area, state);
    }
    if state.conflicts_guided {
        renderers::render_conflicts_guided(mgr, frame, area, state);
    }
    if state.pr_helper {
        renderers::render_pr_helper(mgr, frame, area, state);
    }
    if state.show_merge_log {
        renderers::render_merge_log(mgr, frame, area, state);
    }
    if state.show_rebase_todo {
        renderers::render_rebase_todo(mgr, frame, area, state);
    }
    if state.show_op_log {
        renderers::render_op_log(mgr, frame, area, state);
    }
    if state.merge_base_picker {
        renderers::render_merge_base_picker(mgr, frame, area, state);
    }
    if state.commit_mode {
        renderers::render_commit_input(mgr, frame, area, state);
    }
    if state.branch_create_mode {
        renderers::render_branch_create_input(mgr, frame, area, state);
    }
    if state.stash_create_mode {
        renderers::render_stash_create_input(mgr, frame, area, state);
    }
    if state.rebase_editor_open {
        renderers::render_rebase_builder(mgr, frame, area, state);
    }
    if state.rebase_recovery_open {
        renderers::render_rebase_recovery(mgr, frame, area, state);
    }
    // Reword/edit modal rendering REMOVED - rebase feature removed
    // if state.is_reword_modal_open() { renderers::render_reword_modal(...) }
    // if state.is_edit_modal_open() { renderers::render_edit_modal(...) }
    // Log action menu should be rendered last to ensure it's on top
    if state.log_action_menu {
        renderers::render_log_action_menu(mgr, frame, area, state);
    }
    
    // Render feedback/error messages LAST so they're always on top (production-grade)
    renderers::render_feedback(mgr, frame, area, state);
    
    Ok(())
}

fn render_main(
    mgr: &mut ComponentManager,
    frame: &mut Frame,
    area: Rect,
    state: &AppState,
) -> Result<(), ComponentError> {
    match mgr.active_component {
        FocusPane::Status => {
            let chunks = Layout::default()
                .direction(Direction::Horizontal)
                .constraints([Constraint::Percentage(40), Constraint::Percentage(60)])
                .split(area);
            if let Some(component) = mgr.components.get_mut(&FocusPane::Status) {
                component.render(frame, chunks[0], state);
            }
            mgr.diff.render(frame, chunks[1], state);
        }
        FocusPane::Branches => {
            render_single(mgr, frame, area, FocusPane::Branches, state);
        }
        FocusPane::Log => {
            render_single(mgr, frame, area, FocusPane::Log, state);
        }
        FocusPane::Stash => {
            render_single(mgr, frame, area, FocusPane::Stash, state);
        }
        FocusPane::Tags => {
            render_single(mgr, frame, area, FocusPane::Tags, state);
        }
        FocusPane::Reflog => {
            render_single(mgr, frame, area, FocusPane::Reflog, state);
        }
    }
    Ok(())
}

fn render_single(
    mgr: &mut ComponentManager,
    frame: &mut Frame,
    area: Rect,
    pane: FocusPane,
    state: &AppState,
) {
    if let Some(component) = mgr.components.get_mut(&pane) {
        component.render(frame, area, state);
    }
}