refactor: replace vim keybindings with mouse navigation, remove mock data
- Remove all sample/mock data (artists, albums, tracks, queue, history, calendar) - Delete input module (vim.rs, leader.rs) and related UI (which_key, cmdline) - Add mouse support: click tabs, click list items, scroll wheel navigation - Show real disk space via nix::statvfs instead of hardcoded value - Simplify topbar/statusbar by removing mode display and key hints - Hide album/track sections when no artist is selected
This commit is contained in:
+60
-33
@@ -1,16 +1,16 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use ratatui::{
|
||||
Frame,
|
||||
layout::{Constraint, Layout, Rect},
|
||||
style::{Modifier, Style},
|
||||
text::{Line, Span},
|
||||
widgets::{List, ListItem, ListState, Paragraph},
|
||||
Frame,
|
||||
};
|
||||
|
||||
use crate::data::{Album, AlbumStatus, Artist, Track};
|
||||
use crate::theme;
|
||||
use crate::ui::pane::{section_divider, Pane};
|
||||
use crate::ui::pane::{Pane, section_divider};
|
||||
use crate::ui::progress_bar::progress_bar;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
@@ -53,7 +53,9 @@ impl LibraryState {
|
||||
}
|
||||
|
||||
pub fn selected_artist(&self) -> Option<&Artist> {
|
||||
self.artist_state.selected().and_then(|i| self.artists.get(i))
|
||||
self.artist_state
|
||||
.selected()
|
||||
.and_then(|i| self.artists.get(i))
|
||||
}
|
||||
|
||||
pub fn selected_album(&self) -> Option<&Album> {
|
||||
@@ -101,7 +103,8 @@ impl LibraryState {
|
||||
}
|
||||
}
|
||||
LibraryFocus::Albums => {
|
||||
let max = self.selected_artist()
|
||||
let max = self
|
||||
.selected_artist()
|
||||
.map(|a| a.albums.len().saturating_sub(1))
|
||||
.unwrap_or(0);
|
||||
if let Some(i) = self.album_state.selected() {
|
||||
@@ -309,7 +312,13 @@ fn render_detail_pane(frame: &mut Frame, area: Rect, state: &mut LibraryState) {
|
||||
let artist = state.selected_artist();
|
||||
|
||||
let meta = artist
|
||||
.map(|a| format!("{} · {}", a.country, a.genres.first().map(|s| s.as_str()).unwrap_or("")))
|
||||
.map(|a| {
|
||||
format!(
|
||||
"{} · {}",
|
||||
a.country,
|
||||
a.genres.first().map(|s| s.as_str()).unwrap_or("")
|
||||
)
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
let have_tracks: u16 = artist
|
||||
@@ -319,21 +328,14 @@ fn render_detail_pane(frame: &mut Frame, area: Rect, state: &mut LibraryState) {
|
||||
.map(|a| a.albums.iter().map(|al| al.total).sum())
|
||||
.unwrap_or(0);
|
||||
|
||||
let footer = Line::from(vec![
|
||||
Span::styled("[a]", Style::default().fg(theme::GRAY)),
|
||||
Span::styled(" add ", Style::default().fg(theme::FG2)),
|
||||
Span::styled("[m]", Style::default().fg(theme::GRAY)),
|
||||
Span::styled(" monitor ", Style::default().fg(theme::FG2)),
|
||||
Span::styled("[s]", Style::default().fg(theme::GRAY)),
|
||||
Span::styled(" search ", Style::default().fg(theme::FG2)),
|
||||
Span::styled("[r]", Style::default().fg(theme::GRAY)),
|
||||
Span::styled(" refresh", Style::default().fg(theme::FG2)),
|
||||
Span::raw(" "),
|
||||
Span::styled(
|
||||
let footer = if artist.is_some() {
|
||||
Line::from(vec![Span::styled(
|
||||
format!("{}/{} tracks", have_tracks, total_tracks),
|
||||
Style::default().fg(theme::GRAY),
|
||||
),
|
||||
]);
|
||||
)])
|
||||
} else {
|
||||
Line::from("")
|
||||
};
|
||||
|
||||
let pane = Pane::new("Detail")
|
||||
.meta(&meta)
|
||||
@@ -344,6 +346,15 @@ fn render_detail_pane(frame: &mut Frame, area: Rect, state: &mut LibraryState) {
|
||||
let inner = block.inner(area);
|
||||
frame.render_widget(block, area);
|
||||
|
||||
let Some(artist) = artist else {
|
||||
let msg = Paragraph::new(Span::styled(
|
||||
"No artist selected",
|
||||
Style::default().fg(theme::GRAY),
|
||||
));
|
||||
frame.render_widget(msg, inner);
|
||||
return;
|
||||
};
|
||||
|
||||
let chunks = Layout::vertical([
|
||||
Constraint::Length(6),
|
||||
Constraint::Length(1),
|
||||
@@ -353,11 +364,9 @@ fn render_detail_pane(frame: &mut Frame, area: Rect, state: &mut LibraryState) {
|
||||
])
|
||||
.split(inner);
|
||||
|
||||
if let Some(artist) = artist {
|
||||
render_artist_header(frame, chunks[0], artist);
|
||||
}
|
||||
render_artist_header(frame, chunks[0], artist);
|
||||
|
||||
let albums_count = artist.map(|a| a.albums.len()).unwrap_or(0);
|
||||
let albums_count = artist.albums.len();
|
||||
let albums_label = format!("{} releases", albums_count);
|
||||
let album_divider = section_divider("albums", Some(&albums_label));
|
||||
frame.render_widget(Paragraph::new(album_divider), chunks[1]);
|
||||
@@ -407,7 +416,9 @@ fn render_artist_header(frame: &mut Frame, area: Rect, artist: &Artist) {
|
||||
let lines = vec![
|
||||
Line::from(Span::styled(
|
||||
&artist.name,
|
||||
Style::default().fg(theme::YELLOW).add_modifier(Modifier::BOLD),
|
||||
Style::default()
|
||||
.fg(theme::YELLOW)
|
||||
.add_modifier(Modifier::BOLD),
|
||||
)),
|
||||
Line::from(""),
|
||||
Line::from(vec![
|
||||
@@ -427,7 +438,10 @@ fn render_artist_header(frame: &mut Frame, area: Rect, artist: &Artist) {
|
||||
]),
|
||||
Line::from(vec![
|
||||
Span::styled("albums ", Style::default().fg(theme::GRAY)),
|
||||
Span::styled(artist.albums.len().to_string(), Style::default().fg(theme::FG1)),
|
||||
Span::styled(
|
||||
artist.albums.len().to_string(),
|
||||
Style::default().fg(theme::FG1),
|
||||
),
|
||||
Span::raw(" "),
|
||||
Span::styled("tracks ", Style::default().fg(theme::GRAY)),
|
||||
Span::styled(have.to_string(), Style::default().fg(theme::FG1)),
|
||||
@@ -463,7 +477,8 @@ fn render_albums_list(
|
||||
Style::default().fg(theme::AQUA)
|
||||
};
|
||||
|
||||
let title_width = area.width as usize - 2 - type_str.len() - 1 - 4 - 1 - 10 - 1 - 5 - 1 - 8;
|
||||
let title_width =
|
||||
area.width as usize - 2 - type_str.len() - 1 - 4 - 1 - 10 - 1 - 5 - 1 - 8;
|
||||
let mut title = album.title.clone();
|
||||
if title.len() > title_width {
|
||||
title.truncate(title_width.saturating_sub(1));
|
||||
@@ -560,20 +575,32 @@ impl LibraryState {
|
||||
return Vec::new();
|
||||
};
|
||||
|
||||
if album.id == "bush" {
|
||||
return crate::data::sample_tracks_bush_hall();
|
||||
}
|
||||
|
||||
tracks_for(album)
|
||||
}
|
||||
}
|
||||
|
||||
fn tracks_for(album: &Album) -> Vec<Track> {
|
||||
let titles = [
|
||||
"Opening", "Curtain Call", "Half-Light", "Polaroid", "Switchback",
|
||||
"Slow Dancer", "The Inheritance", "Glassworks", "Interlude", "Aftermath",
|
||||
"Static", "Returner", "Dust Bowl", "Postcard", "Late Reply",
|
||||
"Honeymoon", "Northern Lights", "Cold Open", "Coda", "Reprise",
|
||||
"Opening",
|
||||
"Curtain Call",
|
||||
"Half-Light",
|
||||
"Polaroid",
|
||||
"Switchback",
|
||||
"Slow Dancer",
|
||||
"The Inheritance",
|
||||
"Glassworks",
|
||||
"Interlude",
|
||||
"Aftermath",
|
||||
"Static",
|
||||
"Returner",
|
||||
"Dust Bowl",
|
||||
"Postcard",
|
||||
"Late Reply",
|
||||
"Honeymoon",
|
||||
"Northern Lights",
|
||||
"Cold Open",
|
||||
"Coda",
|
||||
"Reprise",
|
||||
];
|
||||
|
||||
(0..album.total)
|
||||
|
||||
Reference in New Issue
Block a user