| | import { app } from "../../../scripts/app.js"; |
| | import { $el, ComfyDialog } from "../../../scripts/ui.js"; |
| |
|
| | |
| |
|
| | const id = "pysssss.WidgetDefaults"; |
| | const nodeDataKey = Symbol(); |
| |
|
| | app.registerExtension({ |
| | name: id, |
| | beforeRegisterNodeDef(nodeType, nodeData) { |
| | nodeType[nodeDataKey] = nodeData; |
| | }, |
| | setup() { |
| | let defaults; |
| | let setting; |
| |
|
| | const applyDefaults = (defaults) => { |
| | for (const node of Object.values(LiteGraph.registered_node_types)) { |
| | const nodeData = node[nodeDataKey]; |
| | if (!nodeData) continue; |
| | const nodeDefaults = defaults[node.type]; |
| | if (!nodeDefaults) continue; |
| | const inputs = { ...(nodeData.input?.required || {}), ...(nodeData.input?.optional || {}) }; |
| |
|
| | for (const w in nodeDefaults) { |
| | const widgetDef = inputs[w]; |
| | if (widgetDef) { |
| | let v = nodeDefaults[w]; |
| | if (widgetDef[0] === "INT" || widgetDef[0] === "FLOAT") { |
| | v = +v; |
| | } |
| | if (widgetDef[1]) { |
| | widgetDef[1].default = v; |
| | } else { |
| | widgetDef[1] = { default: v }; |
| | } |
| | } |
| | } |
| | } |
| | }; |
| |
|
| | const getDefaults = () => { |
| | let items; |
| | try { |
| | items = JSON.parse(setting.value); |
| | items = items.reduce((p, n) => { |
| | if (!p[n.node]) p[n.node] = {}; |
| | p[n.node][n.widget] = n.value; |
| | return p; |
| | }, {}); |
| | } catch (error) {} |
| | if (!items) { |
| | items = {}; |
| | } |
| | applyDefaults(items); |
| | return items; |
| | }; |
| |
|
| | const onNodeAdded = app.graph.onNodeAdded; |
| | app.graph.onNodeAdded = function (node) { |
| | onNodeAdded?.apply?.(this, arguments); |
| |
|
| | |
| | const nodeDefaults = defaults[node.constructor.type]; |
| | if (!nodeDefaults) return; |
| |
|
| | |
| | const stack = new Error().stack; |
| | if (stack.includes("pasteFromClipboard") || stack.includes("loadGraphData")) { |
| | return; |
| | } |
| |
|
| | for (const k in nodeDefaults) { |
| | if (k.startsWith("property.")) { |
| | const name = k.substring(9); |
| | let v = nodeDefaults[k]; |
| | |
| | if (name in node || ["color", "bgcolor", "title"].includes(name)) { |
| | node[name] = v; |
| | } else { |
| | |
| | if (!node.properties) node.properties = {}; |
| | if (typeof node.properties[name] === "number") v = +v; |
| | else if (typeof node.properties[name] === "boolean") v = v === "true"; |
| | else if (v === "true") v = true; |
| |
|
| | node.properties[name] = v; |
| | } |
| | } |
| | } |
| | }; |
| |
|
| | class WidgetDefaultsDialog extends ComfyDialog { |
| | constructor() { |
| | super(); |
| | this.element.classList.add("comfy-manage-templates"); |
| | this.grid = $el( |
| | "div", |
| | { |
| | style: { |
| | display: "grid", |
| | gridTemplateColumns: "1fr auto auto auto", |
| | gap: "5px", |
| | }, |
| | className: "pysssss-widget-defaults", |
| | }, |
| | [ |
| | $el("label", { |
| | textContent: "Node Class", |
| | }), |
| | $el("label", { |
| | textContent: "Widget Name", |
| | }), |
| | $el("label", { |
| | textContent: "Default Value", |
| | }), |
| | $el("label"), |
| | (this.rows = $el("div", { |
| | style: { |
| | display: "contents", |
| | }, |
| | })), |
| | ] |
| | ); |
| | } |
| |
|
| | createButtons() { |
| | const btns = super.createButtons(); |
| | btns[0].textContent = "Cancel"; |
| | btns.unshift( |
| | $el("button", { |
| | type: "button", |
| | textContent: "Add New", |
| | onclick: () => this.addRow(), |
| | }), |
| | $el("button", { |
| | type: "button", |
| | textContent: "Save", |
| | onclick: () => this.save(), |
| | }) |
| | ); |
| | return btns; |
| | } |
| |
|
| | addRow(node = "", widget = "", value = "") { |
| | let nameInput; |
| | this.rows.append( |
| | $el( |
| | "div", |
| | { |
| | style: { |
| | display: "contents", |
| | }, |
| | className: "pysssss-widget-defaults-row", |
| | }, |
| | [ |
| | $el("input", { |
| | placeholder: "e.g. CheckpointLoaderSimple", |
| | value: node, |
| | }), |
| | $el("input", { |
| | placeholder: "e.g. ckpt_name", |
| | value: widget, |
| | $: (el) => (nameInput = el), |
| | }), |
| | $el("input", { |
| | placeholder: "e.g. myBestModel.safetensors", |
| | value, |
| | }), |
| | $el("button", { |
| | textContent: "Delete", |
| | style: { |
| | fontSize: "12px", |
| | color: "red", |
| | fontWeight: "normal", |
| | }, |
| | onclick: (e) => { |
| | nameInput.value = ""; |
| | e.target.parentElement.style.display = "none"; |
| | }, |
| | }), |
| | ] |
| | ) |
| | ); |
| | } |
| |
|
| | save() { |
| | const rows = this.rows.children; |
| | const items = []; |
| |
|
| | for (const row of rows) { |
| | const inputs = row.querySelectorAll("input"); |
| | const node = inputs[0].value.trim(); |
| | const widget = inputs[1].value.trim(); |
| | const value = inputs[2].value; |
| | if (node && widget) { |
| | items.push({ node, widget, value }); |
| | } |
| | } |
| |
|
| | setting.value = JSON.stringify(items); |
| | defaults = getDefaults(); |
| |
|
| | this.close(); |
| | } |
| |
|
| | show() { |
| | this.rows.replaceChildren(); |
| | for (const nodeName in defaults) { |
| | const node = defaults[nodeName]; |
| | for (const widgetName in node) { |
| | this.addRow(nodeName, widgetName, node[widgetName]); |
| | } |
| | } |
| |
|
| | this.addRow(); |
| | super.show(this.grid); |
| | } |
| | } |
| |
|
| | setting = app.ui.settings.addSetting({ |
| | id, |
| | name: "🐍 Widget Defaults", |
| | type: () => { |
| | return $el("tr", [ |
| | $el("td", [ |
| | $el("label", { |
| | for: id.replaceAll(".", "-"), |
| | textContent: "🐍 Widget & Property Defaults:", |
| | }), |
| | ]), |
| | $el("td", [ |
| | $el("button", { |
| | textContent: "Manage", |
| | onclick: () => { |
| | app.ui.settings.element.close(); |
| | const dialog = new WidgetDefaultsDialog(); |
| | dialog.show(); |
| | }, |
| | style: { |
| | fontSize: "14px", |
| | }, |
| | }), |
| | ]), |
| | ]); |
| | }, |
| | }); |
| | defaults = getDefaults(); |
| | }, |
| | }); |
| |
|