| | |
| | |
| | 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; |
| | }; |
| | } |
| | }, |
| | }); |
| |
|