Improve seed and loop controls
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
import { app } from "../../scripts/app.js";
|
||||
|
||||
const EXTENSION = "ethanfel.prompt_builder.seed_control";
|
||||
const NODE_NAME = "SxCPSeedControl";
|
||||
const SEED_AXES = [
|
||||
"category",
|
||||
"subcategory",
|
||||
"content",
|
||||
"person",
|
||||
"scene",
|
||||
"pose",
|
||||
"role",
|
||||
"expression",
|
||||
"composition",
|
||||
];
|
||||
|
||||
function widget(node, name) {
|
||||
return node.widgets?.find((w) => w.name === name);
|
||||
}
|
||||
|
||||
function isSeedControlNode(node) {
|
||||
return node?._sxcpSeedControlNode || node?.comfyClass === NODE_NAME || node?.type === NODE_NAME;
|
||||
}
|
||||
|
||||
function resizeNode(node) {
|
||||
const size = node.computeSize?.();
|
||||
if (size) node.setSize?.(size);
|
||||
app.graph?.setDirtyCanvas(true, true);
|
||||
}
|
||||
|
||||
function randomSeed() {
|
||||
if (globalThis.crypto?.getRandomValues) {
|
||||
const values = new Uint32Array(1);
|
||||
globalThis.crypto.getRandomValues(values);
|
||||
return values[0];
|
||||
}
|
||||
return Math.floor(Math.random() * 0x100000000);
|
||||
}
|
||||
|
||||
function setWidgetValue(node, w, value) {
|
||||
if (!w) return;
|
||||
const oldValue = w.value;
|
||||
w.value = value;
|
||||
w.callback?.(value, app.canvas, node);
|
||||
node.onWidgetChanged?.(w.name, value, oldValue, w);
|
||||
}
|
||||
|
||||
function materializeNode(node) {
|
||||
const changes = [];
|
||||
for (const axis of SEED_AXES) {
|
||||
const modeWidget = widget(node, `${axis}_seed_mode`);
|
||||
const seedWidget = widget(node, `${axis}_seed`);
|
||||
if (modeWidget?.value !== "random" || !seedWidget) continue;
|
||||
|
||||
const seed = randomSeed();
|
||||
changes.push({
|
||||
node,
|
||||
modeWidget,
|
||||
previousMode: modeWidget.value,
|
||||
seedWidget,
|
||||
previousSeed: seedWidget.value,
|
||||
seed,
|
||||
});
|
||||
setWidgetValue(node, seedWidget, seed);
|
||||
setWidgetValue(node, modeWidget, "fixed");
|
||||
}
|
||||
if (changes.length) resizeNode(node);
|
||||
return changes;
|
||||
}
|
||||
|
||||
function materializeAllForQueue() {
|
||||
const changes = [];
|
||||
for (const node of app.graph?._nodes || []) {
|
||||
if (isSeedControlNode(node)) changes.push(...materializeNode(node));
|
||||
}
|
||||
if (changes.length) app.graph?.setDirtyCanvas(true, true);
|
||||
return changes;
|
||||
}
|
||||
|
||||
function restoreRandomModes(changes) {
|
||||
for (const change of changes) {
|
||||
setWidgetValue(change.node, change.modeWidget, change.previousMode);
|
||||
}
|
||||
if (changes.length) app.graph?.setDirtyCanvas(true, true);
|
||||
}
|
||||
|
||||
function lockRandomSeeds(node) {
|
||||
const changes = materializeNode(node);
|
||||
if (!changes.length) {
|
||||
alert("No random seed modes to lock.");
|
||||
return;
|
||||
}
|
||||
resizeNode(node);
|
||||
}
|
||||
|
||||
function setupNode(node) {
|
||||
node._sxcpSeedControlNode = true;
|
||||
if (!node._sxcpLockRandomSeedsButton) {
|
||||
node._sxcpLockRandomSeedsButton = node.addWidget("button", "Lock Random Seeds Now", null, () => lockRandomSeeds(node));
|
||||
}
|
||||
resizeNode(node);
|
||||
}
|
||||
|
||||
app.registerExtension({
|
||||
name: EXTENSION,
|
||||
|
||||
async setup() {
|
||||
if (app._sxcpSeedControlQueuePatched) return;
|
||||
const originalQueuePrompt = app.queuePrompt;
|
||||
if (!originalQueuePrompt) return;
|
||||
|
||||
app._sxcpSeedControlQueuePatched = true;
|
||||
app.queuePrompt = async function () {
|
||||
const randomSeedChanges = materializeAllForQueue();
|
||||
try {
|
||||
return await originalQueuePrompt.apply(this, arguments);
|
||||
} finally {
|
||||
restoreRandomModes(randomSeedChanges);
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
async beforeRegisterNodeDef(nodeType, nodeData) {
|
||||
if (nodeData.name !== NODE_NAME) return;
|
||||
|
||||
const onNodeCreated = nodeType.prototype.onNodeCreated;
|
||||
nodeType.prototype.onNodeCreated = function () {
|
||||
const result = onNodeCreated?.apply(this, arguments);
|
||||
setupNode(this);
|
||||
return result;
|
||||
};
|
||||
|
||||
const onConfigure = nodeType.prototype.onConfigure;
|
||||
nodeType.prototype.onConfigure = function () {
|
||||
const result = onConfigure?.apply(this, arguments);
|
||||
queueMicrotask(() => setupNode(this));
|
||||
return result;
|
||||
};
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user