use crate::app::{App, Mode};
use crate::ui::layout::centered_rect_fixed;
use crate::ui::style::{CARD_BORDER, accent_style, muted_style, primary_style};
use ratatui::Frame;
use ratatui::layout::{Alignment, Constraint, Direction, Layout, Rect};
use ratatui::style::{Color, Modifier, Style};
use ratatui::text::{Line, Span};
use ratatui::widgets::{Block, Borders, Clear, Paragraph, Wrap};
pub fn draw_about_popup(f: &mut Frame, area: Rect, app: &App) {
let popup_area = centered_rect_fixed(70, 18, area);
f.render_widget(Clear, popup_area);
let block = Block::default()
.borders(Borders::ALL)
.border_type(CARD_BORDER())
.border_style(accent_style())
.title(
Line::from(vec![
Span::raw(" "),
Span::styled("About Gitwig", primary_style()),
Span::raw(" "),
])
.alignment(Alignment::Center),
);
f.render_widget(block.clone(), popup_area);
let inner = block.inner(popup_area);
let about_chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints([
Constraint::Length(18), Constraint::Length(1), Constraint::Min(0), ])
.split(inner);
let is_compat = app.config.compatibility_mode;
let leaf_style = if is_compat { Style::default() } else { Style::default().fg(Color::Green) };
let git_style = if is_compat { Style::default() } else { accent_style() };
let logo_text = if is_compat {
vec![
Line::from(""), Line::from(Span::styled(" .-.-.", leaf_style)),
Line::from(Span::styled(" (_\\_/_)", leaf_style)),
Line::from(Span::styled(" | |", leaf_style)),
Line::from(Span::styled(" | /", leaf_style)),
Line::from(Span::styled(" .-*-/", git_style)),
Line::from(Span::styled(" / | \\", git_style)),
Line::from(Span::styled(" o | o", git_style)),
Line::from(Span::styled(" o", git_style)),
]
} else {
vec![
Line::from(""), Line::from(Span::styled(" ╭───╮", leaf_style)),
Line::from(Span::styled(" ╭─╯ 🌿 ╰─╮", leaf_style)),
Line::from(Span::styled(" ╰─╮ ╭──╯", leaf_style)),
Line::from(Span::styled(" ╰─┬─╯", leaf_style)),
Line::from(Span::styled(" │", git_style)),
Line::from(Span::styled(" ╭─┴─╮", git_style)),
Line::from(Span::styled(" ╭┴─●─╯", git_style)),
Line::from(Span::styled(" ╱ │ ╲", git_style)),
Line::from(Span::styled(" ● │ ●", git_style)),
Line::from(Span::styled(" ●", git_style)),
]
};
let logo_para = Paragraph::new(logo_text).alignment(Alignment::Center);
f.render_widget(logo_para, about_chunks[0]);
let info_chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([
Constraint::Length(1), Constraint::Length(1), Constraint::Length(5), Constraint::Length(1), Constraint::Length(2), Constraint::Length(1), Constraint::Length(3), Constraint::Length(1), Constraint::Length(1), Constraint::Min(0),
])
.split(about_chunks[2]);
let title_line = Line::from(vec![
Span::styled("Gitwig v", primary_style().add_modifier(Modifier::BOLD)),
Span::styled(env!("CARGO_PKG_VERSION"), primary_style().add_modifier(Modifier::BOLD)),
]);
f.render_widget(Paragraph::new(title_line), info_chunks[0]);
let metadata_lines = vec![
Line::from(vec![
Span::styled("Creator: ", primary_style().add_modifier(Modifier::BOLD)),
Span::raw("Tareq M. Yousuf "),
Span::styled("(@tareqmy)", muted_style()),
]),
Line::from(vec![
Span::styled("Website: ", primary_style().add_modifier(Modifier::BOLD)),
Span::styled("https://tareqmy.com/", accent_style()),
]),
Line::from(vec![
Span::styled("GitHub: ", primary_style().add_modifier(Modifier::BOLD)),
Span::styled("https://github.com/tareqmy/gitwig", accent_style()),
]),
Line::from(vec![
Span::styled("License: ", primary_style().add_modifier(Modifier::BOLD)),
Span::styled("MIT", Style::default().add_modifier(Modifier::BOLD)),
]),
Line::from(vec![
Span::styled("Email: ", primary_style().add_modifier(Modifier::BOLD)),
Span::styled("tareq.y@gmail.com", accent_style()),
]),
];
f.render_widget(Paragraph::new(metadata_lines), info_chunks[2]);
let desc_para = Paragraph::new(vec![Line::from(Span::styled(
"A Rust-based Git TUI, representing repository branches like twigs on a leaf.",
muted_style(),
))])
.wrap(Wrap { trim: true });
f.render_widget(desc_para, info_chunks[4]);
let credits_lines = vec![
Line::from(Span::styled("Inspired by:", primary_style().add_modifier(Modifier::BOLD))),
Line::from(vec![
Span::raw(" • "),
Span::styled("Sourcetree", primary_style()),
Span::styled(" (staging & tree visualization)", muted_style()),
]),
Line::from(vec![
Span::raw(" • "),
Span::styled("lazygit", primary_style()),
Span::styled(" & ", muted_style()),
Span::styled("gitui", primary_style()),
Span::styled(" (fast modal terminal UX)", muted_style()),
]),
];
f.render_widget(Paragraph::new(credits_lines), info_chunks[6]);
let close_line = Line::from(vec![
Span::styled("Press ", muted_style()),
Span::styled("Esc", accent_style()),
Span::styled(" / ", muted_style()),
Span::styled("q", accent_style()),
Span::styled(" / ", muted_style()),
Span::styled("v", accent_style()),
Span::styled(" to close", muted_style()),
]);
f.render_widget(Paragraph::new(close_line), info_chunks[8]);
}
use crossterm::event::{KeyCode, KeyEvent};
pub struct AboutPopup;
impl AboutPopup {
pub fn handle_event(app: &mut crate::app::App, key: KeyEvent) -> bool {
let code = key.code;
match code {
KeyCode::Char('v')
| KeyCode::Char('V')
| KeyCode::Esc
| KeyCode::Char('q')
| KeyCode::Char('Q') => {
app.close_dialog();
}
_ => {}
}
false
}
}