checkpoint

This commit is contained in:
Will Miao
2025-03-02 09:21:01 +08:00
parent 002823c6cf
commit 1c329ac6ca
6 changed files with 144 additions and 108 deletions

View File

@@ -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):

View File

@@ -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,)

View File

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

View File

@@ -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);

View File

@@ -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);

View File

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