Spaces:
Running
on
Zero
Running
on
Zero
import { ComfyApp, app } from "../../scripts/app.js"; | |
import { ComfyDialog, $el } from "../../scripts/ui.js"; | |
import { api } from "../../scripts/api.js"; | |
let wildcards_list = []; | |
async function load_wildcards() { | |
let res = await api.fetchApi('/impact/wildcards/list'); | |
let data = await res.json(); | |
wildcards_list = data.data; | |
} | |
load_wildcards(); | |
export function get_wildcards_list() { | |
return wildcards_list; | |
} | |
// temporary implementation (copying from https://github.com/pythongosssss/ComfyUI-WD14-Tagger) | |
// I think this should be included into master!! | |
class ImpactProgressBadge { | |
constructor() { | |
if (!window.__progress_badge__) { | |
window.__progress_badge__ = Symbol("__impact_progress_badge__"); | |
} | |
this.symbol = window.__progress_badge__; | |
} | |
getState(node) { | |
return node[this.symbol] || {}; | |
} | |
setState(node, state) { | |
node[this.symbol] = state; | |
app.canvas.setDirty(true); | |
} | |
addStatusHandler(nodeType) { | |
if (nodeType[this.symbol]?.statusTagHandler) { | |
return; | |
} | |
if (!nodeType[this.symbol]) { | |
nodeType[this.symbol] = {}; | |
} | |
nodeType[this.symbol] = { | |
statusTagHandler: true, | |
}; | |
api.addEventListener("impact/update_status", ({ detail }) => { | |
let { node, progress, text } = detail; | |
const n = app.graph.getNodeById(+(node || app.runningNodeId)); | |
if (!n) return; | |
const state = this.getState(n); | |
state.status = Object.assign(state.status || {}, { progress: text ? progress : null, text: text || null }); | |
this.setState(n, state); | |
}); | |
const self = this; | |
const onDrawForeground = nodeType.prototype.onDrawForeground; | |
nodeType.prototype.onDrawForeground = function (ctx) { | |
const r = onDrawForeground?.apply?.(this, arguments); | |
const state = self.getState(this); | |
if (!state?.status?.text) { | |
return r; | |
} | |
const { fgColor, bgColor, text, progress, progressColor } = { ...state.status }; | |
ctx.save(); | |
ctx.font = "12px sans-serif"; | |
const sz = ctx.measureText(text); | |
ctx.fillStyle = bgColor || "dodgerblue"; | |
ctx.beginPath(); | |
ctx.roundRect(0, -LiteGraph.NODE_TITLE_HEIGHT - 20, sz.width + 12, 20, 5); | |
ctx.fill(); | |
if (progress) { | |
ctx.fillStyle = progressColor || "green"; | |
ctx.beginPath(); | |
ctx.roundRect(0, -LiteGraph.NODE_TITLE_HEIGHT - 20, (sz.width + 12) * progress, 20, 5); | |
ctx.fill(); | |
} | |
ctx.fillStyle = fgColor || "#fff"; | |
ctx.fillText(text, 6, -LiteGraph.NODE_TITLE_HEIGHT - 6); | |
ctx.restore(); | |
return r; | |
}; | |
} | |
} | |
const input_tracking = {}; | |
const input_dirty = {}; | |
const output_tracking = {}; | |
function progressExecuteHandler(event) { | |
if(event.detail.output.aux){ | |
const id = event.detail.node; | |
if(input_tracking.hasOwnProperty(id)) { | |
if(input_tracking.hasOwnProperty(id) && input_tracking[id][0] != event.detail.output.aux[0]) { | |
input_dirty[id] = true; | |
} | |
else{ | |
} | |
} | |
input_tracking[id] = event.detail.output.aux; | |
} | |
} | |
function imgSendHandler(event) { | |
if(event.detail.images.length > 0){ | |
let data = event.detail.images[0]; | |
let filename = `${data.filename} [${data.type}]`; | |
let nodes = app.graph._nodes; | |
for(let i in nodes) { | |
if(nodes[i].type == 'ImageReceiver') { | |
let is_linked = false; | |
if(nodes[i].widgets[1].type == 'converted-widget') { | |
for(let j in nodes[i].inputs) { | |
let input = nodes[i].inputs[j]; | |
if(input.name === 'link_id') { | |
if(input.link) { | |
let src_node = app.graph._nodes_by_id[app.graph.links[input.link].origin_id]; | |
if(src_node.type == 'ImpactInt' || src_node.type == 'PrimitiveNode') { | |
is_linked = true; | |
} | |
} | |
break; | |
} | |
} | |
} | |
else if(nodes[i].widgets[1].value == event.detail.link_id) { | |
is_linked = true; | |
} | |
if(is_linked) { | |
if(data.subfolder) | |
nodes[i].widgets[0].value = `${data.subfolder}/${data.filename} [${data.type}]`; | |
else | |
nodes[i].widgets[0].value = `${data.filename} [${data.type}]`; | |
let img = new Image(); | |
img.onload = (event) => { | |
nodes[i].imgs = [img]; | |
nodes[i].size[1] = Math.max(200, nodes[i].size[1]); | |
app.canvas.setDirty(true); | |
}; | |
img.src = `/view?filename=${data.filename}&type=${data.type}&subfolder=${data.subfolder}`+app.getPreviewFormatParam(); | |
} | |
} | |
} | |
} | |
} | |
function latentSendHandler(event) { | |
if(event.detail.images.length > 0){ | |
let data = event.detail.images[0]; | |
let filename = `${data.filename} [${data.type}]`; | |
let nodes = app.graph._nodes; | |
for(let i in nodes) { | |
if(nodes[i].type == 'LatentReceiver') { | |
if(nodes[i].widgets[1].value == event.detail.link_id) { | |
if(data.subfolder) | |
nodes[i].widgets[0].value = `${data.subfolder}/${data.filename} [${data.type}]`; | |
else | |
nodes[i].widgets[0].value = `${data.filename} [${data.type}]`; | |
let img = new Image(); | |
img.src = `/view?filename=${data.filename}&type=${data.type}&subfolder=${data.subfolder}`+app.getPreviewFormatParam(); | |
nodes[i].imgs = [img]; | |
nodes[i].size[1] = Math.max(200, nodes[i].size[1]); | |
} | |
} | |
} | |
} | |
} | |
function valueSendHandler(event) { | |
let nodes = app.graph._nodes; | |
for(let i in nodes) { | |
if(nodes[i].type == 'ImpactValueReceiver') { | |
if(nodes[i].widgets[2].value == event.detail.link_id) { | |
nodes[i].widgets[1].value = event.detail.value; | |
let typ = typeof event.detail.value; | |
if(typ == 'string') { | |
nodes[i].widgets[0].value = "STRING"; | |
} | |
else if(typ == "boolean") { | |
nodes[i].widgets[0].value = "BOOLEAN"; | |
} | |
else if(typ != "number") { | |
nodes[i].widgets[0].value = typeof event.detail.value; | |
} | |
else if(Number.isInteger(event.detail.value)) { | |
nodes[i].widgets[0].value = "INT"; | |
} | |
else { | |
nodes[i].widgets[0].value = "FLOAT"; | |
} | |
} | |
} | |
} | |
} | |
const impactProgressBadge = new ImpactProgressBadge(); | |
api.addEventListener("stop-iteration", () => { | |
document.getElementById("autoQueueCheckbox").checked = false; | |
}); | |
api.addEventListener("value-send", valueSendHandler); | |
api.addEventListener("img-send", imgSendHandler); | |
api.addEventListener("latent-send", latentSendHandler); | |
api.addEventListener("executed", progressExecuteHandler); | |
app.registerExtension({ | |
name: "Comfy.Impack", | |
loadedGraphNode(node, app) { | |
if (node.comfyClass == "MaskPainter") { | |
input_dirty[node.id + ""] = true; | |
} | |
}, | |
async beforeRegisterNodeDef(nodeType, nodeData, app) { | |
if (nodeData.name == "IterativeLatentUpscale" || nodeData.name == "IterativeImageUpscale" | |
|| nodeData.name == "RegionalSampler"|| nodeData.name == "RegionalSamplerAdvanced") { | |
impactProgressBadge.addStatusHandler(nodeType); | |
} | |
if(nodeData.name == "ImpactControlBridge") { | |
const onConnectionsChange = nodeType.prototype.onConnectionsChange; | |
nodeType.prototype.onConnectionsChange = function (type, index, connected, link_info) { | |
if(!link_info || this.inputs[0].type != '*') | |
return; | |
// assign type | |
let slot_type = '*'; | |
if(type == 2) { | |
slot_type = link_info.type; | |
} | |
else { | |
const node = app.graph.getNodeById(link_info.origin_id); | |
slot_type = node.outputs[link_info.origin_slot].type; | |
} | |
this.inputs[0].type = slot_type; | |
this.outputs[0].type = slot_type; | |
this.outputs[0].label = slot_type; | |
} | |
} | |
if(nodeData.name == "ImpactConditionalBranch" || nodeData.name == "ImpactConditionalBranchSelMode") { | |
const onConnectionsChange = nodeType.prototype.onConnectionsChange; | |
nodeType.prototype.onConnectionsChange = function (type, index, connected, link_info) { | |
if(!link_info || this.inputs[0].type != '*') | |
return; | |
if(index >= 2) | |
return; | |
// assign type | |
let slot_type = '*'; | |
if(type == 2) { | |
slot_type = link_info.type; | |
} | |
else { | |
const node = app.graph.getNodeById(link_info.origin_id); | |
slot_type = node.outputs[link_info.origin_slot].type; | |
} | |
this.inputs[0].type = slot_type; | |
this.inputs[1].type = slot_type; | |
this.outputs[0].type = slot_type; | |
this.outputs[0].label = slot_type; | |
} | |
} | |
if(nodeData.name == "ImpactCompare") { | |
const onConnectionsChange = nodeType.prototype.onConnectionsChange; | |
nodeType.prototype.onConnectionsChange = function (type, index, connected, link_info) { | |
if(!link_info || this.inputs[0].type != '*' || type == 2) | |
return; | |
// assign type | |
const node = app.graph.getNodeById(link_info.origin_id); | |
let slot_type = node.outputs[link_info.origin_slot].type; | |
this.inputs[0].type = slot_type; | |
this.inputs[1].type = slot_type; | |
} | |
} | |
if(nodeData.name === 'ImpactInversedSwitch') { | |
nodeData.output = ['*']; | |
nodeData.output_is_list = [false]; | |
nodeData.output_name = ['output1']; | |
const onConnectionsChange = nodeType.prototype.onConnectionsChange; | |
nodeType.prototype.onConnectionsChange = function (type, index, connected, link_info) { | |
if(!link_info) | |
return; | |
if(type == 2) { | |
// connect output | |
if(connected){ | |
if(app.graph._nodes_by_id[link_info.target_id].type == 'Reroute') { | |
app.graph._nodes_by_id[link_info.target_id].disconnectInput(link_info.target_slot); | |
} | |
if(this.outputs[0].type == '*'){ | |
if(link_info.type == '*') { | |
app.graph._nodes_by_id[link_info.target_id].disconnectInput(link_info.target_slot); | |
} | |
else { | |
// propagate type | |
this.outputs[0].type = link_info.type; | |
this.outputs[0].name = link_info.type; | |
for(let i in this.inputs) { | |
if(this.inputs[i].name != 'select') | |
this.inputs[i].type = link_info.type; | |
} | |
} | |
} | |
} | |
} | |
else { | |
if(app.graph._nodes_by_id[link_info.origin_id].type == 'Reroute') | |
this.disconnectInput(link_info.target_slot); | |
// connect input | |
if(this.inputs[0].type == '*'){ | |
const node = app.graph.getNodeById(link_info.origin_id); | |
let origin_type = node.outputs[link_info.origin_slot].type; | |
if(origin_type == '*') { | |
this.disconnectInput(link_info.target_slot); | |
return; | |
} | |
for(let i in this.inputs) { | |
if(this.inputs[i].name != 'select') | |
this.inputs[i].type = origin_type; | |
} | |
this.outputs[0].type = origin_type; | |
this.outputs[0].name = origin_type; | |
} | |
return; | |
} | |
if (!connected && this.outputs.length > 1) { | |
const stackTrace = new Error().stack; | |
if( | |
!stackTrace.includes('LGraphNode.prototype.connect') && // for touch device | |
!stackTrace.includes('LGraphNode.connect') && // for mouse device | |
!stackTrace.includes('loadGraphData')) { | |
if(this.outputs[link_info.origin_slot].links.length == 0) | |
this.removeOutput(link_info.origin_slot); | |
} | |
} | |
let slot_i = 1; | |
for (let i = 0; i < this.outputs.length; i++) { | |
this.outputs[i].name = `output${slot_i}` | |
slot_i++; | |
} | |
let last_slot = this.outputs[this.outputs.length - 1]; | |
if (last_slot.slot_index == link_info.origin_slot) { | |
this.addOutput(`output${slot_i}`, this.outputs[0].type); | |
} | |
let select_slot = this.inputs.find(x => x.name == "select"); | |
if(this.widgets) { | |
this.widgets[0].options.max = select_slot?this.outputs.length-1:this.outputs.length; | |
this.widgets[0].value = Math.min(this.widgets[0].value, this.widgets[0].options.max); | |
if(this.widgets[0].options.max > 0 && this.widgets[0].value == 0) | |
this.widgets[0].value = 1; | |
} | |
} | |
} | |
if (nodeData.name === 'ImpactMakeImageList' || nodeData.name === 'ImpactMakeImageBatch' || | |
nodeData.name === 'CombineRegionalPrompts' || | |
nodeData.name === 'ImpactCombineConditionings' || nodeData.name === 'ImpactConcatConditionings' || | |
nodeData.name === 'ImpactSEGSConcat' || | |
nodeData.name === 'ImpactSwitch' || nodeData.name === 'LatentSwitch' || nodeData.name == 'SEGSSwitch') { | |
var input_name = "input"; | |
switch(nodeData.name) { | |
case 'ImpactMakeImageList': | |
case 'ImpactMakeImageBatch': | |
input_name = "image"; | |
break; | |
case 'ImpactSEGSConcat': | |
input_name = "segs"; | |
break; | |
case 'CombineRegionalPrompts': | |
input_name = "regional_prompts"; | |
break; | |
case 'ImpactCombineConditionings': | |
case 'ImpactConcatConditionings': | |
input_name = "conditioning"; | |
break; | |
case 'LatentSwitch': | |
input_name = "input"; | |
break; | |
case 'SEGSSwitch': | |
input_name = "input"; | |
break; | |
case 'ImpactSwitch': | |
input_name = "input"; | |
} | |
const onConnectionsChange = nodeType.prototype.onConnectionsChange; | |
nodeType.prototype.onConnectionsChange = function (type, index, connected, link_info) { | |
if(!link_info) | |
return; | |
if(type == 2) { | |
// connect output | |
if(connected && index == 0){ | |
if(nodeData.name == 'ImpactSwitch' && app.graph._nodes_by_id[link_info.target_id]?.type == 'Reroute') { | |
app.graph._nodes_by_id[link_info.target_id].disconnectInput(link_info.target_slot); | |
} | |
if(this.outputs[0].type == '*'){ | |
if(link_info.type == '*') { | |
app.graph._nodes_by_id[link_info.target_id].disconnectInput(link_info.target_slot); | |
} | |
else { | |
// propagate type | |
this.outputs[0].type = link_info.type; | |
this.outputs[0].label = link_info.type; | |
this.outputs[0].name = link_info.type; | |
for(let i in this.inputs) { | |
let input_i = this.inputs[i]; | |
if(input_i.name != 'select' && input_i.name != 'sel_mode') | |
input_i.type = link_info.type; | |
} | |
} | |
} | |
} | |
return; | |
} | |
else { | |
if(nodeData.name == 'ImpactSwitch' && app.graph._nodes_by_id[link_info.origin_id].type == 'Reroute') | |
this.disconnectInput(link_info.target_slot); | |
// connect input | |
if(this.inputs[index].name == 'select' || this.inputs[index].name == 'sel_mode') | |
return; | |
if(this.inputs[0].type == '*'){ | |
const node = app.graph.getNodeById(link_info.origin_id); | |
let origin_type = node.outputs[link_info.origin_slot].type; | |
if(origin_type == '*') { | |
this.disconnectInput(link_info.target_slot); | |
return; | |
} | |
for(let i in this.inputs) { | |
let input_i = this.inputs[i]; | |
if(input_i.name != 'select' && input_i.name != 'sel_mode') | |
input_i.type = origin_type; | |
} | |
this.outputs[0].type = origin_type; | |
this.outputs[0].label = origin_type; | |
this.outputs[0].name = origin_type; | |
} | |
} | |
let select_slot = this.inputs.find(x => x.name == "select"); | |
let mode_slot = this.inputs.find(x => x.name == "sel_mode"); | |
let converted_count = 0; | |
converted_count += select_slot?1:0; | |
converted_count += mode_slot?1:0; | |
if (!connected && (this.inputs.length > 1+converted_count)) { | |
const stackTrace = new Error().stack; | |
if( | |
!stackTrace.includes('LGraphNode.prototype.connect') && // for touch device | |
!stackTrace.includes('LGraphNode.connect') && // for mouse device | |
!stackTrace.includes('loadGraphData') && | |
this.inputs[index].name != 'select') { | |
this.removeInput(index); | |
} | |
} | |
let slot_i = 1; | |
for (let i = 0; i < this.inputs.length; i++) { | |
let input_i = this.inputs[i]; | |
if(input_i.name != 'select'&& input_i.name != 'sel_mode') { | |
input_i.name = `${input_name}${slot_i}` | |
slot_i++; | |
} | |
} | |
let last_slot = this.inputs[this.inputs.length - 1]; | |
if ( | |
(last_slot.name == 'select' && last_slot.name != 'sel_mode' && this.inputs[this.inputs.length - 2].link != undefined) | |
|| (last_slot.name != 'select' && last_slot.name != 'sel_mode' && last_slot.link != undefined)) { | |
this.addInput(`${input_name}${slot_i}`, this.outputs[0].type); | |
} | |
if(this.widgets) { | |
this.widgets[0].options.max = select_slot?this.inputs.length-1:this.inputs.length; | |
this.widgets[0].value = Math.min(this.widgets[0].value, this.widgets[0].options.max); | |
if(this.widgets[0].options.max > 0 && this.widgets[0].value == 0) | |
this.widgets[0].value = 1; | |
} | |
} | |
} | |
}, | |
nodeCreated(node, app) { | |
if(node.comfyClass == "MaskPainter") { | |
node.addWidget("button", "Edit mask", null, () => { | |
ComfyApp.copyToClipspace(node); | |
ComfyApp.clipspace_return_node = node; | |
ComfyApp.open_maskeditor(); | |
}); | |
} | |
switch(node.comfyClass) { | |
case "ToDetailerPipe": | |
case "ToDetailerPipeSDXL": | |
case "BasicPipeToDetailerPipe": | |
case "BasicPipeToDetailerPipeSDXL": | |
case "EditDetailerPipe": | |
case "FaceDetailer": | |
case "DetailerForEach": | |
case "DetailerForEachDebug": | |
case "DetailerForEachPipe": | |
case "DetailerForEachDebugPipe": | |
{ | |
for(let i in node.widgets) { | |
let widget = node.widgets[i]; | |
if(widget.type === "customtext") { | |
widget.dynamicPrompts = false; | |
widget.inputEl.placeholder = "wildcard spec: if kept empty, this option will be ignored"; | |
widget.serializeValue = () => { | |
return node.widgets[i].value; | |
}; | |
} | |
} | |
} | |
break; | |
} | |
if(node.comfyClass == "ImpactSEGSLabelFilter" || node.comfyClass == "SEGSLabelFilterDetailerHookProvider") { | |
Object.defineProperty(node.widgets[0], "value", { | |
set: (value) => { | |
const stackTrace = new Error().stack; | |
if(stackTrace.includes('inner_value_change')) { | |
if(node.widgets[1].value.trim() != "" && !node.widgets[1].value.trim().endsWith(",")) | |
node.widgets[1].value += ", " | |
node.widgets[1].value += value; | |
node.widgets_values[1] = node.widgets[1].value; | |
} | |
node._value = value; | |
}, | |
get: () => { | |
return node._value; | |
} | |
}); | |
} | |
if(node.comfyClass == "UltralyticsDetectorProvider") { | |
let model_name_widget = node.widgets.find((w) => w.name === "model_name"); | |
let orig_draw = node.onDrawForeground; | |
node.onDrawForeground = function (ctx) { | |
const r = orig_draw?.apply?.(this, arguments); | |
let is_seg = model_name_widget.value?.startsWith('segm/') || model_name_widget.value?.includes('-seg'); | |
if(!is_seg) { | |
var slot_pos = new Float32Array(2); | |
var pos = node.getConnectionPos(false, 1, slot_pos); | |
pos[0] -= node.pos[0] - 10; | |
pos[1] -= node.pos[1]; | |
ctx.beginPath(); | |
ctx.strokeStyle = "red"; | |
ctx.lineWidth = 4; | |
ctx.moveTo(pos[0] - 5, pos[1] - 5); | |
ctx.lineTo(pos[0] + 5, pos[1] + 5); | |
ctx.moveTo(pos[0] + 5, pos[1] - 5); | |
ctx.lineTo(pos[0] - 5, pos[1] + 5); | |
ctx.stroke(); | |
} | |
} | |
} | |
if( | |
node.comfyClass == "ImpactWildcardEncode" || node.comfyClass == "ImpactWildcardProcessor" | |
|| node.comfyClass == "ToDetailerPipe" || node.comfyClass == "ToDetailerPipeSDXL" | |
|| node.comfyClass == "EditDetailerPipe" || node.comfyClass == "EditDetailerPipeSDXL" | |
|| node.comfyClass == "BasicPipeToDetailerPipe" || node.comfyClass == "BasicPipeToDetailerPipeSDXL") { | |
node._value = "Select the LoRA to add to the text"; | |
node._wvalue = "Select the Wildcard to add to the text"; | |
var tbox_id = 0; | |
var combo_id = 3; | |
var has_lora = true; | |
switch(node.comfyClass){ | |
case "ImpactWildcardEncode": | |
tbox_id = 0; | |
combo_id = 3; | |
break; | |
case "ImpactWildcardProcessor": | |
tbox_id = 0; | |
combo_id = 4; | |
has_lora = false; | |
break; | |
case "ToDetailerPipe": | |
case "ToDetailerPipeSDXL": | |
case "EditDetailerPipe": | |
case "EditDetailerPipeSDXL": | |
case "BasicPipeToDetailerPipe": | |
case "BasicPipeToDetailerPipeSDXL": | |
tbox_id = 0; | |
combo_id = 1; | |
break; | |
} | |
Object.defineProperty(node.widgets[combo_id+1], "value", { | |
set: (value) => { | |
const stackTrace = new Error().stack; | |
if(stackTrace.includes('inner_value_change')) { | |
if(value != "Select the Wildcard to add to the text") { | |
if(node.widgets[tbox_id].value != '') | |
node.widgets[tbox_id].value += ', ' | |
node.widgets[tbox_id].value += value; | |
} | |
} | |
}, | |
get: () => { return "Select the Wildcard to add to the text"; } | |
}); | |
Object.defineProperty(node.widgets[combo_id+1].options, "values", { | |
set: (x) => {}, | |
get: () => { | |
return wildcards_list; | |
} | |
}); | |
if(has_lora) { | |
Object.defineProperty(node.widgets[combo_id], "value", { | |
set: (value) => { | |
const stackTrace = new Error().stack; | |
if(stackTrace.includes('inner_value_change')) { | |
if(value != "Select the LoRA to add to the text") { | |
let lora_name = value; | |
if (lora_name.endsWith('.safetensors')) { | |
lora_name = lora_name.slice(0, -12); | |
} | |
node.widgets[tbox_id].value += `<lora:${lora_name}>`; | |
if(node.widgets_values) { | |
node.widgets_values[tbox_id] = node.widgets[tbox_id].value; | |
} | |
} | |
} | |
node._value = value; | |
}, | |
get: () => { return "Select the LoRA to add to the text"; } | |
}); | |
} | |
// Preventing validation errors from occurring in any situation. | |
if(has_lora) { | |
node.widgets[combo_id].serializeValue = () => { return "Select the LoRA to add to the text"; } | |
} | |
node.widgets[combo_id+1].serializeValue = () => { return "Select the Wildcard to add to the text"; } | |
} | |
if(node.comfyClass == "ImpactWildcardProcessor" || node.comfyClass == "ImpactWildcardEncode") { | |
node.widgets[0].inputEl.placeholder = "Wildcard Prompt (User input)"; | |
node.widgets[1].inputEl.placeholder = "Populated Prompt (Will be generated automatically)"; | |
node.widgets[1].inputEl.disabled = true; | |
const populated_text_widget = node.widgets.find((w) => w.name == 'populated_text'); | |
const mode_widget = node.widgets.find((w) => w.name == 'mode'); | |
// mode combo | |
Object.defineProperty(mode_widget, "value", { | |
set: (value) => { | |
node._mode_value = value == true || value == "Populate"; | |
populated_text_widget.inputEl.disabled = value == true || value == "Populate"; | |
}, | |
get: () => { | |
if(node._mode_value != undefined) | |
return node._mode_value; | |
else | |
return true; | |
} | |
}); | |
} | |
if (node.comfyClass == "MaskPainter") { | |
node.widgets[0].value = '#placeholder'; | |
Object.defineProperty(node, "images", { | |
set: function(value) { | |
node._images = value; | |
}, | |
get: function() { | |
const id = node.id+""; | |
if(node.widgets[0].value != '#placeholder') { | |
var need_invalidate = false; | |
if(input_dirty.hasOwnProperty(id) && input_dirty[id]) { | |
node.widgets[0].value = {...input_tracking[id][1]}; | |
input_dirty[id] = false; | |
need_invalidate = true | |
this._images = app.nodeOutputs[id].images; | |
} | |
let filename = app.nodeOutputs[id]['aux'][1][0]['filename']; | |
let subfolder = app.nodeOutputs[id]['aux'][1][0]['subfolder']; | |
let type = app.nodeOutputs[id]['aux'][1][0]['type']; | |
let item = | |
{ | |
image_hash: app.nodeOutputs[id]['aux'][0], | |
forward_filename: app.nodeOutputs[id]['aux'][1][0]['filename'], | |
forward_subfolder: app.nodeOutputs[id]['aux'][1][0]['subfolder'], | |
forward_type: app.nodeOutputs[id]['aux'][1][0]['type'] | |
}; | |
if(node._images) { | |
app.nodeOutputs[id].images = [{ | |
...node._images[0], | |
...item | |
}]; | |
node.widgets[0].value = | |
{ | |
...node._images[0], | |
...item | |
}; | |
} | |
else { | |
app.nodeOutputs[id].images = [{ | |
...item | |
}]; | |
node.widgets[0].value = | |
{ | |
...item | |
}; | |
} | |
if(need_invalidate) { | |
Promise.all( | |
app.nodeOutputs[id].images.map((src) => { | |
return new Promise((r) => { | |
const img = new Image(); | |
img.onload = () => r(img); | |
img.onerror = () => r(null); | |
img.src = "/view?" + new URLSearchParams(src).toString(); | |
}); | |
}) | |
).then((imgs) => { | |
this.imgs = imgs.filter(Boolean); | |
this.setSizeForImage?.(); | |
app.graph.setDirtyCanvas(true); | |
}); | |
app.nodeOutputs[id].images[0] = { ...node.widgets[0].value }; | |
} | |
return app.nodeOutputs[id].images; | |
} | |
else { | |
return node._images; | |
} | |
} | |
}); | |
} | |
} | |
}); | |