feat: unify provider interface design
This commit is contained in:
+25
-58
@@ -122,6 +122,7 @@ mod keys;
|
||||
mod miruro_browser;
|
||||
mod miruro_ui;
|
||||
mod thumbnail;
|
||||
mod ui;
|
||||
mod video_state;
|
||||
mod youtube;
|
||||
|
||||
@@ -2883,8 +2884,7 @@ impl App {
|
||||
}
|
||||
|
||||
fn draw(&mut self, frame: &mut Frame, elapsed: Duration) {
|
||||
const SPINNER: [&str; 10] = ["⠋ ", "⠙ ", "⠹ ", "⠸ ", "⠼ ", "⠴ ", "⠦ ", "⠧ ", "⠇ ", "⠏ "];
|
||||
let spinner = SPINNER[(elapsed.as_millis() / 100 % SPINNER.len() as u128) as usize];
|
||||
let spinner = ui::spinner(elapsed);
|
||||
let area = frame.area();
|
||||
let mut tab_lines = vec![Line::default()];
|
||||
let mut row_width = 0;
|
||||
@@ -2919,54 +2919,23 @@ impl App {
|
||||
.min(usize::from(area.height.saturating_sub(10).max(1)))
|
||||
as u16;
|
||||
let first_row = selected_row.saturating_sub(usize::from(tab_height).saturating_sub(1));
|
||||
let [tabs, search, results, status, help] = Layout::vertical([
|
||||
Constraint::Length(tab_height),
|
||||
Constraint::Length(3),
|
||||
Constraint::Min(3),
|
||||
Constraint::Length(3),
|
||||
Constraint::Length(1),
|
||||
])
|
||||
.areas(area);
|
||||
let [tabs, search, results, status, help] = ui::browser_layout(area, tab_height);
|
||||
frame.render_widget(
|
||||
Paragraph::new(tab_lines).scroll((first_row.min(u16::MAX as usize) as u16, 0)),
|
||||
tabs,
|
||||
);
|
||||
let search_style = if self.mode == Mode::Insert {
|
||||
Style::new().fg(Color::Cyan)
|
||||
} else {
|
||||
Style::new()
|
||||
};
|
||||
// End-only editing; scroll long queries without counting UTF-8 bytes as columns.
|
||||
let query_view = &self.views[self.query_view_index()];
|
||||
let query = ratatui::text::Line::raw(query_view.query.as_str());
|
||||
let query_width = query.width();
|
||||
let search_title: Line<'_> = match query_view.source {
|
||||
ViewSource::Category(_) | ViewSource::CategoryGroup(_) => {
|
||||
" tubular / Category search ".into()
|
||||
}
|
||||
_ => " tubular / YouTube search ".into(),
|
||||
let search_title = match query_view.source {
|
||||
ViewSource::Category(_) | ViewSource::CategoryGroup(_) => " tubular / Category search ",
|
||||
_ => " tubular / YouTube search ",
|
||||
};
|
||||
let search_block = Block::bordered()
|
||||
.title(search_title)
|
||||
.border_style(search_style);
|
||||
let input = search_block.inner(search);
|
||||
let scroll = query_width.saturating_sub(input.width.saturating_sub(1) as usize);
|
||||
frame.render_widget(
|
||||
Paragraph::new(query)
|
||||
.scroll((0, scroll.min(u16::MAX as usize) as u16))
|
||||
.block(search_block),
|
||||
ui::search(
|
||||
frame,
|
||||
search,
|
||||
search_title,
|
||||
query_view.query.as_str(),
|
||||
self.mode == Mode::Insert && self.category_dialog.is_none(),
|
||||
);
|
||||
if self.mode == Mode::Insert
|
||||
&& self.category_dialog.is_none()
|
||||
&& input.width != 0
|
||||
&& input.height != 0
|
||||
{
|
||||
let cursor = query_width
|
||||
.saturating_sub(scroll)
|
||||
.min(usize::from(input.width - 1));
|
||||
frame.set_cursor_position((input.x + cursor as u16, input.y));
|
||||
}
|
||||
let view = &mut self.views[self.view_index];
|
||||
let table = Table::new(
|
||||
view.videos.iter().map(|video| {
|
||||
@@ -3023,8 +2992,8 @@ impl App {
|
||||
},
|
||||
self.job.is_some().then_some(spinner),
|
||||
)))
|
||||
.row_highlight_style(Style::new().fg(Color::Black).bg(Color::Cyan))
|
||||
.highlight_symbol("> ");
|
||||
.row_highlight_style(ui::SELECTED)
|
||||
.highlight_symbol(ui::SELECTION_MARKER);
|
||||
frame.render_stateful_widget(table, results, &mut view.selection);
|
||||
frame.render_widget(
|
||||
Paragraph::new(self.status.as_str())
|
||||
@@ -3046,7 +3015,7 @@ impl App {
|
||||
{
|
||||
let popup = preview_area(frame.area());
|
||||
frame.render_widget(Clear, popup);
|
||||
let block = Block::bordered().border_style(Style::new().fg(Color::Cyan));
|
||||
let block = Block::bordered().border_style(ui::ACCENT);
|
||||
let inner = block.inner(popup);
|
||||
let cached = cached_description(&self.descriptions, &video.id);
|
||||
let text = match cached {
|
||||
@@ -3209,7 +3178,7 @@ impl App {
|
||||
let popup = preview_area(frame.area());
|
||||
let block = Block::bordered()
|
||||
.title(self.key_hints.help.as_str())
|
||||
.border_style(Style::new().fg(Color::Cyan));
|
||||
.border_style(ui::ACCENT);
|
||||
let inner = block.inner(popup).inner(ratatui::layout::Margin::new(1, 0));
|
||||
let viewport = (inner.width, inner.height);
|
||||
if help.viewport != Some(viewport) {
|
||||
@@ -3243,7 +3212,7 @@ impl App {
|
||||
BrowserKind::WatchLater => self.key_hints.browser[2].as_str(),
|
||||
BrowserKind::SavedVideos => self.key_hints.browser[3].as_str(),
|
||||
})
|
||||
.border_style(Style::new().fg(Color::Cyan));
|
||||
.border_style(ui::ACCENT);
|
||||
let [input, list, error] = Layout::vertical([
|
||||
Constraint::Length(3),
|
||||
Constraint::Min(1),
|
||||
@@ -3324,8 +3293,8 @@ impl App {
|
||||
});
|
||||
frame.render_stateful_widget(
|
||||
List::new(items)
|
||||
.highlight_symbol("> ")
|
||||
.highlight_style(Style::new().fg(Color::Black).bg(Color::Cyan)),
|
||||
.highlight_symbol(ui::SELECTION_MARKER)
|
||||
.highlight_style(ui::SELECTED),
|
||||
list,
|
||||
&mut browser.selection,
|
||||
);
|
||||
@@ -3374,9 +3343,7 @@ impl App {
|
||||
"Submitting saves this rename immediately; existing memberships and pins are preserved.",
|
||||
),
|
||||
};
|
||||
let mut block = Block::bordered()
|
||||
.title(title)
|
||||
.border_style(Style::new().fg(Color::Cyan));
|
||||
let mut block = Block::bordered().title(title).border_style(ui::ACCENT);
|
||||
if let CategoryNameTarget::Rename { old, .. } = &input.target {
|
||||
block = block.title_bottom(format!(" {old} "));
|
||||
}
|
||||
@@ -3450,7 +3417,7 @@ impl App {
|
||||
" Tags "
|
||||
})
|
||||
.title_bottom(dialog.label.as_str())
|
||||
.border_style(Style::new().fg(Color::Cyan));
|
||||
.border_style(ui::ACCENT);
|
||||
let [input, body, warning, error, help] = Layout::vertical([
|
||||
Constraint::Length(if dialog.name.is_none() { 3 } else { 0 }),
|
||||
Constraint::Min(1),
|
||||
@@ -3522,8 +3489,8 @@ impl App {
|
||||
])
|
||||
}),
|
||||
)
|
||||
.highlight_symbol("> ")
|
||||
.highlight_style(Style::new().fg(Color::Black).bg(Color::Cyan)),
|
||||
.highlight_symbol(ui::SELECTION_MARKER)
|
||||
.highlight_style(ui::SELECTED),
|
||||
body,
|
||||
&mut dialog.selection,
|
||||
);
|
||||
@@ -3695,8 +3662,8 @@ fn choose_provider(
|
||||
bindings.label(Action::SelectorClose),
|
||||
);
|
||||
let providers = List::new(PROVIDERS.map(|(name, _)| name))
|
||||
.highlight_style(Style::new().fg(Color::Black).bg(Color::Cyan))
|
||||
.highlight_symbol("> ");
|
||||
.highlight_style(ui::SELECTED)
|
||||
.highlight_symbol(ui::SELECTION_MARKER);
|
||||
loop {
|
||||
selection.select(Some(selected));
|
||||
terminal.draw(|frame| {
|
||||
@@ -3711,7 +3678,7 @@ fn choose_provider(
|
||||
);
|
||||
let block = Block::bordered()
|
||||
.title(" Browse provider ")
|
||||
.border_style(Style::new().fg(Color::Cyan));
|
||||
.border_style(ui::ACCENT);
|
||||
let [list, hints] = Layout::vertical([Constraint::Min(0), Constraint::Length(2)])
|
||||
.areas(block.inner(popup));
|
||||
frame.render_widget(block, popup);
|
||||
|
||||
Reference in New Issue
Block a user