use crate::components::manager::ComponentManager;
use crate::app::{AppState, state::AmendField};
use crate::ui::style::{self, Emphasis};
use ratatui::Frame;
use ratatui::layout::{Constraint, Direction, Layout, Rect};
use ratatui::widgets::{Paragraph, Clear};
use ratatui::text::{Line, Span};
use crate::components::manager::utils;
pub fn render_commit_input(_manager: &ComponentManager, frame: &mut Frame, area: Rect, state: &AppState) {
let popup_height = if state.amend_mode && state.last_commit_info.is_some() {
22 } else if state.commit_mode {
21 } else {
15
};
let popup = utils::center_rect(80, popup_height, area);
frame.render_widget(Clear, popup);
let title = get_commit_title(state);
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints(if state.amend_mode {
vec![
Constraint::Length(7), Constraint::Min(3), Constraint::Length(2), ]
} else {
vec![
Constraint::Length(6), Constraint::Min(3), Constraint::Length(2), ]
})
.split(popup);
if state.commit_mode && state.amend_mode {
if let Some(ref last_info) = state.last_commit_info {
render_last_commit_info(frame, chunks[0], state, last_info);
} else {
render_author_info(frame, chunks[0], state, "Amend Author Info");
}
} else if state.commit_mode {
render_author_info(frame, chunks[0], state, "Commit Author Info");
}
render_message_input(frame, chunks[1], state, title);
if state.commit_mode {
render_hints(frame, chunks[2], state);
}
}
fn get_commit_title(state: &AppState) -> &str {
if state.amend_mode {
match state.amend_editing_field {
Some(AmendField::Message) => "Amend: Message (Tab=name, Enter=amend, Esc=cancel)",
Some(AmendField::AuthorName) => "Amend: Author Name (Tab=email, Enter=amend, Esc=cancel)",
Some(AmendField::AuthorEmail) => "Amend: Author Email (Tab=message, Enter=amend, Esc=cancel)",
None => "Amend commit (Tab=switch field, Enter=amend, Esc=cancel)",
}
} else {
match state.amend_editing_field {
Some(AmendField::Message) => "Commit: Message (Tab=name, Enter=commit, Esc=cancel)",
Some(AmendField::AuthorName) => "Commit: Author Name (Tab=email, Enter=commit, Esc=cancel)",
Some(AmendField::AuthorEmail) => "Commit: Author Email (Tab=message, Enter=commit, Esc=cancel)",
None => "Commit message (Tab=switch field, Enter=commit, Esc=cancel)",
}
}
}
fn render_message_input(frame: &mut Frame, area: Rect, state: &AppState, title: &str) {
let block = style::pane_block(&state.theme, title.to_string(), true);
let message_style = if state.amend_editing_field.is_some() || state.commit_mode {
style::selection(&state.theme)
} else {
style::body_style(&state.theme)
};
let message_text = match state.amend_editing_field {
Some(AmendField::Message) | None => {
format!("{}{}", state.commit_input, "▊")
}
Some(AmendField::AuthorName) => {
format!("{}{}", state.amend_author_name, "▊")
}
Some(AmendField::AuthorEmail) => {
format!("{}{}", state.amend_author_email, "▊")
}
};
let paragraph = Paragraph::new(message_text)
.style(message_style)
.block(block);
frame.render_widget(paragraph, area);
}
fn render_hints(frame: &mut Frame, area: Rect, state: &AppState) {
let mut issues = Vec::new();
let has_author_change = !state.amend_author_name.trim().is_empty() || !state.amend_author_email.trim().is_empty();
if state.commit_input.trim().is_empty() && (!state.amend_mode || !has_author_change) {
if state.amend_mode {
issues.push("Commit message or author change required");
} else {
issues.push("Commit message is required");
}
}
let mut hint_lines = vec![];
if !issues.is_empty() {
let issues_text = issues.join(" • ");
hint_lines.push(Line::from(vec![
Span::styled("Issues: ", style::text(&state.theme, Emphasis::Muted)),
Span::styled(issues_text, style::text(&state.theme, Emphasis::Normal)),
]));
}
let nav_hint = match state.amend_editing_field {
Some(AmendField::Message) => "Tab → Author Name | Enter → Commit | Esc → Cancel",
Some(AmendField::AuthorName) => "Tab → Author Email | Enter → Commit | Esc → Cancel",
Some(AmendField::AuthorEmail) => "Tab → Message | Enter → Commit | Esc → Cancel",
None => "Tab → Switch fields | Enter → Commit | Esc → Cancel",
};
hint_lines.push(Line::from(vec![
Span::styled("Navigation: ", style::text(&state.theme, Emphasis::Muted)),
Span::styled(nav_hint, style::text(&state.theme, Emphasis::Normal)),
]));
let hint = Paragraph::new(hint_lines)
.style(style::body_style(&state.theme));
frame.render_widget(hint, area);
}
fn render_last_commit_info(frame: &mut Frame, area: Rect, state: &AppState, last_info: &crate::app::state::LastCommitInfo) {
let info_text = vec![
Line::from(vec![
Span::styled("Last commit: ", style::text(&state.theme, Emphasis::Muted)),
Span::styled(&last_info.short_hash, style::text(&state.theme, Emphasis::Normal)),
]),
Line::from(vec![
Span::styled("Author: ", style::text(&state.theme, Emphasis::Muted)),
Span::styled(&last_info.author_name, style::text(&state.theme, Emphasis::Normal)),
Span::styled(" <", style::text(&state.theme, Emphasis::Muted)),
Span::styled(&last_info.author_email, style::text(&state.theme, Emphasis::Normal)),
Span::styled(">", style::text(&state.theme, Emphasis::Muted)),
]),
Line::from(vec![
Span::styled("Date: ", style::text(&state.theme, Emphasis::Muted)),
Span::styled(&last_info.date, style::text(&state.theme, Emphasis::Normal)),
]),
Line::from(""),
build_author_name_line(state),
build_author_email_line(state),
];
let info_block = style::pane_block(&state.theme, "Last Commit Info".to_string(), false);
let info_paragraph = Paragraph::new(info_text)
.style(style::body_style(&state.theme))
.block(info_block);
frame.render_widget(info_paragraph, area);
}
fn render_author_info(frame: &mut Frame, area: Rect, state: &AppState, title: &str) {
let info_text = vec![
Line::from(vec![
Span::styled(title, style::text(&state.theme, Emphasis::Normal)),
]),
Line::from(""),
build_author_name_line(state),
build_author_email_line(state),
];
let info_block = style::pane_block(&state.theme, title.to_string(), false);
let info_paragraph = Paragraph::new(info_text)
.style(style::body_style(&state.theme))
.block(info_block);
frame.render_widget(info_paragraph, area);
}
fn build_author_name_line(state: &AppState) -> Line<'static> {
let name_style = if state.amend_editing_field == Some(AmendField::AuthorName) {
style::selection(&state.theme)
} else {
style::text(&state.theme, Emphasis::Normal)
};
let label_style = if state.amend_editing_field == Some(AmendField::AuthorName) {
style::text(&state.theme, Emphasis::Normal)
} else {
style::text(&state.theme, Emphasis::Muted)
};
let name_display = if state.amend_author_name.is_empty() {
"<name: optional>".to_string()
} else {
state.amend_author_name.clone()
};
Line::from(vec![
Span::styled("▶ Author Name: ", label_style),
Span::styled(name_display, name_style),
if state.amend_editing_field == Some(AmendField::AuthorName) {
Span::styled(" ▊", style::text(&state.theme, Emphasis::Normal))
} else {
Span::raw("")
},
])
}
fn build_author_email_line(state: &AppState) -> Line<'static> {
let email_style = if state.amend_editing_field == Some(AmendField::AuthorEmail) {
style::selection(&state.theme)
} else {
style::text(&state.theme, Emphasis::Normal)
};
let label_style = if state.amend_editing_field == Some(AmendField::AuthorEmail) {
style::text(&state.theme, Emphasis::Normal)
} else {
style::text(&state.theme, Emphasis::Muted)
};
let email_display = if state.amend_author_email.is_empty() {
"<email: optional>".to_string()
} else {
state.amend_author_email.clone()
};
Line::from(vec![
Span::styled("▶ Author Email: ", label_style),
Span::styled(email_display, email_style),
if state.amend_editing_field == Some(AmendField::AuthorEmail) {
Span::styled(" ▊", style::text(&state.theme, Emphasis::Normal))
} else {
Span::raw("")
},
])
}