Feat: UI/UX fixes
All checks were successful
Deploy Website / deploy (push) Has been skipped

This commit is contained in:
2026-03-05 00:28:30 +01:00
parent 60fb62829f
commit 2c8a6794a3
7 changed files with 154 additions and 30 deletions

View File

@@ -14,11 +14,14 @@ pub struct FileBrowserModal<'a> {
title: &'a str,
input: &'a str,
entries: &'a [(String, bool, bool)],
audio_counts: &'a [Option<usize>],
selected: usize,
scroll_offset: usize,
border_color: Option<Color>,
width: u16,
height: u16,
hints: Option<Line<'a>>,
color_path: bool,
}
impl<'a> FileBrowserModal<'a> {
@@ -27,11 +30,14 @@ impl<'a> FileBrowserModal<'a> {
title,
input,
entries,
audio_counts: &[],
selected: 0,
scroll_offset: 0,
border_color: None,
width: 60,
height: 16,
hints: None,
color_path: false,
}
}
@@ -60,6 +66,21 @@ impl<'a> FileBrowserModal<'a> {
self
}
pub fn hints(mut self, hints: Line<'a>) -> Self {
self.hints = Some(hints);
self
}
pub fn audio_counts(mut self, counts: &'a [Option<usize>]) -> Self {
self.audio_counts = counts;
self
}
pub fn color_path(mut self) -> Self {
self.color_path = true;
self
}
pub fn render_centered(self, frame: &mut Frame, term: Rect) -> Rect {
let colors = theme::get();
let border_color = self.border_color.unwrap_or(colors.ui.text_primary);
@@ -70,37 +91,61 @@ impl<'a> FileBrowserModal<'a> {
.border_color(border_color)
.render_centered(frame, term);
let rows = Layout::vertical([Constraint::Length(1), Constraint::Min(1)]).split(inner);
let has_hints = self.hints.is_some();
let constraints = if has_hints {
vec![
Constraint::Length(1),
Constraint::Min(1),
Constraint::Length(1),
]
} else {
vec![Constraint::Length(1), Constraint::Min(1)]
};
let rows = Layout::vertical(constraints).split(inner);
// Input line
frame.render_widget(
Paragraph::new(Line::from(vec![
let input_spans = if self.color_path {
let (path_part, filter_part) = match self.input.rfind('/') {
Some(pos) => (&self.input[..=pos], &self.input[pos + 1..]),
None => ("", self.input),
};
vec![
Span::raw("> "),
Span::styled(path_part.to_string(), Style::new().fg(colors.browser.directory)),
Span::styled(filter_part.to_string(), Style::new().fg(colors.input.text)),
Span::styled("", Style::new().fg(colors.input.cursor)),
]
} else {
vec![
Span::raw("> "),
Span::styled(self.input, Style::new().fg(colors.input.text)),
Span::styled("", Style::new().fg(colors.input.cursor)),
])),
rows[0],
);
]
};
frame.render_widget(Paragraph::new(Line::from(input_spans)), rows[0]);
// Hints bar
if let Some(hints) = self.hints {
let hint_row = rows[2];
frame.render_widget(
Paragraph::new(hints).alignment(ratatui::layout::Alignment::Right),
hint_row,
);
}
// Entries list
let visible_height = rows[1].height as usize;
let visible_entries = self
.entries
.iter()
.enumerate()
.skip(self.scroll_offset)
.take(visible_height);
let lines: Vec<Line> = visible_entries
.enumerate()
.map(|(i, (name, is_dir, is_cagire))| {
let abs_idx = i + self.scroll_offset;
.map(|(abs_idx, (name, is_dir, is_cagire))| {
let is_selected = abs_idx == self.selected;
let prefix = if is_selected { "> " } else { " " };
let display = if *is_dir {
format!("{prefix}{name}/")
} else {
format!("{prefix}{name}")
};
let color = if is_selected {
colors.browser.selected
} else if *is_dir {
@@ -110,7 +155,21 @@ impl<'a> FileBrowserModal<'a> {
} else {
colors.browser.file
};
Line::from(Span::styled(display, Style::new().fg(color)))
let display = if *is_dir {
format!("{prefix}{name}/")
} else {
format!("{prefix}{name}")
};
let mut spans = vec![Span::styled(display, Style::new().fg(color))];
if *is_dir && name != ".." {
if let Some(Some(count)) = self.audio_counts.get(abs_idx) {
spans.push(Span::styled(
format!(" ({count})"),
Style::new().fg(colors.browser.file),
));
}
}
Line::from(spans)
})
.collect();