This repository has been archived on 2026-05-31. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
imagerepo/app/static/js/gallery-series-editor.js
T
bvandeusen 3944605d33 feat(bulk): consensus ML suggestions + frontend polish
Bulk editor now loads consensus ML suggestions across the current
selection via a new POST /api/suggestions/bulk endpoint, powered by
get_bulk_suggestions() in tag_suggestions.py. A tag is surfaced only if
it was suggested for or already applied to >= 80% of the selection;
coverage counts include images that already have the tag so a near-
universal tag isn't penalized for dropping out of suggestion lists.
Accept-only chips (no reject) match the rest of the suggestions UX.

Frontend polish in the same commit:
- Showcase session dedup (exclude seen ids, reset on second lap) and
  aspect-ratio-aware placeholder heights for better column balancing
- Modal touch-swipe navigation on the image wrapper, auto-focus of the
  tag input on desktop (>=768px), and autocomplete flip-up when the
  dropdown would cover the Suggestions section
- Settings template inline styles replaced with utility classes
- Inline series-editor script extracted to gallery-series-editor.js

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-20 18:39:47 -04:00

84 lines
3.1 KiB
JavaScript

// /app/static/js/gallery-series-editor.js
// Series editor wired into the bulk-editor panel on series-filtered gallery views.
// No-ops when the page has no series context.
document.addEventListener('DOMContentLoaded', () => {
const seriesInfo = window.galleryState && window.galleryState.seriesInfo;
if (!seriesInfo) return;
const startPageInput = document.getElementById('startPageInput');
const bulkAddToSeriesBtn = document.getElementById('bulkAddToSeriesBtn');
const feedbackEl = document.getElementById('seriesEditorFeedback');
if (!startPageInput || !bulkAddToSeriesBtn || !feedbackEl) return;
const seriesTagId = seriesInfo.tag_id;
function updateAddButtonState() {
const selectedCount = window.selectedImages ? window.selectedImages.size : 0;
bulkAddToSeriesBtn.disabled = selectedCount === 0;
bulkAddToSeriesBtn.textContent = selectedCount > 0
? `Add ${selectedCount} to Series`
: 'Add to Series';
}
document.addEventListener('selectionChanged', updateAddButtonState);
bulkAddToSeriesBtn.addEventListener('click', async () => {
if (!window.selectedImages || window.selectedImages.size === 0) return;
const imageIds = window.selectionOrder ? [...window.selectionOrder] : Array.from(window.selectedImages);
const startPage = parseInt(startPageInput.value) || 1;
bulkAddToSeriesBtn.disabled = true;
bulkAddToSeriesBtn.textContent = 'Adding...';
feedbackEl.innerHTML = '';
try {
const r = await fetch(`/api/series/${seriesTagId}/pages/bulk-add`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ image_ids: imageIds, start_page: startPage })
});
const data = await r.json();
if (data.ok) {
let msg = `Added ${data.added.length} image(s) to series.`;
if (data.skipped.length > 0) {
msg += ` Skipped ${data.skipped.length}.`;
}
feedbackEl.innerHTML = `<div class="feedback-success">${msg}</div>`;
if (data.added.length > 0) {
const lastPage = data.added[data.added.length - 1].page_number;
startPageInput.value = lastPage + 1;
}
if (window.clearSelection) {
window.clearSelection();
}
const pageCountEl = document.getElementById('seriesPageCount');
if (pageCountEl) {
const currentCount = parseInt(pageCountEl.textContent) || 0;
pageCountEl.textContent = `${currentCount + data.added.length} pages`;
}
// "Read Series" button only renders when page_count > 0. If this was the first
// batch of pages added, reload so the button appears.
const readBtn = document.querySelector('.series-header-actions .primary-btn');
if (!readBtn && data.added.length > 0) {
location.reload();
}
} else {
feedbackEl.innerHTML = `<div class="feedback-error">${data.error}</div>`;
}
} catch (e) {
feedbackEl.innerHTML = `<div class="feedback-error">Error: ${e.message}</div>`;
} finally {
updateAddButtonState();
}
});
updateAddButtonState();
});