mirror of
https://github.com/storytold/lightcraft.git
synced 2026-10-29 12:37:51 -06:00
Merge branch 'worktree-agent-ad641003294616c82'
# Conflicts: # crates/engine/src/library.rs # crates/ui-egui/src/lib.rs # docs/parity.md
This commit is contained in:
@@ -128,7 +128,12 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
.iter()
|
||||
.filter_map(|id| s.develop_of(*id).map(|d| (*id, d)))
|
||||
.filter_map(|(id, d)| {
|
||||
let fresh = DevelopSettings { wb: lightcraft_develop::WhiteBalance { mode: WbMode::AsShot, ..d.wb }, ..Default::default() };
|
||||
// back to the photo's import defaults when a default preset gave it its look
|
||||
let look = s.catalog.photo(id).and_then(|p| p.import_look.clone());
|
||||
let fresh = match look {
|
||||
Some(l) => (*l).clone(),
|
||||
None => DevelopSettings { wb: lightcraft_develop::WhiteBalance { mode: WbMode::AsShot, ..d.wb }, ..Default::default() },
|
||||
};
|
||||
s.develop_op(id, fresh, "Reset")
|
||||
})
|
||||
.collect();
|
||||
|
||||
@@ -11,6 +11,7 @@ mod library;
|
||||
mod masks;
|
||||
mod merge;
|
||||
mod organize;
|
||||
mod prefs;
|
||||
mod preset_files;
|
||||
mod query;
|
||||
mod xmp;
|
||||
@@ -111,6 +112,7 @@ pub fn command_specs() -> &'static [CommandSpec] {
|
||||
v.extend(query::specs());
|
||||
v.extend(xmp::specs());
|
||||
v.extend(preset_files::specs());
|
||||
v.extend(prefs::specs());
|
||||
v
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
//! Library preferences (Settings → Import / Performance): develop defaults applied on import and
|
||||
//! the thumbnail cache budget. Saved in the library's `prefs.json` (see [`crate::library`]).
|
||||
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use super::{CommandSpec, always, bad, cmd};
|
||||
use crate::import::{CameraDefault, ImportDefaults};
|
||||
use crate::{Result, Session};
|
||||
|
||||
const ID: &str = "library.preferences";
|
||||
|
||||
fn current(s: &Session) -> Value {
|
||||
json!({
|
||||
"xmp": s.xmp,
|
||||
"import": s.import_defaults,
|
||||
"cacheMb": if s.cache_mb == 0 { s.cache_bytes() >> 20 } else { u64::from(s.cache_mb) },
|
||||
"persistent": s.library.is_some(),
|
||||
})
|
||||
}
|
||||
|
||||
/// `"default"`, `""` or null = no preset; anything else must name an existing preset.
|
||||
fn preset_ref(s: &Session, v: &Value) -> Result<Option<String>> {
|
||||
match v {
|
||||
Value::Null => Ok(None),
|
||||
Value::String(x) if x.is_empty() || x == "default" => Ok(None),
|
||||
Value::String(x) if s.presets.iter().any(|p| p.id == *x) => Ok(Some(x.clone())),
|
||||
Value::String(x) => Err(bad(ID, format!("unknown preset `{x}` (see preset.list)"))),
|
||||
other => Err(bad(ID, format!("preset must be a preset id or \"default\", not {other}"))),
|
||||
}
|
||||
}
|
||||
|
||||
fn prefs(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let mut d: ImportDefaults = s.import_defaults.clone();
|
||||
let mut changed = false;
|
||||
if let Some(i) = p.get("import") {
|
||||
if let Some(v) = i.get("rawPreset") {
|
||||
d.raw_preset = preset_ref(s, v)?;
|
||||
}
|
||||
if let Some(v) = i.get("otherPreset") {
|
||||
d.other_preset = preset_ref(s, v)?;
|
||||
}
|
||||
if let Some(v) = i.get("perCamera").and_then(Value::as_bool) {
|
||||
d.per_camera = v;
|
||||
}
|
||||
if let Some(a) = i.get("cameras").and_then(Value::as_array) {
|
||||
d.cameras.clear();
|
||||
for c in a {
|
||||
let camera = c.get("camera").and_then(Value::as_str).unwrap_or_default().trim().to_string();
|
||||
if camera.is_empty() {
|
||||
return Err(bad(ID, "each camera default needs `camera` (make + model)"));
|
||||
}
|
||||
let preset = preset_ref(s, c.get("preset").unwrap_or(&Value::Null))?;
|
||||
d.cameras.push(CameraDefault { camera, preset });
|
||||
}
|
||||
}
|
||||
changed = true;
|
||||
}
|
||||
// set or clear one camera's default: {camera: {camera: "Make Model", preset: id|null}}
|
||||
if let Some(c) = p.get("camera") {
|
||||
let camera = c.get("camera").and_then(Value::as_str).unwrap_or_default().trim().to_string();
|
||||
if camera.is_empty() {
|
||||
return Err(bad(ID, "`camera.camera` (make + model) is required"));
|
||||
}
|
||||
let preset = preset_ref(s, c.get("preset").unwrap_or(&Value::Null))?;
|
||||
d.cameras.retain(|x| !x.camera.eq_ignore_ascii_case(&camera));
|
||||
if c.get("remove").and_then(Value::as_bool) != Some(true) {
|
||||
d.cameras.push(CameraDefault { camera, preset });
|
||||
}
|
||||
changed = true;
|
||||
}
|
||||
s.import_defaults = d;
|
||||
if let Some(mb) = p.get("cacheMb") {
|
||||
let mb = mb.as_u64().ok_or_else(|| bad(ID, "cacheMb must be a number of megabytes"))?;
|
||||
s.set_cache_mb(mb.clamp(0, 1 << 20) as u32)?;
|
||||
} else if changed {
|
||||
s.save_prefs()?;
|
||||
}
|
||||
Ok(current(s))
|
||||
}
|
||||
|
||||
pub fn specs() -> Vec<CommandSpec> {
|
||||
vec![cmd!(
|
||||
"library.preferences",
|
||||
"Library Preferences",
|
||||
[],
|
||||
None,
|
||||
"{import?: {rawPreset?: presetId|\"default\", otherPreset?: presetId|\"default\", perCamera?: bool, cameras?: [{camera: \"Make Model\", preset: presetId|null}]}, camera?: {camera, preset?, remove?: bool}, cacheMb?: n (0 = default)} — develop defaults applied on import (raws / other images / per camera) and the thumbnail cache size, saved with the library → {xmp, import, cacheMb, persistent}",
|
||||
always,
|
||||
prefs
|
||||
)]
|
||||
}
|
||||
Reference in New Issue
Block a user