use crate::app::AppState;
use crate::ui::style;
use ratatui::layout::Rect;
use ratatui::style::Style;
use ratatui::widgets::{List, ListItem};
use ratatui::Frame;
#[allow(dead_code)]
pub fn render_status(frame: &mut Frame, area: Rect, state: &AppState) {
let theme = &state.theme;
let selection_style = style::selection(theme);
const FOOTER_HEIGHT: usize = 1;
let total_files = state.status_entries.len();
let available_height = area.height.saturating_sub(FOOTER_HEIGHT as u16) as usize;
let selected_idx = state.status_selected.min(total_files.saturating_sub(1));
let viewport_start = selected_idx
.saturating_sub(available_height / 2) .min(total_files.saturating_sub(available_height).max(0)); let viewport_end = (viewport_start + available_height).min(total_files);
let items: Vec<ListItem> = state
.status_entries
.iter()
.enumerate()
.skip(viewport_start)
.take(viewport_end.saturating_sub(viewport_start))
.filter_map(|(file_idx, entry)| {
if state.conflicts_only && !entry.conflict {
return None;
}
let (item_style, status_indicator) = determine_file_status_style(
file_idx,
selected_idx,
entry,
theme,
&selection_style,
);
Some(ListItem::new(format!("{}{}", status_indicator, entry.path)).style(item_style))
})
.collect();
let chunks = ratatui::layout::Layout::default()
.direction(ratatui::layout::Direction::Vertical)
.constraints([
ratatui::layout::Constraint::Min(1),
ratatui::layout::Constraint::Length(1),
])
.split(area);
let list = List::new(items)
.style(style::body_style(theme))
.block(style::pane_block(theme, "Status", false));
frame.render_widget(list, chunks[0]);
let footer = if total_files == 0 {
"no files".to_string()
} else {
let selected_file_name = state
.status_entries
.get(selected_idx)
.map(|e| e.path.as_str())
.unwrap_or("");
let mut footer_parts: Vec<String> = Vec::new();
if let Some(op) = &state.op_status {
footer_parts.push(op.clone());
}
if let Some(ref workflow) = state.workflow_context {
match workflow.state {
crate::app::workflow::WorkflowState::RebaseInProgress => {
if let Some(ref progress) = workflow.progress {
footer_parts.push(format!("REBASE {}/{}", progress.current, progress.total));
} else {
footer_parts.push("REBASE".to_string());
}
}
crate::app::workflow::WorkflowState::CherryPickInProgress => {
footer_parts.push("CHERRY-PICK".to_string());
}
crate::app::workflow::WorkflowState::RevertInProgress => {
footer_parts.push("REVERT".to_string());
}
crate::app::workflow::WorkflowState::MergeInProgress => {
footer_parts.push("MERGE".to_string());
}
crate::app::workflow::WorkflowState::Conflicts => {
let conflict_count = state.status_entries.iter().filter(|e| e.conflict).count();
footer_parts.push(format!("CONFLICTS ({})", conflict_count));
}
_ => {}
}
}
if state.refreshing {
footer_parts.push("⟳ REFRESHING…".to_string());
}
footer_parts.push(format!("{}/{}", selected_idx + 1, total_files));
footer_parts.push(format!("{}-{}", viewport_start + 1, viewport_end));
if !selected_file_name.is_empty() {
const MAX_FILENAME_DISPLAY_LEN: usize = 30;
let display_name = if selected_file_name.len() > MAX_FILENAME_DISPLAY_LEN {
let truncate_start = selected_file_name.len().saturating_sub(MAX_FILENAME_DISPLAY_LEN - 3);
format!("...{}", &selected_file_name[truncate_start..])
} else {
selected_file_name.to_string()
};
footer_parts.push(display_name);
}
const KEYBOARD_SHORTCUTS: &[&str] = &["r", "s", "u", "P"];
footer_parts.push(format!("[{}]", KEYBOARD_SHORTCUTS.join(" ")));
footer_parts.join(" • ")
};
let mut footer_lines = vec![footer];
if let Some(ahead) = state.merge_notifier_ahead {
if ahead > 0 {
footer_lines.push(format!("{} +{} ahead", state.merge_base_branch, ahead));
} else if ahead == 0 {
footer_lines.push(format!("{} up to date", state.merge_base_branch));
}
}
if let Some(last) = state.op_log.back() {
if !last.is_empty() {
footer_lines.push(last.clone());
}
}
let footer_text = footer_lines.join(" — ");
let footer_style = if state.refreshing {
style::text(theme, style::Emphasis::Header)
} else {
style::text(theme, style::Emphasis::Muted)
};
let footer_widget = ratatui::widgets::Paragraph::new(footer_text).style(footer_style);
frame.render_widget(footer_widget, chunks[1]);
}
fn determine_file_status_style(
file_idx: usize,
selected_idx: usize,
entry: &crate::git::parsers::status::StatusEntry,
theme: &crate::config::ThemeConfig,
selection_style: &Style,
) -> (Style, &'static str) {
if file_idx == selected_idx {
return (*selection_style, "> ");
}
let (color, base_indicator) = if entry.unstaged {
(theme.unstaged_color(), "○ ")
} else if entry.staged {
(theme.staged_color(), "● ")
} else {
(theme.untracked_color(), "○ ")
};
let style = Style::default().fg(color);
let indicator = if entry.staged && entry.unstaged {
"●○ " } else {
base_indicator
};
(style, indicator)
}