fix(model-manager): replace window.confirm with in-app modal
Browsers suppress native confirm() dialogs after repeated use (the "prevent this page from creating additional dialogs" checkbox), which silently broke deletes. Add a promise-based in-app confirmation modal and use it for gallery photo and installed-model deletes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -32,6 +32,33 @@ function fmtDate(epoch) {
|
|||||||
return new Date(epoch * 1000).toLocaleString();
|
return new Date(epoch * 1000).toLocaleString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// In-app confirmation dialog (replaces window.confirm, which browsers can
|
||||||
|
// suppress after repeated use). Returns a Promise<boolean>.
|
||||||
|
function confirmDialog(message, okLabel = "Delete") {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const overlay = $("#confirmModal");
|
||||||
|
$("#confirmMsg").textContent = message;
|
||||||
|
$("#confirmOk").textContent = okLabel;
|
||||||
|
overlay.hidden = false;
|
||||||
|
const done = (val) => {
|
||||||
|
overlay.hidden = true;
|
||||||
|
$("#confirmOk").onclick = null;
|
||||||
|
$("#confirmCancel").onclick = null;
|
||||||
|
overlay.onclick = null;
|
||||||
|
document.removeEventListener("keydown", onKey);
|
||||||
|
resolve(val);
|
||||||
|
};
|
||||||
|
function onKey(e) {
|
||||||
|
if (e.key === "Escape") done(false);
|
||||||
|
if (e.key === "Enter") done(true);
|
||||||
|
}
|
||||||
|
$("#confirmOk").onclick = () => done(true);
|
||||||
|
$("#confirmCancel").onclick = () => done(false);
|
||||||
|
overlay.onclick = (e) => { if (e.target === overlay) done(false); };
|
||||||
|
document.addEventListener("keydown", onKey);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// ---- navigation -----------------------------------------------------------
|
// ---- navigation -----------------------------------------------------------
|
||||||
|
|
||||||
function showView(name) {
|
function showView(name) {
|
||||||
@@ -118,7 +145,7 @@ function renderModels(models) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function deleteModel(m) {
|
async function deleteModel(m) {
|
||||||
if (!confirm(`Delete ${m.name}?`)) return;
|
if (!(await confirmDialog(`Delete ${m.name}?`))) return;
|
||||||
try {
|
try {
|
||||||
await api("/api/models", {
|
await api("/api/models", {
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
@@ -307,7 +334,7 @@ function renderPhotoCard(p) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function deletePhoto(p, card) {
|
async function deletePhoto(p, card) {
|
||||||
if (!confirm(`Permanently delete ${p.name}?`)) return;
|
if (!(await confirmDialog(`Permanently delete ${p.name}?`))) return;
|
||||||
try {
|
try {
|
||||||
await api("/api/gallery", {
|
await api("/api/gallery", {
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
|
|||||||
@@ -194,6 +194,17 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- In-app confirmation dialog (replaces native window.confirm) -->
|
||||||
|
<div id="confirmModal" class="modal-overlay" hidden>
|
||||||
|
<div class="modal">
|
||||||
|
<p id="confirmMsg"></p>
|
||||||
|
<div class="modal-actions">
|
||||||
|
<button class="btn" id="confirmCancel">Cancel</button>
|
||||||
|
<button class="btn danger-solid" id="confirmOk">Delete</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script src="/app.js"></script>
|
<script src="/app.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -172,6 +172,23 @@ code { background: var(--bg-3); padding: 1px 5px; border-radius: 4px; font-size:
|
|||||||
.nav-ico { font-size: 18px; }
|
.nav-ico { font-size: 18px; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---- confirm modal ---- */
|
||||||
|
.modal-overlay {
|
||||||
|
position: fixed; inset: 0; z-index: 200;
|
||||||
|
background: rgba(8,10,16,.7);
|
||||||
|
display: flex; align-items: center; justify-content: center; padding: 24px;
|
||||||
|
}
|
||||||
|
.modal-overlay[hidden] { display: none; }
|
||||||
|
.modal {
|
||||||
|
background: var(--bg-2); border: 1px solid var(--border);
|
||||||
|
border-radius: var(--radius); padding: 22px; max-width: 420px; width: 100%;
|
||||||
|
box-shadow: 0 12px 40px rgba(0,0,0,.5);
|
||||||
|
}
|
||||||
|
.modal p { margin: 0 0 20px; font-size: 15px; line-height: 1.45; word-break: break-word; }
|
||||||
|
.modal-actions { display: flex; justify-content: flex-end; gap: 10px; }
|
||||||
|
.btn.danger-solid { background: var(--red); border-color: var(--red); color: #fff; font-weight: 600; }
|
||||||
|
.btn.danger-solid:hover { background: #e0414f; }
|
||||||
|
|
||||||
/* ---- lightbox ---- */
|
/* ---- lightbox ---- */
|
||||||
.lightbox {
|
.lightbox {
|
||||||
position: fixed; inset: 0; z-index: 100;
|
position: fixed; inset: 0; z-index: 100;
|
||||||
|
|||||||
Reference in New Issue
Block a user