use ratatui::layout::{Constraint, Direction, Layout, Rect};
use ratatui::style::{Modifier, Style};
use ratatui::text::{Line, Span};
use ratatui::widgets::{Block, Borders, Padding, Paragraph};
use ratatui::Frame;
use crate::app::App;
use crate::utils::pad_right;
pub fn render(app: &App, frame: &mut Frame, area: Rect) {
let theme = app.theme();
let key_style = Style::default()
.fg(theme.warning)
.add_modifier(Modifier::BOLD);
let desc_style = Style::default().fg(theme.text_primary);
let value_style = Style::default()
.fg(theme.accent)
.add_modifier(Modifier::BOLD);
let section_title_style = Style::default().fg(theme.text_muted);
let muted_style = Style::default().fg(theme.text_muted);
let outer_block = Block::default()
.title(Line::from(vec![
Span::styled("⚙ ", Style::default().fg(theme.accent)),
Span::styled(
"Options",
Style::default()
.fg(theme.accent)
.add_modifier(Modifier::BOLD),
),
]))
.borders(Borders::ALL)
.border_style(Style::default().fg(theme.border_active))
.style(Style::default().bg(theme.bg))
.padding(Padding::new(1, 1, 0, 0));
let inner_area = outer_block.inner(area);
frame.render_widget(outer_block, area);
let close_line = Paragraph::new(Line::from(vec![
Span::styled("Shift + O", key_style),
Span::styled(" close", desc_style),
]));
let sections = Layout::default()
.direction(Direction::Vertical)
.constraints([
Constraint::Length(1), Constraint::Length(1), Constraint::Length(9), Constraint::Length(1), Constraint::Length(8), Constraint::Length(1), Constraint::Length(8), Constraint::Length(1), Constraint::Length(10), Constraint::Length(1), Constraint::Length(7), Constraint::Length(1), Constraint::Length(10), Constraint::Min(0), ])
.split(inner_area);
frame.render_widget(close_line, sections[0]);
{
let block = Block::default()
.title(Span::styled(" Settings ", section_title_style))
.borders(Borders::ALL)
.border_style(Style::default().fg(theme.border_inactive))
.style(Style::default().bg(theme.bg))
.padding(Padding::new(1, 1, 0, 0));
let theme_name = app.current_theme_name();
let editor_name = app.editor.to_string();
let last_repo = app
.tab()
.repo_path
.as_ref()
.and_then(|p| p.file_name())
.map(|n| n.to_string_lossy().to_string())
.unwrap_or_else(|| "none".to_string());
let on_style = Style::default()
.fg(theme.success)
.add_modifier(Modifier::BOLD);
let settings_path = gitkraft_core::features::persistence::ops::tui_settings_json_path()
.ok()
.map(|p| p.display().to_string())
.unwrap_or_else(|| "~/.config/gitkraft/tui-settings.json".to_string());
let lines = vec![
Line::from(vec![
Span::styled(pad_right("t", 14), key_style),
Span::styled(pad_right("next theme", 16), desc_style),
Span::styled(theme_name, value_style),
]),
Line::from(vec![
Span::styled(pad_right("Shift + T", 14), key_style),
Span::styled(pad_right("theme picker", 16), desc_style),
Span::styled("panel", value_style),
]),
Line::from(vec![
Span::styled(pad_right("Shift + E", 14), key_style),
Span::styled(pad_right("editor", 16), desc_style),
Span::styled(editor_name, value_style),
]),
Line::from(vec![
Span::styled(pad_right(",", 14), key_style),
Span::styled(pad_right("edit settings", 16), desc_style),
Span::styled(settings_path, value_style),
]),
Line::from(vec![
Span::styled(pad_right("commits", 14), muted_style),
Span::styled(pad_right("max loaded", 16), desc_style),
Span::styled("500", value_style),
]),
Line::from(vec![
Span::styled(pad_right("repo", 14), muted_style),
Span::styled(pad_right("current", 16), desc_style),
Span::styled(last_repo, on_style),
]),
];
let paragraph = Paragraph::new(lines).block(block);
frame.render_widget(paragraph, sections[2]);
}
{
let block = Block::default()
.title(Span::styled(" Navigation ", section_title_style))
.borders(Borders::ALL)
.border_style(Style::default().fg(theme.border_inactive))
.style(Style::default().bg(theme.bg))
.padding(Padding::new(1, 1, 0, 0));
let lines = vec![
Line::from(vec![
Span::styled(pad_right("Tab", 14), key_style),
Span::styled("cycle pane forward", desc_style),
]),
Line::from(vec![
Span::styled(pad_right("Shift + Tab", 14), key_style),
Span::styled("cycle pane backward", desc_style),
]),
Line::from(vec![
Span::styled(pad_right("j / ↓", 14), key_style),
Span::styled("next item", desc_style),
]),
Line::from(vec![
Span::styled(pad_right("k / ↑", 14), key_style),
Span::styled("previous item", desc_style),
]),
Line::from(vec![
Span::styled(pad_right("Enter", 14), key_style),
Span::styled("select / activate", desc_style),
]),
Line::from(vec![
Span::styled(pad_right("Esc", 14), key_style),
Span::styled("dismiss / cancel", desc_style),
]),
];
let paragraph = Paragraph::new(lines).block(block);
frame.render_widget(paragraph, sections[4]);
}
{
let block = Block::default()
.title(Span::styled(" Staging ", section_title_style))
.borders(Borders::ALL)
.border_style(Style::default().fg(theme.border_inactive))
.style(Style::default().bg(theme.bg))
.padding(Padding::new(1, 1, 0, 0));
let lines = vec![
Line::from(vec![
Span::styled(pad_right("s", 14), key_style),
Span::styled(pad_right("stage file", 18), desc_style),
Span::styled("selected", value_style),
]),
Line::from(vec![
Span::styled(pad_right("u", 14), key_style),
Span::styled(pad_right("unstage file", 18), desc_style),
Span::styled("selected", value_style),
]),
Line::from(vec![
Span::styled(pad_right("S", 14), key_style),
Span::styled(pad_right("stage all", 18), desc_style),
Span::styled("all files", value_style),
]),
Line::from(vec![
Span::styled(pad_right("U", 14), key_style),
Span::styled(pad_right("unstage all", 18), desc_style),
Span::styled("all files", value_style),
]),
Line::from(vec![
Span::styled(pad_right("d", 14), key_style),
Span::styled(pad_right("discard", 18), desc_style),
Span::styled("confirm ×2", value_style),
]),
Line::from(vec![
Span::styled(pad_right("Tab", 14), key_style),
Span::styled("toggle focus", desc_style),
]),
];
let paragraph = Paragraph::new(lines).block(block);
frame.render_widget(paragraph, sections[6]);
}
{
let block = Block::default()
.title(Span::styled(" Git ", section_title_style))
.borders(Borders::ALL)
.border_style(Style::default().fg(theme.border_inactive))
.style(Style::default().bg(theme.bg))
.padding(Padding::new(1, 1, 0, 0));
let lines = vec![
Line::from(vec![
Span::styled(pad_right("c", 14), key_style),
Span::styled(pad_right("commit", 18), desc_style),
Span::styled("staged changes", value_style),
]),
Line::from(vec![
Span::styled(pad_right("b", 14), key_style),
Span::styled(pad_right("create branch", 18), desc_style),
Span::styled("from HEAD", value_style),
]),
Line::from(vec![
Span::styled(pad_right("z", 14), key_style),
Span::styled(pad_right("stash save", 18), desc_style),
Span::styled("working dir", value_style),
]),
Line::from(vec![
Span::styled(pad_right("Z", 14), key_style),
Span::styled(pad_right("stash pop", 18), desc_style),
Span::styled("latest", value_style),
]),
Line::from(vec![
Span::styled(pad_right("f", 14), key_style),
Span::styled(pad_right("fetch", 18), desc_style),
Span::styled("from origin", value_style),
]),
Line::from(vec![
Span::styled(pad_right("p", 14), key_style),
Span::styled(pad_right("pull (rebase)", 18), desc_style),
Span::styled("from origin", value_style),
]),
Line::from(vec![
Span::styled(pad_right("P", 14), key_style),
Span::styled(pad_right("push", 18), desc_style),
Span::styled("to origin", value_style),
]),
Line::from(vec![
Span::styled(pad_right("F", 14), key_style),
Span::styled(pad_right("force push", 18), desc_style),
Span::styled("--force-with-lease", value_style),
]),
];
let paragraph = Paragraph::new(lines).block(block);
frame.render_widget(paragraph, sections[8]);
}
{
let block = Block::default()
.title(Span::styled(" Branch Actions ", section_title_style))
.borders(Borders::ALL)
.border_style(Style::default().fg(theme.border_inactive))
.style(Style::default().bg(theme.bg))
.padding(Padding::new(1, 1, 0, 0));
let lines = vec![
Line::from(vec![
Span::styled(pad_right("Enter", 14), key_style),
Span::styled(pad_right("checkout", 18), desc_style),
Span::styled("selected branch", value_style),
]),
Line::from(vec![
Span::styled(pad_right("m", 14), key_style),
Span::styled(pad_right("merge", 18), desc_style),
Span::styled("selected → HEAD", value_style),
]),
Line::from(vec![
Span::styled(pad_right("R", 14), key_style),
Span::styled(pad_right("rebase onto", 18), desc_style),
Span::styled("selected branch", value_style),
]),
Line::from(vec![
Span::styled(pad_right("D", 14), key_style),
Span::styled(pad_right("delete branch", 18), desc_style),
Span::styled("selected", value_style),
]),
];
let paragraph = Paragraph::new(lines).block(block);
frame.render_widget(paragraph, sections[10]);
}
{
let block = Block::default()
.title(Span::styled(" Commit Actions ", section_title_style))
.borders(Borders::ALL)
.border_style(Style::default().fg(theme.border_inactive))
.style(Style::default().bg(theme.bg))
.padding(Padding::new(1, 1, 0, 0));
let lines = vec![
Line::from(vec![
Span::styled(pad_right("C", 14), key_style),
Span::styled(pad_right("cherry-pick", 18), desc_style),
Span::styled("onto current", value_style),
]),
Line::from(vec![
Span::styled(pad_right("n", 14), key_style),
Span::styled(pad_right("reset mixed", 18), desc_style),
Span::styled("keep files", value_style),
]),
Line::from(vec![
Span::styled(pad_right("e", 14), key_style),
Span::styled(pad_right("revert", 18), desc_style),
Span::styled("selected commit", value_style),
]),
Line::from(vec![
Span::styled(pad_right("x", 14), key_style),
Span::styled(pad_right("reset soft", 18), desc_style),
Span::styled("to selected", value_style),
]),
Line::from(vec![
Span::styled(pad_right("X", 14), key_style),
Span::styled(pad_right("reset hard", 18), desc_style),
Span::styled("to selected", value_style),
]),
Line::from(vec![
Span::styled(pad_right("a", 14), key_style),
Span::styled(pad_right("all actions…", 18), desc_style),
Span::styled("popup menu", value_style),
]),
];
let paragraph = Paragraph::new(lines).block(block);
frame.render_widget(paragraph, sections[12]);
}
}