- New bulkAction store: tracks active/label/detail state for the pill
and a Map<uid, pending|done|error> for per-tile overlays
- Extract StatusPill.svelte from IndexerStatusPill (generic active/label/detail
props); IndexerStatusPill becomes a one-line wrapper
- +layout.svelte: render a second StatusPill driven by bulkAction store,
alongside the indexer pill in the AnimatedMule header
- BulkActionBar: extend withBusy with optional BulkConfig (ids/label/doneLabel);
pending tiles dim + spinner on start, green checkmark flashes for 400ms before
cache invalidation removes them; red overlay on error, auto-clears after 2s
- onApprove/batchEdit: wire onProgress callback to setDetail so the pill shows
the filename currently being processed during fan-out keep operations
- batch.ts: add completedId as third arg to onProgress (backwards-compatible)
- PhotoTile: derive bulkState from store; pending/done/error overlays sit above
the selection tint; hover-video guarded against pending tiles
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Subscribes to PhotoPrism's /api/v1/ws channel on login and surfaces
index.indexing / index.updating / index.completed events as a small
status pill in the header (next to the AnimatedMule wordmark).
- Shows "Indexing" + the current filename (basename, monospace) during
the scan pass, "Finalizing — <step>" during faces/counts/folders/
purge/moments, and "Indexed in Ns" for ~4s after completion before
fading.
- Per-file events arrive many per second on large libraries — throttled
to 150 ms with a trailing-edge update so the pill stays calm and
always lands on the most recent filename. Step and completion events
bypass the throttle.
- Filename slot is fixed at 24ch so the pill width stays constant
through a run (no horizontal jitter as filenames change length); the
full path is exposed via the parent's `title` for hover.
- WS reconnect uses exponential backoff capped at 30 s, and the store
tears down cleanly on logout so we don't leak sockets across
identities.
Defensive parsing throughout: PhotoPrism's WS protocol isn't a stable
contract, so unknown event shapes are ignored rather than thrown —
worst-case the pill stays idle.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>