diff --git a/crates/ui-egui/src/panels/library_problem.rs b/crates/ui-egui/src/panels/library_problem.rs index e3e4ee205..2cec27801 100644 --- a/crates/ui-egui/src/panels/library_problem.rs +++ b/crates/ui-egui/src/panels/library_problem.rs @@ -201,9 +201,10 @@ pub enum Unavailable { } impl Unavailable { - /// The folder isn't a library that is there to open (gone, a file, not a library any more, or - /// unlistable): what greys out File ▸ Open Recent Library and makes `app.recentLibraries` say - /// `exists: false`. An unreadable catalog or a check that failed doesn't say the library is gone. + /// The folder isn't a library that is there to open (gone, a file, or not a library any more): + /// what greys out File ▸ Open Recent Library and makes `app.recentLibraries` say `exists: false`. + /// A folder that can't be listed, an unreadable catalog or a check that failed doesn't say the + /// library is gone (the launch window still shows why, and offers Open only for one it checked). pub fn is_missing(&self) -> bool { // a folder that refuses to list is there (permissions, macOS privacy): opening it says why matches!(self, Unavailable::NotFound | Unavailable::NotAFolder | Unavailable::NoLibrary) diff --git a/crates/ui-egui/src/panels/settings.rs b/crates/ui-egui/src/panels/settings.rs index e27e3c450..ef9ebf85d 100644 --- a/crates/ui-egui/src/panels/settings.rs +++ b/crates/ui-egui/src/panels/settings.rs @@ -1456,9 +1456,9 @@ pub(crate) fn free_name(location: &std::path::Path, base: &str) -> std::path::Pa /// `app.recentLibraries {remove?: path, clear?: bool}`: the recently opened libraries, newest /// first, as `[{path, name, exists, checking, open, lastOpened}]`. `exists` is what the last -/// background check found (`false`: moved, on a disconnected drive, a folder that can't be listed -/// or no longer a library; `null`: not checked yet, or the check couldn't tell — an unreadable catalog, a check -/// that failed; `checking` says whether a check is running): the command never +/// background check found (`false`: moved, on a disconnected drive, not a folder or no longer a +/// library; `null`: not checked yet, or the check couldn't tell — a folder that can't be listed, an +/// unreadable catalog, a lookup or check that failed; `checking` says whether a check is running): the command never /// looks at a folder itself, as one on an offline network share can take seconds to answer. /// `lastOpened`: ISO 8601 time, `null` when not known. `remove` forgets that /// one entry (an error if the list doesn't hold it) and `clear` forgets them all; neither touches diff --git a/crates/ui-egui/src/tests_launch_library.rs b/crates/ui-egui/src/tests_launch_library.rs index 39f18d76e..b4753a45d 100644 --- a/crates/ui-egui/src/tests_launch_library.rs +++ b/crates/ui-egui/src/tests_launch_library.rs @@ -879,6 +879,13 @@ fn a_failed_lookup_doesnt_mark_a_recent_library_missing() { let inside = make_library(&locked.join("Inside")); let gone = dir.join("Gone").to_string_lossy().into_owned(); std::fs::set_permissions(&locked, std::fs::Permissions::from_mode(0o000)).unwrap(); + if std::fs::read_dir(&locked).is_ok() { + // running as root (some CI containers): the mode bits don't stop it, so no lookup fails + std::fs::set_permissions(&locked, std::fs::Permissions::from_mode(0o755)).unwrap(); + let _ = std::fs::remove_dir_all(&dir); + lightcraft_testkit::skip!(System, "a folder with no permissions can still be read (running as root)"); + return; + } let mut app = LightcraftApp::new(lightcraft_engine::Session::new().with_fs(), Services::default()); app.ui.settings.recent_libraries = vec![inside.clone(), gone.clone()]; let mut h = Headless::new(app, [900.0, 600.0], 1.0); diff --git a/docs/control-protocol.md b/docs/control-protocol.md index 3239c840a..7a8033aa5 100644 --- a/docs/control-protocol.md +++ b/docs/control-protocol.md @@ -190,8 +190,9 @@ Enter opens it and Escape quits (over an open library: closes the window and kee opening a library ends it. While it shows, the settings are saved as usual (a removal is kept). `app.recentLibraries` returns `[{path, name, exists, checking, open, lastOpened}]`. `exists` is what the last -background check found (`false`: moved, on a disconnected drive, a folder that can't be listed or no longer a -library; `null`: not checked yet, or the check couldn't tell, e.g. an unreadable catalog) and `checking` whether one +background check found (`false`: moved, on a disconnected drive, not a folder or no longer a library; `null`: not +checked yet, or the check couldn't tell, e.g. a folder that can't be listed, an unreadable catalog or a lookup that +failed) and `checking` whether one is running: the command never looks at a folder itself. A failed open never marks a library missing (another program may have it). Recent libraries are checked in the background when the app starts (the first 8) and when the launch window opens; one not found is looked for again every