use super::*;
use crate::app::{FolderPicker, Row};
use crate::i18n::Catalog;
use ratatui::widgets::{Borders, Clear};
pub(super) fn draw_picker(
f: &mut Frame,
area: Rect,
p: &FolderPicker,
cat: &Catalog,
t: &Theme,
) -> Vec<(usize, Rect)> {
dim_backdrop(f, area, t);
let w = area.width.saturating_sub(6).clamp(46, 76).min(area.width);
let h = area.height.saturating_sub(4).clamp(14, 26).min(area.height);
let modal = centered_rect(area, w, h);
f.render_widget(Clear, modal);
let block = Block::new()
.borders(Borders::ALL)
.border_style(Style::new().fg(t.border_focus).bg(t.surface0))
.style(Style::new().bg(t.surface0));
let inner = block.inner(modal);
f.render_widget(block, modal);
f.render_widget(
Paragraph::new(Span::styled(
format!(" {}", cat.open_workspace),
Style::new().fg(t.text).bold(),
)),
Rect::new(inner.x, inner.y, inner.width, 1),
);
let path = p.path.display().to_string();
let path = trunc_tail(&path, inner.width.saturating_sub(2) as usize);
f.render_widget(
Paragraph::new(Span::styled(format!(" {path}"), Style::new().fg(t.accent))),
Rect::new(inner.x, inner.y + 1, inner.width, 1),
);
hline(f, inner.x, inner.y + 2, inner.width, t);
let footer_y = inner.bottom().saturating_sub(1);
hline(f, inner.x, footer_y.saturating_sub(1), inner.width, t);
if let Some(buf) = &p.creating {
f.render_widget(
Paragraph::new(Line::from(vec![
Span::styled(
format!(" {}: ", cat.act_new_folder),
Style::new().fg(t.subtext0),
),
Span::styled(buf.clone(), Style::new().fg(t.accent).bold()),
Span::styled("▏", Style::new().fg(t.accent)),
])),
Rect::new(inner.x, footer_y, inner.width, 1),
);
} else if let Some(e) = &p.error {
f.render_widget(
Paragraph::new(Span::styled(
format!(" error: {e}"),
Style::new().fg(t.coral),
)),
Rect::new(inner.x, footer_y, inner.width, 1),
);
} else {
f.render_widget(
Paragraph::new(hint_line(
&[
("↑↓", cat.act_move),
("⏎", cat.act_select),
("←", cat.act_up),
("n", cat.act_new_folder),
("esc", cat.act_cancel),
],
t,
)),
Rect::new(inner.x, footer_y, inner.width, 1),
);
}
let list = Rect::new(
inner.x + 1,
inner.y + 3,
inner.width.saturating_sub(2),
footer_y.saturating_sub(inner.y + 4),
);
let avail = list.height.max(1) as usize;
let scroll = p.cursor.saturating_sub(avail.saturating_sub(1));
let mut rects = Vec::new();
for (vi, i) in (scroll..p.row_count()).take(avail).enumerate() {
let y = list.y + vi as u16;
let row_rect = Rect::new(list.x, y, list.width, 1);
let sel = i == p.cursor;
if sel {
fill_bg(f, row_rect, t.sel_bg);
}
let (icon, label, fg) = match p.row(i) {
Row::OpenFolder => ("✓", cat.open_this_folder.to_string(), t.accent),
Row::OpenWorktree => ("⎇", cat.open_with_worktree.to_string(), t.accent),
Row::Up => ("↑", "..".to_string(), t.subtext0),
Row::Entry(idx) => {
let e = &p.entries[idx];
if e.is_dir {
("▪", format!("{}/", e.name), t.text)
} else {
("·", e.name.clone(), t.overlay0)
}
}
};
f.render_widget(
Paragraph::new(Line::from(vec![
Span::styled(if sel { "▸ " } else { " " }, Style::new().fg(t.accent)),
Span::styled(format!("{icon} "), Style::new().fg(fg)),
Span::styled(
trunc_tail(&label, list.width.saturating_sub(5) as usize),
Style::new().fg(fg),
),
])),
Rect::new(list.x, y, list.width, 1),
);
rects.push((i, row_rect));
}
rects
}
fn trunc_tail(s: &str, max: usize) -> String {
let n = s.chars().count();
if n <= max || max == 0 {
return s.to_string();
}
let tail: String = s.chars().skip(n - max.saturating_sub(1)).collect();
format!("…{tail}")
}
pub(super) fn draw_worktree_prompt(
f: &mut Frame,
area: Rect,
buf: &str,
error: Option<&str>,
cat: &Catalog,
t: &Theme,
) {
dim_backdrop(f, area, t);
let w = area.width.saturating_sub(6).clamp(36, 64).min(area.width);
let modal = centered_rect(area, w, 6);
f.render_widget(Clear, modal);
let block = Block::new()
.borders(Borders::ALL)
.border_style(Style::new().fg(t.border_focus).bg(t.surface0))
.style(Style::new().bg(t.surface0));
let inner = block.inner(modal);
f.render_widget(block, modal);
f.render_widget(
Paragraph::new(Span::styled(
format!(" {}", cat.new_git_worktree),
Style::new().fg(t.text).bold(),
)),
Rect::new(inner.x, inner.y, inner.width, 1),
);
f.render_widget(
Paragraph::new(Line::from(vec![
Span::styled(format!(" {}: ", cat.branch), Style::new().fg(t.subtext0)),
Span::styled(buf.to_string(), Style::new().fg(t.accent).bold()),
Span::styled("▏", Style::new().fg(t.accent)),
])),
Rect::new(inner.x, inner.y + 2, inner.width, 1),
);
let bottom = inner.bottom().saturating_sub(1);
if let Some(e) = error {
let e = trunc_tail(e, inner.width.saturating_sub(2) as usize);
f.render_widget(
Paragraph::new(Span::styled(format!(" {e}"), Style::new().fg(t.coral))),
Rect::new(inner.x, bottom, inner.width, 1),
);
} else {
f.render_widget(
Paragraph::new(hint_line(
&[("⏎", cat.act_create), ("esc", cat.act_cancel)],
t,
)),
Rect::new(inner.x, bottom, inner.width, 1),
);
}
}
fn centered_rect(area: Rect, w: u16, h: u16) -> Rect {
let w = w.min(area.width);
let h = h.min(area.height);
Rect::new(
area.x + (area.width - w) / 2,
area.y + (area.height - h) / 2,
w,
h,
)
}
fn dim_backdrop(f: &mut Frame, area: Rect, t: &Theme) {
let buf = f.buffer_mut();
for y in area.top()..area.bottom() {
for x in area.left()..area.right() {
let cell = &mut buf[(x, y)];
cell.set_fg(t.overlay0);
cell.set_bg(t.crust);
}
}
}
fn hline(f: &mut Frame, x: u16, y: u16, w: u16, t: &Theme) {
let buf = f.buffer_mut();
for i in 0..w {
buf[(x + i, y)]
.set_symbol("─")
.set_style(Style::new().fg(t.surface1).bg(t.surface0));
}
}
fn fill_bg(f: &mut Frame, rect: Rect, color: Color) {
let buf = f.buffer_mut();
for y in rect.y..rect.bottom() {
for x in rect.x..rect.right() {
buf[(x, y)].set_bg(color);
}
}
}