feat: show dimmed video metadata beside thumbnails
This commit is contained in:
+67
-9
@@ -17,7 +17,8 @@
|
|||||||
//! Esc closes the popup; j/k loads the selected video's description and i returns to editing.
|
//! Esc closes the popup; j/k loads the selected video's description and i returns to editing.
|
||||||
//! Normal-mode t toggles a thumbnail in the same-sized popup; j/k follows selection, Esc closes.
|
//! Normal-mode t toggles a thumbnail in the same-sized popup; j/k follows selection, Esc closes.
|
||||||
//! Images use native terminal graphics when supported, otherwise colored half-block characters.
|
//! Images use native terminal graphics when supported, otherwise colored half-block characters.
|
||||||
//! Thumbnails scale up or down proportionally to fill the popup without cropping.
|
//! Thumbnails scale proportionally in the left pane without cropping.
|
||||||
|
//! The right pane shows a wrapped title above author · publication date, dimmed like the footer.
|
||||||
//! curl fetches JPEGs lazily into memory; decoding and image resizing/encoding run off-thread.
|
//! curl fetches JPEGs lazily into memory; decoding and image resizing/encoding run off-thread.
|
||||||
//! Channel tags live in `$XDG_CONFIG_HOME/tubular/config.toml`, or `~/.config/tubular/config.toml`.
|
//! Channel tags live in `$XDG_CONFIG_HOME/tubular/config.toml`, or `~/.config/tubular/config.toml`.
|
||||||
//! `[categories]` stores flat, overlapping tags: one channel can appear in several arrays.
|
//! `[categories]` stores flat, overlapping tags: one channel can appear in several arrays.
|
||||||
@@ -1021,15 +1022,39 @@ impl App {
|
|||||||
{
|
{
|
||||||
let popup = preview_area(frame.area());
|
let popup = preview_area(frame.area());
|
||||||
frame.render_widget(Clear, popup);
|
frame.render_widget(Clear, popup);
|
||||||
let block = Block::bordered()
|
let block = Block::bordered().border_style(Style::new().fg(Color::Cyan));
|
||||||
.title_bottom(Line::from(vec![
|
let [image, _, mut details] = Layout::horizontal([
|
||||||
" ".into(),
|
Constraint::Fill(3),
|
||||||
video.title.as_str().into(),
|
Constraint::Length(2),
|
||||||
" ".into(),
|
Constraint::Fill(2),
|
||||||
]))
|
])
|
||||||
.border_style(Style::new().fg(Color::Cyan));
|
.areas(block.inner(popup));
|
||||||
|
let metadata = Paragraph::new(vec![
|
||||||
|
Line::raw(video.title.as_str()),
|
||||||
|
Line::from(vec![
|
||||||
|
video.channel.as_deref().unwrap_or("Unknown").into(),
|
||||||
|
" · ".into(),
|
||||||
|
video
|
||||||
|
.publication_date
|
||||||
|
.as_deref()
|
||||||
|
.unwrap_or("Unknown")
|
||||||
|
.into(),
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
.wrap(Wrap { trim: true })
|
||||||
|
.dim();
|
||||||
|
let height = if details.is_empty() {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
metadata
|
||||||
|
.line_count(details.width)
|
||||||
|
.min(details.height as usize) as u16
|
||||||
|
};
|
||||||
|
details.y += (details.height - height) / 2;
|
||||||
|
details.height = height;
|
||||||
|
frame.render_widget(metadata, details);
|
||||||
// Drawing polls completion and starts resize work before we choose the title state.
|
// Drawing polls completion and starts resize work before we choose the title state.
|
||||||
self.thumbnail.draw(frame, block.inner(popup));
|
self.thumbnail.draw(frame, image);
|
||||||
frame.render_widget(
|
frame.render_widget(
|
||||||
block.title(loading_title(
|
block.title(loading_title(
|
||||||
" Thumbnail ",
|
" Thumbnail ",
|
||||||
@@ -1234,6 +1259,39 @@ mod tests {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn thumbnail_title_wraps_above_a_dimmed_byline_in_the_right_pane() {
|
||||||
|
use ratatui::{Terminal, backend::TestBackend, style::Modifier};
|
||||||
|
|
||||||
|
let mut app = app(&["tubular"]);
|
||||||
|
app.views[0].videos = youtube::search_results(
|
||||||
|
r#"{"entries":[{"id":"jNQXAC9IVRw","title":"First second third fourth fifth","channel":"Creator","upload_date":"20050423"}]}"#,
|
||||||
|
).unwrap().videos;
|
||||||
|
app.views[0].selection.select(Some(0));
|
||||||
|
app.thumbnail_open = true;
|
||||||
|
for (width, height, x, title_y, title_lines) in [
|
||||||
|
(80, 24, 46, 10, vec!["First second third", "fourth fifth"]),
|
||||||
|
(120, 40, 69, 19, vec!["First second third fourth fifth"]),
|
||||||
|
] {
|
||||||
|
let mut terminal = Terminal::new(TestBackend::new(width, height)).unwrap();
|
||||||
|
terminal
|
||||||
|
.draw(|frame| app.draw(frame, Duration::ZERO))
|
||||||
|
.unwrap();
|
||||||
|
let buffer = terminal.backend().buffer();
|
||||||
|
let mut lines = title_lines;
|
||||||
|
lines.push("Creator · 2005-04-23");
|
||||||
|
for (row, text) in lines.iter().enumerate() {
|
||||||
|
for (column, character) in text.chars().enumerate() {
|
||||||
|
let cell = buffer
|
||||||
|
.cell((x + column as u16, title_y + row as u16))
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(cell.symbol(), character.to_string());
|
||||||
|
assert!(cell.modifier.contains(Modifier::DIM));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn view_tabs_wrap_whole_labels_and_keep_the_selected_row_visible() {
|
fn view_tabs_wrap_whole_labels_and_keep_the_selected_row_visible() {
|
||||||
use ratatui::{Terminal, backend::TestBackend};
|
use ratatui::{Terminal, backend::TestBackend};
|
||||||
|
|||||||
Reference in New Issue
Block a user