// Toast notifications ported from mnw.js:44-68. /** Maximum simultaneously-visible toasts. Beyond this the oldest drops first so * a burst of HTMX errors can't bury the viewport. */ const TOAST_MAX_VISIBLE = 5; interface ToastDetail { message?: string; type?: string; } /** Fire a toast. Rendered by the delegated listener installed by `initToasts`. */ export function showToast(message: string, type = 'error'): void { document.body.dispatchEvent(new CustomEvent('showToast', { detail: { message, type } })); } /** Install the toast renderer (caps the stack, auto-dismisses). Call once at * startup. Ported from mnw.js:47. */ export function initToasts(): void { document.body.addEventListener('showToast', (evt) => { const detail = (evt as CustomEvent).detail; const container = document.getElementById('notifications'); if (!container) return; while (container.childElementCount >= TOAST_MAX_VISIBLE) { container.firstElementChild?.remove(); } const toast = document.createElement('div'); toast.className = 'toast toast-' + (detail.type || 'info'); toast.textContent = detail.message || 'Action completed'; container.appendChild(toast); setTimeout(() => { toast.classList.add('fade-out'); setTimeout(() => toast.remove(), 300); }, 3000); }); }