264 lines
8.2 KiB
JavaScript
264 lines
8.2 KiB
JavaScript
import { api } from "../../../scripts/api.js";
|
|
import { app } from "../../../scripts/app.js";
|
|
import { allow_debug } from "./js_shared.js";
|
|
import { Shapes } from "./utils.js";
|
|
import { BaseSmartWidgetManager } from "./makadi/BaseSmartWidget.js";
|
|
import { SmartButton } from "./makadi/SmartButton.js";
|
|
import { inputsHistoryShow } from "./prompt_gallery.js";
|
|
|
|
app.registerExtension({
|
|
name: "iTools.promptRecord",
|
|
async nodeCreated(node) {
|
|
if (node.comfyClass !== "iToolsPromptRecord") {
|
|
return;
|
|
}
|
|
|
|
const w = node.widgets.find((w) => w.name === "timeline_data");
|
|
if (w) {
|
|
if (allow_debug) console.log("iTools: Found timeline_data widget", node);
|
|
w.hidden = true; // This hides it from the UI but keeps it in node.widgets
|
|
}
|
|
|
|
// init size
|
|
node.size = [300, 150];
|
|
|
|
setTimeout(() => {
|
|
node.setDirtyCanvas(true, true);
|
|
}, 100);
|
|
|
|
// vars
|
|
let inputWidget = node.widgets.filter((w) => w.type == "customtext");
|
|
|
|
// Use the hidden widget defined in Python for persistence
|
|
const historyWidget = node.widgets.find((w) => w.name === "timeline_data");
|
|
|
|
const _inputsHistory = [];
|
|
// Proxy handles synchronization whenever the array is modified (push, splice, length=0)
|
|
const inputsHistory = new Proxy(_inputsHistory, {
|
|
set(target, prop, value) {
|
|
const res = Reflect.set(target, prop, value);
|
|
if (historyWidget && (prop === "length" || !isNaN(prop))) {
|
|
historyWidget.value = JSON.stringify(target);
|
|
}
|
|
return res;
|
|
},
|
|
deleteProperty(target, prop) {
|
|
const res = Reflect.deleteProperty(target, prop);
|
|
if (historyWidget) historyWidget.value = JSON.stringify(target);
|
|
return res;
|
|
},
|
|
});
|
|
|
|
// Restore history when the workflow is loaded
|
|
const originalOnConfigure = node.onConfigure;
|
|
node.onConfigure = function (data) {
|
|
if (originalOnConfigure) originalOnConfigure.apply(this, arguments);
|
|
if (historyWidget && historyWidget.value) {
|
|
try {
|
|
const saved = JSON.parse(historyWidget.value);
|
|
if (Array.isArray(saved)) {
|
|
_inputsHistory.length = 0;
|
|
_inputsHistory.push(...saved);
|
|
if (allow_debug)
|
|
console.log("iTools: Timeline restored", _inputsHistory.length);
|
|
}
|
|
} catch (e) {
|
|
if (allow_debug) console.log("iTools: Failed to parse timeline data");
|
|
}
|
|
}
|
|
};
|
|
|
|
function createButtons(startVisible = true) {
|
|
const bx = 10;
|
|
const by = 9;
|
|
const h = 19;
|
|
const buttonFont = "12px Arial";
|
|
let currentX = bx;
|
|
|
|
const a = new SmartButton(bx, by, 20, h, node, "✖" || "✂" || "✘" || "🧹");
|
|
a.allowVisualHover = true;
|
|
a.textYoffset = 1;
|
|
a.isVisible = startVisible;
|
|
a.shape = Shapes.ROUND_L;
|
|
a.outline = true;
|
|
a.outlineWidth = 0.9;
|
|
a.outlineColor = "#656565";
|
|
a.color = "#222222";
|
|
a.font = buttonFont;
|
|
a.computeSize = () => [-20, -20]; // number of buttons * 4
|
|
a.onClick = async () => {
|
|
// Copy content of inputWidget
|
|
// const t = inputWidget[0].options.getValue();
|
|
// const t2 = await navigator.clipboard.writeText(t);
|
|
|
|
// Clear inputWidget
|
|
inputWidget[0].options.setValue("");
|
|
};
|
|
currentX += 20;
|
|
|
|
const b = new SmartButton(currentX, by, 38, h, node, "Copy");
|
|
b.allowVisualHover = true;
|
|
b.textYoffset = 1;
|
|
b.isVisible = startVisible;
|
|
b.shape = Shapes.SQUARE;
|
|
b.outline = true;
|
|
b.outlineWidth = 0.9;
|
|
b.outlineColor = "#656565";
|
|
b.color = "#222222";
|
|
b.font = buttonFont;
|
|
b.onClick = async () => {
|
|
const t = inputWidget[0].options.getValue();
|
|
// copy if t is not empty
|
|
if (t && t.trim() !== "") {
|
|
await navigator.clipboard.writeText(t);
|
|
}
|
|
};
|
|
currentX += 38;
|
|
|
|
const c = new SmartButton(currentX, by, 40, h, node, "Paste");
|
|
c.allowVisualHover = true;
|
|
c.textYoffset = 1;
|
|
c.isVisible = startVisible;
|
|
c.shape = Shapes.SQUARE;
|
|
c.outlineWidth = 0.9;
|
|
c.outline = true;
|
|
c.outlineColor = "#656565";
|
|
c.color = "#222222";
|
|
c.font = buttonFont;
|
|
c.onClick = async () => {
|
|
// get value from clipboard
|
|
const t = await navigator.clipboard.readText();
|
|
inputWidget[0].options.setValue(t);
|
|
};
|
|
currentX += 40;
|
|
|
|
const add = new SmartButton(
|
|
currentX,
|
|
by,
|
|
20,
|
|
h,
|
|
node,
|
|
"▶" || "✚" || "➤" || "Add to",
|
|
);
|
|
add.allowVisualHover = true;
|
|
add.textYoffset = 1;
|
|
add.isVisible = startVisible;
|
|
add.shape = Shapes.SQUARE;
|
|
add.outline = true;
|
|
add.outlineWidth = 0.9;
|
|
add.outlineColor = "#656565";
|
|
add.color = "#222222";
|
|
add.font = buttonFont;
|
|
add.onClick = () => {
|
|
const t = inputWidget[0].options.getValue();
|
|
if (t && t.trim() !== "") {
|
|
if (!inputsHistory.includes(t)) {
|
|
inputsHistory.push(t);
|
|
if (allow_debug) console.log("Text added to Timeline");
|
|
// Show success message
|
|
app.extensionManager.toast.add({
|
|
severity: "success",
|
|
summary: "Success",
|
|
detail: "Current prompt has been added to the Timeline.",
|
|
life: 2000,
|
|
});
|
|
} else {
|
|
// Show success message
|
|
app.extensionManager.toast.add({
|
|
severity: "info",
|
|
summary: "info",
|
|
detail: "Current prompt already exists in the Timeline!",
|
|
life: 2000,
|
|
});
|
|
}
|
|
}
|
|
};
|
|
currentX += 20;
|
|
|
|
const his = new SmartButton(
|
|
currentX,
|
|
by,
|
|
72,
|
|
h,
|
|
node,
|
|
"Timeline 🧾" || "Run History",
|
|
);
|
|
his.allowVisualHover = true;
|
|
his.textYoffset = 1;
|
|
his.isVisible = startVisible;
|
|
his.shape = Shapes.ROUND_R;
|
|
his.outline = true;
|
|
his.outlineWidth = 0.9;
|
|
his.outlineColor = "#656565";
|
|
his.color = "#222222";
|
|
his.font = buttonFont;
|
|
his.onClick = () => {
|
|
inputsHistoryShow(inputsHistory, inputWidget);
|
|
if (allow_debug) console.log("node", node);
|
|
};
|
|
currentX += 72;
|
|
|
|
// const u = new SmartButton(currentX, by, r - 20, h, node, "↺");
|
|
// u.allowVisualHover = true;
|
|
// u.textYoffset = 1;
|
|
// u.isVisible = startVisible;
|
|
// u.shape = Shapes.CIRCLE;
|
|
// u.outlineWidth = 0.9;
|
|
// u.outlineColor = "#656565";
|
|
// u.color = "#222222";
|
|
// u.font = buttonFont;
|
|
// u.onClick = () => {};
|
|
|
|
// const n = new SmartButton(currentX, by, r - 20, h, node, "↻");
|
|
// n.allowVisualHover = true;
|
|
// n.textYoffset = 1;
|
|
// n.isVisible = startVisible;
|
|
// n.shape = Shapes.CIRCLE;
|
|
// n.outlineWidth = 0.9;
|
|
// n.outlineColor = "#656565";
|
|
// n.color = "#222222";
|
|
// n.font = buttonFont;
|
|
// n.onClick = () => {};
|
|
}
|
|
createButtons();
|
|
|
|
// Override the queuePrompt method
|
|
const originalQueuePrompt = app.queuePrompt;
|
|
app.queuePrompt = function () {
|
|
const t = inputWidget[0].options.getValue();
|
|
if (t && t.trim() !== "" && !inputsHistory.includes(t)) {
|
|
inputsHistory.push(t);
|
|
if (allow_debug) console.log("Inputs history updated");
|
|
}
|
|
// Call the original method and return its result
|
|
return originalQueuePrompt.apply(this, arguments);
|
|
};
|
|
|
|
// Add event listener for execution_complete
|
|
api.addEventListener("execution_success", (event) => {
|
|
// check if linked
|
|
if (node.inputs[0].link) {
|
|
const t = node.executionText;
|
|
if (t && t.trim() !== "" && !inputsHistory.includes(t)) {
|
|
inputsHistory.push(t);
|
|
if (allow_debug)
|
|
console.log("Inputs history updated from linked node");
|
|
}
|
|
}
|
|
});
|
|
|
|
node.onResize = function (newSize) {
|
|
// limit width size while resizing
|
|
node.size[0] = Math.max(250, newSize[0]);
|
|
// node.size[1] = Math.max(150, newSize[1]);
|
|
};
|
|
|
|
const m = new BaseSmartWidgetManager(node, "iToolsPromptRecord");
|
|
const origOnRemoved = node.onRemoved;
|
|
node.onRemoved = function () {
|
|
origOnRemoved?.apply(this, arguments);
|
|
m.destroy();
|
|
};
|
|
},
|
|
});
|