const overlay = document.createElement("section"); overlay.className = "error-overlay"; overlay.hidden = true; overlay.innerHTML = `
`; const messageNode = overlay.querySelector("pre"); const copyButton = overlay.querySelector("[data-action='copy']"); const dismissButton = overlay.querySelector("[data-action='dismiss']"); copyButton.addEventListener("click", async () => { await copyText(messageNode.textContent); showCopyStatus(); }); dismissButton.addEventListener("click", () => { overlay.hidden = true; }); document.addEventListener("DOMContentLoaded", () => { document.body.append(overlay); }); window.addEventListener("error", (event) => { showError(formatErrorEvent(event)); }); window.addEventListener("unhandledrejection", (event) => { showError(formatReason(event.reason)); }); function showError(message) { messageNode.textContent = message; copyButton.textContent = "Copy"; overlay.hidden = false; } async function copyText(value) { if (navigator.clipboard?.writeText) { await navigator.clipboard.writeText(value); return; } const textArea = document.createElement("textarea"); textArea.value = value; textArea.setAttribute("readonly", ""); textArea.className = "error-overlay-copy-buffer"; document.body.append(textArea); textArea.select(); document.execCommand("copy"); textArea.remove(); } function showCopyStatus() { copyButton.textContent = "Copied"; window.setTimeout(() => { copyButton.textContent = "Copy"; }, 1400); } function formatErrorEvent(event) { const location = [event.filename, event.lineno, event.colno].filter(Boolean).join(":"); const detail = formatReason(event.error ?? event.message); return location ? `${detail}\n\n${location}` : detail; } function formatReason(reason) { if (reason instanceof Error) { return reason.stack ?? `${reason.name}: ${reason.message}`; } return String(reason); }