- BackgroundTasks: per-task Retry now dispatches to the correct backend —
retryFailedEmbeddings for embedding failures, queueTaggingJobs for tagging
failures, or both; previously always called embedding retry only
- commands: tag-cloud cache key now hashes both image IDs and embedding bytes
(xxh3) so re-embedding a file after reindex correctly invalidates the cache;
removed now-dead fnv_hash_ids helper
- db: update_folder_path uses SUBSTR instead of replace() to rewrite only the
leading path prefix, preventing corruption when the old folder name also
appears in a subdirectory name
- db: add_user_tag promotes an existing AI-owned tag to user-owned on conflict
instead of DO NOTHING, preventing the tagger from deleting a manually added
tag that coincides with an AI tag
- db/commands: add clear_duplicate_scan_cache / invalidate_duplicate_scan_cache
so deletion removes the stale persisted cache entry; without this a restart
would reload groups containing deleted images
- store: setSimilarScope re-runs loadSimilarByRegion (with saved crop) when the
active collection is Region Search Results, instead of silently switching to
whole-image similarity and discarding the crop
- Backend: new `rename_folder` command (updates display name only, not path)
and `update_folder_path` command (rewrites image paths in DB before reindexing
so thumbnails/embeddings are not regenerated unnecessarily)
- DB: `rename_folder` and `update_folder_path` helpers; `scan_error` column on
folders table to surface indexing failures without silently dropping images
- Store: `renameFolder` and `updateFolderPath` actions
- Sidebar: right-click context menu on each folder row (Reindex, Rename, Locate
Folder, Remove from app); inline rename input on Enter/Escape/blur; missing-
folder recovery banner with Locate/Remove; hover icon buttons (reindex + remove
with Confirm/Cancel) preserved alongside the context menu
BackgroundTasks: dismiss no longer calls clearTaggingJobs; dismissal only
updates local dismissed state
DuplicateFinder: entering the view clears stale groups when the stored scan
scope differs from selectedFolderId; duplicateScanFolderId is recorded on
each scan and cache load so scope is always tracked
Tagger threshold: set_tagger_threshold now sets TAGGER_SESSION_DIRTY so the
worker rebuilds its WdTagger instance and applies the new threshold on the
next batch
Region search global scope: fetch offset+limit+2 candidates before removing
the source image so has_more is accurate across pages
Region search pagination: loadMoreImages now sets loadingImages and checks
galleryRequestToken before appending results, preventing duplicate pages and
stale responses corrupting a new collection
Folder recovery: when index_folder detects a missing path it records the
error in a new folders.scan_error column (ensure_column migration) and emits
done without deleting images, preserving them for recovery. A new
update_folder_path command rewrites image paths in the DB before reindexing
so thumbnails and embeddings are not needlessly regenerated for unchanged
files. The sidebar shows an amber warning icon on affected folders and a
recovery banner with Locate Folder and Remove actions
- upsert_image now resets ai_tagged_at, ai_rating, ai_tagger_model, and
ai_tagger_error to NULL and deletes AI-sourced image_tags when a file
is reindexed after content changes, preventing stale tags/ratings from
the previous version being shown and blocking re-tagging
- get_pending_tagging_jobs_excluding now applies the paused-folder
exclusion in SQL before LIMIT using folder_exclusion_clause, matching
the pattern used by caption and embedding job claimers; previously a
large paused-folder backlog could starve all unpaused folders
- Merged similarRequestToken into galleryRequestToken so all
gallery-producing requests share one generation counter; a late
similarity response can no longer overwrite a subsequently loaded
folder, and vice versa
- Duplicate scanner now emits one DuplicateGroup per distinct full hash
within a sample-hash bucket, preventing disjoint sets (A,A vs B,B)
from being merged and presented as a single deletable group
- Tagger model download now installs the shared ONNX runtime DLLs via
ensure_onnx_runtime so the tagger is ready on a clean install without
requiring the caption model to have been downloaded first
- Caption queue clear now follows the tagging worker pattern: marks
in-flight rows as cancelled and deletes only non-processing rows;
the caption worker skips writing results for cancelled jobs; startup
cleanup removes any leftover cancelled caption rows
- Region search pagination now stores the crop rect in state and uses
find_similar_by_region for subsequent pages instead of falling through
to loadImages which appended unrelated folder results
- Duplicate scanner now runs a full-file hash pass (Phase 3) for large
files (> 64 KB) after the sample-hash shortlist, preventing false
positives from reaching the destructive deletion workflow
- delete_images_from_disk now attempts filesystem removal before touching
the DB; only successfully deleted files have their rows removed,
keeping locked/permission-denied files visible in the library
- AI tag upsert guards user-sourced rows with WHERE source != 'user' so
a tagger re-run cannot silently overwrite manually added tags
- clear_tagging_jobs excludes 'cancelled' rows from the immediate DELETE
so the worker can observe cancellation via is_tagging_job_cancelled
before the rows are cleaned up at next startup
Register the Tauri notification plugin and request notification permission during startup. Send completion notifications for folder scans, embeddings, AI tagging, and duplicate scans, including failure counts where available.
Include tags used by a single image in Explore and invalidate the tag cache after edits so changes appear immediately. Add an accessible, time-limited confirmation step before removing indexed folders from the sidebar.
Move sqlite-vec embedding deletions outside the image transaction to avoid transactional virtual-table failures. Emit a terminal indexing progress event on errors so the frontend reloads partial state and clears its active scan state.
Adds a "Search within image" button to the lightbox that lets the user
draw a crop region on an image and find visually similar results using
that crop's embedding. The crop is embedded in-memory without a temp
file via a new embed_image_crop method on ClipImageEmbedder.
Introduces a folder-scoped cosine search (search_image_ids_by_embedding_in_folder)
to support the current-folder scope option, and wires up the new
find_similar_by_region Tauri command end-to-end from Rust through to
the Zustand store and Lightbox UI.
Introduces tagger.rs for WD-based image tagging with CSV label support.
Adds csv, ureq, and zip dependencies; updates gitignore to exclude Python/JSON files.
- Fix root cause: embedding_source_path() returns Result<PathBuf>, returning
Err for videos without a thumbnail instead of silently falling back to the
raw .mp4 path that CLIP cannot decode
- indexer: split embedding batch into pre-failed (no source) and embeddable
jobs; pre-failed are marked immediately without hitting the CLIP model
- db: retry_failed_embedding_jobs skips videos still without a thumbnail so
they no longer re-fail immediately on retry
- Add get_failed_embedding_images command listing failed files + error per folder
- Gallery: amber warning badge on tiles with embedding_status = 'failed'
- BackgroundTasks: fetch and show failed filenames/errors in expanded panel
- Toolbar: conditional 'Failed Embeddings' amber filter pill shown when any
folder has embedding_failed > 0; filters at DB level via new
embedding_failed_only param on get_images / count_images
- TagCloud: replaced vocabulary/dictionary label system with representative
image thumbnails per cluster; results cached in SQLite by image-id hash
Introduces an Explore view with a tag cloud that clusters image embeddings
using cosine k-means and labels clusters via vocabulary-nearest-neighbour
CLIP matching. Vocabulary embeddings are disk-cached (FNV hash-keyed) to
avoid redundant inference. Enables CUDA for candle dependencies and adds a
build.rs check that surfaces a clear error when the toolkit is missing.
- 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
- add semantic text search with toolbar mode switching and sqlite-vec query support
- improve embedding progress visibility, failure recovery, and similar-image affordances
- add search clearing and keyboard controls for filename vs semantic search modes
- refine background task interactions and gallery/lightbox embedding states
Refs: #4
- add Candle + HF Hub CLIP image embedding pipeline with background embedding worker
- write image embeddings into sqlite-vec and expose similar-image lookup through a new backend command
- surface embedding progress and recovery in the UI, including retries for failed embeddings
- improve gallery/lightbox embedding UX and make similar-image actions directly accessible
Refs: #3, #4
Sets up Tauri v2 + React frontend with SQLite metadata store,
sqlite-vec for CLIP embedding infrastructure, and r2d2 connection
pool replacing the single Arc<Mutex<Connection>>. Indexer now uses
rayon for parallel file metadata collection.