diff --git a/cbd-cli/src/lib.rs b/cbd-cli/src/lib.rs index 32d5f57..57bf0f4 100644 --- a/cbd-cli/src/lib.rs +++ b/cbd-cli/src/lib.rs @@ -76,7 +76,8 @@ pub enum QueueCmd { Show, /// Append tracks/subtrees (by library path) to the end of the queue. Append { paths: Vec }, - /// Insert tracks/subtrees at a position. + /// Insert tracks/subtrees at a position, pushing what was there down + /// (0 = front, past the end = append). Insert { position: u32, paths: Vec }, /// Replace the whole queue with the given tracks/subtrees. Replace { paths: Vec }, diff --git a/cbd-tui/src/app/queue.rs b/cbd-tui/src/app/queue.rs index e310d49..6d99d91 100644 --- a/cbd-tui/src/app/queue.rs +++ b/cbd-tui/src/app/queue.rs @@ -10,7 +10,7 @@ use ratatui::{ use crabidy_core::proto::crabidy::Queue as QueueData; use super::{ - carry_marks, Filter, MarkedPane, MessageFromUi, StatefulList, UiItem, UiItemKind, + carry_marks, Filter, MarkedPane, MessageFromUi, StatefulList, UiItem, UiItemKind, COLOR_GREEN, COLOR_PRIMARY, COLOR_PRIMARY_DARK, COLOR_RED, COLOR_SECONDARY, }; @@ -229,13 +229,24 @@ impl Queue { .map(|(idx, (real, item))| { let active = real == self.current_position; - let title = if active { - format!("> {}", item.title) - } else { - item.title.to_string() - }; + // Markers, in the library's vocabulary: `>` is the playing + // track, `*` is a mark (`s`, or painted in visual mode). A + // row can be both. + let mut title = String::new(); + if active { + title.push_str("> "); + } + if item.marked { + title.push_str("* "); + } + title.push_str(&item.title); let mut style = if active { Style::default().fg(COLOR_RED).add_modifier(Modifier::BOLD) + } else if item.marked { + // Same green as a marked library row. + Style::default() + .fg(COLOR_GREEN) + .add_modifier(Modifier::BOLD) } else if item.is_skipped { // No playable audio: rendered red (not bold — the // playing marker keeps precedence), skipped by @@ -455,6 +466,29 @@ mod tests { /// Renders and returns the buffer plus the y of the row containing /// `needle` and the x of its first character. + #[test] + fn marked_rows_render_a_star_like_the_library() { + let (tx, _rx) = flume::unbounded(); + let mut queue = Queue::new(tx); + queue.update_queue(queue_data(&["one", "two"], false)); + queue.select(Some(1)); + queue.toggle_mark(); + let rows = rendered_rows(&mut queue); + // The playing row keeps `>`; the marked row gets `*`. + assert!( + rows.iter().any(|row| row.contains("* artist - two")), + "marked row needs a star: {rows:?}" + ); + // And the title says so while visual mode is on. + queue.toggle_visual(); + assert!( + rendered_rows(&mut queue) + .iter() + .any(|row| row.contains("VISUAL")), + "visual mode needs a title indicator" + ); + } + fn render_and_find(queue: &mut Queue, needle: &str) -> (ratatui::buffer::Buffer, u16, u16) { let backend = TestBackend::new(40, 8); let mut terminal = Terminal::new(backend).expect("test terminal"); diff --git a/cbd-web/src/app.rs b/cbd-web/src/app.rs index 034eded..f7959a3 100644 --- a/cbd-web/src/app.rs +++ b/cbd-web/src/app.rs @@ -884,6 +884,9 @@ fn LibraryView(store: Store) -> impl IntoView { "‹" {pane.title.clone()} + + "VISUAL" +