diff --git a/l4d2web/l4d2web/static/js/files-overlay.js b/l4d2web/l4d2web/static/js/files-overlay.js index 103d246..8c2cd26 100644 --- a/l4d2web/l4d2web/static/js/files-overlay.js +++ b/l4d2web/l4d2web/static/js/files-overlay.js @@ -89,13 +89,14 @@ options.credentials = "same-origin"; const response = await fetch(url, options); let body = null; + let rawText = ""; try { - const text = await response.text(); - body = text ? JSON.parse(text) : null; + rawText = await response.text(); + body = rawText ? JSON.parse(rawText) : null; } catch (_e) { body = null; } - return { ok: response.ok, status: response.status, body }; + return { ok: response.ok, status: response.status, body, rawText }; } async function postJson(url, payload) { @@ -609,12 +610,34 @@ const content = (window.__filesEditor && window.__filesEditor.getValue) ? window.__filesEditor.getValue() : ta.value; - const r = await postJson(`${baseUrl}/files/save`, { path: relPath, content }); + + // Rename-on-save: if the user edited the filename input, compose the + // new path (sibling rename only — joining parent of relPath with the + // new filename). Send payload.new_path so the server moves and writes + // atomically. Matches the legacy save handler's contract. + const filenameInput = modalContent.querySelector("[data-editor-filename]"); + const editedFilename = filenameInput ? filenameInput.value.trim() : ""; + const originalLeaf = relPath.split("/").pop() || relPath; + const parent = relPath.includes("/") ? relPath.slice(0, relPath.lastIndexOf("/")) : ""; + let newPath = null; + if (editedFilename && editedFilename !== originalLeaf) { + newPath = parent ? `${parent}/${editedFilename}` : editedFilename; + } + + const payload = { path: relPath, content }; + if (newPath) payload.new_path = newPath; + + const r = await postJson(`${baseUrl}/files/save`, payload); if (r.ok) { if (typeof window.closeModal === "function") window.closeModal(); - scheduleRefresh(parentOf(relPath)); + scheduleRefresh(parentOf(newPath || relPath)); + } else if (r.status === 409) { + // Conflict (destination already exists) — show error and keep modal + // open so the user can pick a different filename. + alert(r.rawText || `Conflict: destination already exists.`); + return; } else { - alert((r.body && r.body.error) || `Save failed (HTTP ${r.status}).`); + alert((r.body && r.body.error) || r.rawText || `Save failed (HTTP ${r.status}).`); } return; }