ComfyUI-Manager wraps app.graphToPrompt and drops the graph, so every run and the check before it compiled the canvas: same seeds, nothing of the later marks. A result that is not the copy's is now made by ComfyUI's own method. Going back is one of ComfyUI's undo steps, so Ctrl+Z returns to the state left behind. A save can also open on a new tab, the empty canvas's menu and a + on the bar keep one, and marks show as saves, runs counted as runs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
49 lines
2.1 KiB
JavaScript
49 lines
2.1 KiB
JavaScript
/**
|
|
* A workflow laid on a graph of its own, off the canvas, to be read (an output image's
|
|
* workflow kept as a recipe or a combo). ComfyUI's frontend keeps widget values by the root
|
|
* graph's id, node and name, and a workflow saved from the open canvas carries the canvas's
|
|
* id: configured under that id, the detached graph would share the canvas's widget values and
|
|
* write its own onto them. So the copy gets an id of its own. Its nodes' widgets still make
|
|
* DOM elements on ComfyUI's page (prompt boxes that showed at its top left), which only the
|
|
* nodes' removal takes away: read it through readDetached, which clears it afterwards.
|
|
*/
|
|
|
|
/** A random version-4 UUID; `crypto.randomUUID` exists only on https or this computer. */
|
|
export function freshId() {
|
|
if (typeof globalThis.crypto?.randomUUID === 'function') return globalThis.crypto.randomUUID();
|
|
const bytes = globalThis.crypto.getRandomValues(new Uint8Array(16));
|
|
bytes[6] = (bytes[6] & 0x0f) | 0x40;
|
|
bytes[8] = (bytes[8] & 0x3f) | 0x80;
|
|
const hex = [...bytes].map(byte => byte.toString(16).padStart(2, '0')).join('');
|
|
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
}
|
|
|
|
/** `workflow` (UI JSON, left untouched) on a new LiteGraph graph with an id of its own. */
|
|
export function detachedGraph(workflow) {
|
|
const copy = JSON.parse(JSON.stringify(workflow));
|
|
copy.id = freshId();
|
|
const graph = new globalThis.LiteGraph.LGraph();
|
|
graph.configure(copy);
|
|
return graph;
|
|
}
|
|
|
|
/**
|
|
* What `read(graph)` gives for `workflow` on a detached graph, the graph cleared once it is
|
|
* read (after the promise settles when `read` returns one). What `read` gives must not need
|
|
* the graph's nodes later.
|
|
*/
|
|
export function readDetached(workflow, read) {
|
|
const graph = detachedGraph(workflow);
|
|
let result;
|
|
try {
|
|
result = read(graph);
|
|
} catch (error) {
|
|
graph.clear();
|
|
throw error;
|
|
}
|
|
if (typeof result?.then !== 'function') {
|
|
graph.clear();
|
|
return result;
|
|
}
|
|
return Promise.resolve(result).finally(() => graph.clear());
|
|
} |