fix: address all 6 P2 review issues from PR #8 round 4

- 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
This commit is contained in:
2026-06-07 09:45:57 +01:00
parent 4e5923ba84
commit 183cdff6b1
5 changed files with 61 additions and 21 deletions
+9 -3
View File
@@ -1126,10 +1126,14 @@ export const useGalleryStore = create<GalleryState>((set, get) => ({
setSimilarScope: (similarScope) => {
set({ similarScope });
const { similarSourceImageId, similarSourceFolderId, selectedFolderId } = get();
const { similarSourceImageId, similarSourceFolderId, selectedFolderId, collectionTitle, similarCrop } = get();
if (similarSourceImageId === null) return;
const folderId = similarScope === "current_folder" ? (similarSourceFolderId ?? selectedFolderId) : null;
void get().loadSimilarImages(similarSourceImageId, folderId, true, similarSourceFolderId);
if (collectionTitle === "Region Search Results" && similarCrop !== null) {
void get().loadSimilarByRegion(similarSourceImageId, similarCrop, folderId, similarSourceFolderId);
} else {
void get().loadSimilarImages(similarSourceImageId, folderId, true, similarSourceFolderId);
}
},
suggestImageTags: async (imageId) => {
@@ -1498,7 +1502,7 @@ export const useGalleryStore = create<GalleryState>((set, get) => ({
clearDuplicateSelection: () => set({ duplicateSelectedIds: new Set() }),
deleteSelectedDuplicates: async () => {
const { duplicateSelectedIds } = get();
const { duplicateSelectedIds, duplicateScanFolderId } = get();
const ids = Array.from(duplicateSelectedIds);
if (ids.length === 0) return 0;
const deleted = await invoke<number>("delete_images_from_disk", { params: { image_ids: ids } });
@@ -1509,6 +1513,8 @@ export const useGalleryStore = create<GalleryState>((set, get) => ({
.map((g) => ({ ...g, images: g.images.filter((img) => !duplicateSelectedIds.has(img.id)) }))
.filter((g) => g.images.length > 1),
}));
// Invalidate the persisted cache so a restart doesn't reload stale entries
await invoke("invalidate_duplicate_scan_cache", { folderId: duplicateScanFolderId ?? null });
return deleted;
},