Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions photomap/frontend/static/css/umap-floating-window.css
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,16 @@
font-size: 0.85em;
}

#umapClickBehaviorContainer {
font-size: 0.85em;
}

#umapClickBehaviorContainer label {
cursor: pointer;
user-select: none;
}

#umapClickBehaviorContainer input[type="radio"] {
cursor: pointer;
}

17 changes: 17 additions & 0 deletions photomap/frontend/static/javascript/state.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export const state = {
umapShowLandmarks: true, // Show landmarks in UMAP
umapShowHoverThumbnails: true, // Show hover thumbnails in UMAP
umapExitFullscreenOnSelection: true, // Exit fullscreen when cluster is selected
umapClickSelectsCluster: true, // Whether click selects cluster or single image
};

document.addEventListener("DOMContentLoaded", async function () {
Expand Down Expand Up @@ -123,6 +124,11 @@ export async function restoreFromLocalStorage() {
if (storedUmapExitFullscreenOnSelection !== null) {
state.umapExitFullscreenOnSelection = storedUmapExitFullscreenOnSelection === "true";
}

const storedUmapClickSelectsCluster = localStorage.getItem("umapClickSelectsCluster");
if (storedUmapClickSelectsCluster !== null) {
state.umapClickSelectsCluster = storedUmapClickSelectsCluster === "true";
}
}

// Save state to local storage
Expand All @@ -142,6 +148,7 @@ export function saveSettingsToLocalStorage() {
localStorage.setItem("umapShowLandmarks", state.umapShowLandmarks ? "true" : "false");
localStorage.setItem("umapShowHoverThumbnails", state.umapShowHoverThumbnails ? "true" : "false");
localStorage.setItem("umapExitFullscreenOnSelection", state.umapExitFullscreenOnSelection ? "true" : "false");
localStorage.setItem("umapClickSelectsCluster", state.umapClickSelectsCluster ? "true" : "false");
}

export async function setAlbum(newAlbumKey, force = false) {
Expand Down Expand Up @@ -249,3 +256,13 @@ export function setUmapExitFullscreenOnSelection(exitFullscreenOnSelection) {
);
}
}

export function setUmapClickSelectsCluster(clickSelectsCluster) {
if (state.umapClickSelectsCluster !== clickSelectsCluster) {
state.umapClickSelectsCluster = clickSelectsCluster;
saveSettingsToLocalStorage();
window.dispatchEvent(
new CustomEvent("settingsUpdated", { detail: { umapClickSelectsCluster: clickSelectsCluster } })
);
}
}
55 changes: 51 additions & 4 deletions photomap/frontend/static/javascript/umap.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
import { albumManager } from "./album-manager.js";
import { exitSearchMode } from "./search-ui.js";
import { getImagePath, setSearchResults } from "./search.js";
import { getCurrentSlideIndex } from "./slide-state.js";
import { setUmapExitFullscreenOnSelection, setUmapShowHoverThumbnails, setUmapShowLandmarks, state } from "./state.js";
import { getCurrentSlideIndex, slideState } from "./slide-state.js";
import { setUmapExitFullscreenOnSelection, setUmapShowHoverThumbnails, setUmapShowLandmarks, setUmapClickSelectsCluster, state } from "./state.js";
import { debounce, getPercentile, isColorLight } from "./utils.js";
import { CLUSTER_PALETTE, getClusterColorFromPoints } from "./cluster-utils.js";

Expand Down Expand Up @@ -392,14 +392,24 @@ export async function fetchUmapData() {
// Use the landmark placement algorithm to get the landmark point
const landmarkPoint = getLandmarkForCluster(clusterPoints);
if (landmarkPoint) {
await handleClusterClick(landmarkPoint.index);
// Check if we should select cluster or image
if (state.umapClickSelectsCluster) {
await handleClusterClick(landmarkPoint.index);
} else {
await handleImageClick(landmarkPoint.index);
}
}
} else {
const pt = data.points[0];
const traceName = pt.data?.name;
// Main points or highlighted points behave the same
if (traceName === "All Points" || traceName === "HighlightedPoints") {
await handleClusterClick(pt.customdata);
// Check if we should select cluster or image
if (state.umapClickSelectsCluster) {
await handleClusterClick(pt.customdata);
} else {
await handleImageClick(pt.customdata);
}
}
}
});
Expand Down Expand Up @@ -619,6 +629,31 @@ window.addEventListener("stateReady", () => {
// Update enabled state based on fullscreen mode
updateExitFullscreenCheckboxState();
}

// Click behavior radio buttons - initialize from state
const clickSelectsClusterRadio = document.getElementById("umapClickSelectsClusterRadio");
const clickSelectsImageRadio = document.getElementById("umapClickSelectsImageRadio");
if (clickSelectsClusterRadio && clickSelectsImageRadio) {
// Set initial state
if (state.umapClickSelectsCluster) {
clickSelectsClusterRadio.checked = true;
} else {
clickSelectsImageRadio.checked = true;
}

// Add event listeners
clickSelectsClusterRadio.addEventListener("change", (e) => {
if (e.target.checked) {
setUmapClickSelectsCluster(true);
}
});

clickSelectsImageRadio.addEventListener("change", (e) => {
if (e.target.checked) {
setUmapClickSelectsCluster(false);
}
});
}
});

// Helper function to update the "Exit fullscreen on selection" checkbox state
Expand Down Expand Up @@ -1251,6 +1286,18 @@ async function handleClusterClick(clickedIndex) {
setSearchResults(clusterMembers, "cluster");
}

// Handle single image selection (navigate to clicked image)
async function handleImageClick(clickedIndex) {
const clickedPoint = points.find((p) => p.index === clickedIndex);
if (!clickedPoint) return;

// Clear any existing search selection
exitSearchMode();

// Navigate directly to the clicked image without entering search mode
slideState.navigateToIndex(clickedIndex, false);
}

// -------------------- Window Management --------------------

// --- Show/Hide UMAP Window ---
Expand Down
29 changes: 25 additions & 4 deletions photomap/frontend/templates/modules/umap-floating-window.html
Original file line number Diff line number Diff line change
Expand Up @@ -137,11 +137,11 @@
gap: 24px;
justify-content: center;
align-items: flex-start;
margin-top: 12px;
margin-top: 4px;
"
>
<!-- EPS Spinner Container -->
<div id="umapEpsContainer" style="display: none; padding: 8px">
<div id="umapEpsContainer" style="display: none; padding: 0px">
<label for="umapEpsSpinner">Cluster Strength</label>
<input
type="number"
Expand All @@ -152,6 +152,29 @@
value="0.1"
style="width: 60px"
/>

<!-- Click Behavior Radio Buttons -->
<div id="umapClickBehaviorContainer" style="margin-top: 12px; display: flex; flex-direction: column; gap: 4px;">
<label style="display: flex; align-items: center; gap: 6px; font-size: 0.9em;">
<input
type="radio"
name="umapClickBehavior"
id="umapClickSelectsClusterRadio"
value="cluster"
checked
/>
Click selects cluster
</label>
<label style="display: flex; align-items: center; gap: 6px; font-size: 0.9em;">
<input
type="radio"
name="umapClickBehavior"
id="umapClickSelectsImageRadio"
value="image"
/>
Click selects image
</label>
</div>
</div>

<!-- Colorization Mode Controls (two columns) -->
Expand All @@ -161,8 +184,6 @@
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
margin-bottom: 12px;
padding-bottom: 20px;
"
>
<label style="display: flex; align-items: center; gap: 8px">
Expand Down