diff --git a/l4d2web/l4d2web/static/js/files-overlay.js b/l4d2web/l4d2web/static/js/files-overlay.js index 27f4eca..103d246 100644 --- a/l4d2web/l4d2web/static/js/files-overlay.js +++ b/l4d2web/l4d2web/static/js/files-overlay.js @@ -589,6 +589,57 @@ } }); + // ---------- modal-content save / delete (URL-addressable modal) ---------- + // The new server-rendered editor (loaded into #modal-content) has its own + // .files-editor-save and .files-editor-delete buttons. Those are not the + // same elements as editorEls.saveBtn / editorEls.deleteBtn (which live in + // the old inline dialog still present for create-new-file / binary flows). + // Use event delegation so the handlers fire on dynamically swapped content. + + document.addEventListener("click", async (event) => { + const modalContent = document.getElementById("modal-content"); + if (!modalContent) return; + + const saveBtn = event.target.closest(".files-editor-save"); + if (saveBtn && modalContent.contains(saveBtn)) { + const ta = modalContent.querySelector("textarea[data-rel-path]"); + if (!ta) return; + const relPath = ta.dataset.relPath; + if (!relPath) return; + const content = (window.__filesEditor && window.__filesEditor.getValue) + ? window.__filesEditor.getValue() + : ta.value; + const r = await postJson(`${baseUrl}/files/save`, { path: relPath, content }); + if (r.ok) { + if (typeof window.closeModal === "function") window.closeModal(); + scheduleRefresh(parentOf(relPath)); + } else { + alert((r.body && r.body.error) || `Save failed (HTTP ${r.status}).`); + } + return; + } + + const deleteBtn = event.target.closest(".files-editor-delete"); + if (deleteBtn && modalContent.contains(deleteBtn)) { + const ta = modalContent.querySelector("textarea[data-rel-path]"); + if (!ta) return; + const relPath = ta.dataset.relPath; + if (!relPath) return; + if (!confirm(`Delete ${relPath}?`)) return; + const fd = new FormData(); + fd.append("path", relPath); + fd.append("csrf_token", csrfToken); + const r = await postForm(`${baseUrl}/files/delete`, fd); + if (r.ok) { + if (typeof window.closeModal === "function") window.closeModal(); + scheduleRefresh(parentOf(relPath)); + } else { + alert((r.body && r.body.error) || `Delete failed (HTTP ${r.status}).`); + } + return; + } + }); + // ---------- new-folder modal -------------------------------------------- function openNewFolder(targetFolder) { @@ -994,7 +1045,20 @@ window.location.href = url; } else if (op === "edit") { const editable = action.dataset.editable === "1"; - openEditorForFile(path, editable); + if (editable) { + // Editable text files: open via URL-addressable modal. + const editUrl = `/overlays/${overlayId}/files/edit?path=${encodeURIComponent(path)}`; + if (typeof window.openModal === "function") { + window.openModal(editUrl); + } else { + // Graceful fallback if modal-router didn't load — full-page navigation + // still hits the same route and renders the standalone editor page. + window.location.href = editUrl; + } + } else { + // Binary files: keep old inline dialog (binary replace deferred from pilot). + openEditorForFile(path, false); + } } else if (op === "delete") { const kind = action.dataset.rowKind; const name = action.dataset.rowName || basename(path);