use ratatui::{
layout::{Alignment, Constraint, Direction, Layout, Rect},
style::{Color, Modifier, Style},
text::{Line, Span},
widgets::{Block, Borders, Paragraph, Wrap},
Frame,
};
pub fn get_changelog() -> Vec<Line<'static>> {
const CHANGELOG: &str = include_str!("../../APP_CHANGELOG.md");
let mut lines = Vec::new();
for line in CHANGELOG.lines() {
let trimmed = line.trim();
if trimmed.starts_with("# ") {
continue;
}
if trimmed.starts_with("## ") {
let version = trimmed.trim_start_matches("## ");
lines.push(Line::from(vec![
Span::styled(
version.to_string(),
Style::default()
.fg(Color::Cyan)
.add_modifier(Modifier::BOLD),
),
]));
}
else if trimmed.starts_with("- ") {
let text = trimmed.trim_start_matches("- ");
lines.push(Line::from(vec![
Span::styled("• ", Style::default().fg(Color::Green)),
Span::raw(text.to_string()),
]));
}
else if trimmed.is_empty() {
lines.push(Line::from(""));
}
}
lines
}
pub fn draw_whats_new(f: &mut Frame) {
let area = centered_rect(80, 80, f.area());
let block = Block::default()
.borders(Borders::ALL)
.border_style(Style::default().fg(Color::Cyan))
.title(vec![
Span::raw(" "),
Span::styled(
format!("🎉 trackWork v{} - What's New", env!("CARGO_PKG_VERSION")),
Style::default()
.fg(Color::Cyan)
.add_modifier(Modifier::BOLD),
),
Span::raw(" "),
]);
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([
Constraint::Min(3), Constraint::Length(3), ])
.split(area);
f.render_widget(block, area);
let content_area = Rect {
x: chunks[0].x + 2,
y: chunks[0].y + 1,
width: chunks[0].width.saturating_sub(4),
height: chunks[0].height.saturating_sub(2),
};
let changelog = get_changelog();
let content = Paragraph::new(changelog)
.wrap(Wrap { trim: false })
.alignment(Alignment::Left);
f.render_widget(content, content_area);
let footer_area = Rect {
x: chunks[1].x + 2,
y: chunks[1].y,
width: chunks[1].width.saturating_sub(4),
height: chunks[1].height,
};
let help_text = vec![Line::from(vec![
Span::styled("Press ", Style::default().fg(Color::Gray)),
Span::styled("Enter", Style::default().fg(Color::Green).add_modifier(Modifier::BOLD)),
Span::styled(" or ", Style::default().fg(Color::Gray)),
Span::styled("Esc", Style::default().fg(Color::Green).add_modifier(Modifier::BOLD)),
Span::styled(" to close", Style::default().fg(Color::Gray)),
])];
let footer = Paragraph::new(help_text).alignment(Alignment::Center);
f.render_widget(footer, footer_area);
}
fn centered_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]
}