Use styled modal dialogs for confirmations

This commit is contained in:
Brandon Zhang
2026-03-27 18:51:34 +08:00
parent 920376449a
commit 9e2932fbc3
3 changed files with 99 additions and 6 deletions

View File

@@ -489,9 +489,9 @@
display: flex;
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, 0.72);
backdrop-filter: blur(6px);
-webkit-backdrop-filter: blur(6px);
background: color-mix(in srgb, var(--bg-void) 44%, transparent);
backdrop-filter: blur(2px);
-webkit-backdrop-filter: blur(2px);
}
.auth-card {
@@ -559,6 +559,27 @@
margin: 0;
}
.confirm-card {
max-width: 420px;
}
.confirm-card__icon--danger {
color: var(--red);
background: color-mix(in srgb, var(--red) 12%, transparent);
border-color: color-mix(in srgb, var(--red) 30%, transparent);
}
.confirm-card__actions {
width: 100%;
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--space-3);
}
.confirm-card__actions .btn {
width: 100%;
}
/* ── Quick shortcuts ─────────────────────────────────────────────────────── */
.shortcuts-container {

View File

@@ -27,6 +27,64 @@ export function toast(message, type = 'info') {
}, 3000);
}
export function confirmDialog({
title,
message,
confirmText = 'Confirm',
cancelText = 'Cancel',
confirmClass = 'btn--danger',
}) {
document.getElementById('confirm-modal')?.remove();
return new Promise((resolve) => {
const overlay = document.createElement('div');
overlay.id = 'confirm-modal';
overlay.className = 'auth-overlay';
overlay.tabIndex = -1;
overlay.innerHTML = `
<div class="auth-card confirm-card fade-in" role="dialog" aria-modal="true" aria-labelledby="confirm-title">
<div class="auth-card__icon confirm-card__icon confirm-card__icon--danger">
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M12 9v4"/>
<path d="M12 17h.01"/>
<path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/>
</svg>
</div>
<h2 id="confirm-title" class="auth-card__title">${title}</h2>
<p class="auth-card__desc">${message}</p>
<div class="confirm-card__actions">
<button type="button" class="btn btn--ghost" data-action="cancel">${cancelText}</button>
<button type="button" class="btn ${confirmClass}" data-action="confirm">${confirmText}</button>
</div>
</div>
`;
document.body.appendChild(overlay);
const cancelBtn = overlay.querySelector('[data-action="cancel"]');
const confirmBtn = overlay.querySelector('[data-action="confirm"]');
const close = (result) => {
overlay.remove();
resolve(result);
};
overlay.addEventListener('click', (e) => {
if (e.target === overlay) close(false);
});
cancelBtn.addEventListener('click', () => close(false));
confirmBtn.addEventListener('click', () => close(true));
overlay.addEventListener('keydown', (e) => {
if (e.key === 'Escape') close(false);
});
overlay.focus();
confirmBtn.focus();
});
}
// ── Token auth modal ──────────────────────────────────────────────────────
function showTokenModal(onSuccess) {

View File

@@ -6,7 +6,7 @@
import { state } from './state.js';
import { api } from './api.js';
import { toast } from './app.js';
import { confirmDialog, toast } from './app.js';
// ── SVG icon helpers ──────────────────────────────────────────────────────
@@ -137,7 +137,14 @@ function renderPendingCard(item, index) {
});
deleteBtn.addEventListener('click', async () => {
if (!confirm('Delete this instruction?')) return;
const confirmed = await confirmDialog({
title: 'Delete instruction?',
message: 'This removes the pending instruction from the queue before any agent can consume it.',
confirmText: 'Delete Instruction',
cancelText: 'Keep Instruction',
});
if (!confirmed) return;
deleteBtn.disabled = true;
try {
await api.deleteInstruction(item.id);
@@ -215,7 +222,14 @@ export function initInstructions() {
// Clear history button
clearHistoryBtn.addEventListener('click', async () => {
if (!confirm('Clear all consumed instruction history? This cannot be undone.')) return;
const confirmed = await confirmDialog({
title: 'Clear consumed history?',
message: 'This removes all consumed instructions from the history panel. This action cannot be undone.',
confirmText: 'Clear History',
cancelText: 'Keep History',
});
if (!confirmed) return;
clearHistoryBtn.disabled = true;
try {
const res = await api.clearConsumed();