' +
+ '
Working… this can take a while for large templates.
';
+ // textContent, so a template/label can never inject markup.
+ toast.querySelector(".toast-title").textContent = label + " — in progress";
+ stack.appendChild(toast);
+ return toast;
+ }
+
+ function wireModal(dialog) {
+ // Upgrade an inert/closed dialog into a true modal where supported. On the
+ // no-JS full-page path it already renders `open`; closing then re-opening
+ // via showModal() gives the backdrop + focus trap without a double-open.
+ if (typeof dialog.showModal === "function") {
+ if (dialog.open) {
+ dialog.close();
+ }
+ dialog.showModal();
+ }
+ var cancel = dialog.querySelector("[data-modal-cancel]");
+ if (cancel) {
+ cancel.addEventListener("click", function () {
+ if (dialog.open) {
+ dialog.close();
+ }
+ clearSlot();
+ });
+ }
+ // ESC / backdrop dismiss fires `cancel` (not `close`, and never on a
+ // programmatic close()); clear the slot once the native close settles.
+ dialog.addEventListener("cancel", function () {
+ window.setTimeout(clearSlot, 0);
+ });
+ // On Confirm: close the modal at once and show a busy spinner toast in the
+ // stack until the request settles. The terminal result is appended to the
+ // stack by htmx (hx-swap="beforeend"); on afterRequest we drop our busy
+ // toast and clear the now-defunct dialog from #action-slot. Uses
+ // htmx:beforeSend so the XHR is already prepared -- closing the dialog here
+ // cannot cancel it. busyEl is per-request, captured in this closure.
+ var form = dialog.querySelector("form");
+ if (form) {
+ var busyEl = null;
+ form.addEventListener("htmx:beforeSend", function () {
+ if (dialog.open) {
+ dialog.close();
+ }
+ busyEl = showBusyToast(dialog);
+ });
+ form.addEventListener("htmx:afterRequest", function () {
+ if (busyEl) {
+ dismissToast(busyEl);
+ busyEl = null;
+ }
+ // The dialog stays in #action-slot after the result went to the stack;
+ // remove it now that the action is done. isConnected guards a slot
+ // already cleared by an ESC/Cancel race.
+ if (dialog.isConnected) {
+ clearSlot();
+ }
+ });
+ }
+ }
+
+ function processSlot(root) {
+ // #action-slot only ever holds a confirm