253 lines
10 KiB
JavaScript
253 lines
10 KiB
JavaScript
/**
|
|
* Workflow Integration for ComfyUI_ControlFreak
|
|
* Handles saving and loading mappings from the workflow JSON
|
|
*/
|
|
|
|
import { app } from "../../../../scripts/app.js";
|
|
import { contextProvider } from "./contextProvider.js";
|
|
import { eventBus } from "./eventBus.js";
|
|
|
|
export class WorkflowIntegration {
|
|
constructor(appInstance, mappingEngine) {
|
|
this.app = appInstance || app;
|
|
this.mappingEngine = mappingEngine;
|
|
|
|
// Set up event listeners for graph changes
|
|
this._setupEventListeners();
|
|
}
|
|
|
|
/**
|
|
* Set up event listeners for ComfyUI workflow events
|
|
* @private
|
|
*/
|
|
_setupEventListeners() {
|
|
// Add event listener for when graph is exported to JSON
|
|
const originalGraphToJSON = this.app.graph.toJSON;
|
|
this.app.graph.toJSON = (data) => {
|
|
const result = originalGraphToJSON.call(this.app.graph, data);
|
|
|
|
try {
|
|
// Add our mappings data to the workflow JSON
|
|
if (!result.extra) {
|
|
result.extra = {};
|
|
}
|
|
|
|
// Get the mapping engine to export mappings
|
|
const engine = this.mappingEngine || contextProvider.get('mappingEngine');
|
|
if (engine) {
|
|
const mappings = engine.exportMappingsForWorkflow();
|
|
|
|
// Only add to workflow if we have mappings
|
|
if (mappings && mappings.length > 0) {
|
|
result.extra.controlFreak = {
|
|
// Only include active profile data
|
|
activeProfile: engine.getActiveProfile(),
|
|
// Only include mappings for active profile
|
|
mappings: mappings
|
|
};
|
|
|
|
// Ensure the marker node is in the workflow
|
|
this._ensureMarkerNodeInWorkflow(result);
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error("WorkflowIntegration: Error adding mapping data to workflow JSON:", error);
|
|
}
|
|
|
|
return result;
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Adds a hidden marker node to the workflow JSON if it doesn't exist
|
|
* This ensures ComfyUI-Manager can detect the dependency
|
|
* @private
|
|
* @param {Object} workflowJSON - The workflow JSON object
|
|
*/
|
|
_ensureMarkerNodeInWorkflow(workflowJSON) {
|
|
try {
|
|
// Check if any ControlFreak node already exists
|
|
const hasMarkerNode = workflowJSON.nodes.some(node =>
|
|
node.type === "ControlFreak"
|
|
);
|
|
|
|
// If no marker node exists, add one at a hidden position
|
|
if (!hasMarkerNode) {
|
|
// Add a hidden marker node to indicate ControlFreak is required
|
|
const markerNode = {
|
|
id: `ControlFreak_Marker_${Date.now()}`,
|
|
type: "ControlFreak",
|
|
pos: [-9999, -9999], // Position far off-screen
|
|
inputs: {},
|
|
outputs: {},
|
|
properties: { "hidden": true }
|
|
};
|
|
|
|
workflowJSON.nodes.push(markerNode);
|
|
}
|
|
} catch (error) {
|
|
console.error("WorkflowIntegration: Error adding marker node to workflow:", error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sync the profile and mappings to the workflow data
|
|
* Call this when mappings have changed to update the workflow JSON
|
|
*/
|
|
syncProfileToWorkflow() {
|
|
if (!this.app || !this.app.graph) return;
|
|
|
|
try {
|
|
if (!this.app.graph.extra) {
|
|
this.app.graph.extra = {};
|
|
}
|
|
|
|
// Get the mapping engine to export mappings
|
|
const engine = this.mappingEngine || contextProvider.get('mappingEngine');
|
|
if (engine) {
|
|
const mappings = engine.exportMappingsForWorkflow();
|
|
|
|
// Only update if we have mappings
|
|
if (mappings && mappings.length > 0) {
|
|
this.app.graph.extra.controlFreak = {
|
|
activeProfile: engine.getActiveProfile(),
|
|
mappings: mappings
|
|
};
|
|
|
|
// Ensure a marker node exists in the graph
|
|
this._addMarkerNodeToGraph();
|
|
|
|
// Trigger a graph change event so it gets saved
|
|
this.app.graph.change();
|
|
|
|
// Emit event for profile synced to workflow
|
|
eventBus.emit('workflow:profileSynced', {
|
|
profile: engine.getActiveProfile(),
|
|
mappingsCount: mappings.length
|
|
});
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error("WorkflowIntegration: Error syncing profile to workflow:", error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add a marker node to the current graph if none exists
|
|
* @private
|
|
*/
|
|
_addMarkerNodeToGraph() {
|
|
try {
|
|
// Check if the marker node already exists in the graph
|
|
const hasMarkerNode = this.app.graph.nodes.some(node =>
|
|
node.type === "ControlFreak"
|
|
);
|
|
|
|
// If no marker node exists, add one
|
|
if (!hasMarkerNode && window.LiteGraph) {
|
|
// Create a marker node
|
|
let node = LiteGraph.createNode("ControlFreak");
|
|
// Position off-screen
|
|
node.pos = [-9999, -9999];
|
|
// Add to graph
|
|
this.app.graph.add(node);
|
|
}
|
|
} catch (error) {
|
|
console.error("WorkflowIntegration: Error adding marker node to graph:", error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Read the profile and mappings from the workflow data
|
|
* Call this when a workflow is loaded to get the mapping data
|
|
*/
|
|
syncProfileFromWorkflow() {
|
|
if (!this.app?.graph?.extra?.controlFreak) return;
|
|
|
|
try {
|
|
const workflowData = this.app.graph.extra.controlFreak;
|
|
|
|
// Get the mapping engine
|
|
const engine = this.mappingEngine || contextProvider.get('mappingEngine');
|
|
if (engine) {
|
|
// Import mappings from workflow by adding them individually
|
|
if (workflowData.mappings && Array.isArray(workflowData.mappings)) {
|
|
let importedCount = 0;
|
|
workflowData.mappings.forEach(mapping => {
|
|
// The addMapping method now handles duplicate checks
|
|
engine.addMapping(mapping);
|
|
// Note: We might need to track if addMapping actually added it if we need an accurate count
|
|
importedCount++; // Assuming addMapping handles duplicates and we count all attempts
|
|
});
|
|
|
|
// Save mappings after potentially adding multiple
|
|
engine.saveMappings();
|
|
|
|
// Ensure marker node exists
|
|
this._addMarkerNodeToGraph();
|
|
|
|
// Emit event after import and save
|
|
// Retrieve eventBus from context provider to ensure availability
|
|
const busForImport = contextProvider.get('eventBus');
|
|
if (busForImport) {
|
|
busForImport.emit('mappings:imported', workflowData.mappings);
|
|
} else {
|
|
console.warn("WorkflowIntegration: eventBus not found in contextProvider when emitting mappings:imported");
|
|
}
|
|
}
|
|
|
|
// Set active profile
|
|
if (workflowData.activeProfile) {
|
|
engine.setActiveProfile(workflowData.activeProfile);
|
|
}
|
|
|
|
// Emit event for profile synced from workflow
|
|
// Retrieve eventBus from context provider to ensure availability
|
|
const bus = contextProvider.get('eventBus');
|
|
if (bus) {
|
|
bus.emit('workflow:profileLoaded', {
|
|
profile: workflowData.activeProfile || 'default',
|
|
mappingsCount: workflowData.mappings ? workflowData.mappings.length : 0
|
|
});
|
|
} else {
|
|
console.warn("WorkflowIntegration: eventBus not found in contextProvider during syncProfileFromWorkflow");
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error("WorkflowIntegration: Error loading profile from workflow:", error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle graph loaded event
|
|
* Called by ComfyUI when a workflow is loaded
|
|
* @param {Object} appInstance - The ComfyUI app instance
|
|
*/
|
|
static handleGraphLoaded(appInstance) {
|
|
// Get mapping engine from context
|
|
const mappingEngine = contextProvider.get('mappingEngine');
|
|
|
|
if (mappingEngine && appInstance.graph) {
|
|
try {
|
|
// Load mappings from workflow
|
|
mappingEngine.loadMappings();
|
|
|
|
// Ensure all mappings are properly reconnected
|
|
if (mappingEngine.reconnectMappings) {
|
|
mappingEngine.reconnectMappings();
|
|
}
|
|
|
|
// Check if we have mappings and add marker node if needed
|
|
const workflowIntegration = new WorkflowIntegration(appInstance, mappingEngine);
|
|
if (appInstance.graph.extra?.controlFreak?.mappings?.length > 0) {
|
|
workflowIntegration._addMarkerNodeToGraph();
|
|
}
|
|
|
|
// Emit event for graph loaded
|
|
eventBus.emit('workflow:graphLoaded');
|
|
} catch (error) {
|
|
console.error("WorkflowIntegration: Error handling graph loaded:", error);
|
|
}
|
|
}
|
|
}
|
|
}
|