| |
| |
| import { ComfyWidgets } from "../../../../scripts/widgets.js"; |
| |
| import { api } from "../../../../scripts/api.js"; |
| |
| import { app } from "../../../../scripts/app.js"; |
|
|
| const PathHelper = { |
| get(obj, path) { |
| if (typeof path !== "string") { |
| |
| return path; |
| } |
|
|
| if (path[0] === '"' && path[path.length - 1] === '"') { |
| |
| return JSON.parse(path); |
| } |
|
|
| |
| path = path.split(".").filter(Boolean); |
| for (const p of path) { |
| const k = isNaN(+p) ? p : +p; |
| obj = obj[k]; |
| } |
|
|
| return obj; |
| }, |
| set(obj, path, value) { |
| |
| if (Object(obj) !== obj) return obj; |
| |
| if (!Array.isArray(path)) path = path.toString().match(/[^.[\]]+/g) || []; |
| path.slice(0, -1).reduce( |
| ( |
| a, |
| c, |
| i |
| ) => |
| Object(a[c]) === a[c] |
| ? |
| a[c] |
| : |
| (a[c] = |
| Math.abs(path[i + 1]) >> 0 === +path[i + 1] |
| ? [] |
| : {}), |
| obj |
| )[path[path.length - 1]] = value; |
| return obj; |
| }, |
| }; |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| |
| |
| |
| function evaluateCondition(condition, state) { |
| const left = PathHelper.get(state, condition.left); |
| const right = PathHelper.get(state, condition.right); |
|
|
| let r; |
| if (condition.op === "eq") { |
| r = left === right; |
| } else { |
| r = left !== right; |
| } |
|
|
| return r; |
| } |
|
|
| |
| |
| |
| const callbacks = { |
| |
| |
| |
| async if(cb, state) { |
| |
| let success = true; |
| for (const condition of cb.condition) { |
| const r = evaluateCondition(condition, state); |
| if (!r) { |
| success = false; |
| break; |
| } |
| } |
|
|
| for (const m of cb[success + ""] ?? []) { |
| await invokeCallback(m, state); |
| } |
| }, |
| |
| |
| |
| async fetch(cb, state) { |
| const url = cb.url.replace(/\{([^\}]+)\}/g, (m, v) => { |
| return PathHelper.get(state, v); |
| }); |
| const res = await (await api.fetchApi(url)).json(); |
| state["$result"] = res; |
| for (const m of cb.then) { |
| await invokeCallback(m, state); |
| } |
| }, |
| |
| |
| |
| async set(cb, state) { |
| const value = PathHelper.get(state, cb.value); |
| PathHelper.set(state, cb.target, value); |
| }, |
| async "validate-combo"(cb, state) { |
| const w = state["$this"]; |
| const valid = w.options.values.includes(w.value); |
| if (!valid) { |
| w.value = w.options.values[0]; |
| } |
| }, |
| }; |
|
|
| async function invokeCallback(callback, state) { |
| if (callback.type in callbacks) { |
| |
| await callbacks[callback.type](callback, state); |
| } else { |
| console.warn( |
| "%c[🐍 pysssss]", |
| "color: limegreen", |
| `[binding ${state.$node.comfyClass}.${state.$this.name}]`, |
| "unsupported binding callback type:", |
| callback.type |
| ); |
| } |
| } |
|
|
| app.registerExtension({ |
| name: "pysssss.Binding", |
| beforeRegisterNodeDef(node, nodeData) { |
| const hasBinding = (v) => { |
| if (!v) return false; |
| return Object.values(v).find((c) => c[1]?.["pysssss.binding"]); |
| }; |
| const inputs = { ...nodeData.input?.required, ...nodeData.input?.optional }; |
| if (hasBinding(inputs)) { |
| const onAdded = node.prototype.onAdded; |
| node.prototype.onAdded = function () { |
| const r = onAdded?.apply(this, arguments); |
|
|
| for (const widget of this.widgets || []) { |
| const bindings = inputs[widget.name][1]?.["pysssss.binding"]; |
| if (!bindings) continue; |
|
|
| for (const binding of bindings) { |
| |
| |
| |
| const source = this.widgets.find((w) => w.name === binding.source); |
| if (!source) { |
| console.warn( |
| "%c[🐍 pysssss]", |
| "color: limegreen", |
| `[binding ${node.comfyClass}.${widget.name}]`, |
| "unable to find source binding widget:", |
| binding.source, |
| binding |
| ); |
| continue; |
| } |
|
|
| let lastValue; |
| async function valueChanged() { |
| const state = { |
| $this: widget, |
| $source: source, |
| $node: node, |
| }; |
|
|
| for (const callback of binding.callback) { |
| await invokeCallback(callback, state); |
| } |
|
|
| app.graph.setDirtyCanvas(true, false); |
| } |
|
|
| const cb = source.callback; |
| source.callback = function () { |
| const v = cb?.apply(this, arguments) ?? source.value; |
| if (v !== lastValue) { |
| lastValue = v; |
| valueChanged(); |
| } |
| return v; |
| }; |
|
|
| lastValue = source.value; |
| valueChanged(); |
| } |
| } |
|
|
| return r; |
| }; |
| } |
| }, |
| }); |
|
|