diff --git a/photomap/frontend/static/css/umap-floating-window.css b/photomap/frontend/static/css/umap-floating-window.css index a61ca417..37b0d20a 100644 --- a/photomap/frontend/static/css/umap-floating-window.css +++ b/photomap/frontend/static/css/umap-floating-window.css @@ -61,6 +61,40 @@ font-size: 1em; } +#semanticMapAlbumSelect { + cursor: pointer; + background: transparent; + color: #fff; + font-weight: bold; + font-size: 1em; + font-family: inherit; + border: 1px solid transparent; + border-radius: 4px; + padding: 2px 22px 2px 6px; + margin: 0; + max-width: 60%; + text-overflow: ellipsis; + appearance: none; + -webkit-appearance: none; + -moz-appearance: none; + background-image: url("data:image/svg+xml;utf8,"); + background-repeat: no-repeat; + background-position: right 6px center; +} + +#semanticMapAlbumSelect:hover, +#semanticMapAlbumSelect:focus { + background-color: rgba(255, 255, 255, 0.12); + border-color: rgba(255, 255, 255, 0.25); + outline: none; +} + +#semanticMapAlbumSelect option { + background: #1e1e28; + color: #fff; + font-weight: normal; +} + /* ===== UMAP THUMBNAIL STYLES ===== */ .umap-thumbnail { position: fixed; diff --git a/photomap/frontend/static/javascript/settings.js b/photomap/frontend/static/javascript/settings.js index 49b48084..bf93b854 100644 --- a/photomap/frontend/static/javascript/settings.js +++ b/photomap/frontend/static/javascript/settings.js @@ -278,7 +278,6 @@ function setupAlbumSelector() { const newAlbum = this.value; if (newAlbum !== state.album) { switchAlbum(newAlbum); - closeSettingsModal(); } }); } diff --git a/photomap/frontend/static/javascript/umap.js b/photomap/frontend/static/javascript/umap.js index dabe58a8..691251bf 100644 --- a/photomap/frontend/static/javascript/umap.js +++ b/photomap/frontend/static/javascript/umap.js @@ -11,6 +11,7 @@ import { } from "./cluster-utils.js"; import { exitSearchMode } from "./search-ui.js"; import { getImagePath, setSearchResults } from "./search.js"; +import { switchAlbum } from "./settings.js"; import { getCurrentSlideIndex, slideState } from "./slide-state.js"; import { setUmapClickSelectsCluster, @@ -113,20 +114,6 @@ document.getElementById("umapEpsSpinner").oninput = async () => { }, 1000); }; -// --- Caching Current Album --- -let cachedAlbum = null; -let cachedAlbumName = null; - -async function getCachedAlbum() { - const currentAlbumName = state.album; - if (cachedAlbum && cachedAlbumName === currentAlbumName) { - return cachedAlbum; - } - cachedAlbum = await albumManager.getCurrentAlbum(); - cachedAlbumName = currentAlbumName; - return cachedAlbum; -} - // --- Main UMAP Data Fetch and Plot --- export async function fetchUmapData() { if (mapExists && !state.dataChanged) { @@ -873,7 +860,7 @@ async function initializeUmapWindow() { } state.dataChanged = true; lastUnshadedSize = "medium"; // Reset to medium on album change - setSemanticMapTitle(); + populateSemanticMapAlbumSelect(); fetchUmapData(); toggleFullscreen(true); // Force fullscreen on album change } @@ -1831,19 +1818,56 @@ window.addEventListener("slideshowStartRequested", () => { toggleUmapWindow(false); }); -// Helper to set the semantic map window title to the album display name -function setSemanticMapTitle() { - const titleSpan = document.getElementById("semanticMapTitle"); - if (!titleSpan) { +// Populate the album dropdown in the semantic-map titlebar and select the +// current album. The change listener is attached once in +// setupSemanticMapAlbumSelect(); this only refreshes the options. +async function populateSemanticMapAlbumSelect() { + const select = document.getElementById("semanticMapAlbumSelect"); + if (!select) { return; } - getCachedAlbum().then((album) => { - if (album && album.name) { - titleSpan.textContent = album.name; - } else if (state.album) { - titleSpan.textContent = state.album; - } else { - titleSpan.textContent = "Semantic Map"; + let albums; + try { + albums = await albumManager.fetchAvailableAlbums(); + } catch (err) { + console.error("Failed to load albums for semantic map dropdown:", err); + return; + } + select.innerHTML = ""; + if (!albums || albums.length === 0) { + const option = document.createElement("option"); + option.value = ""; + option.textContent = "Semantic Map"; + option.disabled = true; + option.selected = true; + select.appendChild(option); + return; + } + for (const album of albums) { + const option = document.createElement("option"); + option.value = album.key; + option.textContent = album.name; + select.appendChild(option); + } + if (state.album) { + select.value = state.album; + } +} + +function setupSemanticMapAlbumSelect() { + const select = document.getElementById("semanticMapAlbumSelect"); + if (!select || select.dataset.listenerAttached === "true") { + return; + } + select.dataset.listenerAttached = "true"; + // Block titlebar drag/double-click from hijacking native dropdown behavior. + ["mousedown", "touchstart", "click", "dblclick"].forEach((evt) => { + select.addEventListener(evt, (e) => e.stopPropagation()); + }); + select.addEventListener("change", () => { + const newAlbum = select.value; + if (newAlbum && newAlbum !== state.album) { + switchAlbum(newAlbum); } }); } @@ -1854,7 +1878,11 @@ export function isUmapFullscreen() { } // Set initial title on DOMContentLoaded -document.addEventListener("DOMContentLoaded", initializeUmapWindow); +document.addEventListener("DOMContentLoaded", () => { + setupSemanticMapAlbumSelect(); + populateSemanticMapAlbumSelect(); + initializeUmapWindow(); +}); window.addEventListener("albumChanged", initializeUmapWindow); // ======================================================== diff --git a/photomap/frontend/templates/modules/umap-floating-window.html b/photomap/frontend/templates/modules/umap-floating-window.html index 3b036eb5..ebac00c7 100644 --- a/photomap/frontend/templates/modules/umap-floating-window.html +++ b/photomap/frontend/templates/modules/umap-floating-window.html @@ -4,7 +4,7 @@
- Semantic Map +