LR-BEHAV-BG-TASKS: background export with progress and cancel

- Engine: export split into prepare_export (session thread, cheap) and PreparedExport::run
  (Send: read/decode/render/encode); run_batch with per-photo progress, cancel and collected
  errors; export_batch keeps the stop-at-first-error behaviour for scripted calls.
- Desktop: the Export dialog, Export with Preset (menu and context menu) and Export with
  Previous run their batch on a worker thread (app.export {background: true}); a progress
  panel shows count / file / Cancel; ui.inspect reports export {running, last}.
- Headless: settle waits for a running export; `lightcraft-cli snapshot` finishes it before
  exiting. Web keeps foreground exports (downloads are main-thread).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Brandon ThomasandClaude Opus 5.5 committed 2026-10-01 22:27:06 -04:00
1 parent 8548c157ba
commit 8a83f8c6ce
16 files changed
+414 -65

No files matched your search

+15
View File
@@ -587,6 +587,12 @@ fn snapshot(args: &[String]) -> Result<(), String> {
eprintln!("lightcraft-cli snapshot: imported {n} files in {:.0} ms", ti.elapsed().as_secs_f64() * 1e3);
}
let services = lightcraft_ui_egui::Services {
write_shared: Some(std::sync::Arc::new(|p: &str, b: &[u8]| {
if let Some(dir) = Path::new(p).parent().filter(|d| !d.as_os_str().is_empty()) {
std::fs::create_dir_all(dir).map_err(|e| e.to_string())?;
}
std::fs::write(p, b).map_err(|e| format!("{p}: {e}"))
})),
write: Some(Box::new(|p: &str, b: &[u8]| {
if let Some(dir) = Path::new(p).parent().filter(|d| !d.as_os_str().is_empty()) {
std::fs::create_dir_all(dir).map_err(|e| e.to_string())?;
@@ -672,6 +678,15 @@ fn snapshot(args: &[String]) -> Result<(), String> {
}
eprintln!("lightcraft-cli snapshot: wrote {path} ({}×{})", r["result"]["width"], r["result"]["height"]);
}
// a background export started by the script finishes before we exit (its files would be cut off)
if h.app.export.is_some() {
eprintln!("lightcraft-cli snapshot: waiting for the background export");
let te = std::time::Instant::now();
while h.app.export.is_some() && te.elapsed() < std::time::Duration::from_secs(3600) {
h.step();
std::thread::sleep(std::time::Duration::from_millis(20));
}
}
eprintln!("lightcraft-cli snapshot: done in {:.2} s ({} frames)", t0.elapsed().as_secs_f64(), h.frames());
Ok(())
}