Merge #2701@da434b059 (presets dropped with no library open are refused) into round3a-integration

crates/ui-egui/src/lib.rs: the batch's handle_dropped_files refuses presets
while no library is open, as #2701 does. The drop card says so, and its
"photos wait" line is desktop-only (the web build's drops import into its
session as before). photos_dropped_with_no_library_open_wait_for_one also
drops a preset; LR-IMP-DRAGDROP row updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
francoisgn
2026-10-23 22:05:14 +03:00
7 changed files with 30 additions and 9 deletions
+2 -1
View File
@@ -2385,5 +2385,6 @@
"Not answering yet: the drive may be asleep or offline": "Pas encore de réponse : le disque est peut-être en veille ou hors ligne",
"This library couldn't be checked": "Impossible de vérifier cette bibliothèque",
"Checking recent libraries": "Vérification des bibliothèques récentes",
"The photos are imported once a library is open": "Les photos seront importées dès qu'une bibliothèque sera ouverte"
"The photos are imported once a library is open": "Les photos seront importées dès qu'une bibliothèque sera ouverte",
"Open a library first, then drop the presets again": "Ouvrez d'abord une bibliothèque, puis déposez à nouveau les préréglages"
}
+7 -3
View File
@@ -36,12 +36,16 @@ fn lines(app: &LightcraftApp, paths: Vec<String>, unknown: bool) -> Vec<String>
out.push(crate::i18n::tr("Drop to open the face model").to_string());
}
let plural = |n: usize| if n == 1 { "" } else { "s" };
if !drop.presets.is_empty() {
// no library open yet (desktop: the web build's drops import into its session as before):
// photos wait for the library chosen, presets are refused (the drop handler does the same)
let waiting = crate::panels::library_problem::holds_drops(app) && !cfg!(target_arch = "wasm32");
if !drop.presets.is_empty() && waiting {
out.push(crate::i18n::tr("Open a library first, then drop the presets again").to_string());
} else if !drop.presets.is_empty() {
let n = drop.presets.len();
out.push(crate::i18n::tr_format!("Drop to import {n} preset{}", plural(n), n = n));
}
if !drop.photos.is_empty() && crate::panels::library_problem::holds_drops(app) {
// no library open yet: they wait for the one chosen (the drop handler holds them)
if !drop.photos.is_empty() && waiting {
out.push(crate::i18n::tr("The photos are imported once a library is open").to_string());
} else if !drop.photos.is_empty() && app.lightroom.is_some() && !cfg!(target_arch = "wasm32") {
// the drop is refused while a Lightroom catalog import owns the catalog
+6 -1
View File
@@ -1300,7 +1300,12 @@ impl LightcraftApp {
self.toast(ctx, e);
}
if !drop.presets.is_empty() {
let _ = self.run("file.importPresets", serde_json::json!({"paths": drop.presets}));
// presets live in a library: with none open they would go with the session it replaces
if panels::library_problem::holds_drops(self) {
self.toast(ctx, crate::i18n::tr("Open a library first, then drop the presets again"));
} else {
let _ = self.run("file.importPresets", serde_json::json!({"paths": drop.presets}));
}
}
if drop.photos.is_empty() {
return;
+8 -1
View File
@@ -260,11 +260,18 @@ fn photos_dropped_with_no_library_open_wait_for_one() {
let dir = Scratch::new("no-library");
let mut h = app(&dir);
h.app.library_problem = Some(crate::panels::library_problem::LibraryProblem::new(dir.path("Gone"), "not found"));
drop_files(&mut h, &[dir.path("trip/a.png")], true);
std::fs::write(dir.path("warm.lcpreset"), "{}").unwrap();
drop_files(&mut h, &[dir.path("trip/a.png"), dir.path("warm.lcpreset")], true);
assert!(said(&h, "once a library is open"), "{:?}", h.painted_text());
assert!(said(&h, "drop the presets again") && !said(&h, "1 preset"), "{:?}", h.painted_text());
drop_files(&mut h, &[], true);
drop_files(&mut h, &[dir.path("trip/a.png"), dir.path("trip/b.png")], false);
assert_eq!(h.app.library_problem.as_ref().map(|p| p.pending_import.len()), Some(2));
assert!(h.app.ui.dialog.is_none() && h.app.import.is_none(), "no review, no import into the temporary session");
assert_eq!(photos(&h), 0);
// a preset belongs to a library: refused, not imported into the session the library replaces
h.app.ui.toast = None;
drop_files(&mut h, &[dir.path("warm.lcpreset")], false);
let toast = h.app.ui.toast.as_ref().map(|t| t.0.clone()).unwrap_or_default();
assert!(toast.contains("drop the presets again"), "{toast}");
}
+2 -1
View File
@@ -168,7 +168,8 @@ are `button:libraryRetry`, `button:libraryChoose`, `button:libraryTemporary` (Co
`button:libraryQuit`. A temporary session shows a banner (`indicator:temporarySession`, `button:libraryReopen`) and
writes nothing. `app.openLibrary` opening a library ends it. `pendingImport` counts the files waiting for a
library: those given on the command line, and photos dropped on the window while it (or the launch list below) is
up; they are imported into the library that opens, or into the temporary session.
up (desktop app); they are imported into the library that opens, or into the temporary session. Presets dropped
then are refused with a toast.
### Choosing the library at launch
+2 -2
View File
@@ -70,7 +70,7 @@ differences and missing bindings are in [ui-parity.md](ui-parity.md#shortcuts).
| Id | Feature | Tier | Status | Evidence | Missing / notes |
|---|---|---|---|---|---|
| LR-IMP-ADD-DIALOG | Add photos/folders | P0 | ✅ | `cmd:file.addPhotos`, `cmd:library.importPreview`, `cmd:library.import` (`mode`, `album`, `albumName`, `preset`, `keywords`), `crates/ui-egui/src/import.rs` | review dialog (Import Photos): the scanned source (a scanned folder is not added to Local), candidate grid with thumbnails and checkboxes (duplicates by path/content marked and unchecked; with several file types, a toggle per type checks or unchecks all of its files, e.g. the raws without the JPEGs beside them, issue #344) filling a resizable window (issue #337), transfer mode (add in place / copy / move, each explained), album (existing/new): the destination picker follows the sidebar folder hierarchy and order, searches full paths, and distinguishes duplicate names by their folder path; folders navigate and smart albums are excluded, with None, New album and album-context preselection retained (#2362; `crates/ui-egui/src/album_picker.rs`, `crates/ui-egui/src/tests_album_picker.rs`); preset, keywords; the folder scan runs on a background thread, shown in the activity stack with ✕ to cancel (a NAS folder no longer freezes the window); the import itself (probing, sidecars, copying / moving files) runs on a worker thread and its batches join the library between frames, shown in the activity stack with ✕ to cancel; one undo step. Drag-and-drop goes through the same background import. File dialogs list the importable extensions (`lightcraft_engine::import::EXTENSIONS`) in any letter case on Linux, where GTK and the desktop portal match globs case-sensitively (`apps/lightcraft/src/dialog_filter.rs`, issue #342; 0.4.0 hid `.JPG` / `.NEF`, issue #705). The folder scan, Browse Folder, drag-and-drop and the sidecar lookup match extensions in any case (`crates/engine/src/tests_import.rs`) |
| LR-IMP-DRAGDROP | Drop files/folders to import | P0 | ✅ | `crates/ui-egui/src/lib.rs` (dropped files → the import review), `crates/ui-egui/src/drop_overlay.rs`, `crates/ui-egui/src/tests_drop_import.rs` | while files are dragged over the window a veil and a card say what dropping does (review N items for import, import N presets, open the face model; a note to close the open dialog first when one is open); photos and folders open the import review as File ▸ Import does (Settings ▸ Import ▸ Import dropped files without review imports them at once; the web build always does); a drop while a dialog is open (also in the web build) or while an import is running is refused with a toast, and the card says so; a finished scan waits for an open dialog to close before its review opens; the control channel's `ui.dropFiles` hovers or drops files for scripts; dropping onto a specific album not supported |
| LR-IMP-DRAGDROP | Drop files/folders to import | P0 | ✅ | `crates/ui-egui/src/lib.rs` (dropped files → the import review), `crates/ui-egui/src/drop_overlay.rs`, `crates/ui-egui/src/tests_drop_import.rs` | while files are dragged over the window a veil and a card say what dropping does (review N items for import, import N presets, open the face model; a note to close the open dialog first when one is open); photos and folders open the import review as File ▸ Import does (Settings ▸ Import ▸ Import dropped files without review imports them at once; the web build always does); with no library open on the desktop (the launch list, a library that couldn't open) photos wait for the library opened and presets are refused, and the card says so; a drop while a dialog is open (also in the web build) or while an import is running is refused with a toast, and the card says so; a finished scan waits for an open dialog to close before its review opens; the control channel's `ui.dropFiles` hovers or drops files for scripts; dropping onto a specific album not supported |
| LR-IMP-DUPES | Skip duplicates by content | P1 | ✅ | `crates/engine/src/import.rs`, `crates/engine/src/tests_import.rs` | |
| LR-IMP-DEVICE | Import from camera/card | P1 | ✅ | `cmd:library.devices`, `cmd:file.addFromDevice`, `crates/engine/src/devices.rs` | mounted volumes with a DCIM folder (macOS /Volumes, Linux /media and /run/media, Windows drive letters); File → Import from Device → the import review, copying into the library by default; menus serve the last scan and rescan in the background (never blocking a frame; hot-plugs repaint); copies are verified (a new file, synced, checked against the content hash the scan computed — byte compare with the source when there is none, issue #134; a bad copy is removed and reported as failed, never counted as imported; taken names get -1, -2…, never replaced — issue #96, `crates/engine/src/import_move.rs`); no PTP/MTP (cameras that don't mount as a disk); Settings → Import → Cameras and Cards can open the import review when a camera or card is connected (off by default; a card that was already in doesn't ask, and an open dialog or running import is finished first; `crates/ui-egui/src/import.rs`) |
| LR-IMP-AUTO | Watched-folder auto import | P2 | ✅ | `cmd:library.autoImport`, `cmd:library.autoImportScan`, `crates/ui-egui/src/panels/settings.rs` | Settings ▸ Import ▸ Auto Import: a watched folder whose new photos are added (in place or copied) once complete, into an optional album; scanned every 3 s |
@@ -798,7 +798,7 @@ Latin stays upright. Advanced Japanese composition remains open. Regression cove
| Date | Change | Summary |
|---|---|---|
| 2026-10-23 | minor | LRC-LIB-CATALOG-SWITCH, LR-PREF-GENERAL: Settings ▸ General ▸ At launch (Open the last library / Ask me) with a launch window listing the recent libraries, Alt/Option at launch asks once, File ▸ Open Recent Library ▸ Remove from List and `app.recentLibraries {remove}` (issue #1290); status and counts unchanged (✅) |
| 2026-10-23 | minor | LRC-LIB-CATALOG-SWITCH, LR-PREF-GENERAL: Settings ▸ General ▸ At launch (Open the last library / Ask me) with a launch window listing the recent libraries, Alt/Option at launch asks once, File ▸ Open Recent Library ▸ Remove from List and `app.recentLibraries {remove}` (issue #1290); photos dropped while no library is open wait for the one opened (presets are refused then); status and counts unchanged (✅) |
| 2026-10-23 | minor | LRC-LIB-CATALOG-IMPORT: Import Lightroom Catalog… (and Open Library's offer for a Lightroom catalog) asks whether the catalog merges into the open library or goes into a new library made next to it; Settings ▸ General or "Don't ask again" switches the question off; `file.importLightroom` takes `into` / `name` / `location` (#2672); status unchanged (🟡) |
| 2026-10-22 | minor | LR-VIEW-INFOOVERLAY: the info overlay shows the star rating and flag, and in the full-screen preview on macOS it starts below the window controls (issue #2460); status unchanged |
| 2026-10-22 | patch | KEY-GRID: both grid rows in View show the cycle shortcut and follow keymap changes; direct bindings and native accelerator ownership are preserved (#2500); status and counts unchanged |
+3
View File
@@ -35,6 +35,9 @@
- File ▸ Open Recent Library ▸ **Remove from List** forgets one library, and the launch window's **Remove from
List** does the same. Only the entry goes: the library and its photos stay where they are. Agents use
`app.recentLibraries {remove: path}`; its list now also says when each library was last opened (`lastOpened`).
- Photos dropped on the window while the list (or the window of a library that couldn't open) is up wait for the
library you open and are imported into it, as files given on the command line are; presets dropped then are
refused with a note, since they belong to a library.
## 2026-10-21