Files
kk8bit-KayTool/web/quickadd.js
T
2025-03-20 15:43:22 +08:00

243 lines
9.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { app } from "../../../scripts/app.js";
import { api } from "../../../scripts/api.js";
console.log("[KayTool] Loading QuickAdd extension");
const UNSUPPORTED_NODES = new Set([
"Group",
"Reroute",
"Note"
]);
function addMenuHandler(nodeType, cb) {
const getOpts = nodeType.prototype.getExtraMenuOptions;
nodeType.prototype.getExtraMenuOptions = function () {
const r = getOpts ? getOpts.apply(this, arguments) : [];
cb.apply(this, arguments);
return r;
};
}
function addNode(name, nextTo, options = {}) {
options = { side: "left", select: true, shiftY: 0, shiftX: 0, ...options };
const node = LiteGraph.createNode(name);
app.graph.add(node);
node.pos = [
options.side === "left" ? nextTo.pos[0] - (node.size[0] + options.offset) : nextTo.pos[0] + nextTo.size[0] + options.offset,
nextTo.pos[1] + options.shiftY,
];
if (options.select) {
app.canvas.selectNode(node, false);
}
return node;
}
function kayGetUpstreamNodes(node, graph) {
const upstreamNodes = new Set();
function traverse(node) {
if (!node.inputs) return;
for (const input of node.inputs) {
if (input.link) {
const link = graph.links[input.link];
if (link) {
const originNode = graph._nodes_by_id[link.origin_id];
if (originNode && !upstreamNodes.has(originNode)) {
upstreamNodes.add(originNode);
traverse(originNode);
}
}
}
}
}
traverse(node);
return Array.from(upstreamNodes);
}
function kayGetOutputNodes(nodes) {
return ((nodes === null || nodes === void 0 ? void 0 : nodes.filter((n) => {
var _a;
return (n.mode != LiteGraph.NEVER &&
((_a = n.constructor.nodeData) === null || _a === void 0 ? void 0 : _a.output_node));
})) || []);
}
function kayRecursiveAddNodes(nodeId, oldOutput, newOutput) {
let currentId = nodeId;
let currentNode = oldOutput[currentId];
if (!currentNode || newOutput[currentId]) return;
newOutput[currentId] = currentNode;
for (const inputValue of Object.values(currentNode.inputs || {})) {
if (Array.isArray(inputValue)) {
kayRecursiveAddNodes(inputValue[0], oldOutput, newOutput);
}
}
return newOutput;
}
const KayToolState = {
queueNodeIds: null,
mousePos: null,
activeGroup: null
};
function initializeHooks() {
const originalApiQueuePrompt = api.queuePrompt;
api.queuePrompt = async function (index, prompt) {
if (KayToolState.queueNodeIds?.length && prompt.output) {
const oldOutput = prompt.output;
let newOutput = {};
for (const queueNodeId of KayToolState.queueNodeIds) {
kayRecursiveAddNodes(String(queueNodeId), oldOutput, newOutput);
}
prompt.output = newOutput;
// 移除日志输出
// console.log("[KayTool] Filtered prompt:", JSON.stringify(prompt, null, 2));
}
return originalApiQueuePrompt.apply(this, [index, prompt]);
};
const originalDraw = LGraphCanvas.prototype.draw;
LGraphCanvas.prototype.draw = function() {
if (this.canvas_mouse) {
KayToolState.mousePos = [this.canvas_mouse[0], this.canvas_mouse[1]];
KayToolState.activeGroup = app.graph.getGroupOnPos(...KayToolState.mousePos);
}
return originalDraw.apply(this, arguments);
};
}
async function kayExecuteNodes(graph, nodesToRun) {
KayToolState.queueNodeIds = nodesToRun.map(n => n.id);
try {
// 移除日志输出
// console.log("[KayTool] Executing nodes:", KayToolState.queueNodeIds);
return await app.queuePrompt(0);
} finally {
KayToolState.queueNodeIds = null;
}
}
function addGroupMenuHandler() {
const originalGetCanvasMenuOptions = LGraphCanvas.prototype.getCanvasMenuOptions;
LGraphCanvas.prototype.getCanvasMenuOptions = function() {
const original = originalGetCanvasMenuOptions ?
originalGetCanvasMenuOptions.apply(this, arguments) || [] : [];
if (KayToolState.activeGroup && KayToolState.mousePos) {
try {
const groupNodes = KayToolState.activeGroup._nodes || [];
if (groupNodes.length === 0) {
return original;
}
const allNodesToRun = new Set(groupNodes);
groupNodes.forEach(node => {
const upstream = kayGetUpstreamNodes(node, app.graph);
upstream.forEach(upNode => allNodesToRun.add(upNode));
});
const nodesToRun = Array.from(allNodesToRun);
const outputNodes = kayGetOutputNodes(nodesToRun);
const customMenu = [
{
content: "𝙆 ▶️ Run Group",
disabled: !outputNodes.length,
callback: async () => {
const graph = app.graph;
const groupNodes = KayToolState.activeGroup._nodes || [];
if (!groupNodes.length) return;
const allNodesToRun = new Set(groupNodes);
groupNodes.forEach(node => {
const upstream = kayGetUpstreamNodes(node, graph);
upstream.forEach(upNode => allNodesToRun.add(upNode));
});
const nodesToRun = Array.from(allNodesToRun);
try {
await kayExecuteNodes(graph, nodesToRun);
app.graph.setDirtyCanvas(true, true);
} catch (error) {
console.error("[KayTool] Group execution failed:", error);
app.extensionManager.toast.add({
severity: 'error',
summary: "KayTool Quick Run",
detail: `Failed to execute group: ${error.message}`,
life: 5000,
});
}
}
},
// null // "Run Group" 下方的分割线
];
return [...customMenu, ...original.filter(item => item !== null)];
} catch(e) {
console.error("[KayTool] Group menu error:", e);
}
}
return original;
};
}
app.registerExtension({
name: "KayTool.QuickAdd",
beforeRegisterNodeDef(nodeType, nodeData) {
if (nodeData.input?.required && !UNSUPPORTED_NODES.has(nodeData.name)) {
addMenuHandler(nodeType, function (_, options) {
if (UNSUPPORTED_NODES.has(this.type)) return;
const upstreamNodes = kayGetUpstreamNodes(this, app.graph);
const nodesToRun = [this, ...upstreamNodes];
const outputNodes = kayGetOutputNodes(nodesToRun);
const safeOptions = Array.isArray(options) ? options : [];
safeOptions.unshift(
{
content: "𝙆 ▶️ Run",
disabled: !outputNodes.length,
callback: async () => {
const node = this;
const graph = app.graph;
const upstreamNodes = kayGetUpstreamNodes(node, graph);
const nodesToRun = [node, ...upstreamNodes];
try {
await kayExecuteNodes(graph, nodesToRun);
app.graph.setDirtyCanvas(true, true);
} catch (error) {
console.error("[KayTool] Execution failed:", error);
app.extensionManager.toast.add({
severity: 'error',
summary: "KayTool Quick Run",
detail: `Failed to execute node ${node.id}: ${error.message}`,
life: 5000,
});
}
}
},
// null, // "Run" 下方的分割线
{
content: "𝙆 🛜 Set",
callback: () => { addNode("KaySetNode", this, { side: "right", offset: 20 }); }
},
{
content: "𝙆 🛜 Get",
callback: () => { addNode("KayGetNode", this, { side: "left", offset: 20 }); }
},
// null // "Set/Get" 下方的分割线
);
options.splice(0, options.length, ...safeOptions.filter(Boolean));
});
}
},
setup() {
initializeHooks();
addGroupMenuHandler();
console.log("[KayTool] QuickAdd extension setup complete");
}
});