e76e94a635
When the user clicks the × on a tag pill, the tag is removed from the image and a small '/Removed 'X' — [⊘ Blocklist] [×]/' control surfaces below the tag input. One click on ⊘ Blocklist escalates the removal to a global blocklist add (which also enqueues the retroactive sweep Celery task, removing the tag from every other image that has it). Fades after 8s, hides on image switch or modal close. Targets the composition-descriptor case: WD14 tags like '1girl', 'breasts', 'simple background' that show up on many images. Without this, after removing the pill the user has to navigate to Settings → Maintenance → Suggestion blocklist to add the name, or wait for the next suggestion chip to appear and use its ⊘. Now it's one click at the point of removal. Misclick recovery unchanged: the pill × alone only rejects per-image. The inline offer is opt-in; ignore it or click × to dismiss and the removal stays scoped to the current image only. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
96 lines
4.8 KiB
HTML
96 lines
4.8 KiB
HTML
<!-- /app/templates/_gallery_modal.html -->
|
||
<div id="imageModal" class="modal">
|
||
<div class="modal-content">
|
||
<button id="modalClose" class="modal-close-btn" title="Close (ESC)">×</button>
|
||
<button id="modalPrev" class="modal-button modal-prev">‹</button>
|
||
<button id="modalNext" class="modal-button modal-next">›</button>
|
||
<span class="modal-close-hint">ESC to close</span>
|
||
|
||
<div class="modal-body">
|
||
<div id="modalImageWrapper" class="modal-image-wrapper">
|
||
<img id="modalImage" src="" alt="Full View">
|
||
</div>
|
||
|
||
<div id="modalTagEditor" class="tag-editor modal-sidebar" data-image-id="">
|
||
|
||
<!-- SECTION: Series -->
|
||
<div class="modal-sidebar-section modal-series-section">
|
||
<!-- Shown when image IS in a series -->
|
||
<div id="modalSeriesInfo" class="modal-series-info" style="display: none;">
|
||
<div class="series-info-row">
|
||
<span class="series-info-label">📚</span>
|
||
<span class="series-info-label">Page</span>
|
||
<input type="number" id="modalPageNumber" min="1" class="series-page-input">
|
||
<span class="series-info-label">of</span>
|
||
<a id="modalSeriesName" href="#" class="series-info-link"></a>
|
||
<button id="updatePageBtn" class="btn-small" title="Update page number">Save</button>
|
||
<span id="pageUpdateStatus" class="tag-feedback"></span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Shown when image is NOT in a series -->
|
||
<div id="modalAddToSeries" class="modal-add-to-series" style="display: none;">
|
||
<button id="addToSeriesToggle" class="modal-series-toggle">
|
||
<span>📚 Add to Series</span>
|
||
<span class="toggle-arrow">›</span>
|
||
</button>
|
||
<div id="addToSeriesBody" class="add-series-body" style="display: none;">
|
||
<div class="add-series-controls">
|
||
<select id="seriesSelect" class="series-select">
|
||
<option value="">Select series...</option>
|
||
</select>
|
||
<input type="number" id="addSeriesPageNumber" min="1" value="1" class="series-page-input" placeholder="#">
|
||
<button id="addToSeriesBtn" class="btn-small" disabled>Add</button>
|
||
</div>
|
||
<span id="addToSeriesStatus" class="tag-feedback"></span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- SECTION: Provenance (system-generated tags: post; future: source, archive) -->
|
||
<div class="modal-sidebar-section modal-provenance-section" id="modalProvenanceSection" style="display: none;">
|
||
<div id="modalProvenanceList" class="provenance-list"></div>
|
||
</div>
|
||
|
||
<!-- SECTION: Tags -->
|
||
<div class="modal-sidebar-section modal-tags-section">
|
||
<div id="modalTagList" class="tags"></div>
|
||
<form id="modalTagForm" class="tag-form" autocomplete="off">
|
||
<div class="tag-form-wrapper">
|
||
<input type="text" id="tagInput" name="name" placeholder="Add tag..." autocomplete="off">
|
||
</div>
|
||
<button type="submit">Add</button>
|
||
</form>
|
||
<div id="fandomPickerWrapper" class="fandom-picker-wrapper" aria-hidden="true">
|
||
<div class="fandom-picker-input-row">
|
||
<input type="text" id="fandomPickerInput" class="fandom-picker-input"
|
||
placeholder="Fandom (optional)" autocomplete="off">
|
||
<button type="button" id="fandomPickerClear" class="btn-small"
|
||
aria-label="Clear fandom" title="Clear fandom">×</button>
|
||
</div>
|
||
<div id="fandomPickerAutocomplete"
|
||
class="tag-autocomplete tag-autocomplete-inline" aria-live="polite"></div>
|
||
</div>
|
||
<div id="tagAutocomplete" class="tag-autocomplete tag-autocomplete-inline" aria-live="polite"></div>
|
||
<span id="tagActionFeedback" class="tag-feedback tag-feedback-inline"></span>
|
||
<div id="blocklistOffer" class="blocklist-offer" aria-hidden="true">
|
||
<span class="blocklist-offer-label"></span>
|
||
<button type="button" class="btn-small blocklist-offer-btn">⊘ Blocklist</button>
|
||
<button type="button" class="btn-small blocklist-offer-dismiss" aria-label="Dismiss">×</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- SECTION: Suggestions (ML-backed, modal-only) -->
|
||
<div class="modal-sidebar-section modal-suggestions-section" id="modalSuggestionsSection" style="display: none;">
|
||
<div class="section-header">
|
||
<span class="section-title">Suggestions</span>
|
||
<button type="button" class="suggestions-refresh" id="suggestionsRefreshBtn" aria-label="Refresh suggestions">↻</button>
|
||
</div>
|
||
<div id="modalSuggestionsList" class="suggestions-list"></div>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|