From 5b8c7e09bfd9d29cfed0a2d353bcfebed9963d27 Mon Sep 17 00:00:00 2001 From: Lincoln Stein Date: Wed, 20 May 2026 17:26:36 -0400 Subject: [PATCH] fix(albums): index a freshly-added album when the dir has a stale photomap_index MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the directory chosen for a new album already contained a ``photomap_index/embeddings.npz`` from a prior session (e.g. the user recreated an album over the same folder), creating the album appeared to succeed but the indexing UI got stuck at "Index updated …" in orange with 0% completion that never advanced. The new album silently inherited the old index. Two compounding bugs: * ``startAutoIndexing`` — the only entry point that runs after ``addAlbum`` — called ``showProgressUI`` and nothing else. It assumed indexing was already running, which was only true when ``getIndexMetadata`` failed and fired the ``albumIndexError`` event. With a stale ``embeddings.npz`` in place, the metadata fetch succeeds, no error event fires, and indexing was never started. Now ``startAutoIndexing`` scrolls into view and calls ``startIndexing`` directly. ``startIndexing`` is already idempotent — its backend progress probe attaches to any in-flight run instead of double-POSTing ``/update_index_async/``. * ``updateAlbumCardIndexStatus`` set ``status.style.color = "green"`` inline but didn't reset ``className``. CSS for ``.index-status.indexing`` carries ``!important`` (so the in-progress state's color wins over inline updates from ``updateProgressStatus``), which meant a stale ``.indexing`` class left behind by ``showProgressUI`` overrode the inline green. Resetting className to the bare ``index-status`` fixes the color and makes future state classes win cleanly. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../static/javascript/album-manager.js | 39 ++++++++++++++++--- 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/photomap/frontend/static/javascript/album-manager.js b/photomap/frontend/static/javascript/album-manager.js index 338046c..b1956ff 100644 --- a/photomap/frontend/static/javascript/album-manager.js +++ b/photomap/frontend/static/javascript/album-manager.js @@ -682,15 +682,21 @@ export class AlbumManager { timeStyle: "short", }); const fileCount = metadata.filename_count; + // Reset the className so any leftover ``.indexing`` (orange, + // !important in CSS) from a prior showProgressUI doesn't override + // the inline color set below. + status.className = "index-status"; status.textContent = `Index updated ${modDate} (${fileCount} images)`; status.style.color = "green"; createBtn.textContent = "Update Index"; } else { + status.className = "index-status"; status.textContent = "No index present"; status.style.color = "red"; createBtn.textContent = "Create Index"; } } catch { + status.className = "index-status"; status.textContent = "No index present"; status.style.color = "red"; createBtn.textContent = "Create Index"; @@ -821,13 +827,34 @@ export class AlbumManager { (card) => card.dataset.albumKey === albumKey ); - if (albumCard) { - // Don't start indexing again - it's already running - // Just show the progress UI and let the existing polling handle updates - setTimeout(() => { - this.showProgressUI(albumCard); // This will scroll into view - }, AlbumManager.AUTO_INDEXING_DELAY); + if (!albumCard) { + return; } + + // Kick off indexing for the freshly-added album. We can't rely on the + // ``albumIndexError`` event from ``getIndexMetadata`` to start it + // because the chosen image directory may already contain a stale + // ``photomap_index/embeddings.npz`` from a prior session — in that + // case the metadata fetch succeeds, no error event fires, and without + // this call the new album would just inherit the old index (the bug + // surfaced as "Index updated …" in orange with 0% completion that + // never advances). + // + // ``startIndexing`` itself is idempotent — its backend-progress probe + // attaches to an in-flight indexing run instead of starting a second + // one, so the no-prior-index path (where the event listener also + // calls ``startIndexing``) still only triggers one backend POST. + setTimeout(() => { + const indexingSection = albumCard.querySelector(".indexing-section"); + if (indexingSection) { + indexingSection.scrollIntoView({ + behavior: "smooth", + block: "center", + inline: "nearest", + }); + } + this.startIndexing(albumKey, albumCard); + }, AlbumManager.AUTO_INDEXING_DELAY); } async deleteAlbum(albumKey) {