From da9a20f8f70fd085944ff8794827b208e99804a3 Mon Sep 17 00:00:00 2001 From: LyAhn Date: Mon, 6 Apr 2026 02:26:43 +0100 Subject: [PATCH] Refine semantic search and CPU fallback - make Candle CUDA support opt-in so default builds work on CPU-only machines - improve semantic search loading and empty states in the gallery - keep semantic search UX clearer when no matches are found Refs: #4 --- src-tauri/Cargo.toml | 10 +++++++--- src-tauri/src/embedder.rs | 19 ++++++++----------- src/components/Gallery.tsx | 34 ++++++++++++++++++++++++++++++++-- 3 files changed, 47 insertions(+), 16 deletions(-) diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 3618749..c810d72 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -12,6 +12,10 @@ crate-type = ["staticlib", "cdylib", "rlib"] [build-dependencies] tauri-build = { version = "2", features = [] } +[features] +default = [] +candle-cuda = ["candle-core/cuda", "candle-nn/cuda", "candle-transformers/cuda"] + [dependencies] tauri = { version = "2", features = ["protocol-asset"] } tauri-plugin-opener = "2" @@ -35,8 +39,8 @@ log = "0.4" ffmpeg-sidecar = "2.5.0" xxhash-rust = { version = "0.8", features = ["xxh3"] } sysinfo = "0.38.4" -candle-core = { version = "0.10.2", features = ["cuda"] } -candle-nn = { version = "0.10.2", features = ["cuda"] } -candle-transformers = { version = "0.10.2", features = ["cuda"] } +candle-core = "0.10.2" +candle-nn = "0.10.2" +candle-transformers = "0.10.2" hf-hub = { version = "0.5.0", default-features = false, features = ["ureq", "native-tls"] } tokenizers = "0.22.1" diff --git a/src-tauri/src/embedder.rs b/src-tauri/src/embedder.rs index f074863..f5315de 100644 --- a/src-tauri/src/embedder.rs +++ b/src-tauri/src/embedder.rs @@ -84,21 +84,18 @@ impl ClipImageEmbedder { } fn resolve_device() -> Result { - let cuda_device = Device::cuda_if_available(0)?; - if cuda_device.is_cuda() { - println!("CLIP embedder using CUDA device."); - return Ok(cuda_device); - } - - #[cfg(target_os = "macos")] + #[cfg(feature = "candle-cuda")] { - let metal_device = Device::metal_if_available(0)?; - if metal_device.is_metal() { - println!("CLIP embedder using Metal device."); - return Ok(metal_device); + let cuda_device = Device::cuda_if_available(0)?; + if cuda_device.is_cuda() { + println!("CLIP embedder using CUDA device."); + return Ok(cuda_device); } } + #[cfg(not(feature = "candle-cuda"))] + println!("CLIP embedder built without CUDA support."); + println!("CLIP embedder using CPU device."); Ok(Device::Cpu) } diff --git a/src/components/Gallery.tsx b/src/components/Gallery.tsx index a7265a8..05735d0 100644 --- a/src/components/Gallery.tsx +++ b/src/components/Gallery.tsx @@ -234,6 +234,8 @@ export function Gallery() { const totalImages = useGalleryStore((state) => state.totalImages); const loadingImages = useGalleryStore((state) => state.loadingImages); const zoomPreset = useGalleryStore((state) => state.zoomPreset); + const search = useGalleryStore((state) => state.search); + const searchMode = useGalleryStore((state) => state.searchMode); const parentRef = useRef(null); const [contextMenu, setContextMenu] = useState<{ x: number; y: number; image: ImageRecord } | null>(null); @@ -270,6 +272,26 @@ export function Gallery() { }; }, []); + if (images.length === 0 && loadingImages) { + return ( +
+
+
+

+ {searchMode === "semantic" && search.trim().length > 0 + ? `Searching for matches to "${search}"` + : "Loading media"} +

+

+ {searchMode === "semantic" && search.trim().length > 0 + ? "Semantic search can take a little longer than filename search" + : "Fetching results"} +

+
+
+ ); + } + if (images.length === 0 && !loadingImages) { return (
@@ -278,8 +300,16 @@ export function Gallery() { -

No media found

-

Try adjusting your filters or add a new folder

+

+ {searchMode === "semantic" && search.trim().length > 0 + ? "No semantic matches found" + : "No media found"} +

+

+ {searchMode === "semantic" && search.trim().length > 0 + ? "Try a broader phrase, or wait for more embeddings to finish processing" + : "Try adjusting your filters or add a new folder"} +

);