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.
This commit is contained in:
Dariusz L
2025-07-01 16:42:48 +02:00
parent b2ff5666f9
commit e4da6e4d31
5 changed files with 40 additions and 308 deletions

View File

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

View File

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

View File

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

View File

@@ -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()` | ✅ |

View File

@@ -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<boolean>} - 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 {
</div>
`;
// 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);