use ratatui::Frame;
use ratatui::layout::Rect;
use ratatui::style::{Modifier, Style};
use ratatui::text::{Line, Span};
use ratatui::widgets::Paragraph;
use crate::app::App;
use crate::layout::PaneId;
use crate::pane::Pane;
use crate::playwright::flaky_pane::outcomes_glyphs;
use crate::ui::theme::{self, Theme};
pub fn draw(
frame: &mut Frame,
app: &mut App,
pane_id: PaneId,
area: Rect,
_focused: bool,
) -> Option<(u16, u16)> {
if area.width == 0 || area.height == 0 {
return None;
}
let t = theme::cur();
frame.render_widget(
Paragraph::new("").style(Style::default().bg(t.bg_dark)),
area,
);
app.rects.editor_panes.push((area, pane_id));
let want_sb = area.width >= 8;
let sb_w = if want_sb { 1 } else { 0 };
let body_area = Rect::new(area.x, area.y, area.width - sb_w, area.height);
let sb_area = Rect::new(area.x + area.width - sb_w, area.y, sb_w, area.height);
let area = body_area;
let Some(Pane::Flaky(f)) = app.panes.get_mut(pane_id) else {
return None;
};
f.clamp();
let n = f.items.len();
let mut lines: Vec<Line> = Vec::new();
lines.push(Line::from(vec![
Span::styled(" ", Style::default().bg(t.bg_dark)),
Span::styled(
"≋ ",
Style::default()
.fg(t.purple)
.bg(t.bg_dark)
.add_modifier(Modifier::BOLD),
),
Span::styled(
format!("{n} wobbly test{}", if n == 1 { "" } else { "s" }),
Style::default()
.fg(if n > 0 { t.purple } else { t.comment })
.bg(t.bg_dark)
.add_modifier(Modifier::BOLD),
),
]));
lines.push(Line::from(Span::styled(
" ⏎ jump to source r refresh esc back",
Style::default().fg(t.comment).bg(t.bg_dark),
)));
if f.items.is_empty() {
lines.push(Line::from(Span::styled(
" ",
Style::default().bg(t.bg_dark),
)));
lines.push(Line::from(Span::styled(
" ✓ no flaky tests in recent history",
Style::default().fg(t.green).bg(t.bg_dark),
)));
frame.render_widget(
Paragraph::new(lines).style(Style::default().bg(t.bg_dark)),
area,
);
return None;
}
lines.push(Line::from(Span::styled(
" ",
Style::default().bg(t.bg_dark),
)));
let mut selected_row = lines.len();
let mut last_file: Option<String> = None;
let mut row_indices: Vec<(usize, usize)> = Vec::with_capacity(f.items.len());
for (idx, it) in f.items.iter().enumerate() {
if last_file.as_deref() != Some(it.rel.as_str()) {
lines.push(Line::from(vec![
Span::styled(" ", Style::default().bg(t.bg_dark)),
Span::styled(
it.rel.clone(),
Style::default()
.fg(t.comment)
.bg(t.bg_dark)
.add_modifier(Modifier::DIM),
),
]));
last_file = Some(it.rel.clone());
}
let sel = idx == f.selected;
if sel {
selected_row = lines.len();
}
row_indices.push((lines.len(), idx));
lines.push(item_line(&t, it, sel));
}
let h = area.height as usize;
let total = lines.len();
if total > h {
let above = selected_row;
let max_scroll = total.saturating_sub(h);
let scroll = above.saturating_sub(h / 2).min(max_scroll);
f.scroll = scroll;
} else {
f.scroll = 0;
}
for (line_y, idx) in &row_indices {
if *line_y < f.scroll || *line_y >= f.scroll + h {
continue;
}
let visible_y = line_y - f.scroll;
let screen_y = area.y.saturating_add(visible_y as u16);
if screen_y < area.y.saturating_add(area.height) {
app.rects.list_rows.push((
ratatui::layout::Rect {
x: area.x,
y: screen_y,
width: area.width,
height: 1,
},
pane_id,
*idx,
));
}
}
let total_lines = lines.len();
let scroll = f.scroll;
let visible: Vec<Line> = lines.into_iter().skip(scroll).take(h).collect();
frame.render_widget(
Paragraph::new(visible).style(Style::default().bg(t.bg_dark)),
area,
);
if sb_w > 0 {
crate::ui::scrollbar::paint_simple_scrollbar(frame, sb_area, &t, total_lines, h, scroll);
app.rects.scrollbars.push(crate::app::ScrollbarHit {
area: sb_area,
pane_id,
total: total_lines,
viewport: h,
kind: crate::app::ScrollbarKind::Flaky,
});
}
None
}
fn item_line(t: &Theme, it: &crate::playwright::flaky_pane::FlakyItem, sel: bool) -> Line<'static> {
let bg = if sel { t.bg2 } else { t.bg_dark };
let arrow = if sel { "▶ " } else { " " };
let glyphs = outcomes_glyphs(&it.outcomes);
let mut spans: Vec<Span> = Vec::new();
spans.push(Span::styled(
arrow.to_string(),
Style::default().fg(t.purple).bg(bg),
));
spans.push(Span::styled(
format!("{:>10} ", glyphs),
Style::default().fg(t.purple).bg(bg),
));
let mut title_style = Style::default().fg(t.fg).bg(bg);
if sel {
title_style = title_style.add_modifier(Modifier::BOLD);
}
spans.push(Span::styled(it.title.clone(), title_style));
if it.line > 0 {
spans.push(Span::styled(
format!(":{}", it.line + 1),
Style::default().fg(t.comment).bg(bg),
));
}
Line::from(spans)
}