feat(library): reindex button, live grid, archive-reappear fix, bigger carets

- Add one-click "reindex new files" button to the Library sidebar header
  (RefreshCw, calls startIndex rescan:false), spins + disables while active.
- Refresh the photos grid from the indexer WS stream (throttled during the
  scan + once on completion) so newly indexed files appear live.
- Fix archived photos flashing back into the grid when archiving others:
  drop the per-action settle-driven clearRemoved and reconcile removedIds
  against the actual cache instead (clears an id only once it's gone from
  the deduped pages). Covers archive, delete, and bulk-bar removals.
- Replace the tiny Unicode caret triangles with a 16px Lucide ChevronRight
  that rotates 90deg on expand, across folder tree rows, root, Tags, Review.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-21 23:48:18 +02:00
parent 669e5fde33
commit 6d9b236ef6
6 changed files with 110 additions and 31 deletions

View File

@@ -37,7 +37,7 @@
setFocused,
setOrder,
} from "$lib/stores/selection.svelte";
import { removedIds } from "$lib/stores/bulkAction.svelte";
import { removedIds, clearRemoved } from "$lib/stores/bulkAction.svelte";
import {
openPreview,
setRightSidebarWidth,
@@ -283,6 +283,20 @@
}
const pageCount = $derived(photosQuery.data?.pages.length ?? 0);
// Reconcile the optimistic-removal overlay against the actual cache.
// `removedIds` hides a tile while its photo is still present in a loaded
// page; we drop an id from the set only once it has genuinely left the
// freshly-deduped cache (i.e. every page that held it has refetched
// without it). Driving the clear from the data — rather than from each
// archive action's invalidation promise — removes the race where settling
// one action's refetch un-hid a photo that other, still-stale pages
// continued to carry, making archived tiles flash back into the grid.
$effect(() => {
const present = new Set(dedupedAll.map((p) => p.UID));
const gone = [...removedIds].filter((id) => !present.has(id));
if (gone.length) clearRemoved(gone);
});
$effect(() => {
setOrder(photos.map((p) => p.UID));
});