mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-21 21:22:11 -03:00
refactor(lora-provider): extract mode change logic to shared TypeScript module
- Extract common mode change logic from lora_randomizer.js and lora_stacker.js into new mode-change-handler.ts TypeScript module - Add LORA_PROVIDER_NODE_TYPES constant to centralize LoRA provider node types - Update getActiveLorasFromNode in utils.js to support Lora Cycler's cycler_config widget (single current_lora_filename) - Update getConnectedInputStackers and updateDownstreamLoaders to use isLoraProviderNode helper instead of hardcoded class checks - Register mode change handlers in main.ts for all LoRA provider nodes (Lora Stacker, Lora Randomizer, Lora Cycler) - Add value change callback to Lora Cycler widget to trigger updateDownstreamLoaders when current_lora_filename changes - Remove duplicate mode change logic from lora_stacker.js - Delete lora_randomizer.js (logic now centralized) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,11 @@ import LoraRandomizerWidget from '@/components/LoraRandomizerWidget.vue'
|
||||
import LoraCyclerWidget from '@/components/LoraCyclerWidget.vue'
|
||||
import JsonDisplayWidget from '@/components/JsonDisplayWidget.vue'
|
||||
import type { LoraPoolConfig, LegacyLoraPoolConfig, RandomizerConfig, CyclerConfig } from './composables/types'
|
||||
import {
|
||||
setupModeChangeHandler,
|
||||
createModeChangeCallback,
|
||||
LORA_PROVIDER_NODE_TYPES
|
||||
} from './mode-change-handler'
|
||||
|
||||
const LORA_POOL_WIDGET_MIN_WIDTH = 500
|
||||
const LORA_POOL_WIDGET_MIN_HEIGHT = 400
|
||||
@@ -237,10 +242,15 @@ function createLoraCyclerWidget(node) {
|
||||
return internalValue
|
||||
},
|
||||
setValue(v: CyclerConfig) {
|
||||
const oldFilename = internalValue?.current_lora_filename
|
||||
internalValue = v
|
||||
if (typeof widget.onSetValue === 'function') {
|
||||
widget.onSetValue(v)
|
||||
}
|
||||
// Update downstream loaders when the active LoRA filename changes
|
||||
if (oldFilename !== v?.current_lora_filename) {
|
||||
updateDownstreamLoaders(node)
|
||||
}
|
||||
},
|
||||
serialize: true,
|
||||
getMinHeight() {
|
||||
@@ -250,7 +260,12 @@ function createLoraCyclerWidget(node) {
|
||||
)
|
||||
|
||||
widget.updateConfig = (v: CyclerConfig) => {
|
||||
const oldFilename = internalValue?.current_lora_filename
|
||||
internalValue = v
|
||||
// Update downstream loaders when the active LoRA filename changes
|
||||
if (oldFilename !== v?.current_lora_filename) {
|
||||
updateDownstreamLoaders(node)
|
||||
}
|
||||
}
|
||||
|
||||
// Add method to get pool config from connected node
|
||||
@@ -392,8 +407,30 @@ app.registerExtension({
|
||||
},
|
||||
|
||||
// Add display-only widget to Debug Metadata node
|
||||
// Register mode change handlers for LoRA provider nodes
|
||||
// @ts-ignore
|
||||
async beforeRegisterNodeDef(nodeType, nodeData) {
|
||||
const comfyClass = nodeType.comfyClass
|
||||
|
||||
// Register mode change handlers for LoRA provider nodes
|
||||
if (LORA_PROVIDER_NODE_TYPES.includes(comfyClass)) {
|
||||
const originalOnNodeCreated = nodeType.prototype.onNodeCreated
|
||||
|
||||
nodeType.prototype.onNodeCreated = function () {
|
||||
originalOnNodeCreated?.apply(this, arguments)
|
||||
|
||||
// Create node-specific callback for Lora Stacker (updates direct trigger toggles)
|
||||
const nodeSpecificCallback = comfyClass === "Lora Stacker (LoraManager)"
|
||||
? (activeLoraNames: Set<string>) => updateConnectedTriggerWords(this, activeLoraNames)
|
||||
: undefined
|
||||
|
||||
// Create and set up the mode change handler
|
||||
const onModeChange = createModeChangeCallback(this, updateDownstreamLoaders, nodeSpecificCallback)
|
||||
setupModeChangeHandler(this, onModeChange)
|
||||
}
|
||||
}
|
||||
|
||||
// Add the JSON display widget to Debug Metadata node
|
||||
if (nodeData.name === 'Debug Metadata (LoraManager)') {
|
||||
const onNodeCreated = nodeType.prototype.onNodeCreated
|
||||
|
||||
|
||||
153
vue-widgets/src/mode-change-handler.ts
Normal file
153
vue-widgets/src/mode-change-handler.ts
Normal file
@@ -0,0 +1,153 @@
|
||||
/**
|
||||
* Mode change handler for LoRA provider nodes.
|
||||
*
|
||||
* Provides common mode change logic for nodes that provide LoRA configurations:
|
||||
* - Lora Stacker (LoraManager)
|
||||
* - Lora Randomizer (LoraManager)
|
||||
* - Lora Cycler (LoraManager)
|
||||
*/
|
||||
|
||||
/**
|
||||
* List of node types that act as LoRA providers in the workflow chain.
|
||||
* These nodes can be traversed when collecting active LoRAs and can trigger
|
||||
* downstream loader updates when their mode or active LoRAs change.
|
||||
*/
|
||||
export const LORA_PROVIDER_NODE_TYPES = [
|
||||
"Lora Stacker (LoraManager)",
|
||||
"Lora Randomizer (LoraManager)",
|
||||
"Lora Cycler (LoraManager)",
|
||||
] as const;
|
||||
|
||||
export type LoraProviderNodeType = typeof LORA_PROVIDER_NODE_TYPES[number];
|
||||
|
||||
/**
|
||||
* Check if a node class is a LoRA provider node.
|
||||
*/
|
||||
export function isLoraProviderNode(comfyClass: string): comfyClass is LoraProviderNodeType {
|
||||
return LORA_PROVIDER_NODE_TYPES.includes(comfyClass as LoraProviderNodeType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract active LoRA filenames from a node based on its type.
|
||||
*
|
||||
* For Lora Stacker and Lora Randomizer: extracts from lorasWidget (array of loras, filtered by active)
|
||||
* For Lora Cycler: extracts from cycler_config widget (single current_lora_filename)
|
||||
*/
|
||||
export function getActiveLorasFromNodeByType(node: any): Set<string> {
|
||||
const comfyClass = node?.comfyClass;
|
||||
|
||||
if (comfyClass === "Lora Cycler (LoraManager)") {
|
||||
return extractFromCyclerConfig(node);
|
||||
}
|
||||
|
||||
// Default: use lorasWidget (works for Stacker and Randomizer)
|
||||
return extractFromLorasWidget(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract active LoRAs from a node's lorasWidget.
|
||||
* Used by Lora Stacker and Lora Randomizer.
|
||||
*/
|
||||
function extractFromLorasWidget(node: any): Set<string> {
|
||||
const activeLoraNames = new Set<string>();
|
||||
const lorasWidget = node.lorasWidget || node.widgets?.find((w: any) => w.name === 'loras');
|
||||
|
||||
if (lorasWidget?.value) {
|
||||
lorasWidget.value.forEach((lora: any) => {
|
||||
if (lora.active) {
|
||||
activeLoraNames.add(lora.name);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return activeLoraNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the active LoRA from a Lora Cycler node.
|
||||
* The Cycler has only one active LoRA at a time, stored in cycler_config.current_lora_filename.
|
||||
*/
|
||||
function extractFromCyclerConfig(node: any): Set<string> {
|
||||
const activeLoraNames = new Set<string>();
|
||||
const cyclerWidget = node.widgets?.find((w: any) => w.name === 'cycler_config');
|
||||
|
||||
if (cyclerWidget?.value?.current_lora_filename) {
|
||||
activeLoraNames.add(cyclerWidget.value.current_lora_filename);
|
||||
}
|
||||
|
||||
return activeLoraNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a mode value represents an active node.
|
||||
* Active modes: 0 (Always), 3 (On Trigger)
|
||||
* Inactive modes: 2 (Never), 4 (Bypass)
|
||||
*/
|
||||
export function isNodeActive(mode: number | undefined): boolean {
|
||||
return mode === undefined || mode === 0 || mode === 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup a mode change handler for a node.
|
||||
*
|
||||
* Intercepts the mode property setter to trigger a callback when the mode changes.
|
||||
* This is needed because ComfyUI sets the mode property directly without using a setter.
|
||||
*
|
||||
* @param node - The node to set up the handler for
|
||||
* @param onModeChange - Callback function called when mode changes (receives newMode and oldMode)
|
||||
*/
|
||||
export function setupModeChangeHandler(
|
||||
node: any,
|
||||
onModeChange: (newMode: number, oldMode: number) => void
|
||||
): void {
|
||||
let _mode = node.mode;
|
||||
|
||||
Object.defineProperty(node, 'mode', {
|
||||
get() {
|
||||
return _mode;
|
||||
},
|
||||
set(value: number) {
|
||||
const oldValue = _mode;
|
||||
_mode = value;
|
||||
|
||||
if (oldValue !== value) {
|
||||
onModeChange(value, oldValue);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a mode change callback that updates downstream loaders.
|
||||
*
|
||||
* This is the standard callback used by all LoRA provider nodes.
|
||||
* When mode changes:
|
||||
* 1. Determine if the node is active (mode 0 or 3)
|
||||
* 2. Get active LoRAs (empty set if inactive)
|
||||
* 3. Call the optional node-specific callback (e.g., updateConnectedTriggerWords for Stacker)
|
||||
* 4. Update downstream loaders
|
||||
*
|
||||
* @param node - The node instance
|
||||
* @param updateDownstreamLoaders - Function to update downstream loaders (from utils.js)
|
||||
* @param nodeSpecificCallback - Optional callback for node-specific behavior
|
||||
*/
|
||||
export function createModeChangeCallback(
|
||||
node: any,
|
||||
updateDownstreamLoaders: (node: any) => void,
|
||||
nodeSpecificCallback?: (activeLoraNames: Set<string>) => void
|
||||
): (newMode: number, oldMode: number) => void {
|
||||
return (newMode: number, _oldMode: number) => {
|
||||
const isNodeCurrentlyActive = isNodeActive(newMode);
|
||||
const activeLoraNames = isNodeCurrentlyActive
|
||||
? getActiveLorasFromNodeByType(node)
|
||||
: new Set<string>();
|
||||
|
||||
// Node-specific handling (e.g., Lora Stacker's direct trigger toggle updates)
|
||||
if (nodeSpecificCallback) {
|
||||
nodeSpecificCallback(activeLoraNames);
|
||||
}
|
||||
|
||||
// Always update downstream loaders
|
||||
updateDownstreamLoaders(node);
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user