bugfix - clear button

This commit is contained in:
Chris
2026-07-16 22:21:39 +02:00
parent 7f2934ab82
commit 23769a02fa
3 changed files with 65 additions and 10 deletions
+2 -1
View File
@@ -140,7 +140,8 @@ SaveImage filename so each file records *which prompt* made it).
roomier boxes.
- **📥 Read from JSON** — wire a text/primitive node holding a JSON prompt list into the optional
`json_in` socket, then click to **append** it to the rows.
- **🗑 Clear All** wipes the list.
- **🗑 Clear All** (directly under Read from JSON) wipes the list — click once to arm
("⚠ Sure? Click again"), click again within 4 s to confirm; it disarms itself otherwise.
- The rows are the source of truth; internally they serialize to the JSON array
`[{"positive": "...", "negative": "..."}, ...]` (a bare string is treated as positive-only), stored
in a hidden field that both saves with the workflow and drives execution.
+1 -1
View File
@@ -1,7 +1,7 @@
[project]
name = "comfyui-ai2go-utils"
description = "ComfyUI utility nodes by AIKnowledge2Go."
version = "1.6.0"
version = "1.6.1"
license = { file = "LICENSE" }
dependencies = [
"pillow>=10.3.0",
+62 -8
View File
@@ -7,7 +7,9 @@
* positive and a negative text box, drag-to-reorder (⠿) and per-row remove (🗑). Buttons:
* • 📥 Read from JSON — parse the node wired into the optional `json_in` socket and APPEND its
* prompts to the rows (pulled to the top, next to the input).
* • ➕ Add Prompt / 🗑 Clear All — grow or wipe the list; the node auto-grows AND auto-shrinks to fit.
* • 🗑 Clear All — wipe the list; sits directly under Read from JSON (the two list-level ops
* belong together). Two-step confirm (arm, then click again) — never a blocking confirm().
* • ➕ Add Prompt — grow the list; the node auto-grows AND auto-shrinks to fit.
* • 🔍 Check for prompts — validate + count, reset index to 0, report how many runs to queue.
* The "delete_empty_prompts" toggle (default on) drops rows with an empty positive on Check and just
* before a batch is queued, so blank rows never break a run or skew the count.
@@ -343,8 +345,30 @@ app.registerExtension({
});
addBtn.serialize = false;
const clearBtn = node.addWidget("button", "🗑 Clear All", null, () => {
if (node._pbRows.length && !confirm("Remove all prompts from this node?")) return;
// No window-blocking confirm() here: a native dialog fired from inside the canvas's
// pointer state machine swallows the matching pointerup, which leaves pan/drag state stuck
// (the "canvas flies away" corruption) and re-fires the prompt when the cursor is parked
// over the button. Two-step confirm instead: first press arms, second press clears.
const CLEAR_LABEL = "🗑 Clear All";
let clearArmTimer = null;
const disarmClear = () => {
if (clearArmTimer != null) { clearTimeout(clearArmTimer); clearArmTimer = null; }
clearBtn.label = CLEAR_LABEL;
node.setDirtyCanvas?.(true, true);
};
const clearBtn = node.addWidget("button", CLEAR_LABEL, null, (v, canvas, n, pos, event) => {
// Pan/drag sequences with a second mouse button can replay a widget click at a stale
// cursor position — only a plain left-button press may operate this button.
if (event && typeof event.button === "number" && event.button !== 0) return;
if (!node._pbRows.length) { setStatus("Nothing to clear.", "#8a8a8a"); return; }
if (clearArmTimer == null) {
clearBtn.label = "⚠ Sure? Click again";
clearArmTimer = setTimeout(disarmClear, 4000);
setStatus(`Removes all ${node._pbRows.length} prompt${node._pbRows.length === 1 ? "" : "s"} — click again within 4s to confirm.`, "#e0a03c");
node.setDirtyCanvas?.(true, true);
return;
}
disarmClear();
node._pbRows = [];
render(); syncJson(); syncSize();
setStatus('Cleared. Press "Add Prompt" or "Read from JSON" to start again.', "#8a8a8a");
@@ -370,9 +394,11 @@ app.registerExtension({
node.addDOMWidget("prompt_batch_status", "info", statusEl, { serialize: false });
setStatus('Add prompts, or import JSON — then press "Check for prompts".', "#8a8a8a");
// Hoist "Read from JSON" to just above the index widget, and drop the rows editor right below
// reset (it was appended after the buttons). Final order: [json(hidden), read, index, reset,
// rows, add, clear, check, status].
// Hoist "Read from JSON" (with "Clear All" right under it — the two list-level operations
// belong together) to just above the index widget, and drop the rows editor right below
// reset (it was appended after the buttons). Final order: [json(hidden), read, clear, index,
// reset, rows, add, check, status]. Reordering the buttons is safe because restore skips
// serialize=false widgets and the scalar widgets are mirrored by name (see onSerialize below).
const move = (w, beforeName) => {
const arr = node.widgets, cur = arr.indexOf(w);
if (cur > -1) arr.splice(cur, 1);
@@ -380,14 +406,42 @@ app.registerExtension({
arr.splice(at > -1 ? at : arr.length, 0, w);
};
move(readBtn, "index");
move(clearBtn, "index");
move(rowsWidget, "➕ Add Prompt");
node._pbRebuild();
});
// After a saved workflow loads, prompts_json is restored — rebuild the rows from it.
chainCallback(nodeType.prototype, "onConfigure", function () {
// The frontend saves widgets_values at *absolute* widget indices (serialize=false buttons
// leave null holes) but restores them *compacted* (skipping those buttons) — so every scalar
// that sits after a button shifts by one slot on load. That is why a restored `index` could
// arrive null. Mirror the small scalars by name into node properties on save and restore them
// from there, immune to widget order. prompts_json stays positional: it is widget #0 (aligned
// under both schemes) and far too large to duplicate into properties.
const MIRRORED = ["index", "reset_index_at_batch_start", "delete_empty_prompts"];
chainCallback(nodeType.prototype, "onSerialize", function (o) {
const mirror = {};
for (const name of MIRRORED) {
const w = findWidget(this, name);
if (w) mirror[name] = w.value;
}
o.properties = o.properties || {};
o.properties.ai2go_pb = mirror;
});
// After a saved workflow loads: prefer the by-name mirror over whatever the positional restore
// produced (old workflows without the mirror keep the legacy behavior, which the existing
// guards — serializeValue coercion, "!== false" reset check — already handle). Then rebuild
// the rows from the restored prompts_json.
chainCallback(nodeType.prototype, "onConfigure", function (info) {
const node = this;
const mirror = info?.properties?.ai2go_pb;
if (mirror && typeof mirror === "object") {
for (const name of MIRRORED) {
const w = findWidget(node, name);
if (w && mirror[name] !== undefined) w.value = mirror[name];
}
}
requestAnimationFrame(() => node._pbRebuild?.());
});
},