mirror of
https://github.com/Azornes/Comfyui-LayerForge.git
synced 2026-03-21 20:52:12 -03:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
11dd554204 | ||
|
|
9f21ff13ae |
@@ -6,6 +6,7 @@ import { uploadCanvasAsImage, uploadImageBlob } from "./utils/ImageUploadUtils.j
|
||||
import { processImageToMask } from "./utils/MaskProcessingUtils.js";
|
||||
import { convertToImage } from "./utils/ImageUtils.js";
|
||||
import { updateNodePreview } from "./utils/PreviewUtils.js";
|
||||
import { validateAndFixClipspace } from "./utils/ClipspaceUtils.js";
|
||||
const log = createModuleLogger('SAMDetectorIntegration');
|
||||
/**
|
||||
* SAM Detector Integration for LayerForge
|
||||
@@ -324,6 +325,8 @@ async function handleSAMDetectorResult(node, resultImage) {
|
||||
node.samOriginalImgSrc = null;
|
||||
}
|
||||
}
|
||||
// Store original onClipspaceEditorSave function to restore later
|
||||
let originalOnClipspaceEditorSave = null;
|
||||
// Function to setup SAM Detector hook in menu options
|
||||
export function setupSAMDetectorHook(node, options) {
|
||||
// Hook into "Open in SAM Detector" with delay since Impact Pack adds it asynchronously
|
||||
@@ -347,8 +350,39 @@ export function setupSAMDetectorHook(node, options) {
|
||||
// Set the image to the node for clipspace
|
||||
node.imgs = [uploadResult.imageElement];
|
||||
node.clipspaceImg = uploadResult.imageElement;
|
||||
// Ensure proper clipspace structure for updated ComfyUI
|
||||
if (!ComfyApp.clipspace) {
|
||||
ComfyApp.clipspace = {};
|
||||
}
|
||||
// Set up clipspace with proper indices
|
||||
ComfyApp.clipspace.imgs = [uploadResult.imageElement];
|
||||
ComfyApp.clipspace.selectedIndex = 0;
|
||||
ComfyApp.clipspace.combinedIndex = 0;
|
||||
ComfyApp.clipspace.img_paste_mode = 'selected';
|
||||
// Copy to ComfyUI clipspace
|
||||
ComfyApp.copyToClipspace(node);
|
||||
// Override onClipspaceEditorSave to fix clipspace structure before pasteFromClipspace
|
||||
if (!originalOnClipspaceEditorSave) {
|
||||
originalOnClipspaceEditorSave = ComfyApp.onClipspaceEditorSave;
|
||||
ComfyApp.onClipspaceEditorSave = function () {
|
||||
log.debug("SAM Detector onClipspaceEditorSave called, using unified clipspace validation");
|
||||
// Use the unified clipspace validation function
|
||||
const isValid = validateAndFixClipspace();
|
||||
if (!isValid) {
|
||||
log.error("Clipspace validation failed, cannot proceed with paste");
|
||||
return;
|
||||
}
|
||||
// Call the original function
|
||||
if (originalOnClipspaceEditorSave) {
|
||||
originalOnClipspaceEditorSave.call(ComfyApp);
|
||||
}
|
||||
// Restore the original function after use
|
||||
if (originalOnClipspaceEditorSave) {
|
||||
ComfyApp.onClipspaceEditorSave = originalOnClipspaceEditorSave;
|
||||
originalOnClipspaceEditorSave = null;
|
||||
}
|
||||
};
|
||||
}
|
||||
// Start monitoring for SAM Detector results
|
||||
startSAMDetectorMonitoring(node);
|
||||
log.info("Canvas automatically sent to clipspace and monitoring started");
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { createModuleLogger } from "./LoggerUtils.js";
|
||||
import { showNotification, showInfoNotification } from "./NotificationUtils.js";
|
||||
import { withErrorHandling, createValidationError, createNetworkError, createFileError } from "../ErrorHandler.js";
|
||||
import { safeClipspacePaste } from "./ClipspaceUtils.js";
|
||||
// @ts-ignore
|
||||
import { api } from "../../../scripts/api.js";
|
||||
// @ts-ignore
|
||||
import { ComfyApp } from "../../../scripts/app.js";
|
||||
const log = createModuleLogger('ClipboardManager');
|
||||
export class ClipboardManager {
|
||||
constructor(canvas) {
|
||||
@@ -39,7 +38,12 @@ export class ClipboardManager {
|
||||
*/
|
||||
this.tryClipspacePaste = withErrorHandling(async (addMode) => {
|
||||
log.info("Attempting to paste from ComfyUI Clipspace");
|
||||
ComfyApp.pasteFromClipspace(this.canvas.node);
|
||||
// Use the unified clipspace validation and paste function
|
||||
const pasteSuccess = safeClipspacePaste(this.canvas.node);
|
||||
if (!pasteSuccess) {
|
||||
log.debug("Safe clipspace paste failed");
|
||||
return false;
|
||||
}
|
||||
if (this.canvas.node.imgs && this.canvas.node.imgs.length > 0) {
|
||||
const clipspaceImage = this.canvas.node.imgs[0];
|
||||
if (clipspaceImage && clipspaceImage.src) {
|
||||
|
||||
99
js/utils/ClipspaceUtils.js
Normal file
99
js/utils/ClipspaceUtils.js
Normal file
@@ -0,0 +1,99 @@
|
||||
import { createModuleLogger } from "./LoggerUtils.js";
|
||||
// @ts-ignore
|
||||
import { ComfyApp } from "../../../scripts/app.js";
|
||||
const log = createModuleLogger('ClipspaceUtils');
|
||||
/**
|
||||
* Validates and fixes ComfyUI clipspace structure to prevent 'Cannot read properties of undefined' errors
|
||||
* @returns {boolean} - True if clipspace is valid and ready to use, false otherwise
|
||||
*/
|
||||
export function validateAndFixClipspace() {
|
||||
log.debug("Validating and fixing clipspace structure");
|
||||
// Check if clipspace exists
|
||||
if (!ComfyApp.clipspace) {
|
||||
log.debug("ComfyUI clipspace is not available");
|
||||
return false;
|
||||
}
|
||||
// Validate clipspace structure
|
||||
if (!ComfyApp.clipspace.imgs || ComfyApp.clipspace.imgs.length === 0) {
|
||||
log.debug("ComfyUI clipspace has no images");
|
||||
return false;
|
||||
}
|
||||
log.debug("Current clipspace state:", {
|
||||
hasImgs: !!ComfyApp.clipspace.imgs,
|
||||
imgsLength: ComfyApp.clipspace.imgs?.length,
|
||||
selectedIndex: ComfyApp.clipspace.selectedIndex,
|
||||
combinedIndex: ComfyApp.clipspace.combinedIndex,
|
||||
img_paste_mode: ComfyApp.clipspace.img_paste_mode
|
||||
});
|
||||
// Ensure required indices are set
|
||||
if (ComfyApp.clipspace.selectedIndex === undefined || ComfyApp.clipspace.selectedIndex === null) {
|
||||
ComfyApp.clipspace.selectedIndex = 0;
|
||||
log.debug("Fixed clipspace selectedIndex to 0");
|
||||
}
|
||||
if (ComfyApp.clipspace.combinedIndex === undefined || ComfyApp.clipspace.combinedIndex === null) {
|
||||
ComfyApp.clipspace.combinedIndex = 0;
|
||||
log.debug("Fixed clipspace combinedIndex to 0");
|
||||
}
|
||||
if (!ComfyApp.clipspace.img_paste_mode) {
|
||||
ComfyApp.clipspace.img_paste_mode = 'selected';
|
||||
log.debug("Fixed clipspace img_paste_mode to 'selected'");
|
||||
}
|
||||
// Ensure indices are within bounds
|
||||
const maxIndex = ComfyApp.clipspace.imgs.length - 1;
|
||||
if (ComfyApp.clipspace.selectedIndex > maxIndex) {
|
||||
ComfyApp.clipspace.selectedIndex = maxIndex;
|
||||
log.debug(`Fixed clipspace selectedIndex to ${maxIndex} (max available)`);
|
||||
}
|
||||
if (ComfyApp.clipspace.combinedIndex > maxIndex) {
|
||||
ComfyApp.clipspace.combinedIndex = maxIndex;
|
||||
log.debug(`Fixed clipspace combinedIndex to ${maxIndex} (max available)`);
|
||||
}
|
||||
// Verify the image at combinedIndex exists and has src
|
||||
const combinedImg = ComfyApp.clipspace.imgs[ComfyApp.clipspace.combinedIndex];
|
||||
if (!combinedImg || !combinedImg.src) {
|
||||
log.debug("Image at combinedIndex is missing or has no src, trying to find valid image");
|
||||
// Try to use the first available image
|
||||
for (let i = 0; i < ComfyApp.clipspace.imgs.length; i++) {
|
||||
if (ComfyApp.clipspace.imgs[i] && ComfyApp.clipspace.imgs[i].src) {
|
||||
ComfyApp.clipspace.combinedIndex = i;
|
||||
log.debug(`Fixed combinedIndex to ${i} (first valid image)`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Final check - if still no valid image found
|
||||
const finalImg = ComfyApp.clipspace.imgs[ComfyApp.clipspace.combinedIndex];
|
||||
if (!finalImg || !finalImg.src) {
|
||||
log.error("No valid images found in clipspace after attempting fixes");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
log.debug("Final clipspace structure:", {
|
||||
selectedIndex: ComfyApp.clipspace.selectedIndex,
|
||||
combinedIndex: ComfyApp.clipspace.combinedIndex,
|
||||
img_paste_mode: ComfyApp.clipspace.img_paste_mode,
|
||||
imgsLength: ComfyApp.clipspace.imgs?.length,
|
||||
combinedImgSrc: ComfyApp.clipspace.imgs[ComfyApp.clipspace.combinedIndex]?.src?.substring(0, 50) + '...'
|
||||
});
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Safely calls ComfyApp.pasteFromClipspace after validating clipspace structure
|
||||
* @param {any} node - The ComfyUI node to paste to
|
||||
* @returns {boolean} - True if paste was successful, false otherwise
|
||||
*/
|
||||
export function safeClipspacePaste(node) {
|
||||
log.debug("Attempting safe clipspace paste");
|
||||
if (!validateAndFixClipspace()) {
|
||||
log.debug("Clipspace validation failed, cannot paste");
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
ComfyApp.pasteFromClipspace(node);
|
||||
log.debug("Successfully called pasteFromClipspace");
|
||||
return true;
|
||||
}
|
||||
catch (error) {
|
||||
log.error("Error calling pasteFromClipspace:", error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
[project]
|
||||
name = "layerforge"
|
||||
description = "Photoshop-like layered canvas editor to your ComfyUI workflow. This node is perfect for complex compositing, inpainting, and outpainting, featuring multi-layer support, masking, blend modes, and precise transformations. Includes optional AI-powered background removal for streamlined image editing."
|
||||
version = "1.5.3"
|
||||
version = "1.5.4"
|
||||
license = { text = "MIT License" }
|
||||
dependencies = ["torch", "torchvision", "transformers", "aiohttp", "numpy", "tqdm", "Pillow"]
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import { uploadCanvasAsImage, uploadImageBlob } from "./utils/ImageUploadUtils.j
|
||||
import { processImageToMask } from "./utils/MaskProcessingUtils.js";
|
||||
import { convertToImage } from "./utils/ImageUtils.js";
|
||||
import { updateNodePreview } from "./utils/PreviewUtils.js";
|
||||
import { validateAndFixClipspace } from "./utils/ClipspaceUtils.js";
|
||||
import type { ComfyNode } from './types';
|
||||
|
||||
const log = createModuleLogger('SAMDetectorIntegration');
|
||||
@@ -376,6 +377,9 @@ async function handleSAMDetectorResult(node: ComfyNode, resultImage: HTMLImageEl
|
||||
}
|
||||
|
||||
|
||||
// Store original onClipspaceEditorSave function to restore later
|
||||
let originalOnClipspaceEditorSave: (() => void) | null = null;
|
||||
|
||||
// Function to setup SAM Detector hook in menu options
|
||||
export function setupSAMDetectorHook(node: ComfyNode, options: any[]) {
|
||||
// Hook into "Open in SAM Detector" with delay since Impact Pack adds it asynchronously
|
||||
@@ -408,9 +412,46 @@ export function setupSAMDetectorHook(node: ComfyNode, options: any[]) {
|
||||
node.imgs = [uploadResult.imageElement];
|
||||
(node as any).clipspaceImg = uploadResult.imageElement;
|
||||
|
||||
// Ensure proper clipspace structure for updated ComfyUI
|
||||
if (!ComfyApp.clipspace) {
|
||||
ComfyApp.clipspace = {};
|
||||
}
|
||||
|
||||
// Set up clipspace with proper indices
|
||||
ComfyApp.clipspace.imgs = [uploadResult.imageElement];
|
||||
ComfyApp.clipspace.selectedIndex = 0;
|
||||
ComfyApp.clipspace.combinedIndex = 0;
|
||||
ComfyApp.clipspace.img_paste_mode = 'selected';
|
||||
|
||||
// Copy to ComfyUI clipspace
|
||||
ComfyApp.copyToClipspace(node);
|
||||
|
||||
// Override onClipspaceEditorSave to fix clipspace structure before pasteFromClipspace
|
||||
if (!originalOnClipspaceEditorSave) {
|
||||
originalOnClipspaceEditorSave = ComfyApp.onClipspaceEditorSave;
|
||||
ComfyApp.onClipspaceEditorSave = function() {
|
||||
log.debug("SAM Detector onClipspaceEditorSave called, using unified clipspace validation");
|
||||
|
||||
// Use the unified clipspace validation function
|
||||
const isValid = validateAndFixClipspace();
|
||||
if (!isValid) {
|
||||
log.error("Clipspace validation failed, cannot proceed with paste");
|
||||
return;
|
||||
}
|
||||
|
||||
// Call the original function
|
||||
if (originalOnClipspaceEditorSave) {
|
||||
originalOnClipspaceEditorSave.call(ComfyApp);
|
||||
}
|
||||
|
||||
// Restore the original function after use
|
||||
if (originalOnClipspaceEditorSave) {
|
||||
ComfyApp.onClipspaceEditorSave = originalOnClipspaceEditorSave;
|
||||
originalOnClipspaceEditorSave = null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Start monitoring for SAM Detector results
|
||||
startSAMDetectorMonitoring(node);
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {createModuleLogger} from "./LoggerUtils.js";
|
||||
import { showNotification, showInfoNotification } from "./NotificationUtils.js";
|
||||
import { withErrorHandling, createValidationError, createNetworkError, createFileError } from "../ErrorHandler.js";
|
||||
import { safeClipspacePaste } from "./ClipspaceUtils.js";
|
||||
|
||||
// @ts-ignore
|
||||
import {api} from "../../../scripts/api.js";
|
||||
@@ -56,7 +57,13 @@ export class ClipboardManager {
|
||||
*/
|
||||
tryClipspacePaste = withErrorHandling(async (addMode: AddMode): Promise<boolean> => {
|
||||
log.info("Attempting to paste from ComfyUI Clipspace");
|
||||
ComfyApp.pasteFromClipspace(this.canvas.node);
|
||||
|
||||
// Use the unified clipspace validation and paste function
|
||||
const pasteSuccess = safeClipspacePaste(this.canvas.node);
|
||||
if (!pasteSuccess) {
|
||||
log.debug("Safe clipspace paste failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.canvas.node.imgs && this.canvas.node.imgs.length > 0) {
|
||||
const clipspaceImage = this.canvas.node.imgs[0];
|
||||
|
||||
114
src/utils/ClipspaceUtils.ts
Normal file
114
src/utils/ClipspaceUtils.ts
Normal file
@@ -0,0 +1,114 @@
|
||||
import { createModuleLogger } from "./LoggerUtils.js";
|
||||
// @ts-ignore
|
||||
import { ComfyApp } from "../../../scripts/app.js";
|
||||
|
||||
const log = createModuleLogger('ClipspaceUtils');
|
||||
|
||||
/**
|
||||
* Validates and fixes ComfyUI clipspace structure to prevent 'Cannot read properties of undefined' errors
|
||||
* @returns {boolean} - True if clipspace is valid and ready to use, false otherwise
|
||||
*/
|
||||
export function validateAndFixClipspace(): boolean {
|
||||
log.debug("Validating and fixing clipspace structure");
|
||||
|
||||
// Check if clipspace exists
|
||||
if (!ComfyApp.clipspace) {
|
||||
log.debug("ComfyUI clipspace is not available");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate clipspace structure
|
||||
if (!ComfyApp.clipspace.imgs || ComfyApp.clipspace.imgs.length === 0) {
|
||||
log.debug("ComfyUI clipspace has no images");
|
||||
return false;
|
||||
}
|
||||
|
||||
log.debug("Current clipspace state:", {
|
||||
hasImgs: !!ComfyApp.clipspace.imgs,
|
||||
imgsLength: ComfyApp.clipspace.imgs?.length,
|
||||
selectedIndex: ComfyApp.clipspace.selectedIndex,
|
||||
combinedIndex: ComfyApp.clipspace.combinedIndex,
|
||||
img_paste_mode: ComfyApp.clipspace.img_paste_mode
|
||||
});
|
||||
|
||||
// Ensure required indices are set
|
||||
if (ComfyApp.clipspace.selectedIndex === undefined || ComfyApp.clipspace.selectedIndex === null) {
|
||||
ComfyApp.clipspace.selectedIndex = 0;
|
||||
log.debug("Fixed clipspace selectedIndex to 0");
|
||||
}
|
||||
|
||||
if (ComfyApp.clipspace.combinedIndex === undefined || ComfyApp.clipspace.combinedIndex === null) {
|
||||
ComfyApp.clipspace.combinedIndex = 0;
|
||||
log.debug("Fixed clipspace combinedIndex to 0");
|
||||
}
|
||||
|
||||
if (!ComfyApp.clipspace.img_paste_mode) {
|
||||
ComfyApp.clipspace.img_paste_mode = 'selected';
|
||||
log.debug("Fixed clipspace img_paste_mode to 'selected'");
|
||||
}
|
||||
|
||||
// Ensure indices are within bounds
|
||||
const maxIndex = ComfyApp.clipspace.imgs.length - 1;
|
||||
if (ComfyApp.clipspace.selectedIndex > maxIndex) {
|
||||
ComfyApp.clipspace.selectedIndex = maxIndex;
|
||||
log.debug(`Fixed clipspace selectedIndex to ${maxIndex} (max available)`);
|
||||
}
|
||||
if (ComfyApp.clipspace.combinedIndex > maxIndex) {
|
||||
ComfyApp.clipspace.combinedIndex = maxIndex;
|
||||
log.debug(`Fixed clipspace combinedIndex to ${maxIndex} (max available)`);
|
||||
}
|
||||
|
||||
// Verify the image at combinedIndex exists and has src
|
||||
const combinedImg = ComfyApp.clipspace.imgs[ComfyApp.clipspace.combinedIndex];
|
||||
if (!combinedImg || !combinedImg.src) {
|
||||
log.debug("Image at combinedIndex is missing or has no src, trying to find valid image");
|
||||
// Try to use the first available image
|
||||
for (let i = 0; i < ComfyApp.clipspace.imgs.length; i++) {
|
||||
if (ComfyApp.clipspace.imgs[i] && ComfyApp.clipspace.imgs[i].src) {
|
||||
ComfyApp.clipspace.combinedIndex = i;
|
||||
log.debug(`Fixed combinedIndex to ${i} (first valid image)`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Final check - if still no valid image found
|
||||
const finalImg = ComfyApp.clipspace.imgs[ComfyApp.clipspace.combinedIndex];
|
||||
if (!finalImg || !finalImg.src) {
|
||||
log.error("No valid images found in clipspace after attempting fixes");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
log.debug("Final clipspace structure:", {
|
||||
selectedIndex: ComfyApp.clipspace.selectedIndex,
|
||||
combinedIndex: ComfyApp.clipspace.combinedIndex,
|
||||
img_paste_mode: ComfyApp.clipspace.img_paste_mode,
|
||||
imgsLength: ComfyApp.clipspace.imgs?.length,
|
||||
combinedImgSrc: ComfyApp.clipspace.imgs[ComfyApp.clipspace.combinedIndex]?.src?.substring(0, 50) + '...'
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely calls ComfyApp.pasteFromClipspace after validating clipspace structure
|
||||
* @param {any} node - The ComfyUI node to paste to
|
||||
* @returns {boolean} - True if paste was successful, false otherwise
|
||||
*/
|
||||
export function safeClipspacePaste(node: any): boolean {
|
||||
log.debug("Attempting safe clipspace paste");
|
||||
|
||||
if (!validateAndFixClipspace()) {
|
||||
log.debug("Clipspace validation failed, cannot paste");
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
ComfyApp.pasteFromClipspace(node);
|
||||
log.debug("Successfully called pasteFromClipspace");
|
||||
return true;
|
||||
} catch (error) {
|
||||
log.error("Error calling pasteFromClipspace:", error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user