eazygit 0.5.1

A fast TUI for Git with staging, conflicts, rebase, and palette-first UX
Documentation
//! Debug panel renderer for TEA runtime introspection.
//!
//! Provides a visual debug overlay showing:
//! - Recent actions
//! - Current state summary
//! - Performance metrics
//! - Cache statistics

use crate::app::AppState;
use crate::middleware::ActionLogger;
use crate::ui::style::{self, Emphasis};
use ratatui::Frame;
use ratatui::layout::{Constraint, Direction, Layout, Rect};
use ratatui::widgets::{Block, Borders, Clear, List, ListItem, Paragraph};

/// Render the debug panel overlay.
pub fn render(
    frame: &mut Frame,
    area: Rect,
    state: &AppState,
    action_logger: Option<&ActionLogger>,
    cache_hit_rate: Option<f64>,
) {
    // Create a centered popup
    let popup = center_rect(70, 80, area);
    frame.render_widget(Clear, popup);
    
    // Split into sections
    let chunks = Layout::default()
        .direction(Direction::Vertical)
        .margin(1)
        .constraints([
            Constraint::Length(3),  // Header
            Constraint::Min(10),    // Actions
            Constraint::Length(5),  // Stats
        ])
        .split(popup);
    
    // Header
    let header = Paragraph::new("Debug Panel (Press 'd' to close)")
        .style(style::text(&state.theme, Emphasis::Normal))
        .block(Block::default().borders(Borders::ALL).title("Debug"));
    frame.render_widget(header, popup);
    
    // Recent actions
    if let Some(logger) = action_logger {
        let actions: Vec<ListItem> = logger
            .recent_actions(15)
            .into_iter()
            .map(|record| {
                let prefix = if record.produced_effect { "[+]" } else { "[ ]" };
                let text = format!("{} {:.2?} {}", 
                    prefix, 
                    record.elapsed, 
                    truncate(&record.action, 60)
                );
                ListItem::new(text).style(style::body_style(&state.theme))
            })
            .collect();
        
        let action_list = List::new(actions)
            .block(Block::default().borders(Borders::ALL).title("Recent Actions"));
        frame.render_widget(action_list, chunks[1]);
    }
    
    // Stats
    let stats = vec![
        format!("Focus: {:?}", state.focus),
        format!("Entries: {} status, {} commits, {} branches", 
            state.status_entries.len(),
            state.commits.len(),
            state.branches.len()
        ),
        format!("Cache Hit Rate: {:.1}%", cache_hit_rate.unwrap_or(0.0) * 100.0),
    ];
    
    let stats_text = stats.join(" | ");
    let stats_para = Paragraph::new(stats_text)
        .style(style::text(&state.theme, Emphasis::Muted))
        .block(Block::default().borders(Borders::ALL).title("Stats"));
    frame.render_widget(stats_para, chunks[2]);
}

/// Truncate a string to max length.
fn truncate(s: &str, max: usize) -> String {
    if s.len() <= max {
        s.to_string()
    } else {
        format!("{}...", &s[..max.saturating_sub(3)])
    }
}

/// Center a rect in a given area.
fn center_rect(percent_x: u16, percent_y: u16, r: Rect) -> Rect {
    let popup_layout = Layout::default()
        .direction(Direction::Vertical)
        .constraints([
            Constraint::Percentage((100 - percent_y) / 2),
            Constraint::Percentage(percent_y),
            Constraint::Percentage((100 - percent_y) / 2),
        ])
        .split(r);
    
    Layout::default()
        .direction(Direction::Horizontal)
        .constraints([
            Constraint::Percentage((100 - percent_x) / 2),
            Constraint::Percentage(percent_x),
            Constraint::Percentage((100 - percent_x) / 2),
        ])
        .split(popup_layout[1])[1]
}