use crate::app::App;
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 render(f: &mut Frame, area: Rect, app: &App) {
render_backdrop(f, area);
let popup_area = centered_rect(80, 90, area);
f.render_widget(Clear, popup_area);
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([
Constraint::Length(3), Constraint::Min(0), Constraint::Length(1), ])
.split(popup_area);
render_header(f, chunks[0]);
render_content(f, chunks[1], app);
render_statusbar(f, chunks[2]);
}
fn render_backdrop(f: &mut Frame, area: Rect) {
let block = Block::default().style(Style::default().bg(Color::Rgb(0, 0, 0)));
f.render_widget(block, 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]
}
fn render_header(f: &mut Frame, area: Rect) {
let title_block = Block::default()
.title(" 任务预览 ")
.title_style(
Style::default()
.fg(Color::Rgb(136, 192, 208)) .add_modifier(Modifier::BOLD),
)
.borders(Borders::ALL)
.border_style(Style::default().fg(Color::Rgb(136, 192, 208))) .border_type(ratatui::widgets::BorderType::Rounded)
.style(Style::default().bg(Color::Rgb(46, 52, 64)));
f.render_widget(title_block, area);
}
fn render_content(f: &mut Frame, area: Rect, app: &App) {
let lines: Vec<Line> = app
.preview_content
.lines()
.skip(app.preview_scroll as usize)
.map(|line| {
let trimmed = line.trim_start();
if trimmed.starts_with("# ") {
Line::from(Span::styled(
line,
Style::default()
.fg(Color::Rgb(235, 203, 139)) .add_modifier(Modifier::BOLD),
))
} else if trimmed.starts_with("## ") {
Line::from(Span::styled(
line,
Style::default()
.fg(Color::Rgb(136, 192, 208)) .add_modifier(Modifier::BOLD),
))
} else if trimmed.starts_with("### ") || trimmed.starts_with("#### ") {
Line::from(Span::styled(
line,
Style::default()
.fg(Color::Rgb(129, 161, 193)) .add_modifier(Modifier::BOLD),
))
} else if trimmed.starts_with("- [ ]") || trimmed.starts_with("* [ ]") {
Line::from(Span::styled(
line,
Style::default().fg(Color::Rgb(136, 192, 208)),
))
} else if trimmed.starts_with("- [x]") || trimmed.starts_with("* [x]") {
Line::from(Span::styled(
line,
Style::default().fg(Color::Rgb(163, 190, 140)), ))
} else if trimmed.starts_with("- ")
|| trimmed.starts_with("* ")
|| trimmed.starts_with("+ ")
{
Line::from(Span::styled(
line,
Style::default().fg(Color::Rgb(163, 190, 140)),
))
} else if trimmed.starts_with("> ") {
Line::from(Span::styled(
line,
Style::default()
.fg(Color::Rgb(180, 142, 173)) .add_modifier(Modifier::ITALIC),
))
} else if trimmed.starts_with("```") {
Line::from(Span::styled(
line,
Style::default().fg(Color::Rgb(208, 135, 112)), ))
} else if trimmed.starts_with(" ") || trimmed.starts_with("\t") {
Line::from(Span::styled(
line,
Style::default()
.fg(Color::Rgb(216, 222, 233))
.add_modifier(Modifier::DIM),
))
} else {
Line::from(Span::styled(
line,
Style::default().fg(Color::Rgb(216, 222, 233)),
))
}
})
.collect();
let paragraph = Paragraph::new(lines)
.block(
Block::default()
.borders(Borders::ALL)
.border_style(Style::default().fg(Color::Rgb(76, 86, 106))) .border_type(ratatui::widgets::BorderType::Rounded)
.style(Style::default().bg(Color::Rgb(46, 52, 64))), )
.wrap(Wrap { trim: false });
f.render_widget(paragraph, area);
}
fn render_statusbar(f: &mut Frame, area: Rect) {
let help_text = Line::from(vec![
Span::raw(" "),
Span::styled("j/k", Style::default().fg(Color::Rgb(136, 192, 208))), Span::raw(" 滚动 "),
Span::styled("ESC", Style::default().fg(Color::Rgb(136, 192, 208))), Span::raw(" 返回 "),
]);
let paragraph = Paragraph::new(help_text)
.alignment(Alignment::Left)
.style(Style::default().bg(Color::Rgb(59, 66, 82)));
f.render_widget(paragraph, area);
}