fix: ask before replacing a saved preset
Saving a character, object, or scene reused an existing name and overwrote that preset without asking. Updating the preset already linked to the region still saves directly.
This commit is contained in:
@@ -73,6 +73,8 @@ Load shows the request error (for example `HTTP 500`) when the store cannot be r
|
||||
- Ensure the ComfyUI user directory is writable
|
||||
- Try a shorter name (max 64 characters)
|
||||
|
||||
Saving asks before it replaces a character, object, or scene that already uses that name. Saving again to the preset already linked to the region does not ask. If the preset list cannot be loaded, the save still goes ahead.
|
||||
|
||||
### Connected import JSON did not change the prompt
|
||||
|
||||
The `import_json` input overrides the editor only when it is a JSON object. A leading UTF-8 byte-order mark is ignored. If the value is empty, not valid JSON, or a JSON array, the node keeps the editor caption and prints a line starting with `[ByAE Ideogram]`.
|
||||
|
||||
@@ -8,7 +8,9 @@ globalThis.window = {
|
||||
},
|
||||
};
|
||||
|
||||
const { loadStores, deleteStoreEntry, saveStoreEntry, normalizeStoreName } = await import("../web/js/ideogram4_shared.js");
|
||||
const {
|
||||
loadStores, deleteStoreEntry, saveStoreEntry, normalizeStoreName, savedPresetCollision,
|
||||
} = await import("../web/js/ideogram4_shared.js");
|
||||
|
||||
describe("loadStores", () => {
|
||||
const originalFetch = globalThis.fetch;
|
||||
@@ -54,6 +56,26 @@ describe("loadStores", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("savedPresetCollision", () => {
|
||||
it("names a different preset that would be replaced", () => {
|
||||
assert.equal(savedPresetCollision(["Ada", "Bea"], "Bea"), "Bea");
|
||||
assert.equal(savedPresetCollision(["Ada"], "Cara"), null);
|
||||
assert.equal(savedPresetCollision(["Ada"], " "), null);
|
||||
assert.equal(savedPresetCollision([], "Ada"), null);
|
||||
});
|
||||
|
||||
it("does not treat an update of the linked preset as a replacement", () => {
|
||||
assert.equal(savedPresetCollision(["Ada"], "Ada", "Ada"), null);
|
||||
assert.equal(savedPresetCollision(["Ada", "Bea"], "Bea", "Ada"), "Bea");
|
||||
});
|
||||
|
||||
it("matches a composed name to the stored spelling", () => {
|
||||
const nfd = "Émilie".normalize("NFD");
|
||||
assert.equal(savedPresetCollision(["Émilie"], nfd), "Émilie");
|
||||
assert.equal(savedPresetCollision(["Émilie"], ` ${nfd} `, "Émilie"), null);
|
||||
});
|
||||
});
|
||||
|
||||
describe("saveStoreEntry", () => {
|
||||
const originalFetch = globalThis.fetch;
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
} from "./utility.js";
|
||||
import {
|
||||
loadStores, saveStoreEntry, deleteStoreEntry, installBuilderQueueHooks, installBgUpdateListener,
|
||||
normalizeStoreName,
|
||||
normalizeStoreName, savedPresetCollision,
|
||||
} from "./ideogram4_shared.js";
|
||||
import {
|
||||
captionText, cleanPalette, dimsFromAspect, elementToRegion, ideogramResolutionWarning,
|
||||
@@ -1959,9 +1959,11 @@ app.registerExtension({
|
||||
sceneSaveBtn.addEventListener("mousedown", (e) => e.stopPropagation());
|
||||
sceneSaveBtn.addEventListener("click", () => {
|
||||
showModal("Save Scene", [{ key: "name", label: "Scene name", placeholder: "my-scene" }], async (r) => {
|
||||
const name = (r.name || "").trim();
|
||||
const name = normalizeStoreName(r.name || "");
|
||||
if (!name) return;
|
||||
try {
|
||||
const collision = savedPresetCollision(await existingPresetNames("scenes"), name);
|
||||
if (collision && !confirm(`Replace saved scene "${collision}"? This cannot be undone.`)) return;
|
||||
await saveStoreEntry("scenes", name, captureSceneState());
|
||||
console.log("ByAE Ideogram: saved scene:", name);
|
||||
} catch (e) {
|
||||
@@ -2056,13 +2058,30 @@ app.registerExtension({
|
||||
return ta;
|
||||
}
|
||||
|
||||
async function existingPresetNames(bucket) {
|
||||
try {
|
||||
const stores = await loadStores();
|
||||
return Object.keys(stores[bucket] || {});
|
||||
} catch (e) {
|
||||
console.warn("ByAE Ideogram: could not check existing presets before save", e);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
async function persistBoxToStore(b, type, name) {
|
||||
const bucket = type === "object" ? "objects" : "characters";
|
||||
const safeName = normalizeStoreName(name);
|
||||
if (!safeName) return;
|
||||
const linked = b.storeRef && b.storeRef.type === type ? b.storeRef.name : "";
|
||||
try {
|
||||
const savedName = await saveStoreEntry(bucket, name, boxPayload(b));
|
||||
if (normalizeStoreName(linked) !== safeName) {
|
||||
const collision = savedPresetCollision(await existingPresetNames(bucket), safeName, linked);
|
||||
if (collision && !confirm(`Replace saved ${type} "${collision}"? This cannot be undone.`)) return;
|
||||
}
|
||||
const savedName = await saveStoreEntry(bucket, safeName, boxPayload(b));
|
||||
b.storeRef = { type, name: savedName };
|
||||
commit();
|
||||
console.log("ByAE Ideogram: saved", type, name);
|
||||
console.log("ByAE Ideogram: saved", type, savedName);
|
||||
} catch (e) {
|
||||
console.error("ByAE Ideogram: failed to save:", e);
|
||||
alert("Failed to save: " + e.message);
|
||||
|
||||
@@ -13,6 +13,19 @@ export function normalizeStoreName(name) {
|
||||
return String(name ?? "").trim().normalize("NFC");
|
||||
}
|
||||
|
||||
/**
|
||||
* Stored name this save would replace, or null.
|
||||
* Saving the preset already linked to the region is an update, not a replacement.
|
||||
*/
|
||||
export function savedPresetCollision(existingNames, name, linkedName) {
|
||||
const target = normalizeStoreName(name);
|
||||
if (!target) return null;
|
||||
const match = (existingNames || []).find((entry) => normalizeStoreName(entry) === target);
|
||||
if (!match) return null;
|
||||
if (linkedName && normalizeStoreName(linkedName) === target) return null;
|
||||
return match;
|
||||
}
|
||||
|
||||
export async function loadStores() {
|
||||
try {
|
||||
const resp = await fetch(STORE_API, {
|
||||
|
||||
Reference in New Issue
Block a user