fix(files): support rename-on-save in URL-addressable modal
Task 9's new save delegation read only the editor content, not the filename input — so typing a new filename and clicking Save silently discarded the rename and wrote to the original path. Matches the legacy save handler's payload.new_path contract: if the user edited the filename, compose new_path = parent/filename and send it. 409 conflict (destination exists) shows an alert and keeps the modal open so the user can adjust. Also exposes rawText in fetchJson return so plain-text server error messages (e.g. "destination already exists") reach the alert call. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
64cf203890
commit
33a2e529f6
1 changed files with 29 additions and 6 deletions
|
|
@ -89,13 +89,14 @@
|
||||||
options.credentials = "same-origin";
|
options.credentials = "same-origin";
|
||||||
const response = await fetch(url, options);
|
const response = await fetch(url, options);
|
||||||
let body = null;
|
let body = null;
|
||||||
|
let rawText = "";
|
||||||
try {
|
try {
|
||||||
const text = await response.text();
|
rawText = await response.text();
|
||||||
body = text ? JSON.parse(text) : null;
|
body = rawText ? JSON.parse(rawText) : null;
|
||||||
} catch (_e) {
|
} catch (_e) {
|
||||||
body = null;
|
body = null;
|
||||||
}
|
}
|
||||||
return { ok: response.ok, status: response.status, body };
|
return { ok: response.ok, status: response.status, body, rawText };
|
||||||
}
|
}
|
||||||
|
|
||||||
async function postJson(url, payload) {
|
async function postJson(url, payload) {
|
||||||
|
|
@ -609,12 +610,34 @@
|
||||||
const content = (window.__filesEditor && window.__filesEditor.getValue)
|
const content = (window.__filesEditor && window.__filesEditor.getValue)
|
||||||
? window.__filesEditor.getValue()
|
? window.__filesEditor.getValue()
|
||||||
: ta.value;
|
: 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 (r.ok) {
|
||||||
if (typeof window.closeModal === "function") window.closeModal();
|
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 {
|
} 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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue