Add clipspace utils with full backward support

Refactored clipspace handling into ClipspaceUtils with validateAndFixClipspace() and safeClipspacePaste() for consistent, defensive logic. Ensures full backward compatibility with all ComfyUI versions and eliminates duplicated code.
This commit is contained in:
Dariusz L
2025-08-06 23:08:02 +02:00
parent 1a1d8748cb
commit 9f21ff13ae
6 changed files with 303 additions and 4 deletions

View File

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

View File

@@ -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
View 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;
}
}