diff --git a/js/CanvasInteractions.js b/js/CanvasInteractions.js
index 6379861..bd9b78f 100644
--- a/js/CanvasInteractions.js
+++ b/js/CanvasInteractions.js
@@ -33,6 +33,9 @@ export class CanvasInteractions {
this.canvas.canvas.addEventListener('wheel', this.handleWheel.bind(this), {passive: false});
this.canvas.canvas.addEventListener('keydown', this.handleKeyDown.bind(this));
this.canvas.canvas.addEventListener('keyup', this.handleKeyUp.bind(this));
+
+ // Add paste event listener like ComfyUI does
+ document.addEventListener('paste', this.handlePasteEvent.bind(this));
this.canvas.canvas.addEventListener('mouseenter', (e) => {
this.canvas.isMouseOver = true;
@@ -42,6 +45,12 @@ export class CanvasInteractions {
this.canvas.isMouseOver = false;
this.handleMouseLeave(e);
});
+
+ // Add drag & drop support
+ this.canvas.canvas.addEventListener('dragover', this.handleDragOver.bind(this));
+ this.canvas.canvas.addEventListener('dragenter', this.handleDragEnter.bind(this));
+ this.canvas.canvas.addEventListener('dragleave', this.handleDragLeave.bind(this));
+ this.canvas.canvas.addEventListener('drop', this.handleDrop.bind(this));
}
resetInteractionState() {
@@ -343,16 +352,13 @@ export class CanvasInteractions {
}
if (e.key.toLowerCase() === 'c') {
if (this.canvas.selectedLayers.length > 0) {
- e.preventDefault();
- e.stopPropagation();
this.canvas.canvasLayers.copySelectedLayers();
}
return;
}
if (e.key.toLowerCase() === 'v') {
- e.preventDefault();
- e.stopPropagation();
- this.canvas.canvasLayers.handlePaste('mouse');
+ // Don't prevent default - let the natural paste event fire
+ // which is handled by handlePasteEvent
return;
}
}
@@ -717,4 +723,137 @@ export class CanvasInteractions {
this.canvas.viewport.y -= rectY;
}
}
+
+ // Drag & Drop handlers
+ handleDragOver(e) {
+ e.preventDefault();
+ e.stopPropagation(); // Prevent ComfyUI from handling this event
+ e.dataTransfer.dropEffect = 'copy';
+ }
+
+ handleDragEnter(e) {
+ e.preventDefault();
+ e.stopPropagation(); // Prevent ComfyUI from handling this event
+ this.canvas.canvas.style.backgroundColor = 'rgba(45, 90, 160, 0.1)';
+ this.canvas.canvas.style.border = '2px dashed #2d5aa0';
+ }
+
+ handleDragLeave(e) {
+ e.preventDefault();
+ e.stopPropagation(); // Prevent ComfyUI from handling this event
+ // Only reset if we're actually leaving the canvas (not just moving between child elements)
+ if (!this.canvas.canvas.contains(e.relatedTarget)) {
+ this.canvas.canvas.style.backgroundColor = '';
+ this.canvas.canvas.style.border = '';
+ }
+ }
+
+ async handleDrop(e) {
+ e.preventDefault();
+ e.stopPropagation(); // CRITICAL: Prevent ComfyUI from handling this event and loading workflow
+
+ log.info("Canvas drag & drop event intercepted - preventing ComfyUI workflow loading");
+
+ // Reset visual feedback
+ this.canvas.canvas.style.backgroundColor = '';
+ this.canvas.canvas.style.border = '';
+
+ const files = Array.from(e.dataTransfer.files);
+ const worldCoords = this.canvas.getMouseWorldCoordinates(e);
+
+ log.info(`Dropped ${files.length} file(s) onto canvas at position (${worldCoords.x}, ${worldCoords.y})`);
+
+ for (const file of files) {
+ if (file.type.startsWith('image/')) {
+ try {
+ await this.loadDroppedImageFile(file, worldCoords);
+ log.info(`Successfully loaded dropped image: ${file.name}`);
+ } catch (error) {
+ log.error(`Failed to load dropped image ${file.name}:`, error);
+ }
+ } else {
+ log.warn(`Skipped non-image file: ${file.name} (${file.type})`);
+ }
+ }
+ }
+
+ async loadDroppedImageFile(file, worldCoords) {
+ const reader = new FileReader();
+ reader.onload = async (e) => {
+ const img = new Image();
+ img.onload = async () => {
+ // Check fit_on_add widget to determine add mode
+ const fitOnAddWidget = this.canvas.node.widgets.find(w => w.name === "fit_on_add");
+ const addMode = fitOnAddWidget && fitOnAddWidget.value ? 'fit' : 'center';
+
+ // Use the same method as "Add Image" button for consistency
+ await this.canvas.addLayer(img, {}, addMode);
+ };
+ img.onerror = () => {
+ log.error(`Failed to load dropped image: ${file.name}`);
+ };
+ img.src = e.target.result;
+ };
+ reader.onerror = () => {
+ log.error(`Failed to read dropped file: ${file.name}`);
+ };
+ reader.readAsDataURL(file);
+ }
+
+ // Paste event handler that respects clipboard preference
+ async handlePasteEvent(e) {
+ // Check if we should handle this paste event
+ const shouldHandle = this.canvas.isMouseOver ||
+ this.canvas.canvas.contains(document.activeElement) ||
+ document.activeElement === this.canvas.canvas ||
+ document.activeElement === document.body;
+
+ if (!shouldHandle) {
+ log.debug("Paste event ignored - not focused on canvas");
+ return;
+ }
+
+ log.info("Paste event detected, checking clipboard preference");
+
+ // Check clipboard preference first
+ const preference = this.canvas.canvasLayers.clipboardPreference;
+
+ if (preference === 'clipspace') {
+ // For clipspace preference, always delegate to ClipboardManager
+ log.info("Clipboard preference is clipspace, delegating to ClipboardManager");
+ e.preventDefault();
+ e.stopPropagation();
+ await this.canvas.canvasLayers.clipboardManager.handlePaste('mouse', preference);
+ return;
+ }
+
+ // For system preference, check direct image data first, then delegate
+ const clipboardData = e.clipboardData;
+ if (clipboardData && clipboardData.items) {
+ for (const item of clipboardData.items) {
+ if (item.type.startsWith('image/')) {
+ e.preventDefault();
+ e.stopPropagation();
+
+ const file = item.getAsFile();
+ if (file) {
+ log.info("Found direct image data in paste event");
+ const reader = new FileReader();
+ reader.onload = async (event) => {
+ const img = new Image();
+ img.onload = async () => {
+ await this.canvas.canvasLayers.addLayerWithImage(img, {}, 'mouse');
+ };
+ img.src = event.target.result;
+ };
+ reader.readAsDataURL(file);
+ return;
+ }
+ }
+ }
+ }
+
+ // If no direct image data, delegate to ClipboardManager with system preference
+ await this.canvas.canvasLayers.clipboardManager.handlePaste('mouse', preference);
+ }
}
diff --git a/js/CanvasLayers.js b/js/CanvasLayers.js
index b42ace2..7125691 100644
--- a/js/CanvasLayers.js
+++ b/js/CanvasLayers.js
@@ -34,17 +34,68 @@ export class CanvasLayers {
async copySelectedLayers() {
if (this.canvas.selectedLayers.length === 0) return;
+
+ // Always copy to internal clipboard first
this.internalClipboard = this.canvas.selectedLayers.map(layer => ({...layer}));
log.info(`Copied ${this.internalClipboard.length} layer(s) to internal clipboard.`);
- try {
- const blob = await this.getFlattenedSelectionAsBlob();
- if (blob) {
+
+ // Get flattened image
+ const blob = await this.getFlattenedSelectionAsBlob();
+ if (!blob) {
+ log.warn("Failed to create flattened selection blob");
+ return;
+ }
+
+ // Copy to external clipboard based on preference
+ if (this.clipboardPreference === 'clipspace') {
+ try {
+ // Copy to ComfyUI Clipspace
+ const dataURL = await new Promise((resolve) => {
+ const reader = new FileReader();
+ reader.onload = () => resolve(reader.result);
+ reader.readAsDataURL(blob);
+ });
+
+ // Create temporary image for clipspace
+ const img = new Image();
+ img.onload = () => {
+ // Add to ComfyUI Clipspace
+ if (this.canvas.node.imgs) {
+ this.canvas.node.imgs = [img];
+ } else {
+ this.canvas.node.imgs = [img];
+ }
+
+ // Use ComfyUI's clipspace functionality
+ if (ComfyApp.copyToClipspace) {
+ ComfyApp.copyToClipspace(this.canvas.node);
+ log.info("Flattened selection copied to ComfyUI Clipspace.");
+ } else {
+ log.warn("ComfyUI copyToClipspace not available");
+ }
+ };
+ img.src = dataURL;
+
+ } catch (error) {
+ log.error("Failed to copy image to ComfyUI Clipspace:", error);
+ // Fallback to system clipboard
+ try {
+ const item = new ClipboardItem({'image/png': blob});
+ await navigator.clipboard.write([item]);
+ log.info("Fallback: Flattened selection copied to system clipboard.");
+ } catch (fallbackError) {
+ log.error("Failed to copy to system clipboard as fallback:", fallbackError);
+ }
+ }
+ } else {
+ // Copy to system clipboard (default behavior)
+ try {
const item = new ClipboardItem({'image/png': blob});
await navigator.clipboard.write([item]);
- log.info("Flattened selection copied to the system clipboard.");
+ log.info("Flattened selection copied to system clipboard.");
+ } catch (error) {
+ log.error("Failed to copy image to system clipboard:", error);
}
- } catch (error) {
- log.error("Failed to copy image to system clipboard:", error);
}
}
@@ -89,55 +140,14 @@ export class CanvasLayers {
try {
log.info(`Paste operation started with preference: ${this.clipboardPreference}`);
- if (this.internalClipboard.length > 0) {
- log.info("Pasting from internal clipboard");
- this.pasteLayers();
- return;
- }
-
- if (this.clipboardPreference === 'clipspace') {
- log.info("Attempting paste from ComfyUI Clipspace");
- if (!await this.tryClipspacePaste(addMode)) {
- log.info("No image found in ComfyUI Clipspace");
- }
- } else if (this.clipboardPreference === 'system') {
- log.info("Attempting paste from system clipboard");
- await this.trySystemClipboardPaste(addMode);
- }
+ // Delegate all clipboard handling to ClipboardManager (it will check internal clipboard first)
+ await this.clipboardManager.handlePaste(addMode, this.clipboardPreference);
} catch (err) {
log.error("Paste operation failed:", err);
}
}
- async tryClipspacePaste(addMode) {
- try {
- log.info("Attempting to paste from ComfyUI Clipspace");
- const clipspaceResult = ComfyApp.pasteFromClipspace(this.canvas.node);
-
- if (this.canvas.node.imgs && this.canvas.node.imgs.length > 0) {
- const clipspaceImage = this.canvas.node.imgs[0];
- if (clipspaceImage && clipspaceImage.src) {
- log.info("Successfully got image from ComfyUI Clipspace");
- const img = new Image();
- img.onload = async () => {
- await this.addLayerWithImage(img, {}, addMode);
- };
- img.src = clipspaceImage.src;
- return true;
- }
- }
- return false;
- } catch (clipspaceError) {
- log.warn("ComfyUI Clipspace paste failed:", clipspaceError);
- return false;
- }
- }
-
- async trySystemClipboardPaste(addMode) {
- return await this.clipboardManager.trySystemClipboardPaste(addMode);
- }
-
addLayerWithImage = withErrorHandling(async (image, layerProps = {}, addMode = 'default') => {
if (!image) {
diff --git a/js/CanvasView.js b/js/CanvasView.js
index ff16e62..2906431 100644
--- a/js/CanvasView.js
+++ b/js/CanvasView.js
@@ -553,6 +553,7 @@ async function createCanvasWidget(node, widget, app) {
textContent: "Paste Image",
title: "Paste image from clipboard",
onclick: () => {
+ // Use the direct handlePaste method from CanvasLayers
const fitOnAddWidget = node.widgets.find(w => w.name === "fit_on_add");
const addMode = fitOnAddWidget && fitOnAddWidget.value ? 'fit' : 'center';
canvas.canvasLayers.handlePaste(addMode);
@@ -581,6 +582,77 @@ async function createCanvasWidget(node, widget, app) {
button.style.backgroundColor = "#4a4a4a";
}
log.info(`Clipboard preference toggled to: ${canvas.canvasLayers.clipboardPreference}`);
+ },
+ onmouseenter: (e) => {
+ const currentPreference = canvas.canvasLayers.clipboardPreference;
+ let tooltipContent = '';
+
+ if (currentPreference === 'system') {
+ tooltipContent = `
+
📋 System Clipboard Mode
+
+ | Ctrl + C | Copy selected layers to internal clipboard + system clipboard as flattened image |
+ | Ctrl + V | Priority: |
+ | 1️⃣ Internal clipboard (copied layers) |
+ | 2️⃣ System clipboard (images, screenshots) |
+ | 3️⃣ System clipboard (file paths, URLs) |
+ | Paste Image | Same as Ctrl+V but respects fit_on_add setting |
+ | Drag & Drop | Load images directly from files |
+
+
+ ⚠️ Security Note: "Paste Image" button for external images may not work due to browser security restrictions. Use Ctrl+V instead or Drag & Drop.
+
+
+ 💡 Best for: Working with screenshots, copied images, file paths, and urls.
+
+ `;
+ } else {
+ tooltipContent = `
+ 📋 ComfyUI Clipspace Mode
+
+ | Ctrl + C | Copy selected layers to internal clipboard + ComfyUI Clipspace as flattened image |
+ | Ctrl + V | Priority: |
+ | 1️⃣ Internal clipboard (copied layers) |
+ | 2️⃣ ComfyUI Clipspace (workflow images) |
+ | 3️⃣ System clipboard (fallback) |
+ | Paste Image | Same as Ctrl+V but respects fit_on_add setting |
+ | Drag & Drop | Load images directly from files |
+
+
+ 💡 Best for: ComfyUI workflow integration and node-to-node image transfer
+
+ `;
+ }
+
+ helpTooltip.innerHTML = tooltipContent;
+ helpTooltip.style.visibility = 'hidden';
+ helpTooltip.style.display = 'block';
+
+ const buttonRect = e.target.getBoundingClientRect();
+ const tooltipRect = helpTooltip.getBoundingClientRect();
+ const viewportWidth = window.innerWidth;
+ const viewportHeight = window.innerHeight;
+
+ let left = buttonRect.left;
+ let top = buttonRect.bottom + 5;
+
+ if (left + tooltipRect.width > viewportWidth) {
+ left = viewportWidth - tooltipRect.width - 10;
+ }
+
+ if (top + tooltipRect.height > viewportHeight) {
+ top = buttonRect.top - tooltipRect.height - 5;
+ }
+
+ if (left < 10) left = 10;
+ if (top < 10) top = 10;
+
+ helpTooltip.style.left = `${left}px`;
+ helpTooltip.style.top = `${top}px`;
+ helpTooltip.style.visibility = 'visible';
+ },
+ onmouseleave: () => {
+ helpTooltip.style.display = 'none';
}
}),
]),
@@ -989,53 +1061,8 @@ async function createCanvasWidget(node, widget, app) {
height: "100%"
}
}, [controlPanel, canvasContainer]);
- const handleFileLoad = async (file) => {
- log.info("File dropped:", file.name);
- if (!file.type.startsWith('image/')) {
- log.info("Dropped file is not an image.");
- return;
- }
-
- const reader = new FileReader();
- reader.onload = async (event) => {
- log.debug("FileReader finished loading dropped file as data:URL.");
- const img = new Image();
- img.onload = async () => {
- log.debug("Image object loaded from dropped data:URL.");
- const fitOnAddWidget = node.widgets.find(w => w.name === "fit_on_add");
- const addMode = fitOnAddWidget && fitOnAddWidget.value ? 'fit' : 'center';
-
- await canvas.addLayer(img, {}, addMode);
- log.info("Dropped layer added and state saved.");
- };
- img.src = event.target.result;
- };
- reader.readAsDataURL(file);
- };
-
- mainContainer.addEventListener('dragover', (e) => {
- e.preventDefault();
- e.stopPropagation();
- canvasContainer.classList.add('drag-over');
- });
-
- mainContainer.addEventListener('dragleave', (e) => {
- e.preventDefault();
- e.stopPropagation();
- canvasContainer.classList.remove('drag-over');
- });
-
- mainContainer.addEventListener('drop', async (e) => {
- e.preventDefault();
- e.stopPropagation();
- canvasContainer.classList.remove('drag-over');
-
- if (e.dataTransfer.files) {
- for (const file of e.dataTransfer.files) {
- await handleFileLoad(file);
- }
- }
- });
+ // Drag & drop is now handled by CanvasInteractions.js
+ // Removed duplicate handlers to prevent double loading
const mainWidget = node.addDOMWidget("mainContainer", "widget", mainContainer);
diff --git a/js/utils/ClipboardManager.js b/js/utils/ClipboardManager.js
index 6dce54d..5021f80 100644
--- a/js/utils/ClipboardManager.js
+++ b/js/utils/ClipboardManager.js
@@ -1,5 +1,6 @@
import {createModuleLogger} from "./LoggerUtils.js";
import {api} from "../../../scripts/api.js";
+import {ComfyApp} from "../../../scripts/app.js";
const log = createModuleLogger('ClipboardManager');
@@ -10,68 +11,158 @@ export class ClipboardManager {
}
/**
- * Attempts to paste from system clipboard
+ * Main paste handler that delegates to appropriate methods
+ * @param {string} addMode - The mode for adding the layer
+ * @param {string} preference - Clipboard preference ('system' or 'clipspace')
+ * @returns {Promise} - True if successful, false otherwise
+ */
+ async handlePaste(addMode = 'mouse', preference = 'system') {
+ try {
+ log.info(`ClipboardManager handling paste with preference: ${preference}`);
+
+ // PRIORITY 1: Check internal clipboard first (copied layers)
+ if (this.canvas.canvasLayers.internalClipboard.length > 0) {
+ log.info("Found layers in internal clipboard, pasting layers");
+ this.canvas.canvasLayers.pasteLayers();
+ return true;
+ }
+
+ // PRIORITY 2: Check external clipboard based on preference
+ if (preference === 'clipspace') {
+ log.info("Attempting paste from ComfyUI Clipspace");
+ const success = await this.tryClipspacePaste(addMode);
+ if (success) {
+ return true;
+ }
+ log.info("No image found in ComfyUI Clipspace");
+ }
+
+ // PRIORITY 3: Always try system clipboard (either as primary or fallback)
+ log.info("Attempting paste from system clipboard");
+ return await this.trySystemClipboardPaste(addMode);
+
+ } catch (err) {
+ log.error("ClipboardManager paste operation failed:", err);
+ return false;
+ }
+ }
+
+ /**
+ * Attempts to paste from ComfyUI Clipspace
+ * @param {string} addMode - The mode for adding the layer
+ * @returns {Promise} - True if successful, false otherwise
+ */
+ async tryClipspacePaste(addMode) {
+ try {
+ log.info("Attempting to paste from ComfyUI Clipspace");
+ const clipspaceResult = ComfyApp.pasteFromClipspace(this.canvas.node);
+
+ if (this.canvas.node.imgs && this.canvas.node.imgs.length > 0) {
+ const clipspaceImage = this.canvas.node.imgs[0];
+ if (clipspaceImage && clipspaceImage.src) {
+ log.info("Successfully got image from ComfyUI Clipspace");
+ const img = new Image();
+ img.onload = async () => {
+ await this.canvas.canvasLayers.addLayerWithImage(img, {}, addMode);
+ };
+ img.src = clipspaceImage.src;
+ return true;
+ }
+ }
+ return false;
+ } catch (clipspaceError) {
+ log.warn("ComfyUI Clipspace paste failed:", clipspaceError);
+ return false;
+ }
+ }
+
+ /**
+ * System clipboard paste - handles both image data and text paths
* @param {string} addMode - The mode for adding the layer
* @returns {Promise} - True if successful, false otherwise
*/
async trySystemClipboardPaste(addMode) {
- if (!navigator.clipboard?.read) {
- log.info("Browser does not support clipboard read API");
- return false;
- }
-
- try {
- log.info("Attempting to paste from system clipboard");
- const clipboardItems = await navigator.clipboard.read();
-
- for (const item of clipboardItems) {
- // First, try to find actual image data
- const imageType = item.types.find(type => type.startsWith('image/'));
-
- if (imageType) {
- const blob = await item.getType(imageType);
- const reader = new FileReader();
- reader.onload = (event) => {
- const img = new Image();
- img.onload = async () => {
- await this.canvas.canvasLayers.addLayerWithImage(img, {}, addMode);
- };
- img.src = event.target.result;
- };
- reader.readAsDataURL(blob);
- log.info("Successfully pasted image from system clipboard");
- return true;
- }
-
- // If no image data found, check for text that might be a file path
- const textType = item.types.find(type => type === 'text/plain');
- if (textType) {
- const textBlob = await item.getType(textType);
- const text = await textBlob.text();
+ log.info("ClipboardManager: Checking system clipboard for images and paths");
+
+ // First try modern clipboard API for both images and text
+ if (navigator.clipboard?.read) {
+ try {
+ const clipboardItems = await navigator.clipboard.read();
+
+ for (const item of clipboardItems) {
+ log.debug("Clipboard item types:", item.types);
- if (this.isValidImagePath(text)) {
- log.info("Found image file path in clipboard:", text);
+ // Check for image data first
+ const imageType = item.types.find(type => type.startsWith('image/'));
+ if (imageType) {
try {
- // Try to load the image using different methods
- const success = await this.loadImageFromPath(text, addMode);
- if (success) {
- return true;
+ const blob = await item.getType(imageType);
+ const reader = new FileReader();
+ reader.onload = (event) => {
+ const img = new Image();
+ img.onload = async () => {
+ log.info("Successfully loaded image from system clipboard");
+ await this.canvas.canvasLayers.addLayerWithImage(img, {}, addMode);
+ };
+ img.src = event.target.result;
+ };
+ reader.readAsDataURL(blob);
+ log.info("Found image data in system clipboard");
+ return true;
+ } catch (error) {
+ log.debug("Error reading image data:", error);
+ }
+ }
+
+ // Check for text types (file paths, URLs)
+ const textTypes = ['text/plain', 'text/uri-list'];
+ for (const textType of textTypes) {
+ if (item.types.includes(textType)) {
+ try {
+ const textBlob = await item.getType(textType);
+ const text = await textBlob.text();
+
+ if (this.isValidImagePath(text)) {
+ log.info("Found image path in clipboard:", text);
+ const success = await this.loadImageFromPath(text, addMode);
+ if (success) {
+ return true;
+ }
+ }
+ } catch (error) {
+ log.debug(`Error reading ${textType}:`, error);
}
- } catch (pathError) {
- log.warn("Error loading image from path:", pathError);
}
}
}
+ } catch (error) {
+ log.debug("Modern clipboard API failed:", error);
}
-
- log.info("No image or valid image path found in system clipboard");
- return false;
- } catch (error) {
- log.warn("System clipboard paste failed:", error);
- return false;
}
+
+ // Fallback to text-only API
+ if (navigator.clipboard?.readText) {
+ try {
+ const text = await navigator.clipboard.readText();
+ log.debug("Found text in clipboard:", text);
+
+ if (text && this.isValidImagePath(text)) {
+ log.info("Found valid image path in clipboard:", text);
+ const success = await this.loadImageFromPath(text, addMode);
+ if (success) {
+ return true;
+ }
+ }
+ } catch (error) {
+ log.debug("Could not read text from clipboard:", error);
+ }
+ }
+
+ log.debug("No images or valid image paths found in system clipboard");
+ return false;
}
+
/**
* Validates if a text string is a valid image file path or URL
* @param {string} text - The text to validate
@@ -141,13 +232,13 @@ export class ClipboardManager {
}
/**
- * Attempts to load an image from a file path using various methods
+ * Attempts to load an image from a file path using simplified methods
* @param {string} filePath - The file path to load
* @param {string} addMode - The mode for adding the layer
* @returns {Promise} - True if successful, false otherwise
*/
async loadImageFromPath(filePath, addMode) {
- // Method 1: Try direct loading for URLs
+ // Method 1: Direct loading for URLs
if (filePath.startsWith('http://') || filePath.startsWith('https://')) {
try {
const img = new Image();
@@ -170,143 +261,44 @@ export class ClipboardManager {
}
}
- // Method 2: Try to load via ComfyUI's view endpoint for local files
+ // Method 2: Load local files via backend endpoint
try {
- log.info("Attempting to load local file via ComfyUI view endpoint");
- const success = await this.loadImageViaComfyUIView(filePath, addMode);
+ log.info("Attempting to load local file via backend");
+ const success = await this.loadFileViaBackend(filePath, addMode);
if (success) {
return true;
}
} catch (error) {
- log.warn("ComfyUI view endpoint method failed:", error);
+ log.warn("Backend loading failed:", error);
}
- // Method 3: Try to prompt user to select the file manually
+ // Method 3: Fallback to file picker
try {
- log.info("Attempting to load local file via file picker");
+ log.info("Falling back to file picker");
const success = await this.promptUserForFile(filePath, addMode);
if (success) {
return true;
}
} catch (error) {
- log.warn("File picker method failed:", error);
+ log.warn("File picker failed:", error);
}
- // Method 4: Show user a helpful message about the limitation
+ // Method 4: Show user a helpful message
this.showFilePathMessage(filePath);
return false;
}
/**
- * Attempts to load an image using ComfyUI's API methods
+ * Loads a local file via the ComfyUI backend endpoint
* @param {string} filePath - The file path to load
* @param {string} addMode - The mode for adding the layer
* @returns {Promise} - True if successful, false otherwise
*/
- async loadImageViaComfyUIView(filePath, addMode) {
+ async loadFileViaBackend(filePath, addMode) {
try {
- // First, try to get folder paths to understand ComfyUI structure
- const folderPaths = await this.getComfyUIFolderPaths();
- log.debug("ComfyUI folder paths:", folderPaths);
+ log.info("Loading file via ComfyUI backend:", filePath);
- // Extract filename from path
- const fileName = filePath.split(/[\\\/]/).pop();
-
- // Method 1: Try to upload the file to ComfyUI first, then load it
- const uploadSuccess = await this.uploadFileToComfyUI(filePath, addMode);
- if (uploadSuccess) {
- return true;
- }
-
- // Method 2: Try different view endpoints if file might already exist in ComfyUI
- const viewConfigs = [
- // Direct filename approach
- { filename: fileName },
- // Full path approach
- { filename: filePath },
- // Input folder approach
- { filename: fileName, type: 'input' },
- // Temp folder approach
- { filename: fileName, type: 'temp' },
- // Output folder approach
- { filename: fileName, type: 'output' }
- ];
-
- for (const config of viewConfigs) {
- try {
- // Build query parameters
- const params = new URLSearchParams();
- params.append('filename', config.filename);
- if (config.type) {
- params.append('type', config.type);
- }
- if (config.subfolder) {
- params.append('subfolder', config.subfolder);
- }
-
- const viewUrl = api.apiURL(`/view?${params.toString()}`);
- log.debug("Trying ComfyUI view URL:", viewUrl);
-
- const img = new Image();
- const success = await new Promise((resolve) => {
- img.onload = async () => {
- log.info("Successfully loaded image via ComfyUI view endpoint:", viewUrl);
- await this.canvas.canvasLayers.addLayerWithImage(img, {}, addMode);
- resolve(true);
- };
- img.onerror = () => {
- log.debug("Failed to load image via ComfyUI view endpoint:", viewUrl);
- resolve(false);
- };
-
- // Set a timeout to avoid hanging
- setTimeout(() => {
- resolve(false);
- }, 3000);
-
- img.src = viewUrl;
- });
-
- if (success) {
- return true;
- }
- } catch (error) {
- log.debug("Error with view config:", config, error);
- continue;
- }
- }
-
- return false;
- } catch (error) {
- log.warn("Error in loadImageViaComfyUIView:", error);
- return false;
- }
- }
-
- /**
- * Gets ComfyUI folder paths using the API
- * @returns {Promise