mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-25 07:05:43 -03:00
checkpoint
This commit is contained in:
@@ -54,6 +54,8 @@ class LoraManagerLoader:
|
|||||||
loaded_loras = []
|
loaded_loras = []
|
||||||
all_trigger_words = []
|
all_trigger_words = []
|
||||||
|
|
||||||
|
print(f"kwargs: {kwargs}")
|
||||||
|
|
||||||
if 'loras' in kwargs:
|
if 'loras' in kwargs:
|
||||||
for lora in kwargs['loras']:
|
for lora in kwargs['loras']:
|
||||||
if not lora.get('active', False):
|
if not lora.get('active', False):
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
|
import json
|
||||||
from server import PromptServer # type: ignore
|
from server import PromptServer # type: ignore
|
||||||
from .utils import FlexibleOptionalInputType, any_type
|
from .utils import FlexibleOptionalInputType, any_type
|
||||||
import json
|
|
||||||
|
|
||||||
class TriggerWordToggle:
|
class TriggerWordToggle:
|
||||||
NAME = "TriggerWord Toggle (LoraManager)"
|
NAME = "TriggerWord Toggle (LoraManager)"
|
||||||
@@ -30,12 +30,18 @@ class TriggerWordToggle:
|
|||||||
"message": trigger_words
|
"message": trigger_words
|
||||||
})
|
})
|
||||||
|
|
||||||
|
print(f"kwargs: {kwargs}")
|
||||||
|
|
||||||
filtered_triggers = trigger_words
|
filtered_triggers = trigger_words
|
||||||
|
|
||||||
if 'hidden_trigger_words' in kwargs:
|
if 'toggle_trigger_words' in kwargs:
|
||||||
try:
|
try:
|
||||||
# Parse the hidden trigger words JSON
|
# Get trigger word toggle data
|
||||||
trigger_data = json.loads(kwargs['hidden_trigger_words']) if isinstance(kwargs['hidden_trigger_words'], str) else kwargs['hidden_trigger_words']
|
trigger_data = kwargs['toggle_trigger_words']
|
||||||
|
|
||||||
|
# Convert to list if it's a JSON string
|
||||||
|
if isinstance(trigger_data, str):
|
||||||
|
trigger_data = json.loads(trigger_data)
|
||||||
|
|
||||||
# Create dictionaries to track active state of words
|
# Create dictionaries to track active state of words
|
||||||
active_state = {item['text']: item.get('active', False) for item in trigger_data}
|
active_state = {item['text']: item.get('active', False) for item in trigger_data}
|
||||||
@@ -43,7 +49,7 @@ class TriggerWordToggle:
|
|||||||
# Split original trigger words
|
# Split original trigger words
|
||||||
original_words = [word.strip() for word in trigger_words.split(',')]
|
original_words = [word.strip() for word in trigger_words.split(',')]
|
||||||
|
|
||||||
# Filter words: keep those not in hidden_trigger_words or those that are active
|
# Filter words: keep those not in toggle_trigger_words or those that are active
|
||||||
filtered_words = [word for word in original_words if word not in active_state or active_state[word]]
|
filtered_words = [word for word in original_words if word not in active_state or active_state[word]]
|
||||||
|
|
||||||
# Join them in the same format as input
|
# Join them in the same format as input
|
||||||
@@ -55,7 +61,4 @@ class TriggerWordToggle:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error processing trigger words: {e}")
|
print(f"Error processing trigger words: {e}")
|
||||||
|
|
||||||
for key, value in kwargs.items():
|
|
||||||
print(f"{key}: {value}")
|
|
||||||
|
|
||||||
return (filtered_triggers,)
|
return (filtered_triggers,)
|
||||||
@@ -58,7 +58,7 @@ app.registerExtension({
|
|||||||
const result = addLorasWidget(node, "loras", {
|
const result = addLorasWidget(node, "loras", {
|
||||||
defaultVal: mergedLoras // Pass object directly
|
defaultVal: mergedLoras // Pass object directly
|
||||||
}, (value) => {
|
}, (value) => {
|
||||||
// TODO
|
console.log("Loras data updated:", value);
|
||||||
});
|
});
|
||||||
|
|
||||||
node.lorasWidget = result.widget;
|
node.lorasWidget = result.widget;
|
||||||
@@ -71,7 +71,10 @@ app.registerExtension({
|
|||||||
const mergedLoras = mergeLoras(value, currentLoras);
|
const mergedLoras = mergeLoras(value, currentLoras);
|
||||||
|
|
||||||
node.lorasWidget.value = mergedLoras;
|
node.lorasWidget.value = mergedLoras;
|
||||||
|
// node.graph.setDirtyCanvas(true, true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
console.log("node: ", node);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -277,8 +277,8 @@ export function addLorasWidget(node, name, opts, callback) {
|
|||||||
widget.value = newValue;
|
widget.value = newValue;
|
||||||
widget.callback?.(newValue);
|
widget.callback?.(newValue);
|
||||||
|
|
||||||
// Re-render
|
// Remove re-render call - this is causing double callback
|
||||||
renderLoras(newValue, widget);
|
// renderLoras(newValue, widget);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add label to toggle all
|
// Add label to toggle all
|
||||||
@@ -342,8 +342,8 @@ export function addLorasWidget(node, name, opts, callback) {
|
|||||||
widget.value = newValue;
|
widget.value = newValue;
|
||||||
widget.callback?.(newValue);
|
widget.callback?.(newValue);
|
||||||
|
|
||||||
// Re-render
|
// Remove re-render call - this is causing double callback
|
||||||
renderLoras(newValue, widget);
|
// renderLoras(newValue, widget);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -404,15 +404,15 @@ export function addLorasWidget(node, name, opts, callback) {
|
|||||||
const loraIndex = lorasData.findIndex(l => l.name === name);
|
const loraIndex = lorasData.findIndex(l => l.name === name);
|
||||||
|
|
||||||
if (loraIndex >= 0) {
|
if (loraIndex >= 0) {
|
||||||
lorasData[loraIndex].strength = Math.max(0, lorasData[loraIndex].strength - 0.05).toFixed(2);
|
lorasData[loraIndex].strength = (lorasData[loraIndex].strength - 0.05).toFixed(2);
|
||||||
|
|
||||||
// Update value and trigger widget callback
|
// Update value and trigger widget callback
|
||||||
const newValue = formatLoraValue(lorasData);
|
const newValue = formatLoraValue(lorasData);
|
||||||
widget.value = newValue;
|
widget.value = newValue;
|
||||||
widget.callback?.(newValue);
|
widget.callback?.(newValue);
|
||||||
|
|
||||||
// Re-render
|
// Remove re-render call - this is causing double callback
|
||||||
renderLoras(newValue, widget);
|
// renderLoras(newValue, widget);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -478,8 +478,8 @@ export function addLorasWidget(node, name, opts, callback) {
|
|||||||
widget.value = newLorasValue;
|
widget.value = newLorasValue;
|
||||||
widget.callback?.(newLorasValue);
|
widget.callback?.(newLorasValue);
|
||||||
|
|
||||||
// 重新渲染
|
// Remove re-render call - this is causing double callback
|
||||||
renderLoras(newLorasValue, widget);
|
// renderLoras(newLorasValue, widget);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -504,8 +504,8 @@ export function addLorasWidget(node, name, opts, callback) {
|
|||||||
widget.value = newValue;
|
widget.value = newValue;
|
||||||
widget.callback?.(newValue);
|
widget.callback?.(newValue);
|
||||||
|
|
||||||
// Re-render
|
// Remove re-render call - this is causing double callback
|
||||||
renderLoras(newValue, widget);
|
// renderLoras(newValue, widget);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -559,10 +559,10 @@ export function addLorasWidget(node, name, opts, callback) {
|
|||||||
|
|
||||||
widget.callback = callback;
|
widget.callback = callback;
|
||||||
|
|
||||||
widget.computeSize = (width, height) => {
|
// widget.computeSize = (width) => {
|
||||||
// console.log("loras_widget computeSize called: ", width, height);
|
// // console.log("loras_widget computeSize called: ", width, height);
|
||||||
return [400, 200];
|
// return [400, 200];
|
||||||
};
|
// };
|
||||||
|
|
||||||
// Render initial state
|
// Render initial state
|
||||||
renderLoras(widgetValue, widget);
|
renderLoras(widgetValue, widget);
|
||||||
|
|||||||
@@ -13,38 +13,50 @@ export function addTagsWidget(node, name, opts, callback) {
|
|||||||
width: "100%",
|
width: "100%",
|
||||||
});
|
});
|
||||||
|
|
||||||
// Initialize default value
|
// Initialize default value as array
|
||||||
const defaultValue = opts?.defaultVal || "[]";
|
const defaultValue = opts?.defaultVal || [];
|
||||||
|
let initialTagsData = [];
|
||||||
|
|
||||||
// Parse trigger words and states from string
|
try {
|
||||||
const parseTagsValue = (value) => {
|
// Convert string input to array if needed
|
||||||
if (!value) return [];
|
initialTagsData = typeof defaultValue === 'string' ?
|
||||||
|
JSON.parse(defaultValue) : (Array.isArray(defaultValue) ? defaultValue : []);
|
||||||
|
} catch (e) {
|
||||||
|
console.warn("Invalid default tags data format", e);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
// Normalize tag data to ensure consistent format
|
||||||
return JSON.parse(value);
|
const normalizeTagData = (data) => {
|
||||||
} catch (e) {
|
if (!Array.isArray(data)) return [];
|
||||||
// If it's not valid JSON, try legacy format or return empty array
|
|
||||||
console.warn("Invalid tags data format", e);
|
return data.map(item => {
|
||||||
return [];
|
// If it's already in the correct format, return as is
|
||||||
}
|
if (item && typeof item === 'object' && 'text' in item) {
|
||||||
|
return {
|
||||||
|
text: item.text,
|
||||||
|
active: item.active !== undefined ? item.active : true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// If it's just a string, convert to object
|
||||||
|
else if (typeof item === 'string') {
|
||||||
|
return { text: item, active: true };
|
||||||
|
}
|
||||||
|
// Default fallback
|
||||||
|
return { text: String(item), active: true };
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Format tags data back to string
|
// Function to render tags from array data
|
||||||
const formatTagsValue = (tagsData) => {
|
const renderTags = (tagsData, widget) => {
|
||||||
return JSON.stringify(tagsData);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Function to render tags from data
|
|
||||||
const renderTags = (value, widget) => {
|
|
||||||
// Clear existing tags
|
// Clear existing tags
|
||||||
while (container.firstChild) {
|
while (container.firstChild) {
|
||||||
container.removeChild(container.firstChild);
|
container.removeChild(container.firstChild);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse the tags data
|
// Ensure we're working with normalized data
|
||||||
const tagsData = parseTagsValue(value);
|
const normalizedTags = normalizeTagData(tagsData);
|
||||||
|
|
||||||
tagsData.forEach((tagData) => {
|
normalizedTags.forEach((tagData) => {
|
||||||
const { text, active } = tagData;
|
const { text, active } = tagData;
|
||||||
const tagEl = document.createElement("div");
|
const tagEl = document.createElement("div");
|
||||||
tagEl.className = "comfy-tag";
|
tagEl.className = "comfy-tag";
|
||||||
@@ -59,17 +71,16 @@ export function addTagsWidget(node, name, opts, callback) {
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
// Toggle active state for this tag
|
// Toggle active state for this tag
|
||||||
const tagsData = parseTagsValue(widget.value);
|
const updatedTags = [...widget.value];
|
||||||
const tagIndex = tagsData.findIndex((t) => t.text === text);
|
const tagIndex = updatedTags.findIndex((t) => t.text === text);
|
||||||
|
|
||||||
if (tagIndex >= 0) {
|
if (tagIndex >= 0) {
|
||||||
tagsData[tagIndex].active = !tagsData[tagIndex].active;
|
updatedTags[tagIndex].active = !updatedTags[tagIndex].active;
|
||||||
updateTagStyle(tagEl, tagsData[tagIndex].active);
|
updateTagStyle(tagEl, updatedTags[tagIndex].active);
|
||||||
|
|
||||||
// Update value and trigger widget callback
|
// Update widget value and trigger callback
|
||||||
const newValue = formatTagsValue(tagsData);
|
widget.value = updatedTags;
|
||||||
widget.value = newValue;
|
widget.callback?.(updatedTags);
|
||||||
widget.callback?.(newValue);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -123,8 +134,8 @@ export function addTagsWidget(node, name, opts, callback) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store the value in a variable to avoid recursion
|
// Store the value as array
|
||||||
let widgetValue = defaultValue;
|
let widgetValue = normalizeTagData(initialTagsData);
|
||||||
|
|
||||||
// Create widget with initial properties
|
// Create widget with initial properties
|
||||||
const widget = node.addDOMWidget(name, "tags", container, {
|
const widget = node.addDOMWidget(name, "tags", container, {
|
||||||
@@ -132,43 +143,43 @@ export function addTagsWidget(node, name, opts, callback) {
|
|||||||
return widgetValue;
|
return widgetValue;
|
||||||
},
|
},
|
||||||
setValue: function(v) {
|
setValue: function(v) {
|
||||||
// Format the incoming value if it's not in the expected JSON format
|
// Handle input formats but always normalize to array
|
||||||
let parsedValue = v;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Try to parse as JSON first
|
if (typeof v === "string") {
|
||||||
if (typeof v === "string" && (v.startsWith("[") || v.startsWith("{"))) {
|
// If JSON string, parse it
|
||||||
JSON.parse(v);
|
if (v.startsWith("[") || v.startsWith("{")) {
|
||||||
// If no error, it's already valid JSON
|
const parsed = JSON.parse(v);
|
||||||
parsedValue = v;
|
widgetValue = normalizeTagData(parsed);
|
||||||
} else if (typeof v === "string") {
|
} else {
|
||||||
// If it's a comma-separated string of trigger words, convert to tag format
|
// If it's a comma-separated string of tags
|
||||||
const triggerWords = v
|
const tagStrings = v
|
||||||
.split(",")
|
.split(",")
|
||||||
.map((word) => word.trim())
|
.map((word) => word.trim())
|
||||||
.filter((word) => word);
|
.filter((word) => word);
|
||||||
|
|
||||||
// Get existing tags to merge with new ones
|
// Preserve active states from existing tags where possible
|
||||||
const existingTags = parseTagsValue(widgetValue || "[]");
|
const existingTagsMap = {};
|
||||||
const existingTagsMap = {};
|
widgetValue.forEach((tag) => {
|
||||||
existingTags.forEach((tag) => {
|
existingTagsMap[tag.text] = tag.active;
|
||||||
existingTagsMap[tag.text] = tag.active;
|
});
|
||||||
});
|
|
||||||
|
|
||||||
// Create new tags with merging logic
|
widgetValue = tagStrings.map((text) => ({
|
||||||
const newTags = triggerWords.map((word) => ({
|
text,
|
||||||
text: word,
|
active: text in existingTagsMap ? existingTagsMap[text] : true,
|
||||||
active: word in existingTagsMap ? existingTagsMap[word] : true,
|
}));
|
||||||
}));
|
}
|
||||||
|
} else if (Array.isArray(v)) {
|
||||||
parsedValue = JSON.stringify(newTags);
|
// Directly use array input but ensure proper format
|
||||||
|
widgetValue = normalizeTagData(v);
|
||||||
|
} else {
|
||||||
|
// Default to empty array for invalid inputs
|
||||||
|
widgetValue = [];
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn("Error formatting tags value:", e);
|
console.warn("Error formatting tags value:", e);
|
||||||
// Keep the original value if there's an error
|
// Keep existing value if there's an error
|
||||||
}
|
}
|
||||||
|
|
||||||
widgetValue = parsedValue || ""; // Store in our local variable instead
|
|
||||||
renderTags(widgetValue, widget);
|
renderTags(widgetValue, widget);
|
||||||
},
|
},
|
||||||
getHeight: function() {
|
getHeight: function() {
|
||||||
@@ -187,10 +198,6 @@ export function addTagsWidget(node, name, opts, callback) {
|
|||||||
widget.options.setValue(defaultValue);
|
widget.options.setValue(defaultValue);
|
||||||
|
|
||||||
widget.callback = callback;
|
widget.callback = callback;
|
||||||
widget.serializeValue = function(workflowNode, widgetIndex) {
|
|
||||||
console.log("Serializing tags widget", widget.value);
|
|
||||||
return widget.value;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Render initial state
|
// Render initial state
|
||||||
renderTags(widgetValue, widget);
|
renderTags(widgetValue, widget);
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { app } from "../../scripts/app.js";
|
import { app } from "../../scripts/app.js";
|
||||||
import { api } from "../../scripts/api.js";
|
import { api } from "../../scripts/api.js";
|
||||||
import { addTagsWidget } from "./tags_widget.js";
|
import { addTagsWidget } from "./tags_widget.js";
|
||||||
import { hideWidgetForGood } from "./utils.js";
|
|
||||||
|
|
||||||
// TriggerWordToggle extension for ComfyUI
|
// TriggerWordToggle extension for ComfyUI
|
||||||
app.registerExtension({
|
app.registerExtension({
|
||||||
@@ -19,22 +18,14 @@ app.registerExtension({
|
|||||||
if (node.comfyClass === "TriggerWord Toggle (LoraManager)") {
|
if (node.comfyClass === "TriggerWord Toggle (LoraManager)") {
|
||||||
// Enable widget serialization
|
// Enable widget serialization
|
||||||
node.serialize_widgets = true;
|
node.serialize_widgets = true;
|
||||||
node.size = [400, 200];
|
|
||||||
|
|
||||||
// Wait for node to be properly initialized
|
// Wait for node to be properly initialized
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
// add a hidden widget for excluded trigger words to send to Python
|
|
||||||
node.hiddenWidget = node.addWidget("text", "hidden_trigger_words", "", (value) => {
|
|
||||||
// empty callback
|
|
||||||
});
|
|
||||||
hideWidgetForGood(node, node.hiddenWidget);
|
|
||||||
|
|
||||||
// Get the widget object directly from the returned object
|
// Get the widget object directly from the returned object
|
||||||
const result = addTagsWidget(node, "trigger_words", {
|
const result = addTagsWidget(node, "toggle_trigger_words", {
|
||||||
defaultVal: "[]"
|
defaultVal: []
|
||||||
}, (value) => {
|
}, (value) => {
|
||||||
// update value of hidden widget
|
|
||||||
node.hiddenWidget.value = value;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
node.tagWidget = result.widget;
|
node.tagWidget = result.widget;
|
||||||
@@ -42,13 +33,11 @@ app.registerExtension({
|
|||||||
// Restore saved value if exists
|
// Restore saved value if exists
|
||||||
if (node.widgets_values && node.widgets_values.length > 0) {
|
if (node.widgets_values && node.widgets_values.length > 0) {
|
||||||
// 0 is input, 1 is hidden widget, 2 is tag widget
|
// 0 is input, 1 is hidden widget, 2 is tag widget
|
||||||
const savedValue = node.widgets_values[2];
|
const savedValue = node.widgets_values[1];
|
||||||
if (savedValue) {
|
if (savedValue) {
|
||||||
result.widget.value = savedValue;
|
result.widget.value = savedValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("trigger word toggle node: ", node);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -68,8 +57,40 @@ app.registerExtension({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (node.tagWidget) {
|
if (node.tagWidget) {
|
||||||
// Use widget.value setter instead of setValue
|
// Convert comma-separated message to tag object format
|
||||||
node.tagWidget.value = message;
|
if (typeof message === 'string') {
|
||||||
|
// Get existing tags to preserve active states
|
||||||
|
const existingTags = node.tagWidget.value || [];
|
||||||
|
|
||||||
|
const tempWidget = node.tagWidget;
|
||||||
|
console.log("height of node: ", node.size[1]);
|
||||||
|
// console.log("tempWidget: ", tempWidget);
|
||||||
|
console.log("tagWidget height: ", tempWidget.options.getHeight());
|
||||||
|
|
||||||
|
// Create a map of existing tags and their active states
|
||||||
|
const existingTagMap = {};
|
||||||
|
existingTags.forEach(tag => {
|
||||||
|
existingTagMap[tag.text] = tag.active;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Process the incoming message
|
||||||
|
const tagArray = message
|
||||||
|
.split(',')
|
||||||
|
.map(word => word.trim())
|
||||||
|
.filter(word => word)
|
||||||
|
.map(word => ({
|
||||||
|
text: word,
|
||||||
|
// Keep previous active state if exists, otherwise default to true
|
||||||
|
active: existingTagMap[word] !== undefined ? existingTagMap[word] : true
|
||||||
|
}));
|
||||||
|
|
||||||
|
node.tagWidget.value = tagArray;
|
||||||
|
console.log("tagWidget new height: ", tempWidget.options.getHeight());
|
||||||
|
const computed = node.computeSize();
|
||||||
|
node.size[1] = computed[1];
|
||||||
|
console.log("computed height: ", computed[1]);
|
||||||
|
node.setDirtyCanvas(true, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user