# Conflicts:
#	docs/whats-new.md
This commit is contained in:
bflatastic committed 2026-10-13 11:02:52 +07:00
commit 6eb04f9a1c
3 files changed
+29 -11

No files matched your search

+25 -10
View File
@@ -10,13 +10,6 @@ use serde_json::{Value, json};
use crate::backend::{Backend, ProgressHook};
/// File extensions recognised as photos when expanding folders.
pub const PHOTO_EXTENSIONS: &[&str] = &[
"jpg", "jpeg", "png", "tif", "tiff", "webp", "dng", "cr2", "cr3", "nef", "arw", "raf", "orf", "rw2", "rwl", "raw", "pef", "srw", "psd", "jxl",
"gif", "bmp", "avif", // containers LightCraft cannot decode but imports as preview only (their embedded JPEG)
"iiq", "crw", "mrw", "x3f", "kdc", "mos", "erf", "3fr", "fff",
];
/// Headless backend: a [`Session`] with filesystem hooks.
pub struct Headless {
pub session: Session,
@@ -175,15 +168,16 @@ impl Backend for Headless {
}
/// Expand folders (recursively, sorted; bounded, see `lightcraft_engine::walk`) into photo files
/// and make paths absolute.
/// and make paths absolute. Folders take the extensions `library.import` takes
/// (`lightcraft_engine::import::EXTENSIONS`), so the two can't drift apart (issue #1080).
pub fn expand_paths(paths: &[String]) -> Vec<String> {
let absolute = |p: &Path| std::path::absolute(p).unwrap_or_else(|_| p.to_path_buf()).to_string_lossy().to_string();
let photo = |p: &Path| p.extension().is_some_and(|e| PHOTO_EXTENSIONS.contains(&e.to_string_lossy().to_lowercase().as_str()));
let mut out = Vec::new();
for p in paths {
let path = Path::new(p);
if path.is_dir() {
let w = lightcraft_engine::walk::files_in(path, None, lightcraft_engine::walk::Limits::default(), photo);
let w =
lightcraft_engine::walk::files_in(path, None, lightcraft_engine::walk::Limits::default(), lightcraft_engine::import::is_supported);
out.extend(w.files.iter().map(|f| absolute(f)));
} else {
// Explicit files are kept even with unknown extensions (the probe decides).
@@ -226,6 +220,27 @@ mod tests {
assert_eq!(r["dir"], json!(dir.display().to_string()), "{r}");
}
/// `--import <folder>` picks up every extension `library.import` takes, HEIC included (issue #1080).
#[test]
fn expand_paths_takes_the_engine_extensions() {
let dir = std::env::temp_dir().join(format!("lc-mcp-expand-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(&dir).unwrap();
for e in lightcraft_engine::import::EXTENSIONS {
std::fs::write(dir.join(format!("a.{e}")), b"x").unwrap();
}
std::fs::write(dir.join("B.HEIC"), b"x").unwrap();
std::fs::write(dir.join("notes.txt"), b"x").unwrap();
let found = expand_paths(&[dir.to_string_lossy().to_string()]);
let _ = std::fs::remove_dir_all(&dir);
let ext = |f: &String| Path::new(f).extension().map(|e| e.to_string_lossy().to_string()).unwrap_or_default();
let mut got: Vec<String> = found.iter().map(ext).collect();
got.sort();
let mut want: Vec<String> = lightcraft_engine::import::EXTENSIONS.iter().map(|e| e.to_string()).chain(["HEIC".to_string()]).collect();
want.sort();
assert_eq!(got, want);
}
/// A headless session stamps imports and edits with the system clock, not the engine's fixed test clock.
#[test]
fn headless_uses_the_system_clock() {
+1 -1
View File
@@ -23,7 +23,7 @@ mod tools;
pub use backend::{Backend, ProgressHook, Remote};
pub use base64::{base64_decode, base64_encode};
pub use headless::{Headless, PHOTO_EXTENSIONS, encode_image, expand_paths, write_image};
pub use headless::{Headless, encode_image, expand_paths, write_image};
pub use server::{PROTOCOL_VERSION, Server};
pub use tools::{COMMAND_TOOL_PREFIX, ToolResult, call_tool, command_tool_name, helper_tools, tool_definitions};
+3
View File
@@ -69,6 +69,9 @@
its embedded JPEG or a thumbnail to show, the loupe now says so at the bottom of the photo ("Original offline · no
smart preview" or "This photo can't be opened", the reason on hover) instead of showing that preview as if it were
the photo.
### `--import <folder>` takes HEIC
- `lightcraft-cli run --import <folder>` (and the MCP tools that take folders) now picks up HEIC, HEIF and Nikon NRW
files, the same files as `library.import` (issue #1080). They used to be skipped without a word.
### JPEG XL export
- Export as JPEG XL (issue #247): Format ▸ JPEG XL in the Export dialog, `format: "jxl"` for agents and the CLI