From d65014b1351bc5c7591beaa69be1334e0380f92e Mon Sep 17 00:00:00 2001 From: digital-garbage <133010426+digital-garbage@users.noreply.github.com> Date: Tue, 6 Oct 2026 12:25:17 +0300 Subject: [PATCH] fix: a field left open on a swapped-away node drops its value and says so Co-Authored-By: Claude Opus 5.5 --- app/ui/features/settings/models.js | 1 + app/ui/features/settings/models.test.js | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/app/ui/features/settings/models.js b/app/ui/features/settings/models.js index 52f152b1..d88225a3 100644 --- a/app/ui/features/settings/models.js +++ b/app/ui/features/settings/models.js @@ -49,6 +49,7 @@ export const models = (app) => function mount() { } const setInput = (slot, name) => async (value) => { const live = slotsNow().find((x) => x.id === slot.id) || slot; // the slot as it is now, not as drawn: an answer may have replaced it + if (live.node !== slot.node) return say(`That field belonged to ${slot.node}, which was swapped for ${live.node}: the value was not kept.`); if (JSON.stringify((live.inputs || {})[name]) === JSON.stringify(value)) return; // a blur that changed nothing saves nothing await ps.save({ inputs: { [slot.id]: { [name]: value } } }); say([...(ps.refused() || []), ...ps.saveNotes()].join(" ")); diff --git a/app/ui/features/settings/models.test.js b/app/ui/features/settings/models.test.js index 9b72bc39..d23547b3 100644 --- a/app/ui/features/settings/models.test.js +++ b/app/ui/features/settings/models.test.js @@ -80,3 +80,24 @@ test("a problem that names no slot first (a loop) still shows its nodes in the s assert.match(page.node.textContent, /cannot end up feeding itself/); page.node.remove(); }); + +test("a field still on screen for a node that was swapped away does not put its value on the new node", async () => { + let slots = [{ id: "k", node: "FunPackSampler", inputs: { steps: 8 } }]; + const saved = []; + const ps = { slots: () => slots, snapshot: () => ({ slots }), ensureLoaded: async () => {}, subscribe: () => () => {}, restore: async () => ({ refused: [] }), + save: async (b) => { saved.push(b); }, loading: () => false, loadError: () => null, saveNotes: () => [], incomplete: () => [], refused: () => [], + queueable: () => true, removedIds: () => [], offered: () => [] }; + const nodes = { FunPackSampler: { widgets: [{ name: "steps", type: "INT", min: 1, max: 100 }] }, Other: { widgets: [] } }; + const page = models({ pipeline: ps, api: { describeNodes: async () => ({ nodes }), packProviders: async () => ({}) } })(); + document.body.append(page.node); + await new Promise((r) => setTimeout(r, 0)); + page.node.querySelector(".fp-node-card").click(); // open it + const field = page.node.querySelector("input[type=number]"); + slots = [{ id: "k", node: "Other", inputs: {} }]; // the swap's answer lands; the field stays + field.value = "20"; + field.dispatchEvent(new window.Event("change", { bubbles: true })); + await new Promise((r) => setTimeout(r, 0)); + assert.deepEqual(saved, []); + assert.match(page.node.textContent, /was swapped for Other: the value was not kept/); + page.node.remove(); +});