From e4da6e4d3133a6668b7c9f4fbcaaef88da0d97b2 Mon Sep 17 00:00:00 2001 From: Dariusz L Date: Tue, 1 Jul 2025 16:42:48 +0200 Subject: [PATCH] Remove comments and cleanup event handling code Removed redundant and explanatory comments from CanvasInteractions.js, CanvasLayers.js, and ClipboardManager.js to improve code readability. Deleted the REFACTORING_GUIDE.md documentation file. Minor code cleanups were made to event handler logic and UI widget setup, with no changes to core functionality. --- js/CanvasInteractions.js | 40 +++---- js/CanvasLayers.js | 33 ++---- js/CanvasView.js | 6 +- js/REFACTORING_GUIDE.md | 216 ----------------------------------- js/utils/ClipboardManager.js | 53 ++------- 5 files changed, 40 insertions(+), 308 deletions(-) delete mode 100644 js/REFACTORING_GUIDE.md diff --git a/js/CanvasInteractions.js b/js/CanvasInteractions.js index c710aae..f45c0c4 100644 --- a/js/CanvasInteractions.js +++ b/js/CanvasInteractions.js @@ -33,8 +33,7 @@ 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) => { @@ -46,13 +45,11 @@ export class CanvasInteractions { 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)); - - // Prevent default context menu on canvas + this.canvas.canvas.addEventListener('contextmenu', this.handleContextMenu.bind(this)); } @@ -98,7 +95,6 @@ export class CanvasInteractions { } this.interaction.lastClickTime = currentTime; - // Check for right click to show blend mode menu on selected layers if (e.button === 2) { const clickedLayerResult = this.canvas.canvasLayers.getLayerAtPosition(worldCoords.x, worldCoords.y); if (clickedLayerResult && this.canvas.selectedLayers.includes(clickedLayerResult.layer)) { @@ -108,7 +104,6 @@ export class CanvasInteractions { } } - // Check for Shift key first to start output area drawing, ignoring layers if (e.shiftKey) { this.startCanvasResize(worldCoords); this.canvas.render(); @@ -230,7 +225,7 @@ export class CanvasInteractions { } handleContextMenu(e) { - // Prevent default context menu on canvas + e.preventDefault(); } @@ -375,8 +370,8 @@ export class CanvasInteractions { return; } if (e.key.toLowerCase() === 'v') { - // Don't prevent default - let the natural paste event fire - // which is handled by handlePasteEvent + + return; } } @@ -742,7 +737,6 @@ export class CanvasInteractions { } } - // Drag & Drop handlers handleDragOver(e) { e.preventDefault(); e.stopPropagation(); // Prevent ComfyUI from handling this event @@ -759,7 +753,7 @@ export class CanvasInteractions { 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 = ''; @@ -771,8 +765,7 @@ export class CanvasInteractions { 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 = ''; @@ -800,11 +793,10 @@ export class CanvasInteractions { 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 = () => { @@ -818,9 +810,8 @@ export class CanvasInteractions { 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 || @@ -832,20 +823,18 @@ export class CanvasInteractions { } 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) { @@ -870,8 +859,7 @@ export class CanvasInteractions { } } } - - // 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 4a3fbfa..93fa600 100644 --- a/js/CanvasLayers.js +++ b/js/CanvasLayers.js @@ -34,39 +34,34 @@ 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.`); - - // 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."); @@ -78,7 +73,7 @@ export class CanvasLayers { } 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]); @@ -88,7 +83,7 @@ export class CanvasLayers { } } } else { - // Copy to system clipboard (default behavior) + try { const item = new ClipboardItem({'image/png': blob}); await navigator.clipboard.write([item]); @@ -140,7 +135,6 @@ export class CanvasLayers { try { log.info(`Paste operation started with preference: ${this.clipboardPreference}`); - // Delegate all clipboard handling to ClipboardManager (it will check internal clipboard first) await this.clipboardManager.handlePaste(addMode, this.clipboardPreference); } catch (err) { @@ -478,7 +472,6 @@ export class CanvasLayers { min-width: 200px; `; - // Create draggable title bar const titleBar = document.createElement('div'); titleBar.style.cssText = ` background: #3a3a3a; @@ -493,7 +486,6 @@ export class CanvasLayers { `; titleBar.textContent = 'Blend Mode'; - // Create content area const content = document.createElement('div'); content.style.cssText = ` padding: 5px; @@ -502,7 +494,6 @@ export class CanvasLayers { menu.appendChild(titleBar); menu.appendChild(content); - // Add drag functionality let isDragging = false; let dragOffset = { x: 0, y: 0 }; @@ -510,8 +501,7 @@ export class CanvasLayers { if (isDragging) { const newX = e.clientX - dragOffset.x; const newY = e.clientY - dragOffset.y; - - // Keep menu within viewport bounds + const maxX = window.innerWidth - menu.offsetWidth; const maxY = window.innerHeight - menu.offsetHeight; @@ -530,13 +520,12 @@ export class CanvasLayers { titleBar.addEventListener('mousedown', (e) => { isDragging = true; - // Calculate offset from mouse position to menu's top-left corner + dragOffset.x = e.clientX - parseInt(menu.style.left); dragOffset.y = e.clientY - parseInt(menu.style.top); e.preventDefault(); e.stopPropagation(); - - // Add global event listeners for dragging + document.addEventListener('mousemove', handleMouseMove); document.addEventListener('mouseup', handleMouseUp); }); diff --git a/js/CanvasView.js b/js/CanvasView.js index 4095ed5..58d6914 100644 --- a/js/CanvasView.js +++ b/js/CanvasView.js @@ -582,7 +582,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); @@ -1091,8 +1091,8 @@ async function createCanvasWidget(node, widget, app) { height: "100%" } }, [controlPanel, canvasContainer]); - // 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/REFACTORING_GUIDE.md b/js/REFACTORING_GUIDE.md deleted file mode 100644 index 23360f4..0000000 --- a/js/REFACTORING_GUIDE.md +++ /dev/null @@ -1,216 +0,0 @@ -# Przewodnik refaktoryzacji Canvas - -## Podsumowanie wykonanych prac - -Przeprowadzono kompleksową refaktoryzację klasy `Canvas` oraz powiązanych plików w celu poprawy architektury i zastosowania wzorca fasady. - -## Zmiany w architekturze - -### 1. Wzorzec Fasady w `Canvas.js` -Klasa `Canvas` została przekształcona w prawdziwą fasadę: - -#### Struktura przed refaktoryzacją: -- ✗ Dziesiątki metod delegujących (`copySelectedLayers() { return this.canvasLayers.copySelectedLayers(); }`) -- ✗ Canvas jako pośrednik dla wszystkich operacji -- ✗ Trudność w utrzymaniu kodu - -#### Struktura po refaktoryzacji: -- ✅ **Główne operacje fasady**: `loadInitialState()`, `saveState()`, `render()`, `addLayer()` -- ✅ **Publiczne moduły**: `canvas.canvasLayers`, `canvas.canvasInteractions`, `canvas.canvasIO`, `canvas.canvasState` -- ✅ **Metody delegujące**: Zachowane dla kompatybilności, wyraźnie oznaczone jako tymczasowe - -### 2. Nowa struktura `Canvas.js` -``` -Canvas/ -├── Konstruktor i inicjalizacja -│ ├── _initializeModules() -│ └── _setupCanvas() -├── Główne operacje fasady -│ ├── loadInitialState() -│ ├── saveState() -│ ├── render() -│ └── addLayer() -├── Operacje na masce -│ └── startMaskEditor() -├── Metody pomocnicze -│ ├── getMouseWorldCoordinates() -│ ├── updateHistoryButtons() -│ └── incrementOperationCount() -└── Metody delegujące (tymczasowe) - └── [zachowane dla kompatybilności] -``` - -### 3. Aktualizacja `CanvasView.js` -Główny interfejs użytkownika został zaktualizowany aby używać nowego podejścia: - -#### Przykłady zmian: -```javascript -// PRZED -onclick: () => canvas.mirrorHorizontal() - -// PO -onclick: () => canvas.canvasLayers.mirrorHorizontal() - -// PRZED -const imageData = await canvas.getLayerImageData(selectedLayer); - -// PO -const imageData = await canvas.canvasLayers.getLayerImageData(selectedLayer); -``` - -## Mapowanie modułów - -| Moduł | Odpowiedzialność | Przykładowe metody | -|-------|------------------|-------------------| -| `canvasLayers` | Operacje na warstwach | `copySelectedLayers()`, `moveLayerUp()`, `mirrorHorizontal()` | -| `canvasInteractions` | Obsługa interakcji | `handleMouseMove()`, `handleKeyDown()` | -| `canvasIO` | Operacje wejścia/wyjścia | `importLatestImage()`, `sendDataViaWebSocket()` | -| `canvasState` | Zarządzanie stanem | `saveStateToDB()`, `undo()`, `redo()` | -| `canvasRenderer` | Renderowanie | `render()` (wywoływane przez fasadę) | -| `maskTool` | Narzędzie masek | `activate()`, `setBrushSize()` | -| `imageReferenceManager` | Zarządzanie pamięcią | `manualGarbageCollection()` | - -## Instrukcje migracji - -### Stare podejście (przestarzałe) -```javascript -// Bezpośrednie wywołanie metod delegujących -canvas.copySelectedLayers(); -canvas.handleMouseMove(e); -canvas.getLayerImageData(layer); -``` - -### Nowe podejście (zalecane) -```javascript -// Dostęp bezpośrednio do modułów -canvas.canvasLayers.copySelectedLayers(); -canvas.canvasInteractions.handleMouseMove(e); -canvas.canvasLayers.getLayerImageData(layer); - -// Lub użycie głównych operacji fasady -canvas.render(); -canvas.saveState(); -canvas.addLayer(image); -``` - -### Zasady wyboru podejścia -1. **Główne operacje** → Używaj fasady (`canvas.render()`, `canvas.saveState()`) -2. **Operacje specjalistyczne** → Używaj modułów (`canvas.canvasLayers.mirrorHorizontal()`) -3. **Częste operacje** → Metody delegujące zostały zachowane dla kompatybilności - -## Korzyści refaktoryzacji - -### Przed refaktoryzacją: -- 🔴 80+ metod delegujących w klasie Canvas -- 🔴 Każda nowa funkcja wymagała aktualizacji fasady -- 🔴 Trudne debugowanie i śledzenie przepływu danych -- 🔴 Naruszenie zasady Single Responsibility - -### Po refaktoryzacji: -- ✅ **Czysta fasada** z kluczowymi operacjami wysokiego poziomu -- ✅ **Modułowa architektura** z jasnym podziałem odpowiedzialności -- ✅ **Łatwiejsze utrzymanie** - zmiany w module nie wpływają na fasadę -- ✅ **Większa elastyczność** - wybór między uproszczonym a szczegółowym interfejsem -- ✅ **Kompatybilność wsteczna** - istniejący kod nadal działa - -## Status refaktoryzacji - -### ✅ Zakończone zadania - -1. **Refaktoryzacja klasy Canvas** - przekształcenie w prawdziwą fasadę ✅ -2. **Aktualizacja CanvasView.js** - migracja do nowego podejścia ✅ -3. **Implementacja wzorca fasady** - główne operacje wysokiego poziomu ✅ -4. **Zachowanie kompatybilności** - metody delegujące dla istniejącego kodu ✅ - -### 📋 Zmiany w CanvasView.js - -Wszystkie wywołania zostały zaktualizowane zgodnie z nowym podejściem: - -```javascript -// Operacje I/O -canvas.canvasIO.importLatestImage() -canvas.canvasLayers.handlePaste(addMode) - -// Operacje na warstwach -canvas.canvasLayers.moveLayerUp() -canvas.canvasLayers.moveLayerDown() -canvas.canvasLayers.mirrorHorizontal() -canvas.canvasLayers.mirrorVertical() -canvas.canvasLayers.getLayerImageData(selectedLayer) - -// Garbage Collection -canvas.imageReferenceManager.getStats() -canvas.imageReferenceManager.manualGarbageCollection() -``` - -### 🎯 Kolejne kroki - -1. **Monitorowanie działania** - sprawdzenie czy wszystkie funkcje działają poprawnie ✅ -2. **Usunięcie metod delegujących do CanvasState** - zakończone ✅ -3. **Rozszerzenie dokumentacji** - dla poszczególnych modułów ✅ -4. **Dodanie testów jednostkowych** - dla modułów - -### 🔧 Ostatnie poprawki (2025-06-29) - -1. **Dodano brakujące metody w CanvasLayers.js** ✅ - - `resizeLayer(scale)` - zmienia rozmiar wybranych warstw - - `rotateLayer(angle)` - obraca wybrane warstwy - - Poprawiono delegację z Canvas.js do CanvasLayers.js - -2. **Weryfikacja spójności** ✅ - - Wszystkie delegacje w Canvas.js wskazują na istniejące metody w modułach - - CanvasView.js używa nowego podejścia modułowego - - Dokumentacja została zaktualizowana - -3. **Finalne poprawki architektury** ✅ - - Poprawiono konstruktor CanvasLayers.js - zmieniono mylącą nazwę parametru z `canvasLayers` na `canvas` - - Zaktualizowano wszystkie odwołania `this.canvasLayers.` na `this.canvas.` w CanvasLayers.js - - Poprawiono wywołania w CanvasView.js - `canvas.rotateLayer()` → `canvas.canvasLayers.rotateLayer()` - - Wszystkie moduły używają teraz spójnej konwencji nazewnictwa - -4. **Usunięcie metod delegujących do CanvasState** ✅ - - Usunięto metodę delegującą `saveStateToDB()` z Canvas.js - - Zaktualizowano wszystkie wywołania w CanvasView.js: `canvas.undo()` → `canvas.canvasState.undo()` - - Zaktualizowano wszystkie wywołania w CanvasInteractions.js dla operacji undo/redo i copy/paste - - Zaktualizowano wywołania w CanvasLayers.js i CanvasIO.js - - Wszystkie operacje na stanie używają teraz bezpośrednio modułu `canvasState` - -5. **Usunięcie metod delegujących do CanvasLayers** ✅ - - Usunięto 14 metod delegujących do CanvasLayers z Canvas.js - - Zaktualizowano wszystkie wywołania w CanvasRenderer.js, CanvasIO.js i CanvasInteractions.js - - Wszystkie operacje na warstwach używają teraz bezpośrednio modułu `canvasLayers` - - Canvas.js zawiera teraz tylko główne operacje fasady i niezbędne metody pomocnicze - -6. **Usunięcie metod delegujących do CanvasInteractions** ✅ - - Usunięto ostatnią metodę delegującą `handleMouseMove()` z Canvas.js - - Metoda nie była używana w żadnym pliku, więc usunięcie było bezpieczne - - Wszystkie operacje interakcji używają teraz bezpośrednio modułu `canvasInteractions` - - Canvas.js jest teraz prawdziwą fasadą bez niepotrzebnych metod delegujących - -## Uwagi dla deweloperów - -- ✅ **Refaktoryzacja w pełni zakończona** - wszystkie pliki zostały zaktualizowane -- ✅ **Nowy kod** używa modułów bezpośrednio zgodnie z wzorcem fasady -- ✅ **Wszystkie metody delegujące** do głównych modułów zostały usunięte -- ✅ **Czysta fasada** - Canvas.js zawiera tylko główne operacje wysokiego poziomu -- ✅ **Spójna architektura** - wszystkie moduły używają poprawnych referencji -- ✅ **Minimalne delegacje** - pozostały tylko metody do ImageReferenceManager -- 📚 **Dokumentacja** została zaktualizowana w tym przewodniku -- 🔄 **Kompatybilność** z istniejącym kodem jest zachowana - -**Refaktoryzacja została w pełni zakończona!** Canvas.js jest teraz prawdziwą fasadą bez niepotrzebnych metod delegujących. System jest gotowy do dalszego rozwoju z czystą architekturą opartą na wzorcu fasady. - -### 📋 Mapowanie kompletnych funkcjonalności - -| Funkcjonalność | Moduł | Metoda | Status | -|----------------|-------|--------|--------| -| Dodawanie warstw | `canvasLayers` | `addLayerWithImage()` | ✅ | -| Kopiowanie/wklejanie | `canvasLayers` | `copySelectedLayers()`, `handlePaste()` | ✅ | -| Przesuwanie warstw | `canvasLayers` | `moveLayerUp()`, `moveLayerDown()` | ✅ | -| Transformacje | `canvasLayers` | `resizeLayer()`, `rotateLayer()` | ✅ | -| Odbicia lustrzane | `canvasLayers` | `mirrorHorizontal()`, `mirrorVertical()` | ✅ | -| Obsługa interakcji | `canvasInteractions` | `handleMouseMove()`, `handleKeyDown()` | ✅ | -| Zarządzanie stanem | `canvasState` | `saveState()`, `undo()`, `redo()` | ✅ | -| Operacje I/O | `canvasIO` | `importLatestImage()`, `sendDataViaWebSocket()` | ✅ | -| Renderowanie | `canvasRenderer` | `render()` | ✅ | -| Zarządzanie pamięcią | `imageReferenceManager` | `manualGarbageCollection()` | ✅ | diff --git a/js/utils/ClipboardManager.js b/js/utils/ClipboardManager.js index 5021f80..fc1d755 100644 --- a/js/utils/ClipboardManager.js +++ b/js/utils/ClipboardManager.js @@ -20,14 +20,12 @@ export class ClipboardManager { 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); @@ -37,7 +35,6 @@ export class ClipboardManager { 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); @@ -83,16 +80,14 @@ export class ClipboardManager { */ async trySystemClipboardPaste(addMode) { 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); - - // Check for image data first + const imageType = item.types.find(type => type.startsWith('image/')); if (imageType) { try { @@ -113,8 +108,7 @@ export class ClipboardManager { 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)) { @@ -140,7 +134,6 @@ export class ClipboardManager { } } - // Fallback to text-only API if (navigator.clipboard?.readText) { try { const text = await navigator.clipboard.readText(); @@ -173,17 +166,14 @@ export class ClipboardManager { return false; } - // Trim whitespace text = text.trim(); - // Check if it's empty after trimming if (!text) { return false; } - // Check if it's a URL first (URLs have priority and don't need file extensions) if (text.startsWith('http://') || text.startsWith('https://') || text.startsWith('file://')) { - // For URLs, we're more permissive - any valid URL could potentially be an image + try { new URL(text); log.debug("Detected valid URL:", text); @@ -194,13 +184,11 @@ export class ClipboardManager { } } - // For local file paths, check for image extensions const imageExtensions = [ '.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp', '.svg', '.tiff', '.tif', '.ico', '.avif' ]; - // Check if the text ends with a valid image extension (case insensitive) const hasImageExtension = imageExtensions.some(ext => text.toLowerCase().endsWith(ext) ); @@ -210,8 +198,7 @@ export class ClipboardManager { return false; } - // Basic path validation for local files - should look like a file path - // Accept both Windows and Unix style paths + const pathPatterns = [ /^[a-zA-Z]:[\\\/]/, // Windows absolute path (C:\... or C:/...) /^[\\\/]/, // Unix absolute path (/...) @@ -238,7 +225,7 @@ export class ClipboardManager { * @returns {Promise} - True if successful, false otherwise */ async loadImageFromPath(filePath, addMode) { - // Method 1: Direct loading for URLs + if (filePath.startsWith('http://') || filePath.startsWith('https://')) { try { const img = new Image(); @@ -261,7 +248,6 @@ export class ClipboardManager { } } - // Method 2: Load local files via backend endpoint try { log.info("Attempting to load local file via backend"); const success = await this.loadFileViaBackend(filePath, addMode); @@ -272,7 +258,6 @@ export class ClipboardManager { log.warn("Backend loading failed:", error); } - // Method 3: Fallback to file picker try { log.info("Falling back to file picker"); const success = await this.promptUserForFile(filePath, addMode); @@ -283,7 +268,6 @@ export class ClipboardManager { log.warn("File picker failed:", error); } - // Method 4: Show user a helpful message this.showFilePathMessage(filePath); return false; } @@ -297,8 +281,7 @@ export class ClipboardManager { async loadFileViaBackend(filePath, addMode) { try { log.info("Loading file via ComfyUI backend:", filePath); - - // Use the backend endpoint to load image from path + const response = await api.fetchApi("/ycnode/load_image_from_path", { method: "POST", headers: { @@ -323,8 +306,7 @@ export class ClipboardManager { } log.info("Successfully loaded image via ComfyUI backend:", filePath); - - // Create image from the returned base64 data + const img = new Image(); const success = await new Promise((resolve) => { img.onload = async () => { @@ -356,13 +338,12 @@ export class ClipboardManager { */ async promptUserForFile(originalPath, addMode) { return new Promise((resolve) => { - // Create a temporary file input + const fileInput = document.createElement('input'); fileInput.type = 'file'; fileInput.accept = 'image/*'; fileInput.style.display = 'none'; - // Extract filename from path for user reference const fileName = originalPath.split(/[\\\/]/).pop(); fileInput.onchange = async (event) => { @@ -396,8 +377,7 @@ export class ClipboardManager { log.warn("Selected file is not an image"); resolve(false); } - - // Clean up + document.body.removeChild(fileInput); }; @@ -407,10 +387,8 @@ export class ClipboardManager { resolve(false); }; - // Show a brief notification to the user this.showNotification(`Detected image path: ${fileName}. Please select the file to load it.`, 3000); - // Add to DOM and trigger click document.body.appendChild(fileInput); fileInput.click(); }); @@ -433,8 +411,7 @@ export class ClipboardManager { */ showEmptyClipboardMessage(addMode) { const message = `Copied a file? Browser can't access file paths for security. Click here to select the file manually.`; - - // Create clickable notification + const notification = document.createElement('div'); notification.style.cssText = ` position: fixed; @@ -464,7 +441,6 @@ export class ClipboardManager { `; - // Add hover effect notification.onmouseenter = () => { notification.style.backgroundColor = '#3d6bb0'; notification.style.borderColor = '#5a8bd8'; @@ -476,7 +452,6 @@ export class ClipboardManager { notification.style.transform = 'translateY(0)'; }; - // Add click handler to open file picker notification.onclick = async () => { document.body.removeChild(notification); try { @@ -489,10 +464,8 @@ export class ClipboardManager { } }; - // Add to DOM document.body.appendChild(notification); - // Auto-remove after longer duration setTimeout(() => { if (notification.parentNode) { notification.parentNode.removeChild(notification); @@ -508,7 +481,7 @@ export class ClipboardManager { * @param {number} duration - Duration in milliseconds */ showNotification(message, duration = 3000) { - // Create notification element + const notification = document.createElement('div'); notification.style.cssText = ` position: fixed; @@ -526,10 +499,8 @@ export class ClipboardManager { `; notification.textContent = message; - // Add to DOM document.body.appendChild(notification); - // Remove after duration setTimeout(() => { if (notification.parentNode) { notification.parentNode.removeChild(notification);