Skip to content

Commit c27145e

Browse files
committed
ui(thumbnail): size at 16:9 halfblock aspect and center vertically
The previous 20x10 image area was 1:1 in effective halfblock pixels, so 16:9 source thumbnails fit-rendered as a 5-row strip glued to the top-left corner — "stuck-on" rather than composed. Resize to 32 cols x 9 rows = 32x18 halfblock pixels = 16:9 exact, so typical YouTube/SoundCloud thumbnails fill the rectangle without letterboxing. Wrap in a 34-col outer gutter with 1-char horizontal margins, vertically centered between Min(0) spacers above and below. Negative space becomes part of the composition — the image reads as a deliberate anchor rather than an artifact. No border/frame: matches looper's existing chrome-light aesthetic (scatter visualizer is also unframed).
1 parent 86a21aa commit c27145e

1 file changed

Lines changed: 26 additions & 6 deletions

File tree

src/tui.rs

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -337,15 +337,35 @@ fn draw_normal_in(frame: &mut ratatui::Frame, area: ratatui::layout::Rect, state
337337

338338
draw_header(frame, chunks[0], state);
339339

340-
// If a thumbnail is loaded, give the left 20 columns to the image and
341-
// let the visualizer flex into the remaining space; otherwise the
342-
// visualizer fills the whole row as before.
340+
// If a thumbnail is loaded, dedicate a 34-column gutter on the left to
341+
// it and let the visualizer flex into the remaining space. The image
342+
// itself is 32x9 char cells (16:9 in halfblock pixels — exact aspect
343+
// match for typical YouTube/SoundCloud thumbnails), vertically centered
344+
// in the gutter with 1-char horizontal margins. Negative space above
345+
// and below is intentional: it makes the image read as an anchor rather
346+
// than a corner sticker.
343347
let (image_area, scatter_area) = if state.thumbnail.is_some() {
344-
let split = Layout::default()
348+
let outer = Layout::default()
345349
.direction(Direction::Horizontal)
346-
.constraints([Constraint::Length(20), Constraint::Min(20)])
350+
.constraints([Constraint::Length(34), Constraint::Min(20)])
347351
.split(chunks[1]);
348-
(Some(split[0]), split[1])
352+
let vertical = Layout::default()
353+
.direction(Direction::Vertical)
354+
.constraints([
355+
Constraint::Min(0),
356+
Constraint::Length(9),
357+
Constraint::Min(0),
358+
])
359+
.split(outer[0]);
360+
let horizontal = Layout::default()
361+
.direction(Direction::Horizontal)
362+
.constraints([
363+
Constraint::Length(1),
364+
Constraint::Length(32),
365+
Constraint::Length(1),
366+
])
367+
.split(vertical[1]);
368+
(Some(horizontal[1]), outer[1])
349369
} else {
350370
(None, chunks[1])
351371
};

0 commit comments

Comments
 (0)