feat: manual albums + gallery multi-select with bulk actions

Albums (manual collections):
- New albums/album_images tables with FK cascades; DB functions and Tauri
  commands for create/rename/delete/delete-many/reorder/list, add/remove
  images, and paginated get_album_images.
- Distinct sidebar "ALBUMS" section with cover thumbnails, create/rename/
  delete, and a Manage multi-select mode for bulk album deletion.
- Album view reuses the gallery grid (activeView "album" + selectedAlbumId);
  spans folders; add from the bulk bar or the lightbox, remove from within.

Gallery multi-select + bulk actions:
- gallerySelectedIds selection model with a top-left corner checkbox that
  reveals on corner hover; click-to-toggle in selection mode, double-click
  to open.
- Floating BulkActionBar: tag (inline autocomplete popover), rating,
  favorite, add-to-album, and a delete with an explicit "from disk"
  confirmation. Batch commands bulk_update_details/bulk_add_tags/
  bulk_remove_tag.

Also:
- Duplicate Finder delete now requires confirmation with clear "from disk"
  wording (was single-click fire-and-forget).
- CPU/CUDA build-variant badge in Settings (get_build_variant).
- Rating/favorite no longer re-sorts derived collections (similar/region/
  semantic/tag/album results); single and bulk paths replace in place there.
- Album-aware indexed-images/media-updated handlers so thumbnails paint and
  newly-indexed files don't leak into an album view.
- CHANGELOG updated.
This commit is contained in:
2026-06-27 15:23:54 +01:00
parent 55cd3b5aa7
commit 6bef90b7fb
15 changed files with 1901 additions and 44 deletions
+2
View File
@@ -24,6 +24,7 @@ export default function App() {
const loadImages = useGalleryStore((state) => state.loadImages);
const loadCaptionModelStatus = useGalleryStore((state) => state.loadCaptionModelStatus);
const loadDuplicateScanCache = useGalleryStore((state) => state.loadDuplicateScanCache);
const loadAlbums = useGalleryStore((state) => state.loadAlbums);
const loadMutedFolderIds = useGalleryStore((state) => state.loadMutedFolderIds);
const loadNotificationsPaused = useGalleryStore((state) => state.loadNotificationsPaused);
const subscribeToProgress = useGalleryStore((state) => state.subscribeToProgress);
@@ -51,6 +52,7 @@ export default function App() {
void loadBackgroundJobProgress();
void loadCaptionModelStatus();
void loadDuplicateScanCache();
void loadAlbums();
return loadImages(true);
});
let unlisten: (() => void) | undefined;