diff --git a/flake.lock b/flake.lock index 6eb7ca4..e07324e 100644 --- a/flake.lock +++ b/flake.lock @@ -90,11 +90,11 @@ "nixpkgs-stable": "nixpkgs-stable" }, "locked": { - "lastModified": 1749918208, - "narHash": "sha256-lSNJKfE5XvZ0BTbdl/sAZ+3nUxhPnhB1g9Dj1o84MPw=", + "lastModified": 1749921358, + "narHash": "sha256-mhVXykmXD59JAwcYmnW3lJoavq23ysfSbPQC9gRdzm8=", "owner": "nix-community", "repo": "emacs-overlay", - "rev": "90d12e22421147992240f87578dee2e8ff0635af", + "rev": "5479bde495cf43766d53b3789a316e83ef7f8905", "type": "github" }, "original": { @@ -1797,11 +1797,11 @@ ] }, "locked": { - "lastModified": 1749875058, - "narHash": "sha256-LGeVwJLNJ+QPsuQMGsm5atAkFZgrRG3t62bB0+loOi4=", + "lastModified": 1749925521, + "narHash": "sha256-cbTj62MYbyZFYYTeA5zviOF/tr6ZzRKaJh1BEFsoE6c=", "owner": "0xc000022070", "repo": "zen-browser-flake", - "rev": "12fbcbb395776b50a848f87434bb786ef4f09b9d", + "rev": "bb4ab4a99523e3b4ef1f6ba1fc49d21f41e4921b", "type": "github" }, "original": { diff --git a/modules/home/mpd.nix b/modules/home/mpd.nix index 8b41e89..e1a60df 100644 --- a/modules/home/mpd.nix +++ b/modules/home/mpd.nix @@ -4,8 +4,6 @@ home.packages = with pkgs; [ mpc nix-config.packages.x86_64-linux.rmpc-latest - # termusic-patched - termusic-git ]; services = { diff --git a/modules/home/niri.nix b/modules/home/niri.nix index f9ee50e..c0dd559 100644 --- a/modules/home/niri.nix +++ b/modules/home/niri.nix @@ -256,8 +256,11 @@ in softness = 10; spread = 8; }; - min-width = 250; - min-height = 250; + + min-width = 500; + min-height = 400; + # tiled-state = false; + open-focused = true; } ]; diff --git a/modules/home/waybar.nix b/modules/home/waybar.nix index 4d1d9c0..f285500 100644 --- a/modules/home/waybar.nix +++ b/modules/home/waybar.nix @@ -30,11 +30,17 @@ in { "wireplumber" "bluetooth" "power-profiles-daemon" - "upower" + # "upower" + "battery" "idle_inhibitor" "custom/notification" ]; + "battery" = { + "format" = "{capacity}% {icon}"; + "format-icons" = ["" "" "" "" ""]; + }; + "bluetooth" = { "format" = ""; "format-off" = ""; @@ -290,12 +296,6 @@ window#waybar { animation-direction: alternate; } -#mpris { -} - -#workspaces { -} - #workspaces button { color: #666; padding: 3px 6px; diff --git a/overlays/termusic.nix b/overlays/termusic.nix deleted file mode 100644 index c267970..0000000 --- a/overlays/termusic.nix +++ /dev/null @@ -1,17 +0,0 @@ -final: prev: { - termusic-git = prev.termusic.overrideAttrs (old: rec { - version = "HEAD"; - - src = prev.fetchFromGitHub { - owner = "tramhao"; - repo = "termusic"; - rev = "HEAD"; - hash = "sha256-rK4kZlKXxH4fh7ODCwfx/MfDZaFOPMgw7XLOKHtistc="; - }; - - cargoDeps = final.rustPlatform.fetchCargoVendor { - inherit src; - hash = "sha256-q8rfB57sSmzvuY+QomBY8boOADLSYQnt1aRVi6fz1bY="; - }; - }); -} diff --git a/patches/termusic-503.patch b/patches/termusic-503.patch deleted file mode 100644 index 0ae4ebc..0000000 --- a/patches/termusic-503.patch +++ /dev/null @@ -1,673 +0,0 @@ -From a319c68c62ae93c1604c84d488192ca0e8551cb5 Mon Sep 17 00:00:00 2001 -From: hasezoey -Date: Wed, 28 May 2025 16:21:25 +0200 -Subject: [PATCH 1/3] feat(tui::components::music_library): change music - library tree loading to be async and non-blocking - -Downside is that the placeholder still acts as a correct path and that multiple loads can be done and overwrite eachother. - -fixes #481 ---- - CHANGELOG.md | 1 + - lib/src/types.rs | 13 ++ - tui/src/ui/components/config_editor/view.rs | 2 +- - tui/src/ui/components/music_library.rs | 226 +++++++++++--------- - tui/src/ui/components/playlist.rs | 2 +- - tui/src/ui/components/tag_editor/update.rs | 2 +- - tui/src/ui/model/mod.rs | 19 +- - tui/src/ui/model/update.rs | 32 +-- - 8 files changed, 172 insertions(+), 125 deletions(-) - -diff --git a/CHANGELOG.md b/CHANGELOG.md -index 2cd911a25..0641e5f29 100644 ---- a/CHANGELOG.md -+++ b/CHANGELOG.md -@@ -21,6 +21,7 @@ - - Fix(tui): on track change, dont select "current track" playlist item if the old "current track" was not selected. - - Fix(tui): re-select the (approximately) same playlist item after a shuffle. - - Fix(tui): escape key will now no longer also act as a quit key. -+- Fix(tui): changed the music library tree loading to be async. - - Fix(server): with `rusty-soundtouch` on `rusty` backend, dont take initial samples until necessary. - - Fix(server): on rusty backend, always decode and use `f32` samples. (instead of `i16`) - - Fix(server): on rusty backend, update `soundtouch` version to fix build issues on latest arch & gcc 15. -diff --git a/lib/src/types.rs b/lib/src/types.rs -index d26802f9d..f60080802 100644 ---- a/lib/src/types.rs -+++ b/lib/src/types.rs -@@ -1,3 +1,4 @@ -+use std::path::PathBuf; - use std::sync::Arc; - - use crate::config::v2::tui::{keys::KeyBinding, theme::styles::ColorTermusic}; -@@ -387,6 +388,14 @@ pub enum KFMsg { - PodcastRefreshAllFeedsBlurUp, - } - -+/// Basically a Tree Node, but without having to include `tui-realm-treeview` as another dependency for lib -+#[derive(Debug, Clone, PartialEq, Eq)] -+pub struct RecVec { -+ pub id: T, -+ pub value: V, -+ pub children: Vec>, -+} -+ - #[derive(Clone, Debug, PartialEq, Eq)] - pub enum LIMsg { - TreeStepInto(String), -@@ -397,6 +406,10 @@ pub enum LIMsg { - SwitchRoot, - AddRoot, - RemoveRoot, -+ -+ /// A requested node is ready from loading. -+ /// `(Tree, FocusNode)` -+ TreeNodeReady(RecVec, Option), - } - - #[derive(Clone, Copy, Debug, PartialEq, Eq)] -diff --git a/tui/src/ui/components/config_editor/view.rs b/tui/src/ui/components/config_editor/view.rs -index de259702a..1a96d2177 100644 ---- a/tui/src/ui/components/config_editor/view.rs -+++ b/tui/src/ui/components/config_editor/view.rs -@@ -1732,7 +1732,7 @@ impl Model { - } - - pub fn umount_config_editor(&mut self) { -- self.library_reload_tree(); -+ self.library_scan_dir(&self.library.tree_path, None); - self.playlist_reload(); - self.database_reload(); - self.progress_reload(); -diff --git a/tui/src/ui/components/music_library.rs b/tui/src/ui/components/music_library.rs -index afd6cc6ad..6a4d65e5c 100644 ---- a/tui/src/ui/components/music_library.rs -+++ b/tui/src/ui/components/music_library.rs -@@ -1,14 +1,15 @@ --use crate::ui::model::Model; -+use crate::ui::model::{DownloadTracker, Model}; - use crate::ui::tui_cmd::TuiCmd; - use crate::utils::get_pin_yin; - use anyhow::{bail, Context, Result}; - use std::fs::{remove_dir_all, remove_file, rename, DirEntry}; - use std::path::{Path, PathBuf}; -+use std::sync::mpsc::Sender; - use termusiclib::config::v2::server::config_extra::ServerConfigVersionedDefaulted; - use termusiclib::config::v2::server::ScanDepth; - use termusiclib::config::SharedTuiSettings; - use termusiclib::ids::Id; --use termusiclib::types::{GSMsg, LIMsg, Msg, PLMsg, TEMsg, YSMsg}; -+use termusiclib::types::{GSMsg, LIMsg, Msg, PLMsg, RecVec, TEMsg, YSMsg}; - use tui_realm_treeview::{Node, Tree, TreeView, TREE_CMD_CLOSE, TREE_CMD_OPEN, TREE_INITIAL_NODE}; - use tuirealm::command::{Cmd, CmdResult, Direction, Position}; - use tuirealm::event::{Key, KeyEvent, KeyModifiers, NoUserEvent}; -@@ -234,14 +235,6 @@ impl Component for MusicLibrary { - } - - impl Model { -- pub fn library_scan_dir>(&mut self, p: P) { -- self.library.tree_path = p.into(); -- self.library.tree = Tree::new(Self::library_dir_tree( -- &self.library.tree_path, -- self.config_server.read().get_library_scan_depth(), -- )); -- } -- - pub fn library_upper_dir(&self) -> Option { - self.library - .tree_path -@@ -249,12 +242,58 @@ impl Model { - .map(std::path::Path::to_path_buf) - } - -- pub fn library_dir_tree(p: &Path, depth: ScanDepth) -> Node { -- let name: String = match p.file_name() { -+ /// Execute [`Self::library_scan`] from a `&self` instance. -+ #[inline] -+ pub fn library_scan_dir>(&self, path: P, focus_node: Option) { -+ Self::library_scan( -+ self.tx_to_main.clone(), -+ self.download_tracker.clone(), -+ path, -+ self.config_server.read().get_library_scan_depth(), -+ focus_node, -+ ); -+ } -+ -+ pub fn loading_tree() -> Tree { -+ Tree::new(Node::new("/dev/null".to_string(), "Loading...".to_string())) -+ } -+ -+ /// Execute a library scan on a different thread. -+ /// -+ /// Executes [`Self::library_dir_tree`] on a different thread and send a [`LIMsg::TreeNodeReady`] on finish -+ pub fn library_scan>( -+ tx: Sender, -+ download_tracker: DownloadTracker, -+ path: P, -+ depth: ScanDepth, -+ focus_node: Option, -+ ) { -+ let path = path.into(); -+ std::thread::Builder::new() -+ .name("library tree scan".to_string()) -+ .spawn(move || { -+ download_tracker.increase_one(path.to_string_lossy()); -+ let root_node = Self::library_dir_tree(&path, depth); -+ -+ let _ = tx.send(Msg::Library(LIMsg::TreeNodeReady(root_node, focus_node))); -+ download_tracker.decrease_one(&path.to_string_lossy()); -+ }) -+ .expect("Failed to spawn thread"); -+ } -+ -+ /// Scan the given `path` for up to `depth`, and return a [`Node`] tree. -+ /// -+ /// Note: consider using [`Self::library_scan`] instead of this directly. -+ fn library_dir_tree(path: &Path, depth: ScanDepth) -> RecVec { -+ let name: String = match path.file_name() { - None => "/".to_string(), - Some(n) => n.to_string_lossy().into_owned(), - }; -- let mut node: Node = Node::new(p.to_string_lossy().into_owned(), name); -+ let mut node = RecVec { -+ id: path.to_path_buf(), -+ value: name, -+ children: Vec::new(), -+ }; - - let depth = match depth { - ScanDepth::Limited(v) => v, -@@ -262,8 +301,8 @@ impl Model { - ScanDepth::Unlimited => u32::MAX, - }; - -- if depth > 0 && p.is_dir() { -- if let Ok(paths) = std::fs::read_dir(p) { -+ if depth > 0 && path.is_dir() { -+ if let Ok(paths) = std::fs::read_dir(path) { - let mut paths: Vec<(String, PathBuf)> = paths - .filter_map(std::result::Result::ok) - .filter(|p| !p.file_name().to_string_lossy().starts_with('.')) -@@ -273,12 +312,14 @@ impl Model { - paths.sort_by(|a, b| alphanumeric_sort::compare_str(&a.0, &b.0)); - - for p in paths { -- node.add_child(Self::library_dir_tree(&p.1, ScanDepth::Limited(depth - 1))); -+ node.children -+ .push(Self::library_dir_tree(&p.1, ScanDepth::Limited(depth - 1))); - } - } - } - node - } -+ - pub fn library_dir_children(p: &Path) -> Vec { - let mut children: Vec = vec![]; - if p.is_dir() { -@@ -300,88 +341,69 @@ impl Model { - children - } - -- pub fn library_reload_with_node_focus(&mut self, node: Option<&str>) { -+ /// Reload the library with the given `node` as a focus, also starts a new database sync worker for the current path. -+ pub fn library_reload_with_node_focus(&mut self, node: Option) { - self.db.sync_database(self.library.tree_path.as_path()); - self.database_reload(); -- self.library_reload_tree(); -- if let Some(n) = node { -- assert!(self -- .app -- .attr( -- &Id::Library, -- Attribute::Custom(TREE_INITIAL_NODE), -- AttrValue::String(n.to_string()), -- ) -- .is_ok()); -+ self.library_scan_dir(&self.library.tree_path, node); -+ } -+ -+ /// Convert a [`RecVec`] to a [`Node`]. -+ fn recvec_to_node(vec: RecVec) -> Node { -+ let mut node = Node::new(vec.id.to_string_lossy().to_string(), vec.value); -+ -+ for val in vec.children { -+ node.add_child(Self::recvec_to_node(val)); - } -+ -+ node - } - -- pub fn library_reload_tree(&mut self) { -- self.library.tree = Tree::new(Self::library_dir_tree( -- self.library.tree_path.as_ref(), -- self.config_server.read().get_library_scan_depth(), -- )); -- let current_node = match self.app.state(&Id::Library).ok().unwrap() { -+ /// Apply the given [`RecVec`] as a tree -+ pub fn library_apply_as_tree( -+ &mut self, -+ msg: RecVec, -+ focus_node: Option, -+ ) { -+ let root_path = msg.id.clone(); -+ let root_node = Self::recvec_to_node(msg); -+ -+ let old_current_node = match self.app.state(&Id::Library).ok().unwrap() { - State::One(StateValue::String(id)) => Some(id), - _ => None, - }; -- let mut focus = false; -- -- if let Ok(f) = self.app.query(&Id::Library, Attribute::Focus) { -- if Some(AttrValue::Flag(true)) == f { -- focus = true; -- } -- } - -- if focus { -- self.app.umount(&Id::Library).ok(); -- assert!(self -- .app -- .mount( -- Id::Library, -- Box::new(MusicLibrary::new( -- &self.library.tree, -- current_node, -- self.config_tui.clone(), -- ),), -- Vec::new() -- ) -- .is_ok()); -- self.app.active(&Id::Library).ok(); -- return; -+ self.library.tree_path = root_path; -+ self.library.tree = Tree::new(root_node); -+ -+ // remount preserves focus -+ let _ = self.app.remount( -+ Id::Library, -+ Box::new(MusicLibrary::new( -+ &self.library.tree, -+ old_current_node, -+ self.config_tui.clone(), -+ )), -+ Vec::new(), -+ ); -+ -+ // focus the specified node -+ if let Some(id) = focus_node { -+ let _ = self.app.attr( -+ &Id::Library, -+ Attribute::Custom(TREE_INITIAL_NODE), -+ AttrValue::String(id), -+ ); - } -- -- assert!(self -- .app -- .remount( -- Id::Library, -- Box::new(MusicLibrary::new( -- &self.library.tree, -- current_node, -- self.config_tui.clone(), -- ),), -- Vec::new() -- ) -- .is_ok()); - } - -- // Kept for debugging focus issue -- // if let Ok(f) = self.app.query(&Id::Library, Attribute::Focus) { -- // if Some(AttrValue::Flag(true)) == f { -- // error!("focus after remount: true"); -- // } else { -- // error!("focus after remount: false"); -- // } -- // } - pub fn library_stepinto(&mut self, node_id: &str) { -- self.library_scan_dir(PathBuf::from(node_id)); -- self.library_reload_tree(); -+ self.library_scan_dir(PathBuf::from(node_id), None); - } - - pub fn library_stepout(&mut self) { - if let Some(p) = self.library_upper_dir() { -- self.library_scan_dir(p); -- self.library_reload_tree(); -+ self.library_scan_dir(p, None); - } - } - -@@ -396,6 +418,7 @@ impl Model { - } - } - -+ /// Delete the currently selected node from the filesystem and reload the tree and remove the deleted paths from the playlist. - pub fn library_delete_node(&mut self) -> Result<()> { - if let Ok(State::One(StateValue::String(node_id))) = self.app.state(&Id::Library) { - if let Some(mut route) = self.library.tree.root().route_by_node(&node_id) { -@@ -407,28 +430,27 @@ impl Model { - remove_dir_all(p)?; - } - -- // this is to keep the state of playlist -- self.library_reload_tree(); -- let tree = self.library.tree.clone(); -- if let Some(new_node) = tree.root().node_by_route(&route) { -- self.library_reload_with_node_focus(Some(new_node.id())); -- } else { -- //special case 1: old route not available but have siblings -- if let Some(last) = route.last_mut() { -- if last > &mut 0 { -- *last -= 1; -- } -- } -- if let Some(new_node) = tree.root().node_by_route(&route) { -- self.library_reload_with_node_focus(Some(new_node.id())); -- } else { -- //special case 2: old route not available and no siblings -- route.truncate(route.len() - 1); -- if let Some(new_node) = tree.root().node_by_route(&route) { -- self.library_reload_with_node_focus(Some(new_node.id())); -+ let mut tree = self.library.tree.clone(); -+ tree.root_mut().remove_child(&node_id); -+ let mut focus_node: Option = None; -+ // case 1: the route still exists due to having a sibling beyond the index which now takes the same index -+ if let Some(node) = tree.root().node_by_route(&route) { -+ focus_node = Some(node.id().to_string()); -+ } else if !route.is_empty() { -+ let _ = route.pop(); -+ // case 2: the route does not exist anymore, but there is a parent in the route -+ if let Some(parent) = tree.root().node_by_route(&route) { -+ // case 2.1: the parent has children, select the last of them -+ if let Some(last_child) = parent.children().last() { -+ focus_node = Some(last_child.id().to_string()); -+ } else { -+ // case 2.2: the parent exists, but has no children -+ focus_node = Some(parent.id().to_string()); - } - } - } -+ -+ self.library_scan_dir(&self.library.tree_path, focus_node); - } - // this line remove the deleted songs from playlist - self.playlist_update_library_delete(); -@@ -459,7 +481,7 @@ impl Model { - p_parent.join(pold_filename) - }; - rename(pold, new_node_id.as_path())?; -- self.library_reload_with_node_focus(new_node_id.to_str()); -+ self.library_reload_with_node_focus(Some(new_node_id.to_string_lossy().to_string())); - } - self.library.yanked_node_id = None; - self.playlist_update_library_delete(); -@@ -520,7 +542,7 @@ impl Model { - } - if let Some(dir) = vec.get(index) { - let pathbuf = PathBuf::from(dir); -- self.library_scan_dir(pathbuf); -+ self.library_scan_dir(pathbuf, None); - self.library_reload_with_node_focus(None); - } - } -diff --git a/tui/src/ui/components/playlist.rs b/tui/src/ui/components/playlist.rs -index 96254dd8b..a2a333b9e 100644 ---- a/tui/src/ui/components/playlist.rs -+++ b/tui/src/ui/components/playlist.rs -@@ -888,7 +888,7 @@ impl Model { - // TODO: move this to server? - self.playback.playlist.save_m3u(filename)?; - -- self.library_reload_with_node_focus(Some(&filename.to_string_lossy())); -+ self.library_reload_with_node_focus(Some(filename.to_string_lossy().to_string())); - - Ok(()) - } -diff --git a/tui/src/ui/components/tag_editor/update.rs b/tui/src/ui/components/tag_editor/update.rs -index ca268cacf..5b2554501 100644 ---- a/tui/src/ui/components/tag_editor/update.rs -+++ b/tui/src/ui/components/tag_editor/update.rs -@@ -34,7 +34,7 @@ impl Model { - TEMsg::TagEditorClose => { - if let Some(s) = self.tageditor_song.clone() { - // TODO: this should be re-done and take actual track ids themself, or at least verified to use the same functions to result in the same id -- self.library_reload_with_node_focus(Some(&s.path_as_id_str())); -+ self.library_reload_with_node_focus(Some(s.path_as_id_str().to_string())); - } - self.umount_tageditor(); - } -diff --git a/tui/src/ui/model/mod.rs b/tui/src/ui/model/mod.rs -index 8916513aa..c5515e81d 100644 ---- a/tui/src/ui/model/mod.rs -+++ b/tui/src/ui/model/mod.rs -@@ -35,7 +35,7 @@ use super::components::TETrack; - use super::tui_cmd::TuiCmd; - use crate::ui::Application; - use crate::CombinedSettings; --use download_tracker::DownloadTracker; -+pub use download_tracker::DownloadTracker; - - mod download_tracker; - mod playlist; -@@ -362,10 +362,7 @@ impl Model { - } = config; - let path = Self::get_full_path_from_config(&config_server.read()); - // TODO: refactor music library tree to be Paths instead? -- let tree = Tree::new(Self::library_dir_tree( -- &path, -- config_server.read().get_library_scan_depth(), -- )); -+ let tree = Self::loading_tree(); - - let viuer_supported = if config_tui.read().cover_features_enabled() { - get_viuer_support() -@@ -412,6 +409,16 @@ impl Model { - let ce_theme = config_tui.read().settings.theme.clone(); - let xywh = xywh::Xywh::from(&config_tui.read().settings.coverart); - -+ let download_tracker = DownloadTracker::default(); -+ -+ Self::library_scan( -+ tx_to_main.clone(), -+ download_tracker.clone(), -+ &path, -+ config_server.read().get_library_scan_depth(), -+ None, -+ ); -+ - Self { - app, - quit: false, -@@ -460,7 +467,7 @@ impl Model { - taskpool, - tx_to_main, - rx_to_main, -- download_tracker: DownloadTracker::default(), -+ download_tracker, - current_track_lyric: None, - playback: Playback::new(), - cmd_to_server_tx, -diff --git a/tui/src/ui/model/update.rs b/tui/src/ui/model/update.rs -index 7e9111ec5..066f56f8f 100644 ---- a/tui/src/ui/model/update.rs -+++ b/tui/src/ui/model/update.rs -@@ -57,7 +57,7 @@ impl Update for Model { - None - } - Msg::Library(m) => { -- self.update_library(&m); -+ self.update_library(m); - None - } - Msg::GeneralSearch(m) => { -@@ -110,7 +110,7 @@ impl Update for Model { - - Msg::Podcast(m) => self.update_podcast(m), - Msg::LyricMessage(m) => self.update_lyric_textarea(m), -- Msg::Download(m) => self.update_download_msg(&m), -+ Msg::Download(m) => self.update_download_msg(m), - Msg::Xywh(m) => self.update_xywh_msg(m), - - Msg::ForceRedraw => None, -@@ -558,13 +558,13 @@ impl Model { - None - } - -- fn update_library(&mut self, msg: &LIMsg) { -+ fn update_library(&mut self, msg: LIMsg) { - match msg { - LIMsg::TreeBlur => { - assert!(self.app.active(&Id::Playlist).is_ok()); - } - LIMsg::TreeStepInto(path) => { -- self.library_stepinto(path); -+ self.library_stepinto(&path); - } - LIMsg::TreeStepOut => { - self.library_stepout(); -@@ -588,6 +588,9 @@ impl Model { - self.mount_error_popup(e.context("library remove root")); - } - } -+ LIMsg::TreeNodeReady(vec, focus_node) => { -+ self.library_apply_as_tree(vec, focus_node); -+ } - } - } - -@@ -916,20 +919,20 @@ impl Model { - } - - // change status bar text to indicate the downloading state -- fn update_download_msg(&mut self, msg: &DLMsg) -> Option { -+ fn update_download_msg(&mut self, msg: DLMsg) -> Option { - self.redraw = true; - match msg { - DLMsg::DownloadRunning(url, title) => { -- self.download_tracker.increase_one(&**url); -+ self.download_tracker.increase_one(&*url); - self.show_message_timeout_label_help( -- self.download_tracker.message_download_start(title), -+ self.download_tracker.message_download_start(&title), - None, - None, - None, - ); - } - DLMsg::DownloadSuccess(url) => { -- self.download_tracker.decrease_one(url); -+ self.download_tracker.decrease_one(&url); - self.show_message_timeout_label_help( - self.download_tracker.message_download_complete(), - None, -@@ -945,13 +948,14 @@ impl Model { - if self.download_tracker.visible() { - return None; - } -- self.library_reload_with_node_focus(file.as_deref()); -+ self.library_reload_with_node_focus(file); - } - DLMsg::DownloadErrDownload(url, title, error_message) => { -- self.download_tracker.decrease_one(url); -+ self.download_tracker.decrease_one(&url); - self.mount_error_popup(anyhow!("download failed: {error_message}")); - self.show_message_timeout_label_help( -- self.download_tracker.message_download_error_response(title), -+ self.download_tracker -+ .message_download_error_response(&title), - None, - None, - None, -@@ -961,17 +965,17 @@ impl Model { - self.mount_error_popup(anyhow!("download ok but tag info is not complete.")); - self.show_message_timeout_label_help( - self.download_tracker -- .message_download_error_embed_data(title), -+ .message_download_error_embed_data(&title), - None, - None, - None, - ); - } - DLMsg::MessageShow((title, text)) => { -- self.mount_message(title, text); -+ self.mount_message(&title, &text); - } - DLMsg::MessageHide((title, text)) => { -- self.umount_message(title, text); -+ self.umount_message(&title, &text); - } - DLMsg::FetchPhotoSuccess(image_wrapper) => { - self.show_image(&image_wrapper.data).ok(); - -From 8b1fd1216fb81bfa982958086832c10b52d6e494 Mon Sep 17 00:00:00 2001 -From: hasezoey -Date: Wed, 28 May 2025 16:25:41 +0200 -Subject: [PATCH 2/3] fix(tui::components::music_library::library_stepout): - focus the last root node in the new tree - -Instead of always focusing the root node of the new tree. ---- - CHANGELOG.md | 1 + - tui/src/ui/components/music_library.rs | 5 ++++- - 2 files changed, 5 insertions(+), 1 deletion(-) - -diff --git a/CHANGELOG.md b/CHANGELOG.md -index 0641e5f29..3e05516e9 100644 ---- a/CHANGELOG.md -+++ b/CHANGELOG.md -@@ -22,6 +22,7 @@ - - Fix(tui): re-select the (approximately) same playlist item after a shuffle. - - Fix(tui): escape key will now no longer also act as a quit key. - - Fix(tui): changed the music library tree loading to be async. -+- Fix(tui): in the music library tree, when stepping out now the correct node if focused instead of the root node. - - Fix(server): with `rusty-soundtouch` on `rusty` backend, dont take initial samples until necessary. - - Fix(server): on rusty backend, always decode and use `f32` samples. (instead of `i16`) - - Fix(server): on rusty backend, update `soundtouch` version to fix build issues on latest arch & gcc 15. -diff --git a/tui/src/ui/components/music_library.rs b/tui/src/ui/components/music_library.rs -index 6a4d65e5c..498a29ffb 100644 ---- a/tui/src/ui/components/music_library.rs -+++ b/tui/src/ui/components/music_library.rs -@@ -397,13 +397,16 @@ impl Model { - } - } - -+ /// Handle stepping into a node on the tree - pub fn library_stepinto(&mut self, node_id: &str) { - self.library_scan_dir(PathBuf::from(node_id), None); - } - -+ /// Handle stepping out of the current root node on the tree - pub fn library_stepout(&mut self) { - if let Some(p) = self.library_upper_dir() { -- self.library_scan_dir(p, None); -+ let focus_node = Some(self.library.tree_path.to_string_lossy().to_string()); -+ self.library_scan_dir(p, focus_node); - } - } - - -From 47a13fc83e3c353a516d83e892ea87c7e26ed430 Mon Sep 17 00:00:00 2001 -From: hasezoey -Date: Wed, 28 May 2025 16:32:42 +0200 -Subject: [PATCH 3/3] style(tui::components::music_library): add a todo to try - to load the directory if not loaded yet - ---- - tui/src/ui/components/music_library.rs | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/tui/src/ui/components/music_library.rs b/tui/src/ui/components/music_library.rs -index 498a29ffb..d98cd6c09 100644 ---- a/tui/src/ui/components/music_library.rs -+++ b/tui/src/ui/components/music_library.rs -@@ -90,6 +90,7 @@ impl MusicLibrary { - let current_node = self.component.tree_state().selected().unwrap(); - let p: &Path = Path::new(current_node); - if p.is_dir() { -+ // TODO: try to load the directory if it is not loaded yet. - (self.perform(Cmd::Custom(TREE_CMD_OPEN)), None) - } else { - (