function esc(s) { return String(s ?? '') .replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); } function showToast(msg, type = 'success') { const colors = { success: '#3fb950', danger: '#f85149', warning: '#d29922' }; const id = 'toast-' + Date.now(); document.getElementById('toastContainer').insertAdjacentHTML('beforeend', ` `); const el = document.getElementById(id); const t = new bootstrap.Toast(el, { delay: 3500 }); t.show(); el.addEventListener('hidden.bs.toast', () => el.remove()); } async function apiFetch(url, opts) { const res = await fetch(url, opts); if (res.status === 401) { location.reload(); return null; } return res; }