From 1acd2ef5d2695fcb1ae065e9aa1f1a1b2a0c6098 Mon Sep 17 00:00:00 2001 From: pythongosssss <125205205+pythongosssss@users.noreply.github.com> Date: Sat, 8 Apr 2023 14:39:10 +0100 Subject: [PATCH] Adds menu for adding + managing node templates --- web/extensions/core/nodeTemplates.js | 184 +++++++++++++++++++++++++++ 1 file changed, 184 insertions(+) create mode 100644 web/extensions/core/nodeTemplates.js diff --git a/web/extensions/core/nodeTemplates.js b/web/extensions/core/nodeTemplates.js new file mode 100644 index 00000000..69d09cde --- /dev/null +++ b/web/extensions/core/nodeTemplates.js @@ -0,0 +1,184 @@ +import { app } from "/scripts/app.js"; +import { ComfyDialog, $el } from "/scripts/ui.js"; + +// Adds the ability to save and add multiple nodes as a template +// To save: +// Select multiple nodes (ctrl + drag to select a region or ctrl+click individual nodes) +// Right click the canvas +// Save Node Template -> give it a name +// +// To add: +// Right click the canvas +// Node templates -> click the one to add +// +// To delete/rename: +// Right click the canvas +// Node templates -> Manage + +const id = "Comfy.NodeTemplates"; + +class ManageTemplates extends ComfyDialog { + constructor() { + super(); + this.element.classList.add("comfy-manage-templates"); + this.templates = this.load(); + } + + createButtons() { + const btns = super.createButtons(); + btns[0].textContent = "Cancel"; + btns.unshift( + $el("button", { + type: "button", + textContent: "Save", + onclick: () => this.save(), + }) + ); + return btns; + } + + load() { + const templates = localStorage.getItem(id); + if (templates) { + return JSON.parse(templates); + } else { + return []; + } + } + + save() { + // Find all visible inputs and save them as our new list + const inputs = this.element.querySelectorAll("input"); + const updated = []; + + for (let i = 0; i < inputs.length; i++) { + const input = inputs[i]; + if (input.parentElement.style.display !== "none") { + const t = this.templates[i]; + t.name = input.value.trim() || input.getAttribute("data-name"); + updated.push(t); + } + } + + this.templates = updated; + this.store(); + this.close(); + } + + store() { + localStorage.setItem(id, JSON.stringify(this.templates)); + } + + show() { + // Show list of template names + delete button + super.show( + $el( + "div", + { + style: { + display: "grid", + gridTemplateColumns: "1fr auto", + gap: "5px", + }, + }, + this.templates.flatMap((t) => { + let nameInput; + return [ + $el( + "label", + { + textContent: "Name: ", + }, + [ + $el("input", { + value: t.name, + dataset: { name: t.name }, + $: (el) => (nameInput = el), + }), + ] + ), + $el("button", { + textContent: "Delete", + style: { + fontSize: "12px", + color: "red", + fontWeight: "normal", + }, + onclick: (e) => { + nameInput.value = ""; + e.target.style.display = "none"; + e.target.previousElementSibling.style.display = "none"; + }, + }), + ]; + }) + ) + ); + } +} + +app.registerExtension({ + name: id, + setup() { + const manage = new ManageTemplates(); + + const clipboardAction = (cb) => { + // We use the clipboard functions but dont want to overwrite the current user clipboard + // Restore it after we've run our callback + const old = localStorage.getItem("litegrapheditor_clipboard"); + cb(); + localStorage.setItem("litegrapheditor_clipboard", old); + }; + + const orig = LGraphCanvas.prototype.getCanvasMenuOptions; + LGraphCanvas.prototype.getCanvasMenuOptions = function () { + const options = orig.apply(this, arguments); + + options.push(null); + options.push({ + content: `Save Selected as Template`, + disabled: !Object.keys(app.canvas.selected_nodes || {}).length, + callback: () => { + const name = prompt("Enter name"); + if (!name || !name.trim()) return; + + clipboardAction(() => { + app.canvas.copyToClipboard(); + manage.templates.push({ + name, + data: localStorage.getItem("litegrapheditor_clipboard"), + }); + manage.store(); + }); + }, + }); + + // Map each template to a menu item + const subItems = manage.templates.map((t) => ({ + content: t.name, + callback: () => { + clipboardAction(() => { + localStorage.setItem("litegrapheditor_clipboard", t.data); + app.canvas.pasteFromClipboard(); + }); + }, + })); + + if (subItems.length) { + subItems.push(null, { + content: "Manage", + callback: () => manage.show(), + }); + + options.push({ + content: "Node Templates", + submenu: { + options: subItems, + }, + }); + } + + return options; + }; + }, +});