style: compact playback selection markers

This commit is contained in:
2026-10-22 15:23:15 +02:00
parent 2d30120072
commit f9e78c4549
5 changed files with 7 additions and 9 deletions
+2
View File
@@ -63,6 +63,8 @@ Each submitted selection opens one independent player instance without replacing
In video and episode lists, **Space** checks/unchecks the hovered row; **Ctrl-Space** clears checks. **Enter** plays checked rows in their current displayed order, regardless of the order they were checked. With no checks, Enter plays the hovered row. These controls also work in collection views and metadata previews; Miruro catalog titles and saved shows cannot be checked.
Checked videos/episodes show a single `*`; unchecked rows leave that marker blank. Category and tag selectors keep their checkboxes.
Checks belong to each cached view/screen and follow stable video identities through pagination and reordering. Refresh/replacement queries clear them; collection filtering/removal prunes checks for hidden/removed rows. Successful launcher creation clears checks, while immediate launch failures retain them. Saving, removing and marking watch status still affect the hovered video, not every checked row. Configure `video.select.toggle` and `video.select.clear`; terminals must transmit Ctrl-Space, or bind another chord.
Two or more checked videos launch `terebi --new-instance --queue-file '/private/session/queue.json'`. The private version-1 manifest contains ordered canonical episode/video page URLs and one independent `progress_file` per item. Terebi reuses its queue navigation and resolves each item when selected, not in advance; no expiring playback sources are stored. Each item's watching/watched state and resume identity remain independent, including after Tubular restarts. Single-video launches retain the command above. See the [queue contract](docs/terebi-url-playback-handoff.md#ordered-stream-queue-handoff).
+3 -3
View File
@@ -3067,9 +3067,9 @@ impl App {
let channel = Span::raw(video.channel.as_deref().unwrap_or("Unknown"));
Row::new([
Cell::from(if view.checked.contains(&video.id) {
"[x]"
"*"
} else {
"[ ]"
" "
}),
Cell::from(if self.video_state.is_watched(&video.id) {
"✓"
@@ -3127,7 +3127,7 @@ impl App {
.height(2)
}),
[
Constraint::Length(3),
Constraint::Length(1),
Constraint::Length(1),
Constraint::Fill(1),
],
+2 -2
View File
@@ -1659,9 +1659,9 @@ impl Browser {
.as_ref()
.is_some_and(|id| self.screen().checked.contains(id))
{
"[x] "
"* "
} else {
"[ ] "
" "
}
} else {
""
-2
View File
@@ -231,8 +231,6 @@ fn playback_checks_render_independently_and_live_hints_use_configured_keys() {
.iter()
.map(|cell| cell.symbol())
.collect();
assert!(rendered.contains("[x]"));
assert!(rendered.contains("[ ]"));
assert!(rendered.contains("1 checked"));
app.key(KeyEvent::new(KeyCode::F(21), KeyModifiers::NONE));
assert!(app.views[0].checked.is_empty());
-2
View File
@@ -610,8 +610,6 @@ fn episode_checks_follow_returned_order_and_cached_screen_history() {
.map(|cell| cell.symbol())
.collect();
assert!(text.contains("2 checked"));
assert!(text.contains("[x] Episode 3"));
assert!(text.contains("[x] Episode 7"));
if let Content::Series {
data: Some(data), ..