Add custom nodes, Civitai loras (LFS), and vast.ai setup script
Some checks failed
Python Linting / Run Ruff (push) Has been cancelled
Python Linting / Run Pylint (push) Has been cancelled
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.10, [self-hosted Linux], stable) (push) Has been cancelled
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.11, [self-hosted Linux], stable) (push) Has been cancelled
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.12, [self-hosted Linux], stable) (push) Has been cancelled
Full Comfy CI Workflow Runs / test-unix-nightly (12.1, , linux, 3.11, [self-hosted Linux], nightly) (push) Has been cancelled
Execution Tests / test (macos-latest) (push) Has been cancelled
Execution Tests / test (ubuntu-latest) (push) Has been cancelled
Execution Tests / test (windows-latest) (push) Has been cancelled
Test server launches without errors / test (push) Has been cancelled
Unit Tests / test (macos-latest) (push) Has been cancelled
Unit Tests / test (ubuntu-latest) (push) Has been cancelled
Unit Tests / test (windows-2022) (push) Has been cancelled
Some checks failed
Python Linting / Run Ruff (push) Has been cancelled
Python Linting / Run Pylint (push) Has been cancelled
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.10, [self-hosted Linux], stable) (push) Has been cancelled
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.11, [self-hosted Linux], stable) (push) Has been cancelled
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.12, [self-hosted Linux], stable) (push) Has been cancelled
Full Comfy CI Workflow Runs / test-unix-nightly (12.1, , linux, 3.11, [self-hosted Linux], nightly) (push) Has been cancelled
Execution Tests / test (macos-latest) (push) Has been cancelled
Execution Tests / test (ubuntu-latest) (push) Has been cancelled
Execution Tests / test (windows-latest) (push) Has been cancelled
Test server launches without errors / test (push) Has been cancelled
Unit Tests / test (macos-latest) (push) Has been cancelled
Unit Tests / test (ubuntu-latest) (push) Has been cancelled
Unit Tests / test (windows-2022) (push) Has been cancelled
Includes 30 custom nodes committed directly, 7 Civitai-exclusive loras stored via Git LFS, and a setup script that installs all dependencies and downloads HuggingFace-hosted models on vast.ai. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
27
custom_nodes/comfyui-inspire-pack/js/common.js
Normal file
27
custom_nodes/comfyui-inspire-pack/js/common.js
Normal file
@@ -0,0 +1,27 @@
|
||||
import { api } from "../../scripts/api.js";
|
||||
|
||||
function nodeFeedbackHandler(event) {
|
||||
let nodes = app.graph._nodes_by_id;
|
||||
let node = nodes[event.detail.node_id];
|
||||
if(node) {
|
||||
if(event.detail.type == "text") {
|
||||
const w = node.widgets.find((w) => event.detail.widget_name === w.name);
|
||||
if(w) {
|
||||
w.value = event.detail.data;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
api.addEventListener("inspire-node-feedback", nodeFeedbackHandler);
|
||||
|
||||
|
||||
function nodeOutputLabelHandler(event) {
|
||||
let nodes = app.graph._nodes_by_id;
|
||||
let node = nodes[event.detail.node_id];
|
||||
if(node) {
|
||||
node.outputs[event.detail.output_idx].label = event.detail.label;
|
||||
}
|
||||
}
|
||||
|
||||
api.addEventListener("inspire-node-output-label", nodeOutputLabelHandler);
|
||||
71
custom_nodes/comfyui-inspire-pack/js/image_util.js
Normal file
71
custom_nodes/comfyui-inspire-pack/js/image_util.js
Normal file
@@ -0,0 +1,71 @@
|
||||
import { ComfyApp, app } from "../../scripts/app.js";
|
||||
|
||||
function load_image(str) {
|
||||
let base64String = canvas.toDataURL('image/png');
|
||||
let img = new Image();
|
||||
img.src = base64String;
|
||||
}
|
||||
|
||||
app.registerExtension({
|
||||
name: "Comfy.Inspire.img",
|
||||
|
||||
nodeCreated(node, app) {
|
||||
if(node.comfyClass == "LoadImage //Inspire") {
|
||||
let w = node.widgets.find(obj => obj.name === 'image_data');
|
||||
|
||||
Object.defineProperty(w, 'value', {
|
||||
set(v) {
|
||||
if(v != '[IMAGE DATA]')
|
||||
w._value = v;
|
||||
},
|
||||
get() {
|
||||
const stackTrace = new Error().stack;
|
||||
if(!stackTrace.includes('draw') && !stackTrace.includes('graphToPrompt') && stackTrace.includes('app.js')) {
|
||||
return "[IMAGE DATA]";
|
||||
}
|
||||
else {
|
||||
return w._value;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let set_img_act = (v) => {
|
||||
node._img = v;
|
||||
var canvas = document.createElement('canvas');
|
||||
canvas.width = v[0].width;
|
||||
canvas.height = v[0].height;
|
||||
|
||||
var context = canvas.getContext('2d');
|
||||
context.drawImage(v[0], 0, 0, v[0].width, v[0].height);
|
||||
|
||||
var base64Image = canvas.toDataURL('image/png');
|
||||
w.value = base64Image;
|
||||
};
|
||||
|
||||
Object.defineProperty(node, 'imgs', {
|
||||
set(v) {
|
||||
if (v && !v[0].complete) {
|
||||
let orig_onload = v[0].onload;
|
||||
v[0].onload = function(v2) {
|
||||
if(orig_onload)
|
||||
orig_onload();
|
||||
set_img_act(v);
|
||||
};
|
||||
}
|
||||
else {
|
||||
set_img_act(v);
|
||||
}
|
||||
},
|
||||
get() {
|
||||
if(this._img == undefined && w.value != '') {
|
||||
this._img = [new Image()];
|
||||
if(w.value && w.value != '[IMAGE DATA]')
|
||||
this._img[0].src = w.value;
|
||||
}
|
||||
|
||||
return this._img;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
36
custom_nodes/comfyui-inspire-pack/js/inspire-backend.js
Normal file
36
custom_nodes/comfyui-inspire-pack/js/inspire-backend.js
Normal file
@@ -0,0 +1,36 @@
|
||||
import { api } from "../../scripts/api.js";
|
||||
|
||||
async function refresh_data(node) {
|
||||
let response = await api.fetchApi('/inspire/cache/list');
|
||||
node.widgets[0].value = await response.text();
|
||||
}
|
||||
|
||||
async function remove_key(node, key) {
|
||||
await api.fetchApi(`/inspire/cache/remove?key=${key}`);
|
||||
node.widgets[1].value = '';
|
||||
refresh_data(node);
|
||||
}
|
||||
|
||||
async function clear_data(node) {
|
||||
await api.fetchApi('/inspire/cache/clear');
|
||||
refresh_data(node);
|
||||
}
|
||||
|
||||
async function set_cache_settings(node) {
|
||||
await api.fetchApi('/inspire/cache/settings', {
|
||||
method: "POST",
|
||||
headers: {"Content-Type": "application/json",},
|
||||
body: node.widgets[0].value,
|
||||
});
|
||||
refresh_data(node);
|
||||
}
|
||||
|
||||
export function register_cache_info(node, app) {
|
||||
if(node.comfyClass == "ShowCachedInfo //Inspire") {
|
||||
node.addWidget("button", "Remove Key", null, () => { remove_key(node, node.widgets[1].value); });
|
||||
node.addWidget("button", "Save Settings", null, () => { set_cache_settings(node); });
|
||||
node.addWidget("button", "Refresh", null, () => { refresh_data(node); });
|
||||
node.addWidget("button", "Clear", null, () => { clear_data(node); });
|
||||
refresh_data(node);
|
||||
}
|
||||
}
|
||||
301
custom_nodes/comfyui-inspire-pack/js/inspire-flex.js
Normal file
301
custom_nodes/comfyui-inspire-pack/js/inspire-flex.js
Normal file
@@ -0,0 +1,301 @@
|
||||
import { ComfyApp, app } from "../../scripts/app.js";
|
||||
|
||||
export function register_concat_conditionings_with_multiplier_node(nodeType, nodeData, app) {
|
||||
if (nodeData.name === 'ConcatConditioningsWithMultiplier //Inspire') {
|
||||
var input_name = "conditioning";
|
||||
|
||||
const onConnectionsChange = nodeType.prototype.onConnectionsChange;
|
||||
let this_handler = async function (type, index, connected, link_info) {
|
||||
let last_state = this.state_change_handling;
|
||||
try {
|
||||
this.state_change_handling = true;
|
||||
if(!link_info || link_info.type != 'CONDITIONING')
|
||||
return;
|
||||
|
||||
let self = this;
|
||||
|
||||
function get_input_count(prefix, linked_only) {
|
||||
let cnt = 0;
|
||||
for(let i in self.inputs) {
|
||||
if(linked_only && !self.inputs[i].link)
|
||||
continue;
|
||||
|
||||
if(self.inputs[i].name.startsWith(prefix))
|
||||
cnt+=1;
|
||||
}
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
function get_widget_count(prefix) {
|
||||
let cnt = 0;
|
||||
for(let i in self.widgets) {
|
||||
if(self.widgets[i].name.startsWith(prefix))
|
||||
cnt+=1;
|
||||
}
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
function get_unconnected() {
|
||||
let unconnected = [];
|
||||
for(let i in self.inputs) {
|
||||
let input = self.inputs[i];
|
||||
if(input.name.startsWith('conditioning')) {
|
||||
if(input.link == undefined)
|
||||
unconnected.push(i);
|
||||
}
|
||||
}
|
||||
|
||||
return unconnected;
|
||||
}
|
||||
|
||||
let unconnected = get_unconnected();
|
||||
|
||||
function renames() {
|
||||
let con_i = 1;
|
||||
|
||||
let rename_map = {};
|
||||
|
||||
for(let i in self.inputs) {
|
||||
let input = self.inputs[i];
|
||||
if(input.name.startsWith('conditioning')) {
|
||||
let orig_i = Number(input.name.substring(12));
|
||||
if(orig_i != con_i) {
|
||||
rename_map[orig_i] = con_i;
|
||||
input.name = 'conditioning'+con_i;
|
||||
}
|
||||
con_i++;
|
||||
}
|
||||
}
|
||||
|
||||
// update multiplier input
|
||||
for(let i in self.inputs) {
|
||||
let input = self.inputs[i];
|
||||
if(input.name.startsWith('multiplier')) {
|
||||
let orig_i = Number(input.name.substring(10));
|
||||
if(rename_map[orig_i]) {
|
||||
input.name = 'multiplier'+rename_map[orig_i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update multiplier widget
|
||||
for(let i in self.widgets) {
|
||||
let w = self.widgets[i];
|
||||
if(w.name.startsWith('multiplier')) {
|
||||
let orig_i = Number(w.name.substring(10));
|
||||
if(rename_map[orig_i]) {
|
||||
w.name = 'multiplier'+rename_map[orig_i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return con_i;
|
||||
}
|
||||
|
||||
function remove_multiplier_link(i, link_id) {
|
||||
let link = app.graph.links[link_id];
|
||||
const node = app.graph.getNodeById(link.origin_id);
|
||||
let x = node.outputs[link.origin_slot].links.findIndex((w) => w == link_id);
|
||||
node.outputs[link.origin_slot].links.splice(x, 1);
|
||||
self.disconnectInput(i);
|
||||
app.graph.links.splice(link_id, 1);
|
||||
}
|
||||
|
||||
async function remove_target_multiplier(target_name) {
|
||||
// remove strength from slot
|
||||
for(let i in self.inputs) {
|
||||
let input = self.inputs[i];
|
||||
if(input.name.startsWith(target_name)) {
|
||||
if(input.link) {
|
||||
remove_multiplier_link(i, input.link);
|
||||
}
|
||||
await self.removeInput(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const widget_index = self.widgets.findIndex((w) => w.name == target_name);
|
||||
self.widgets.splice(widget_index, 1);
|
||||
}
|
||||
|
||||
async function remove_garbage() {
|
||||
let unconnected = get_unconnected();
|
||||
|
||||
// remove unconnected conditionings
|
||||
while(unconnected.length > 0) {
|
||||
let last_one = unconnected.reverse()[0];
|
||||
self.removeInput(last_one);
|
||||
unconnected = get_unconnected();
|
||||
}
|
||||
|
||||
// remove dangling multipliers
|
||||
let conds = new Set();
|
||||
let muls = new Set();
|
||||
for(let i in self.inputs) {
|
||||
let input = self.inputs[i];
|
||||
if(input.link && input.name.startsWith('conditioning')) {
|
||||
let index = Number(input.name.substring(12));
|
||||
conds.add(index);
|
||||
}
|
||||
else if(input.name.startsWith('multiplier')) {
|
||||
let index = Number(input.name.substring(10));
|
||||
muls.add(index);
|
||||
}
|
||||
}
|
||||
for(let i in self.widgets) {
|
||||
let index = Number(self.widgets[i].name.substring(10));
|
||||
muls.add(index);
|
||||
}
|
||||
|
||||
let dangling_muls = [...muls].filter(x => !conds.has(x));
|
||||
while(dangling_muls.length > 0) {
|
||||
let remove_target = dangling_muls.pop();
|
||||
let target_name = `multiplier${remove_target}`;
|
||||
await remove_target_multiplier(target_name);
|
||||
}
|
||||
}
|
||||
|
||||
async function ensure_multipliers() {
|
||||
if(self.ensuring_multipliers) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
self.ensuring_multipliers = true;
|
||||
|
||||
let ncon = get_input_count('conditioning', true);
|
||||
let nmul = get_input_count('multiplier', false) + get_widget_count('multiplier');
|
||||
|
||||
if(ncon == 0 && nmul == 0)
|
||||
ncon = 1;
|
||||
|
||||
for(let i = nmul+1; i<=ncon; i++) {
|
||||
let config = { min: 0, max: 10, step: 0.1, round: 0.01, precision: 2 };
|
||||
|
||||
// NOTE: addWidget trigger calling ensure_multipliers
|
||||
let widget = await self.addWidget("number", `multiplier${i}`, 1.0, function (v) {
|
||||
if (config.round) {
|
||||
self.value = Math.round(v/config.round)*config.round;
|
||||
} else {
|
||||
self.value = v;
|
||||
}
|
||||
}, config);
|
||||
}
|
||||
}
|
||||
finally{
|
||||
self.ensuring_multipliers = null;
|
||||
}
|
||||
}
|
||||
|
||||
async function recover_multipliers() {
|
||||
if(self.recover_multipliers) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
self.recover_multipliers = true;
|
||||
for(let i = 1; i<self.widgets_values.length; i++) {
|
||||
let config = { min: 0, max: 10, step: 0.1, round: 0.01, precision: 2 };
|
||||
|
||||
// NOTE: addWidget trigger calling recover_multipliers
|
||||
let widget = await self.addWidget("number", `multiplier${i+1}`, 1.0, function (v) {
|
||||
if (config.round) {
|
||||
self.value = Math.round(v/config.round)*config.round;
|
||||
} else {
|
||||
self.value = v;
|
||||
}
|
||||
}, config);
|
||||
}
|
||||
}
|
||||
finally{
|
||||
self.recover_multipliers = null;
|
||||
}
|
||||
}
|
||||
|
||||
async function ensure_inputs() {
|
||||
if(get_unconnected() == 0) {
|
||||
let con_i = renames();
|
||||
self.addInput(`conditioning${con_i}`, self.outputs[0].type);
|
||||
}
|
||||
}
|
||||
|
||||
const stackTrace = new Error().stack;
|
||||
if(!stackTrace.includes('loadGraphData') && !stackTrace.includes('pasteFromClipboard')) {
|
||||
await remove_garbage();
|
||||
await ensure_inputs();
|
||||
}
|
||||
|
||||
if(!stackTrace.includes('loadGraphData')) {
|
||||
await ensure_multipliers();
|
||||
}
|
||||
else {
|
||||
await recover_multipliers();
|
||||
}
|
||||
|
||||
await this.setSize( this.computeSize() );
|
||||
}
|
||||
finally {
|
||||
this.state_change_handling = last_state;
|
||||
}
|
||||
}
|
||||
|
||||
nodeType.prototype.onConnectionsChange = this_handler;
|
||||
}
|
||||
}
|
||||
|
||||
function ensure_splitter_outputs(node, output_name, value, type) {
|
||||
if(node.outputs.length != (value + 1)) {
|
||||
while(node.outputs.length != (value + 1)) {
|
||||
if(node.outputs.length > value + 1) {
|
||||
node.removeOutput(node.outputs.length-1);
|
||||
}
|
||||
else {
|
||||
node.addOutput(`output${node.outputs.length+1}`, type);
|
||||
}
|
||||
}
|
||||
|
||||
for(let i in node.outputs) {
|
||||
let output = node.outputs[i];
|
||||
output.name = `${output_name} ${parseInt(i)+1}`;
|
||||
}
|
||||
|
||||
if(node.outputs[0].label == type || node.outputs[0].label == 'remained')
|
||||
delete node.outputs[0].label;
|
||||
|
||||
|
||||
let last_output = node.outputs[node.outputs.length-1];
|
||||
last_output.name = 'remained';
|
||||
}
|
||||
}
|
||||
|
||||
export function register_splitter(node, app) {
|
||||
if(node.comfyClass === 'ImageBatchSplitter //Inspire' || node.comfyClass === 'LatentBatchSplitter //Inspire') {
|
||||
let split_count = node.widgets[0];
|
||||
|
||||
let output_name = 'output';
|
||||
let output_type = "*";
|
||||
|
||||
if(node.comfyClass === 'ImageBatchSplitter //Inspire') {
|
||||
output_name = 'image';
|
||||
output_type = "IMAGE";
|
||||
}
|
||||
else if(node.comfyClass === 'LatentBatchSplitter //Inspire') {
|
||||
output_name = 'latent';
|
||||
output_type = "LATENT";
|
||||
}
|
||||
|
||||
ensure_splitter_outputs(node, output_name, split_count.value, output_type);
|
||||
|
||||
Object.defineProperty(split_count, "value", {
|
||||
set: async function(value) {
|
||||
if(value < 0 || value > 50)
|
||||
return;
|
||||
|
||||
ensure_splitter_outputs(node, output_name, value, output_type);
|
||||
},
|
||||
get: function() {
|
||||
return node.outputs.length - 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
7
custom_nodes/comfyui-inspire-pack/js/inspire-loop.js
Normal file
7
custom_nodes/comfyui-inspire-pack/js/inspire-loop.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import { inspireProgressBadge } from "./progress-badge.js"
|
||||
|
||||
export function register_loop_node(nodeType, nodeData, app) {
|
||||
if(nodeData.name == 'ForeachListEnd //Inspire') {
|
||||
inspireProgressBadge.addStatusHandler(nodeType);
|
||||
}
|
||||
}
|
||||
17
custom_nodes/comfyui-inspire-pack/js/inspire-pack.js
Normal file
17
custom_nodes/comfyui-inspire-pack/js/inspire-pack.js
Normal file
@@ -0,0 +1,17 @@
|
||||
import { ComfyApp, app } from "../../scripts/app.js";
|
||||
import { register_concat_conditionings_with_multiplier_node, register_splitter } from "./inspire-flex.js";
|
||||
import { register_cache_info } from "./inspire-backend.js";
|
||||
import { register_loop_node } from "./inspire-loop.js";
|
||||
|
||||
app.registerExtension({
|
||||
name: "Comfy.Inspire",
|
||||
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
||||
await register_concat_conditionings_with_multiplier_node(nodeType, nodeData, app);
|
||||
await register_loop_node(nodeType, nodeData, app);
|
||||
},
|
||||
|
||||
nodeCreated(node, app) {
|
||||
register_cache_info(node, app);
|
||||
register_splitter(node, app);
|
||||
}
|
||||
})
|
||||
165
custom_nodes/comfyui-inspire-pack/js/lora_block_weight.js
Normal file
165
custom_nodes/comfyui-inspire-pack/js/lora_block_weight.js
Normal file
@@ -0,0 +1,165 @@
|
||||
import { ComfyApp, app } from "../../scripts/app.js";
|
||||
|
||||
app.registerExtension({
|
||||
name: "Comfy.Inspire.LBW",
|
||||
|
||||
nodeCreated(node, app) {
|
||||
if(node.comfyClass == "LoraLoaderBlockWeight //Inspire" || node.comfyClass == "MakeLBW //Inspire") {
|
||||
// category filter
|
||||
const lora_names_widget = node.widgets[node.widgets.findIndex(obj => obj.name === 'lora_name')];
|
||||
var full_lora_list = lora_names_widget.options.values;
|
||||
const category_filter_widget = node.widgets[node.widgets.findIndex(obj => obj.name === 'category_filter')];
|
||||
|
||||
Object.defineProperty(lora_names_widget.options, "values", {
|
||||
set: (x) => {
|
||||
full_lora_list = x;
|
||||
},
|
||||
get: () => {
|
||||
if(category_filter_widget.value == 'All')
|
||||
return full_lora_list;
|
||||
|
||||
let l = full_lora_list.filter(x => x.startsWith(category_filter_widget.value));
|
||||
return l;
|
||||
}
|
||||
});
|
||||
|
||||
// vector selector
|
||||
let preset_i = 9;
|
||||
let vector_i = 10;
|
||||
|
||||
if(node.comfyClass == "MakeLBW //Inspire") {
|
||||
preset_i = 7;
|
||||
vector_i = 8;
|
||||
}
|
||||
|
||||
node._value = "Preset";
|
||||
|
||||
node.widgets[preset_i].callback = (v, canvas, node, pos, e) => {
|
||||
node.widgets[vector_i].value = node._value.split(':')[1];
|
||||
if(node.widgets_values) {
|
||||
node.widgets_values[vector_i] = node.widgets[preset_i].value;
|
||||
}
|
||||
}
|
||||
|
||||
Object.defineProperty(node.widgets[preset_i], "value", {
|
||||
set: (value) => {
|
||||
if(value != "Preset")
|
||||
node._value = value;
|
||||
},
|
||||
get: () => {
|
||||
return node._value;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if(node.comfyClass == "XY Input: Lora Block Weight //Inspire") {
|
||||
// category filter
|
||||
const lora_names_widget = node.widgets[node.widgets.findIndex(obj => obj.name === 'lora_name')];
|
||||
var full_lora_list = lora_names_widget.options.values;
|
||||
const category_filter_widget = node.widgets[node.widgets.findIndex(obj => obj.name === 'category_filter')];
|
||||
|
||||
Object.defineProperty(lora_names_widget.options, "values", {
|
||||
set: (x) => {
|
||||
full_lora_list = x;
|
||||
},
|
||||
get: () => {
|
||||
if(category_filter_widget.value == 'All')
|
||||
return full_lora_list;
|
||||
|
||||
let l = full_lora_list.filter(x => x.startsWith(category_filter_widget.value));
|
||||
return l;
|
||||
}
|
||||
});
|
||||
|
||||
// vector selector
|
||||
let preset_i = 9;
|
||||
let vector_i = 10;
|
||||
node._value = "Preset";
|
||||
|
||||
node.widgets[preset_i].callback = (v, canvas, node, pos, e) => {
|
||||
let value = node._value;
|
||||
if(!value.startsWith('@') && node.widgets[vector_i].value != "")
|
||||
node.widgets[vector_i].value += "\n";
|
||||
if(value.startsWith('@')) {
|
||||
let spec = value.split(':')[1];
|
||||
var n;
|
||||
var sub_n = null;
|
||||
var block = null;
|
||||
|
||||
if(isNaN(spec)) {
|
||||
let sub_spec = spec.split(',');
|
||||
|
||||
if(sub_spec.length != 3) {
|
||||
node.widgets_values[vector_i] = '!! SPEC ERROR !!';
|
||||
node._value = '';
|
||||
return;
|
||||
}
|
||||
|
||||
n = parseInt(sub_spec[0].trim());
|
||||
sub_n = parseInt(sub_spec[1].trim());
|
||||
block = parseInt(sub_spec[2].trim());
|
||||
}
|
||||
else {
|
||||
n = parseInt(spec.trim());
|
||||
}
|
||||
|
||||
node.widgets[vector_i].value = "";
|
||||
if(sub_n == null) {
|
||||
for(let i=1; i<=n; i++) {
|
||||
var temp = "";
|
||||
for(let j=1; j<=n; j++) {
|
||||
if(temp!='')
|
||||
temp += ',';
|
||||
if(j==i)
|
||||
temp += 'A';
|
||||
else
|
||||
temp += '0';
|
||||
}
|
||||
|
||||
node.widgets[vector_i].value += `B${i}:${temp}\n`;
|
||||
}
|
||||
}
|
||||
else {
|
||||
for(let i=1; i<=sub_n; i++) {
|
||||
var temp = "";
|
||||
for(let j=1; j<=n; j++) {
|
||||
if(temp!='')
|
||||
temp += ',';
|
||||
|
||||
if(block!=j)
|
||||
temp += '0';
|
||||
else {
|
||||
temp += ' ';
|
||||
for(let k=1; k<=sub_n; k++) {
|
||||
if(k==i)
|
||||
temp += 'A ';
|
||||
else
|
||||
temp += '0 ';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
node.widgets[vector_i].value += `B${block}.SUB${i}:${temp}\n`;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
node.widgets[vector_i].value += `${value}/${value.split(':')[0]}`;
|
||||
}
|
||||
if(node.widgets_values) {
|
||||
node.widgets_values[vector_i] = node.widgets[preset_i].value;
|
||||
}
|
||||
}
|
||||
|
||||
Object.defineProperty(node.widgets[preset_i], "value", {
|
||||
set: (value) => {
|
||||
if(value != 'Preset')
|
||||
node._value = value;
|
||||
},
|
||||
get: () => {
|
||||
return node._value;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
76
custom_nodes/comfyui-inspire-pack/js/progress-badge.js
Normal file
76
custom_nodes/comfyui-inspire-pack/js/progress-badge.js
Normal file
@@ -0,0 +1,76 @@
|
||||
import { api } from "../../scripts/api.js";
|
||||
|
||||
// copying from https://github.com/pythongosssss/ComfyUI-WD14-Tagger
|
||||
class InspireProgressBadge {
|
||||
constructor() {
|
||||
if (!window.__progress_badge__) {
|
||||
window.__progress_badge__ = Symbol("__inspire_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("inspire/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;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export const inspireProgressBadge = new InspireProgressBadge();
|
||||
|
||||
339
custom_nodes/comfyui-inspire-pack/js/prompt.js
Normal file
339
custom_nodes/comfyui-inspire-pack/js/prompt.js
Normal file
@@ -0,0 +1,339 @@
|
||||
import { ComfyApp, app } from "../../scripts/app.js";
|
||||
import { api } from "../../scripts/api.js";
|
||||
|
||||
let get_wildcards_list;
|
||||
let get_wildcard_label;
|
||||
let is_wildcard_label;
|
||||
let load_wildcard_status;
|
||||
|
||||
try {
|
||||
const ImpactPack = await import("../ComfyUI-Impact-Pack/impact-pack.js");
|
||||
console.log("[Inspire Pack] Impact Pack module loaded:", ImpactPack);
|
||||
get_wildcards_list = ImpactPack.get_wildcards_list;
|
||||
get_wildcard_label = ImpactPack.get_wildcard_label;
|
||||
is_wildcard_label = ImpactPack.is_wildcard_label;
|
||||
load_wildcard_status = ImpactPack.load_wildcard_status;
|
||||
console.log("[Inspire Pack] Functions imported:", {
|
||||
get_wildcards_list: !!get_wildcards_list,
|
||||
get_wildcard_label: !!get_wildcard_label,
|
||||
is_wildcard_label: !!is_wildcard_label,
|
||||
load_wildcard_status: !!load_wildcard_status
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
console.error("[Inspire Pack] Failed to import Impact Pack module:", error);
|
||||
}
|
||||
|
||||
// Fallback for get_wildcards_list
|
||||
if(!get_wildcards_list) {
|
||||
console.warn("[Inspire Pack] get_wildcards_list not available. Using fallback.");
|
||||
get_wildcards_list = () => {
|
||||
return ["Impact Pack isn't installed or needs browser cache refresh."];
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback for on-demand features (backward compatibility with older Impact Pack)
|
||||
if(!get_wildcard_label) {
|
||||
get_wildcard_label = () => { return "Select the Wildcard to add to the text"; };
|
||||
}
|
||||
if(!is_wildcard_label) {
|
||||
is_wildcard_label = (value) => { return value === "Select the Wildcard to add to the text"; };
|
||||
}
|
||||
if(!load_wildcard_status) {
|
||||
load_wildcard_status = async () => {}; // No-op for older versions
|
||||
}
|
||||
|
||||
let pb_cache = {};
|
||||
|
||||
async function get_prompt_builder_items(category) {
|
||||
if(pb_cache[category])
|
||||
return pb_cache[category];
|
||||
else {
|
||||
let res = await api.fetchApi(`/inspire/prompt_builder?category=${category}`);
|
||||
let data = await res.json();
|
||||
pb_cache[category] = data.presets;
|
||||
return data.presets;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
app.registerExtension({
|
||||
name: "Comfy.Inspire.Prompts",
|
||||
|
||||
nodeCreated(node, app) {
|
||||
if(node.comfyClass == "WildcardEncode //Inspire") {
|
||||
const wildcard_text_widget_index = node.widgets.findIndex((w) => w.name == 'wildcard_text');
|
||||
const populated_text_widget_index = node.widgets.findIndex((w) => w.name == 'populated_text');
|
||||
const mode_widget_index = node.widgets.findIndex((w) => w.name == 'mode');
|
||||
|
||||
const wildcard_text_widget = node.widgets[wildcard_text_widget_index];
|
||||
const populated_text_widget = node.widgets[populated_text_widget_index];
|
||||
|
||||
// lora selector, wildcard selector
|
||||
let combo_id = 5;
|
||||
|
||||
// lora
|
||||
node.widgets[combo_id].callback = (value, canvas, node, pos, e) => {
|
||||
let lora_name = node._value;
|
||||
if(lora_name.endsWith('.safetensors')) {
|
||||
lora_name = lora_name.slice(0, -12);
|
||||
}
|
||||
|
||||
wildcard_text_widget.value += `<lora:${lora_name}>`;
|
||||
}
|
||||
|
||||
Object.defineProperty(node.widgets[combo_id], "value", {
|
||||
set: (value) => {
|
||||
if (value !== "Select the LoRA to add to the text")
|
||||
node._value = value;
|
||||
},
|
||||
|
||||
get: () => { return "Select the LoRA to add to the text"; }
|
||||
});
|
||||
|
||||
// wildcard
|
||||
node.widgets[combo_id+1].callback = async (value, canvas, node, pos, e) => {
|
||||
if(wildcard_text_widget.value != '')
|
||||
wildcard_text_widget.value += ', '
|
||||
|
||||
wildcard_text_widget.value += node._wildcard_value;
|
||||
|
||||
// Reload wildcard status to update loaded count (Impact Pack staged feature)
|
||||
await load_wildcard_status();
|
||||
app.canvas.setDirty(true);
|
||||
}
|
||||
|
||||
Object.defineProperty(node.widgets[combo_id+1], "value", {
|
||||
set: (value) => {
|
||||
if (!is_wildcard_label(value))
|
||||
node._wildcard_value = value;
|
||||
},
|
||||
get: () => { return get_wildcard_label(); }
|
||||
});
|
||||
|
||||
Object.defineProperty(node.widgets[combo_id+1].options, "values", {
|
||||
set: (x) => {},
|
||||
get: () => {
|
||||
return get_wildcards_list();
|
||||
}
|
||||
});
|
||||
|
||||
// Preventing validation errors from occurring in any situation.
|
||||
node.widgets[combo_id].serializeValue = () => { return "Select the LoRA to add to the text"; }
|
||||
node.widgets[combo_id+1].serializeValue = () => {
|
||||
// Always serialize as the default label (not the dynamic on-demand label)
|
||||
return "Select the Wildcard to add to the text";
|
||||
}
|
||||
|
||||
// wildcard populating
|
||||
populated_text_widget.inputEl.disabled = true;
|
||||
const mode_widget = node.widgets[mode_widget_index];
|
||||
|
||||
// mode combo
|
||||
Object.defineProperty(mode_widget, "value", {
|
||||
set: (value) => {
|
||||
if(value == true)
|
||||
node._mode_value = "populate";
|
||||
else if(value == false)
|
||||
node._mode_value = "fixed";
|
||||
else
|
||||
node._mode_value = value; // combo value
|
||||
|
||||
populated_text_widget.inputEl.disabled = node._mode_value == 'populate';
|
||||
},
|
||||
get: () => {
|
||||
if(node._mode_value != undefined)
|
||||
return node._mode_value;
|
||||
else
|
||||
return 'populate';
|
||||
}
|
||||
});
|
||||
}
|
||||
else if(node.comfyClass == "MakeBasicPipe //Inspire") {
|
||||
const pos_wildcard_text_widget = node.widgets.find((w) => w.name == 'positive_wildcard_text');
|
||||
const pos_populated_text_widget = node.widgets.find((w) => w.name == 'positive_populated_text');
|
||||
const neg_wildcard_text_widget = node.widgets.find((w) => w.name == 'negative_wildcard_text');
|
||||
const neg_populated_text_widget = node.widgets.find((w) => w.name == 'negative_populated_text');
|
||||
|
||||
const mode_widget = node.widgets.find((w) => w.name == 'wildcard_mode');
|
||||
const direction_widget = node.widgets.find((w) => w.name == 'Add selection to');
|
||||
|
||||
// lora selector, wildcard selector
|
||||
let combo_id = 5;
|
||||
|
||||
node.widgets[combo_id].callback = (value, canvas, node, pos, e) => {
|
||||
let lora_name = node._lora_value;
|
||||
if (lora_name.endsWith('.safetensors')) {
|
||||
lora_name = lora_name.slice(0, -12);
|
||||
}
|
||||
|
||||
if(direction_widget.value) {
|
||||
pos_wildcard_text_widget.value += `<lora:${lora_name}>`;
|
||||
}
|
||||
else {
|
||||
neg_wildcard_text_widget.value += `<lora:${lora_name}>`;
|
||||
}
|
||||
}
|
||||
Object.defineProperty(node.widgets[combo_id], "value", {
|
||||
set: (value) => {
|
||||
if (value !== "Select the LoRA to add to the text")
|
||||
node._lora_value = value;
|
||||
},
|
||||
get: () => { return "Select the LoRA to add to the text"; }
|
||||
});
|
||||
|
||||
node.widgets[combo_id+1].callback = async (value, canvas, node, pos, e) => {
|
||||
let w = null;
|
||||
if(direction_widget.value) {
|
||||
w = pos_wildcard_text_widget;
|
||||
}
|
||||
else {
|
||||
w = neg_wildcard_text_widget;
|
||||
}
|
||||
|
||||
if(w.value != '')
|
||||
w.value += ', '
|
||||
|
||||
w.value += node._wildcard_value;
|
||||
|
||||
// Reload wildcard status to update loaded count (Impact Pack staged feature)
|
||||
await load_wildcard_status();
|
||||
app.canvas.setDirty(true);
|
||||
}
|
||||
|
||||
Object.defineProperty(node.widgets[combo_id+1], "value", {
|
||||
set: (value) => {
|
||||
if (!is_wildcard_label(value))
|
||||
node._wildcard_value = value;
|
||||
},
|
||||
get: () => { return get_wildcard_label(); }
|
||||
});
|
||||
|
||||
Object.defineProperty(node.widgets[combo_id+1].options, "values", {
|
||||
set: (x) => {},
|
||||
get: () => {
|
||||
return get_wildcards_list();
|
||||
}
|
||||
});
|
||||
|
||||
// Preventing validation errors from occurring in any situation.
|
||||
node.widgets[combo_id].serializeValue = () => { return "Select the LoRA to add to the text"; }
|
||||
node.widgets[combo_id+1].serializeValue = () => {
|
||||
// Always serialize as the default label (not the dynamic on-demand label)
|
||||
return "Select the Wildcard to add to the text";
|
||||
}
|
||||
|
||||
// wildcard populating
|
||||
pos_populated_text_widget.inputEl.disabled = true;
|
||||
neg_populated_text_widget.inputEl.disabled = true;
|
||||
|
||||
// mode combo
|
||||
Object.defineProperty(mode_widget, "value", {
|
||||
set: (value) => {
|
||||
if(value == true)
|
||||
node._mode_value = "populate";
|
||||
else if(value == false)
|
||||
node._mode_value = "fixed";
|
||||
else
|
||||
node._mode_value = value; // combo value
|
||||
|
||||
pos_populated_text_widget.inputEl.disabled = node._mode_value == 'populate';
|
||||
neg_populated_text_widget.inputEl.disabled = node._mode_value == 'populate';
|
||||
},
|
||||
get: () => {
|
||||
if(node._mode_value != undefined)
|
||||
return node._mode_value;
|
||||
else
|
||||
return 'populate';
|
||||
}
|
||||
});
|
||||
}
|
||||
else if(node.comfyClass == "PromptBuilder //Inspire") {
|
||||
const preset_widget = node.widgets[node.widgets.findIndex(obj => obj.name === 'preset')];
|
||||
const category_widget = node.widgets[node.widgets.findIndex(obj => obj.name === 'category')];
|
||||
|
||||
Object.defineProperty(preset_widget.options, "values", {
|
||||
set: (x) => {},
|
||||
get: () => {
|
||||
get_prompt_builder_items(category_widget.value);
|
||||
if(pb_cache[category_widget.value] == undefined) {
|
||||
return ["#PRESET"];
|
||||
}
|
||||
return pb_cache[category_widget.value];
|
||||
}
|
||||
});
|
||||
|
||||
preset_widget.callback = (value, canvas, node, pos, e) => {
|
||||
if(node.widgets[2].value) {
|
||||
node.widgets[2].value += ', ';
|
||||
}
|
||||
|
||||
const y = node._preset_value.split(':');
|
||||
if(y.length == 2)
|
||||
node.widgets[2].value += y[1].trim();
|
||||
else
|
||||
node.widgets[2].value += node._preset_value.trim();
|
||||
}
|
||||
|
||||
Object.defineProperty(preset_widget, "value", {
|
||||
set: (value) => {
|
||||
if (value !== "#PRESET")
|
||||
node._preset_value = value;
|
||||
},
|
||||
get: () => { return '#PRESET'; }
|
||||
});
|
||||
|
||||
preset_widget.serializeValue = (workflowNode, widgetIndex) => { return "#PRESET"; };
|
||||
}
|
||||
else if(node.comfyClass == "SeedExplorer //Inspire"
|
||||
|| node.comfyClass == "RegionalSeedExplorerMask //Inspire"
|
||||
|| node.comfyClass == "RegionalSeedExplorerColorMask //Inspire") {
|
||||
const prompt_widget = node.widgets[node.widgets.findIndex(obj => obj.name === 'seed_prompt')];
|
||||
const seed_widget = node.widgets[node.widgets.findIndex(obj => obj.name === 'additional_seed')];
|
||||
const strength_widget = node.widgets[node.widgets.findIndex(obj => obj.name === 'additional_strength')];
|
||||
|
||||
let allow_init_seed = node.comfyClass == "SeedExplorer //Inspire";
|
||||
|
||||
node.addWidget("button", "Add to prompt", null, () => {
|
||||
if(!prompt_widget.value?.trim() && allow_init_seed) {
|
||||
prompt_widget.value = ''+seed_widget.value;
|
||||
}
|
||||
else {
|
||||
if(prompt_widget.value?.trim())
|
||||
prompt_widget.value += ', ';
|
||||
|
||||
prompt_widget.value += `${seed_widget.value}:${strength_widget.value.toFixed(2)}`;
|
||||
seed_widget.value += 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
const original_queuePrompt = api.queuePrompt;
|
||||
async function queuePrompt_with_widget_idxs(number, { output, workflow }, ...args) {
|
||||
workflow.widget_idx_map = {};
|
||||
|
||||
for(let i in app.graph._nodes_by_id) {
|
||||
let widgets = app.graph._nodes_by_id[i].widgets;
|
||||
if(widgets) {
|
||||
for(let j in widgets) {
|
||||
if(['seed', 'noise_seed', 'sampler_name', 'scheduler'].includes(widgets[j].name)
|
||||
&& widgets[j].type != 'converted-widget') {
|
||||
if(workflow.widget_idx_map[i] == undefined) {
|
||||
workflow.widget_idx_map[i] = {};
|
||||
}
|
||||
|
||||
workflow.widget_idx_map[i][widgets[j].name] = parseInt(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return await original_queuePrompt.call(api, number, { output, workflow }, ...args);
|
||||
}
|
||||
|
||||
api.queuePrompt = queuePrompt_with_widget_idxs;
|
||||
64
custom_nodes/comfyui-inspire-pack/js/regional.js
Normal file
64
custom_nodes/comfyui-inspire-pack/js/regional.js
Normal file
@@ -0,0 +1,64 @@
|
||||
import { ComfyApp, app } from "../../scripts/app.js";
|
||||
import { ComfyDialog, $el } from "../../scripts/ui.js";
|
||||
import { api } from "../../scripts/api.js";
|
||||
|
||||
app.registerExtension({
|
||||
name: "Comfy.Inspire.Regional",
|
||||
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
||||
if (nodeData.name === 'ApplyRegionalIPAdapters //Inspire') {
|
||||
var input_name = "input";
|
||||
var base_slot = 0;
|
||||
|
||||
switch(nodeData.name) {
|
||||
case 'ApplyRegionalIPAdapters //Inspire':
|
||||
input_name = "regional_ipadapter";
|
||||
base_slot = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
const onConnectionsChange = nodeType.prototype.onConnectionsChange;
|
||||
nodeType.prototype.onConnectionsChange = function (type, index, connected, link_info) {
|
||||
if(!link_info || type == 2)
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (!connected && (this.inputs.length > base_slot+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')) {
|
||||
this.removeInput(index);
|
||||
}
|
||||
}
|
||||
|
||||
let slot_i = 1;
|
||||
for (let i = base_slot; i < this.inputs.length; i++) {
|
||||
let input_i = this.inputs[i];
|
||||
input_i.name = `${input_name}${slot_i}`
|
||||
slot_i++;
|
||||
}
|
||||
|
||||
let last_slot = this.inputs[this.inputs.length - 1];
|
||||
if (last_slot.link != undefined) {
|
||||
this.addInput(`${input_name}${slot_i}`, this.inputs[base_slot].type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}});
|
||||
28
custom_nodes/comfyui-inspire-pack/js/seed.js
Normal file
28
custom_nodes/comfyui-inspire-pack/js/seed.js
Normal file
@@ -0,0 +1,28 @@
|
||||
import { api } from "../../scripts/api.js";
|
||||
|
||||
function globalSeedHandler(event) {
|
||||
let nodes = app.graph._nodes_by_id;
|
||||
|
||||
for(let i in nodes) {
|
||||
let node = nodes[i];
|
||||
|
||||
if(node.type == 'GlobalSeed //Inspire') {
|
||||
if(node.widgets) {
|
||||
const w = node.widgets.find((w) => w.name == 'value');
|
||||
const last_w = node.widgets.find((w) => w.name == 'last_seed');
|
||||
last_w.value = w.value;
|
||||
if(event.detail.value != null)
|
||||
w.value = event.detail.value;
|
||||
}
|
||||
}
|
||||
else
|
||||
if(node.widgets) {
|
||||
const w = node.widgets.find((w) => (w.name == 'seed' || w.name == 'noise_seed') && w.type == 'number');
|
||||
if(w && event.detail.seed_map[node.id] != undefined) {
|
||||
w.value = event.detail.seed_map[node.id];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
api.addEventListener("inspire-global-seed", globalSeedHandler);
|
||||
Reference in New Issue
Block a user