From a0ceb3b97c9e958daeeef2d5db7c517395a1c069 Mon Sep 17 00:00:00 2001 From: Dariusz L Date: Tue, 1 Jul 2025 06:42:20 +0200 Subject: [PATCH] Remove auto mask loading docs and clean up code comments Deleted documentation files related to automatic mask loading. Cleaned up and streamlined comments across Canvas.js, CanvasInteractions.js, CanvasLayers.js, CanvasView.js, MaskTool.js, mask_utils.js, and example scripts for improved clarity and maintainability. No functional changes to core logic. --- Doc/AutoMaskLoading | 186 ----------------------- MASK_AUTO_LOADING_README.md | 227 ---------------------------- js/Canvas.js | 180 ++++++++-------------- js/CanvasInteractions.js | 3 +- js/CanvasLayers.js | 35 +---- js/CanvasView.js | 48 ++---- js/MaskTool.js | 14 +- js/examples/mask_editor_examples.js | 50 +++--- js/utils/mask_utils.js | 25 ++- 9 files changed, 126 insertions(+), 642 deletions(-) delete mode 100644 Doc/AutoMaskLoading delete mode 100644 MASK_AUTO_LOADING_README.md diff --git a/Doc/AutoMaskLoading b/Doc/AutoMaskLoading deleted file mode 100644 index 5c16fd9..0000000 --- a/Doc/AutoMaskLoading +++ /dev/null @@ -1,186 +0,0 @@ -# Automatyczne Nakładanie Masek w Mask Editorze - -## Przegląd - -Ta funkcjonalność pozwala na automatyczne nakładanie predefiniowanych masek po otwarciu mask editora ComfyUI. Główną zaletą jest możliwość wysłania czystego obrazu (bez maski) do editora, a następnie automatyczne nałożenie maski w edytorze, co pozwala na prawidłowe działanie narzędzi takich jak gumka. - -## Kluczowe Funkcje - -### 1. Wysyłanie Czystego Obrazu -- Możliwość wysłania obrazu bez istniejącej maski do mask editora -- Editor "pamięta" oryginalny obraz pod maską -- Prawidłowe działanie gumki i innych narzędzi edycji - -### 2. Automatyczne Nakładanie Maski -- Maska jest nakładana automatycznie po otwarciu editora -- Obsługa zarówno nowego jak i starego mask editora ComfyUI -- Natychmiastowe nakładanie bez opóźnień - -### 3. Elastyczne Formaty Masek -- Obsługa obiektów Image -- Obsługa elementów HTMLCanvasElement -- Automatyczna konwersja formatów - -## API - -### Canvas.startMaskEditor(predefinedMask, sendCleanImage) - -Główna metoda do uruchamiania mask editora z predefiniowaną maską. - -**Parametry:** -- `predefinedMask` (Image|HTMLCanvasElement|null) - Opcjonalna maska do nałożenia -- `sendCleanImage` (boolean) - Czy wysłać czysty obraz (domyślnie false) - -**Przykład:** -```javascript -// Wyślij czysty obraz i nałóż maskę w edytorze -await canvas.startMaskEditor(maskImage, true); - -// Wyślij obraz z istniejącą maską i dodaj nową maskę -await canvas.startMaskEditor(maskImage, false); -``` - -### Funkcje Pomocnicze (mask_utils.js) - -#### start_mask_editor_with_predefined_mask(canvasInstance, maskImage, sendCleanImage) - -Pomocnicza funkcja dla łatwiejszego użycia. - -**Parametry:** -- `canvasInstance` - Instancja Canvas -- `maskImage` - Obraz maski -- `sendCleanImage` - Czy wysłać czysty obraz (domyślnie true) - -#### create_mask_from_image_src(imageSrc) - -Tworzy obiekt Image z URL lub data URL. - -**Parametry:** -- `imageSrc` (string) - URL obrazu - -**Zwraca:** Promise - -#### canvas_to_mask_image(canvas) - -Konwertuje HTMLCanvasElement do obiektu Image. - -**Parametry:** -- `canvas` (HTMLCanvasElement) - Canvas do konwersji - -**Zwraca:** Promise - -## Jak to Działa - -### 1. Przygotowanie Obrazu -```javascript -// Wybór metody w zależności od parametru sendCleanImage -if (sendCleanImage) { - blob = await this.canvasLayers.getFlattenedCanvasAsBlob(); -} else { - blob = await this.canvasLayers.getFlattenedCanvasForMaskEditor(); -} -``` - -### 2. Otwieranie Editora -```javascript -// Standardowy proces otwierania mask editora -ComfyApp.copyToClipspace(this.node); -ComfyApp.clipspace_return_node = this.node; -ComfyApp.open_maskeditor(); -``` - -### 3. Oczekiwanie na Otwarcie -```javascript -waitForMaskEditorAndApplyMask() { - const checkEditor = () => { - if (mask_editor_showing(app)) { - // Editor się otworzył - nałóż maskę - setTimeout(() => { - this.applyMaskToEditor(this.pendingMask); - }, 200); - } else { - setTimeout(checkEditor, 100); - } - }; - checkEditor(); -} -``` - -### 4. Nakładanie Maski -```javascript -// Automatyczne wykrywanie typu editora -const useNewEditor = app.ui.settings.getSettingValue('Comfy.MaskEditor.UseNewEditor'); - -if (useNewEditor) { - await this.applyMaskToNewEditor(maskData); -} else { - await this.applyMaskToOldEditor(maskData); -} -``` - -## Obsługa Różnych Editorów - -### Nowy Editor (MessageBroker) -```javascript -const editor = MaskEditorDialog.instance; -const messageBroker = editor.getMessageBroker(); -const maskCanvas = await messageBroker.pull('maskCanvas'); -const maskCtx = await messageBroker.pull('maskCtx'); -``` - -### Stary Editor (Bezpośredni Dostęp) -```javascript -const maskCanvas = document.getElementById('maskCanvas'); -const maskCtx = maskCanvas.getContext('2d'); -``` - -## Przetwarzanie Masek - -Maski są automatycznie przetwarzane do odpowiedniego formatu: - -```javascript -// Konwersja do formatu editora -for (let i = 0; i < data.length; i += 4) { - const alpha = data[i + 3]; - data[i] = maskColor.r; // R - data[i + 1] = maskColor.g; // G - data[i + 2] = maskColor.b; // B - data[i + 3] = alpha; // Zachowaj alpha -} -``` - -## Obsługa Błędów - -System zawiera kompleksową obsługę błędów: - -```javascript -try { - await this.applyMaskToEditor(maskData); - log.info("Predefined mask applied successfully"); -} catch (error) { - log.error("Failed to apply predefined mask:", error); -} -``` - -## Kompatybilność - -- ✅ Nowy mask editor ComfyUI (MessageBroker) -- ✅ Stary mask editor ComfyUI (bezpośredni dostęp) -- ✅ Wszystkie formaty masek (Image, Canvas) -- ✅ Automatyczne wykrywanie typu editora - -## Zalety - -1. **Zachowanie Oryginalnego Obrazu**: Editor "pamięta" co jest pod maską -2. **Prawidłowe Działanie Gumki**: Możliwość usuwania części maski -3. **Elastyczność**: Obsługa różnych formatów i editorów -4. **Automatyzacja**: Brak potrzeby ręcznego nakładania masek -5. **Kompatybilność**: Działa z istniejącym kodem - -## Przypadki Użycia - -- Automatyczne nakładanie masek z AI/ML modeli -- Predefiniowane szablony masek -- Integracja z zewnętrznymi narzędziami -- Workflow automatyzacja -- Batch processing masek diff --git a/MASK_AUTO_LOADING_README.md b/MASK_AUTO_LOADING_README.md deleted file mode 100644 index ffe2716..0000000 --- a/MASK_AUTO_LOADING_README.md +++ /dev/null @@ -1,227 +0,0 @@ -# Automatyczne Nakładanie Masek w Mask Editorze - -## 🎯 Cel - -Ta funkcjonalność rozwiązuje problem automatycznego nakładania predefiniowanych masek w mask editorze ComfyUI. Główną zaletą jest możliwość wysłania **czystego obrazu** (bez maski) do editora, a następnie automatyczne nałożenie maski w edytorze, co pozwala na prawidłowe działanie narzędzi takich jak gumka. - -## ✨ Kluczowe Zalety - -- 🖼️ **Czysty Obraz**: Wysyłanie obrazu bez istniejącej maski -- 🎨 **Prawidłowa Gumka**: Editor "pamięta" oryginalny obraz pod maską -- ⚡ **Automatyzacja**: Maska nakładana automatycznie po otwarciu -- 🔄 **Kompatybilność**: Obsługa nowego i starego mask editora -- 📐 **Elastyczność**: Różne formaty masek (Image, Canvas) - -## 🚀 Szybki Start - -### Automatyczne Zachowanie (Zalecane) - -```javascript -import { start_mask_editor_auto } from './js/utils/mask_utils.js'; - -// Uruchom mask editor z automatycznym zachowaniem: -// - Wyślij czysty obraz (bez maski) -// - Automatycznie nałóż istniejącą maskę z canvas -start_mask_editor_auto(canvasInstance); -``` - -### Użycie z Predefiniowaną Maską - -```javascript -import { start_mask_editor_with_predefined_mask, create_mask_from_image_src } from './js/utils/mask_utils.js'; - -// Załaduj maskę z URL -const maskImage = await create_mask_from_image_src('/path/to/mask.png'); - -// Uruchom mask editor z czystym obrazem i predefiniowaną maską -start_mask_editor_with_predefined_mask(canvasInstance, maskImage, true); -``` - -### Bezpośrednie Użycie Canvas API - -```javascript -// Automatyczne zachowanie (domyślne) -await canvasInstance.startMaskEditor(); - -// Z predefiniowaną maską -await canvasInstance.startMaskEditor(maskImage, true); -``` - -## 📚 API Reference - -### Canvas.startMaskEditor(predefinedMask, sendCleanImage) - -**Parametry:** -- `predefinedMask` (Image|HTMLCanvasElement|null) - Maska do nałożenia -- `sendCleanImage` (boolean) - Czy wysłać czysty obraz (domyślnie false) - -### Funkcje Pomocnicze - -#### `start_mask_editor_with_predefined_mask(canvasInstance, maskImage, sendCleanImage)` -Główna funkcja pomocnicza do uruchamiania editora z maską. - -#### `create_mask_from_image_src(imageSrc)` -Tworzy obiekt Image z URL. - -#### `canvas_to_mask_image(canvas)` -Konwertuje Canvas do Image. - -## 💡 Przykłady Użycia - -### 1. Maska z URL -```javascript -const maskImage = await create_mask_from_image_src('/masks/face_mask.png'); -start_mask_editor_with_predefined_mask(canvas, maskImage, true); -``` - -### 2. Maska z Canvas -```javascript -// Stwórz maskę programowo -const maskCanvas = document.createElement('canvas'); -const ctx = maskCanvas.getContext('2d'); -// ... rysowanie maski ... - -const maskImage = await canvas_to_mask_image(maskCanvas); -start_mask_editor_with_predefined_mask(canvas, maskImage, true); -``` - -### 3. Maska z Danych Binarnych -```javascript -const blob = new Blob([binaryData], { type: 'image/png' }); -const dataUrl = URL.createObjectURL(blob); -const maskImage = await create_mask_from_image_src(dataUrl); -start_mask_editor_with_predefined_mask(canvas, maskImage, true); -``` - -## 🔧 Jak to Działa - -### 1. Przygotowanie Obrazu -System wybiera odpowiednią metodę w zależności od parametru `sendCleanImage`: -- `true`: Wysyła czysty obraz bez maski -- `false`: Wysyła obraz z istniejącą maską - -### 2. Otwieranie Editora -Standardowy proces otwierania mask editora ComfyUI. - -### 3. Automatyczne Nakładanie -Po otwarciu editora system: -- Wykrywa typ editora (nowy/stary) -- Przetwarza maskę do odpowiedniego formatu -- Nakłada maskę na canvas editora -- Zapisuje stan dla undo/redo - -## 🎛️ Tryby Działania - -### Czysty Obraz (Zalecany) -```javascript -// Wyślij czysty obraz, nałóż maskę w edytorze -await canvas.startMaskEditor(maskImage, true); -``` -**Zalety:** -- Gumka działa prawidłowo -- Editor "pamięta" oryginalny obraz -- Pełna funkcjonalność narzędzi - -### Kombinowany -```javascript -// Wyślij obraz z istniejącą maską, dodaj nową maskę -await canvas.startMaskEditor(maskImage, false); -``` -**Zastosowanie:** -- Łączenie wielu masek -- Dodawanie do istniejącej maski - -## 🔍 Obsługa Błędów - -```javascript -try { - const maskImage = await create_mask_from_image_src('/path/to/mask.png'); - start_mask_editor_with_predefined_mask(canvas, maskImage, true); -} catch (error) { - console.error('Błąd ładowania maski:', error); - // Fallback - uruchom bez maski - await canvas.startMaskEditor(); -} -``` - -## 🏗️ Architektura - -### Komponenty -- **Canvas.js**: Główna logika i API -- **mask_utils.js**: Funkcje pomocnicze -- **Detektory Editora**: Automatyczne wykrywanie typu editora -- **Procesory Masek**: Konwersja formatów - -### Przepływ Danych -``` -Maska → Przetwarzanie → Editor → Automatyczne Nakładanie -``` - -## 🔧 Konfiguracja - -### Wymagania -- ComfyUI z mask editorem -- Obsługa ES6 modules -- Canvas API - -### Integracja -```javascript -import { Canvas } from './js/Canvas.js'; -import { start_mask_editor_with_predefined_mask } from './js/utils/mask_utils.js'; -``` - -## 📋 Przypadki Użycia - -- 🤖 **AI/ML Modele**: Automatyczne maski z modeli -- 📝 **Szablony**: Predefiniowane wzorce masek -- 🔗 **Integracje**: Zewnętrzne narzędzia i API -- ⚙️ **Workflow**: Automatyzacja procesów -- 📦 **Batch Processing**: Masowe przetwarzanie - -## 🐛 Rozwiązywanie Problemów - -### Maska się nie nakłada -- Sprawdź czy obraz maski jest załadowany -- Upewnij się że editor jest w pełni otwarty -- Sprawdź logi w konsoli - -### Gumka nie działa -- Użyj `sendCleanImage = true` -- Sprawdź czy maska ma prawidłowy kanał alpha - -### Błędy kompatybilności -- Sprawdź ustawienia mask editora w ComfyUI -- Upewnij się że używasz odpowiedniej wersji - -## 📁 Struktura Plików - -``` -js/ -├── Canvas.js # Główne API -├── utils/ -│ └── mask_utils.js # Funkcje pomocnicze -├── examples/ -│ └── mask_editor_examples.js # Przykłady użycia -└── Doc/ - └── AutoMaskLoading # Szczegółowa dokumentacja -``` - -## 🔄 Kompatybilność - -- ✅ Nowy mask editor ComfyUI (MessageBroker) -- ✅ Stary mask editor ComfyUI (bezpośredni dostęp) -- ✅ Wszystkie formaty masek (Image, Canvas, URL) -- ✅ Automatyczne wykrywanie konfiguracji - -## 📈 Wydajność - -- Minimalne opóźnienie nakładania (200ms) -- Automatyczna optymalizacja formatów -- Efektywne zarządzanie pamięcią -- Asynchroniczne operacje - ---- - -**Autor:** Cline AI Assistant -**Wersja:** 1.0.0 -**Data:** 2025-06-30 diff --git a/js/Canvas.js b/js/Canvas.js index 46dd049..34234f8 100644 --- a/js/Canvas.js +++ b/js/Canvas.js @@ -50,19 +50,15 @@ export class Canvas { this.dataInitialized = false; this.pendingDataCheck = null; this.imageCache = new Map(); - - // Inicjalizacja modułów + this._initializeModules(callbacks); - - // Podstawowa konfiguracja + this._setupCanvas(); - - // Delegacja interaction dla kompatybilności wstecznej + this.interaction = this.canvasInteractions.interaction; console.log('Canvas widget element:', this.node); - - // Dodaj metodę do kontroli widoczności podglądu + this.previewVisible = true; // Domyślnie widoczny this.setPreviewVisibility(false); } @@ -95,16 +91,14 @@ export class Canvas { async setPreviewVisibility(visible) { this.previewVisible = visible; console.log("Canvas preview visibility set to:", visible); - - // Znajdź i kontroluj ImagePreviewWidget + const imagePreviewWidget = await this.waitForWidget("$$canvas-image-preview", this.node); if (imagePreviewWidget) { console.log("Found $$canvas-image-preview widget, controlling visibility"); if (visible) { console.log("=== SHOWING WIDGET ==="); - - // Pokaż widget + if (imagePreviewWidget.options) { imagePreviewWidget.options.hidden = false; } @@ -123,8 +117,7 @@ export class Canvas { console.log("ImagePreviewWidget shown"); } else { console.log("=== HIDING WIDGET ==="); - - // Ukryj widget + if (imagePreviewWidget.options) { imagePreviewWidget.options.hidden = true; } @@ -154,7 +147,7 @@ export class Canvas { * @private */ _initializeModules(callbacks) { - // Moduły są publiczne dla bezpośredniego dostępu gdy potrzebne + this.maskTool = new MaskTool(this, {onStateChange: this.onStateChange}); this.canvasState = new CanvasState(this); this.canvasInteractions = new CanvasInteractions(this); @@ -172,17 +165,15 @@ export class Canvas { this.initCanvas(); this.canvasInteractions.setupEventListeners(); this.canvasIO.initNodeData(); - - // Inicjalizacja warstw z domyślną przezroczystością + this.layers = this.layers.map(layer => ({ ...layer, opacity: 1 })); } - // ========================================== - // GŁÓWNE OPERACJE FASADY - // ========================================== + + /** * Ładuje stan canvas z bazy danych @@ -300,9 +291,8 @@ export class Canvas { return this.canvasIO.importLatestImage(); } - // ========================================== - // OPERACJE NA MASCE - // ========================================== + + /** * Uruchamia edytor masek @@ -310,11 +300,10 @@ export class Canvas { * @param {boolean} sendCleanImage - Czy wysłać czysty obraz (bez maski) do editora */ async startMaskEditor(predefinedMask = null, sendCleanImage = true) { - // Zapisz obecny stan maski przed otwarciem editora (dla obsługi Cancel) + this.savedMaskState = await this.saveMaskState(); this.maskEditorCancelled = false; - - // Jeśli nie ma predefiniowanej maski, stwórz ją z istniejącej maski canvas + if (!predefinedMask && this.maskTool && this.maskTool.maskCanvas) { try { predefinedMask = await this.createMaskFromCurrentMask(); @@ -322,17 +311,15 @@ export class Canvas { log.warn("Could not create mask from current mask:", error); } } - - // Przechowaj maskę do późniejszego użycia + this.pendingMask = predefinedMask; - - // Wybierz odpowiednią metodę w zależności od parametru sendCleanImage + let blob; if (sendCleanImage) { - // Wyślij czysty obraz bez maski (domyślne zachowanie) + blob = await this.canvasLayers.getFlattenedCanvasAsBlob(); } else { - // Używamy specjalnej metody która łączy pełny obraz z istniejącą maską + blob = await this.canvasLayers.getFlattenedCanvasForMaskEditor(); } @@ -373,11 +360,9 @@ export class Canvas { this.editorWasShowing = false; this.waitWhileMaskEditing(); - - // Nasłuchuj na przycisk Cancel + this.setupCancelListener(); - - // Jeśli mamy predefiniowaną maskę, czekaj na otwarcie editora i nałóż ją + if (predefinedMask) { this.waitForMaskEditorAndApplyMask(); } @@ -388,9 +373,8 @@ export class Canvas { } } - // ========================================== - // METODY POMOCNICZE - // ========================================== + + /** * Inicjalizuje podstawowe właściwości canvas @@ -503,9 +487,8 @@ export class Canvas { } } - // ========================================== - // METODY DLA EDYTORA MASEK - // ========================================== + + /** * Czeka na otwarcie mask editora i automatycznie nakłada predefiniowaną maskę @@ -518,15 +501,15 @@ export class Canvas { attempts++; if (mask_editor_showing(app)) { - // Editor się otworzył - sprawdź czy jest w pełni zainicjalizowany + const useNewEditor = app.ui.settings.getSettingValue('Comfy.MaskEditor.UseNewEditor'); let editorReady = false; if (useNewEditor) { - // Sprawdź czy nowy editor jest gotowy - różne metody wykrywania + const MaskEditorDialog = window.MaskEditorDialog; if (MaskEditorDialog && MaskEditorDialog.instance) { - // Sprawdź czy ma MessageBroker i czy canvas jest dostępny + try { const messageBroker = MaskEditorDialog.instance.getMessageBroker(); if (messageBroker) { @@ -534,16 +517,15 @@ export class Canvas { log.info("New mask editor detected as ready via MessageBroker"); } } catch (e) { - // MessageBroker jeszcze nie gotowy + editorReady = false; } } - - // Alternatywne wykrywanie - sprawdź czy istnieje element maskEditor + if (!editorReady) { const maskEditorElement = document.getElementById('maskEditor'); if (maskEditorElement && maskEditorElement.style.display !== 'none') { - // Sprawdź czy ma canvas wewnątrz + const canvas = maskEditorElement.querySelector('canvas'); if (canvas) { editorReady = true; @@ -552,7 +534,7 @@ export class Canvas { } } } else { - // Sprawdź czy stary editor jest gotowy + const maskCanvas = document.getElementById('maskCanvas'); editorReady = maskCanvas && maskCanvas.getContext && maskCanvas.width > 0; if (editorReady) { @@ -561,21 +543,21 @@ export class Canvas { } if (editorReady) { - // Editor jest gotowy - nałóż maskę po krótkim opóźnieniu + log.info("Applying mask to editor after", attempts * 100, "ms wait"); setTimeout(() => { this.applyMaskToEditor(this.pendingMask); this.pendingMask = null; // Wyczyść po użyciu }, 300); // Krótsze opóźnienie gdy już wiemy że jest gotowy } else if (attempts < maxAttempts) { - // Editor widoczny ale nie gotowy - sprawdź ponownie + if (attempts % 10 === 0) { log.info("Waiting for mask editor to be ready... attempt", attempts, "/", maxAttempts); } setTimeout(checkEditor, 100); } else { log.warn("Mask editor timeout - editor not ready after", maxAttempts * 100, "ms"); - // Spróbuj nałożyć maskę mimo wszystko + log.info("Attempting to apply mask anyway..."); setTimeout(() => { this.applyMaskToEditor(this.pendingMask); @@ -583,7 +565,7 @@ export class Canvas { }, 100); } } else if (attempts < maxAttempts) { - // Editor jeszcze nie widoczny - sprawdź ponownie + setTimeout(checkEditor, 100); } else { log.warn("Mask editor timeout - editor not showing after", maxAttempts * 100, "ms"); @@ -600,28 +582,28 @@ export class Canvas { */ async applyMaskToEditor(maskData) { try { - // Sprawdź czy używamy nowego czy starego editora + const useNewEditor = app.ui.settings.getSettingValue('Comfy.MaskEditor.UseNewEditor'); if (useNewEditor) { - // Sprawdź czy nowy editor jest rzeczywiście dostępny + const MaskEditorDialog = window.MaskEditorDialog; if (MaskEditorDialog && MaskEditorDialog.instance) { - // Nowy editor - użyj MessageBroker + await this.applyMaskToNewEditor(maskData); } else { log.warn("New editor setting enabled but instance not found, trying old editor"); await this.applyMaskToOldEditor(maskData); } } else { - // Stary editor - bezpośredni dostęp do canvas + await this.applyMaskToOldEditor(maskData); } log.info("Predefined mask applied to mask editor successfully"); } catch (error) { log.error("Failed to apply predefined mask to editor:", error); - // Spróbuj alternatywną metodę + try { log.info("Trying alternative mask application method..."); await this.applyMaskToOldEditor(maskData); @@ -637,7 +619,7 @@ export class Canvas { * @param {Image|HTMLCanvasElement} maskData - Dane maski */ async applyMaskToNewEditor(maskData) { - // Pobierz instancję nowego editora + const MaskEditorDialog = window.MaskEditorDialog; if (!MaskEditorDialog || !MaskEditorDialog.instance) { throw new Error("New mask editor instance not found"); @@ -645,20 +627,16 @@ export class Canvas { const editor = MaskEditorDialog.instance; const messageBroker = editor.getMessageBroker(); - - // Pobierz canvas maski z editora + const maskCanvas = await messageBroker.pull('maskCanvas'); const maskCtx = await messageBroker.pull('maskCtx'); const maskColor = await messageBroker.pull('getMaskColor'); - // Konwertuj maskę do odpowiedniego formatu const processedMask = await this.processMaskForEditor(maskData, maskCanvas.width, maskCanvas.height, maskColor); - - // Nałóż maskę na canvas + maskCtx.clearRect(0, 0, maskCanvas.width, maskCanvas.height); maskCtx.drawImage(processedMask, 0, 0); - - // Zapisz stan dla undo/redo + messageBroker.publish('saveState'); } @@ -667,19 +645,17 @@ export class Canvas { * @param {Image|HTMLCanvasElement} maskData - Dane maski */ async applyMaskToOldEditor(maskData) { - // Znajdź canvas maski w starym edytorze + const maskCanvas = document.getElementById('maskCanvas'); if (!maskCanvas) { throw new Error("Old mask editor canvas not found"); } const maskCtx = maskCanvas.getContext('2d'); - - // Konwertuj maskę do odpowiedniego formatu (dla starego editora używamy białego koloru) + const maskColor = { r: 255, g: 255, b: 255 }; const processedMask = await this.processMaskForEditor(maskData, maskCanvas.width, maskCanvas.height, maskColor); - - // Nałóż maskę na canvas + maskCtx.clearRect(0, 0, maskCanvas.width, maskCanvas.height); maskCtx.drawImage(processedMask, 0, 0); } @@ -707,18 +683,14 @@ export class Canvas { tempCanvas.height = targetHeight; const tempCtx = tempCanvas.getContext('2d'); - // Wyczyść canvas tempCtx.clearRect(0, 0, targetWidth, targetHeight); - // Skaluj maskę do originalSize zamiast targetSize - // originalSize to prawdziwy rozmiar obrazu w mask editorze + const scaleToOriginal = Math.min(originalWidth / this.width, originalHeight / this.height); - - // Maska powinna pokryć cały obszar originalSize + const scaledWidth = this.width * scaleToOriginal; const scaledHeight = this.height * scaleToOriginal; - - // Wyśrodkuj na target canvas (który reprezentuje viewport mask editora) + const offsetX = (targetWidth - scaledWidth) / 2; const offsetY = (targetHeight - scaledHeight) / 2; @@ -733,22 +705,18 @@ export class Canvas { offset: { x: offsetX, y: offsetY } }); - // Pobierz dane obrazu i przetwórz je const imageData = tempCtx.getImageData(0, 0, targetWidth, targetHeight); const data = imageData.data; - // Konwertuj maskę do formatu editora (alpha channel jako maska) for (let i = 0; i < data.length; i += 4) { const alpha = data[i + 3]; // Oryginalny kanał alpha - - // Ustaw kolor maski + data[i] = maskColor.r; // R data[i + 1] = maskColor.g; // G data[i + 2] = maskColor.b; // B data[i + 3] = alpha; // Zachowaj oryginalny alpha } - // Zapisz przetworzone dane z powrotem tempCtx.putImageData(imageData, 0, 0); log.info("Mask processing completed - full size scaling applied"); @@ -833,20 +801,18 @@ export class Canvas { const maskCtx = this.maskTool.maskCtx; const destX = -this.maskTool.x; const destY = -this.maskTool.y; - - // Zamiast dodawać maskę (screen), zastąp całą maskę (source-over) - // Najpierw wyczyść obszar który będzie zastąpiony + + maskCtx.globalCompositeOperation = 'source-over'; maskCtx.clearRect(destX, destY, this.width, this.height); - - // Teraz narysuj nową maskę + maskCtx.drawImage(maskAsImage, destX, destY); this.render(); this.saveState(); const new_preview = new Image(); - // Użyj nowej metody z maską jako kanałem alpha + const blob = await this.canvasLayers.getFlattenedCanvasWithMaskAsBlob(); if (blob) { new_preview.src = URL.createObjectURL(blob); @@ -859,9 +825,8 @@ export class Canvas { this.render(); } - // ========================================== - // OBSŁUGA ANULOWANIA MASK EDITORA - // ========================================== + + /** * Zapisuje obecny stan maski przed otwarciem editora @@ -872,7 +837,6 @@ export class Canvas { return null; } - // Skopiuj dane z mask canvas const maskCanvas = this.maskTool.maskCanvas; const savedCanvas = document.createElement('canvas'); savedCanvas.width = maskCanvas.width; @@ -898,14 +862,12 @@ export class Canvas { return; } - // Przywróć dane maski if (savedState.maskData) { const maskCtx = this.maskTool.maskCtx; maskCtx.clearRect(0, 0, this.maskTool.maskCanvas.width, this.maskTool.maskCanvas.height); maskCtx.drawImage(savedState.maskData, 0, 0); } - // Przywróć pozycję maski if (savedState.maskPosition) { this.maskTool.x = savedState.maskPosition.x; this.maskTool.y = savedState.maskPosition.y; @@ -930,25 +892,20 @@ export class Canvas { */ async handleMaskEditorClose() { console.log("Node object after mask editor close:", this.node); - - // Sprawdź czy editor został anulowany + if (this.maskEditorCancelled) { log.info("Mask editor was cancelled - restoring original mask state"); - - // Przywróć oryginalny stan maski + if (this.savedMaskState) { await this.restoreMaskState(this.savedMaskState); } - - // Wyczyść flagi + this.maskEditorCancelled = false; this.savedMaskState = null; - - // Nie przetwarzaj wyniku z editora + return; } - // Kontynuuj normalną obsługę save if (!this.node.imgs || !this.node.imgs.length === 0 || !this.node.imgs[0].src) { log.warn("Mask editor was closed without a result."); return; @@ -995,20 +952,18 @@ export class Canvas { const maskCtx = this.maskTool.maskCtx; const destX = -this.maskTool.x; const destY = -this.maskTool.y; - - // Zamiast dodawać maskę (screen), zastąp całą maskę (source-over) - // Najpierw wyczyść obszar który będzie zastąpiony + + maskCtx.globalCompositeOperation = 'source-over'; maskCtx.clearRect(destX, destY, this.width, this.height); - - // Teraz narysuj nową maskę + maskCtx.drawImage(maskAsImage, destX, destY); this.render(); this.saveState(); const new_preview = new Image(); - // Użyj nowej metody z maską jako kanałem alpha + const blob = await this.canvasLayers.getFlattenedCanvasWithMaskAsBlob(); if (blob) { new_preview.src = URL.createObjectURL(blob); @@ -1019,8 +974,7 @@ export class Canvas { } this.render(); - - // Wyczyść zapisany stan po pomyślnym save + this.savedMaskState = null; } } diff --git a/js/CanvasInteractions.js b/js/CanvasInteractions.js index d04852a..6379861 100644 --- a/js/CanvasInteractions.js +++ b/js/CanvasInteractions.js @@ -194,8 +194,7 @@ export class CanvasInteractions { this.resetInteractionState(); this.canvas.render(); } - - // Clear internal clipboard when mouse leaves canvas + if (this.canvas.canvasLayers.internalClipboard.length > 0) { this.canvas.canvasLayers.internalClipboard = []; log.info("Internal clipboard cleared - mouse left canvas"); diff --git a/js/CanvasLayers.js b/js/CanvasLayers.js index 37612d7..5a9c8a2 100644 --- a/js/CanvasLayers.js +++ b/js/CanvasLayers.js @@ -51,7 +51,6 @@ export class CanvasLayers { this.canvas.saveState(); const newLayers = []; - // Calculate the center of the copied layers let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity; this.internalClipboard.forEach(layer => { minX = Math.min(minX, layer.x); @@ -63,7 +62,6 @@ export class CanvasLayers { const centerX = (minX + maxX) / 2; const centerY = (minY + maxY) / 2; - // Calculate offset to position at mouse cursor const mouseX = this.canvas.lastMousePosition.x; const mouseY = this.canvas.lastMousePosition.y; const offsetX = mouseX - centerX; @@ -89,14 +87,12 @@ export class CanvasLayers { try { log.info(`Paste operation started with preference: ${this.clipboardPreference}`); - // ALWAYS FIRST: Check Internal Clipboard if (this.internalClipboard.length > 0) { log.info("Pasting from internal clipboard"); this.pasteLayers(); return; } - // SECOND: Use preference setting if (this.clipboardPreference === 'clipspace') { log.info("Attempting paste from ComfyUI Clipspace"); if (!await this.tryClipspacePaste(addMode)) { @@ -116,8 +112,7 @@ export class CanvasLayers { try { log.info("Attempting to paste from ComfyUI Clipspace"); const clipspaceResult = ComfyApp.pasteFromClipspace(this.canvas.node); - - // Check if clipspace operation was successful and node has images + if (this.canvas.node.imgs && this.canvas.node.imgs.length > 0) { const clipspaceImage = this.canvas.node.imgs[0]; if (clipspaceImage && clipspaceImage.src) { @@ -672,7 +667,6 @@ export class CanvasLayers { tempCanvas.height = this.canvas.height; const tempCtx = tempCanvas.getContext('2d'); - // Najpierw renderuj wszystkie warstwy const sortedLayers = [...this.canvas.layers].sort((a, b) => a.zIndex - b.zIndex); sortedLayers.forEach(layer => { @@ -696,14 +690,12 @@ export class CanvasLayers { tempCtx.restore(); }); - // Pobierz dane obrazu const imageData = tempCtx.getImageData(0, 0, tempCanvas.width, tempCanvas.height); const data = imageData.data; - // Pobierz maskę z maskTool (używając tej samej logiki co w CanvasIO) const toolMaskCanvas = this.canvas.maskTool.getMask(); if (toolMaskCanvas) { - // Stwórz tymczasowy canvas dla maski w rozmiarze output area + const tempMaskCanvas = document.createElement('canvas'); tempMaskCanvas.width = this.canvas.width; tempMaskCanvas.height = this.canvas.height; @@ -711,7 +703,6 @@ export class CanvasLayers { tempMaskCtx.clearRect(0, 0, tempMaskCanvas.width, tempMaskCanvas.height); - // Użyj tej samej logiki co w CanvasIO const maskX = this.canvas.maskTool.x; const maskY = this.canvas.maskTool.y; @@ -737,7 +728,6 @@ export class CanvasLayers { ); } - // Konwertuj maskę do formatu alpha (tak jak w CanvasIO) const tempMaskData = tempMaskCtx.getImageData(0, 0, this.canvas.width, this.canvas.height); for (let i = 0; i < tempMaskData.data.length; i += 4) { const alpha = tempMaskData.data[i + 3]; @@ -746,21 +736,18 @@ export class CanvasLayers { } tempMaskCtx.putImageData(tempMaskData, 0, 0); - // Zastosuj maskę do obrazu const maskImageData = tempMaskCtx.getImageData(0, 0, this.canvas.width, this.canvas.height); const maskData = maskImageData.data; for (let i = 0; i < data.length; i += 4) { const originalAlpha = data[i + 3]; const maskAlpha = maskData[i + 3] / 255; // Użyj kanału alpha maski - - // ODWRÓCONA LOGIKA: Tam gdzie jest maska (alpha = 1) = przezroczysty - // Tam gdzie nie ma maski (alpha = 0) = widoczny + + const invertedMaskAlpha = 1 - maskAlpha; data[i + 3] = originalAlpha * invertedMaskAlpha; } - // Zapisz zmodyfikowane dane obrazu tempCtx.putImageData(imageData, 0, 0); } @@ -781,7 +768,6 @@ export class CanvasLayers { tempCanvas.height = this.canvas.height; const tempCtx = tempCanvas.getContext('2d'); - // Renderuj wszystkie warstwy (pełny obraz) const sortedLayers = [...this.canvas.layers].sort((a, b) => a.zIndex - b.zIndex); sortedLayers.forEach(layer => { @@ -805,14 +791,12 @@ export class CanvasLayers { tempCtx.restore(); }); - // Pobierz dane obrazu const imageData = tempCtx.getImageData(0, 0, tempCanvas.width, tempCanvas.height); const data = imageData.data; - // Pobierz maskę z maskTool i zastosuj ją jako kanał alpha const toolMaskCanvas = this.canvas.maskTool.getMask(); if (toolMaskCanvas) { - // Stwórz tymczasowy canvas dla maski w rozmiarze output area + const tempMaskCanvas = document.createElement('canvas'); tempMaskCanvas.width = this.canvas.width; tempMaskCanvas.height = this.canvas.height; @@ -820,7 +804,6 @@ export class CanvasLayers { tempMaskCtx.clearRect(0, 0, tempMaskCanvas.width, tempMaskCanvas.height); - // Użyj tej samej logiki co w CanvasIO const maskX = this.canvas.maskTool.x; const maskY = this.canvas.maskTool.y; @@ -846,7 +829,6 @@ export class CanvasLayers { ); } - // Konwertuj maskę do formatu alpha const tempMaskData = tempMaskCtx.getImageData(0, 0, this.canvas.width, this.canvas.height); for (let i = 0; i < tempMaskData.data.length; i += 4) { const alpha = tempMaskData.data[i + 3]; @@ -855,21 +837,18 @@ export class CanvasLayers { } tempMaskCtx.putImageData(tempMaskData, 0, 0); - // Zastosuj maskę do obrazu - NORMALNA LOGIKA dla edytora masek const maskImageData = tempMaskCtx.getImageData(0, 0, this.canvas.width, this.canvas.height); const maskData = maskImageData.data; for (let i = 0; i < data.length; i += 4) { const originalAlpha = data[i + 3]; const maskAlpha = maskData[i + 3] / 255; - - // ODWRÓCONA LOGIKA dla edytora: Tam gdzie jest maska (alpha = 1) = przezroczysty - // Tam gdzie nie ma maski (alpha = 0) = widoczny + + const invertedMaskAlpha = 1 - maskAlpha; data[i + 3] = originalAlpha * invertedMaskAlpha; } - // Zapisz zmodyfikowane dane obrazu tempCtx.putImageData(imageData, 0, 0); } diff --git a/js/CanvasView.js b/js/CanvasView.js index 9835003..417f666 100644 --- a/js/CanvasView.js +++ b/js/CanvasView.js @@ -485,8 +485,7 @@ async function createCanvasWidget(node, widget, app) { } else { helpTooltip.innerHTML = standardShortcuts; } - - // Najpierw wyświetlamy tooltip z visibility: hidden aby obliczyć jego wymiary + helpTooltip.style.visibility = 'hidden'; helpTooltip.style.display = 'block'; @@ -494,29 +493,23 @@ async function createCanvasWidget(node, widget, app) { const tooltipRect = helpTooltip.getBoundingClientRect(); const viewportWidth = window.innerWidth; const viewportHeight = window.innerHeight; - - // Obliczamy pozycję + let left = buttonRect.left; let top = buttonRect.bottom + 5; - - // Sprawdzamy czy tooltip wychodzi poza prawy brzeg ekranu + if (left + tooltipRect.width > viewportWidth) { left = viewportWidth - tooltipRect.width - 10; } - - // Sprawdzamy czy tooltip wychodzi poza dolny brzeg ekranu + if (top + tooltipRect.height > viewportHeight) { - // Wyświetlamy nad przyciskiem zamiast pod + top = buttonRect.top - tooltipRect.height - 5; } - - // Upewniamy się, że tooltip nie wychodzi poza lewy brzeg + if (left < 10) left = 10; - - // Upewniamy się, że tooltip nie wychodzi poza górny brzeg + if (top < 10) top = 10; - - // Ustawiamy finalną pozycję i pokazujemy tooltip + helpTooltip.style.left = `${left}px`; helpTooltip.style.top = `${top}px`; helpTooltip.style.visibility = 'visible'; @@ -671,7 +664,7 @@ async function createCanvasWidget(node, widget, app) { const height = parseInt(document.getElementById('canvas-height').value) || canvas.height; canvas.updateOutputAreaSize(width, height); document.body.removeChild(dialog); - // updateOutput is triggered by saveState in updateOutputAreaSize + }; document.getElementById('cancel-size').onclick = () => { @@ -946,8 +939,7 @@ async function createCanvasWidget(node, widget, app) { const updateOutput = async () => { triggerWidget.value = (triggerWidget.value + 1) % 99999999; - - // ZAWSZE generuj obraz (potrzebny dla funkcji masek) + try { const new_preview = new Image(); const blob = await canvas.getFlattenedCanvasWithMaskAsBlob(); @@ -961,8 +953,7 @@ async function createCanvasWidget(node, widget, app) { } catch (error) { console.error("Error updating node preview:", error); } - - // app.graph.runStep(); // Potentially not needed if we just want to mark dirty + }; const canvasContainer = $el("div.painterCanvasContainer.painter-container", { @@ -1111,8 +1102,6 @@ async function createCanvasWidget(node, widget, app) { canvas.loadInitialState(); }, 100); - - // Konfiguracja opcji ukrywania podglądu const showPreviewWidget = node.widgets.find(w => w.name === "show_preview"); if (showPreviewWidget) { const originalCallback = showPreviewWidget.callback; @@ -1121,20 +1110,17 @@ async function createCanvasWidget(node, widget, app) { if (originalCallback) { originalCallback.call(this, value); } - - // Kontroluj widoczność podglądu w Canvas + if (canvas && canvas.setPreviewVisibility) { canvas.setPreviewVisibility(value); } - - // Wymuś przerysowanie node'a + if (node.graph && node.graph.canvas) { node.setDirtyCanvas(true, true); } }; - - // Podgląd jest automatycznie ukrywany w konstruktorze Canvas.js - // Nie potrzebujemy już wywoływać setInitialPreviewVisibility + + } @@ -1213,7 +1199,6 @@ app.registerExtension({ return; } - // Iterate through every widget attached to this node this.widgets.forEach(w => { log.debug(`Widget name: ${w.name}, type: ${w.type}, value: ${w.value}`); }); @@ -1265,8 +1250,7 @@ app.registerExtension({ originalGetExtraMenuOptions?.apply(this, arguments); const self = this; - - // Usuń standardową opcję "Open in MaskEditor" jeśli istnieje + const maskEditorIndex = options.findIndex(option => option && option.content === "Open in MaskEditor" ); diff --git a/js/MaskTool.js b/js/MaskTool.js index 5320d0a..1d9d2d2 100644 --- a/js/MaskTool.js +++ b/js/MaskTool.js @@ -162,7 +162,7 @@ export class MaskTool { if (this.brushHardness === 1) { this.maskCtx.strokeStyle = `rgba(255, 255, 255, ${this.brushStrength})`; } else { - // hardness: 1 = hard edge, 0 = soft edge + const innerRadius = gradientRadius * this.brushHardness; const gradient = this.maskCtx.createRadialGradient( canvasX, canvasY, innerRadius, @@ -281,18 +281,16 @@ export class MaskTool { } setMask(image) { - // `this.x` i `this.y` przechowują pozycję lewego górnego rogu płótna maski - // względem lewego górnego rogu widoku. Zatem (-this.x, -this.y) to pozycja - // lewego górnego rogu widoku na płótnie maski. + + + const destX = -this.x; const destY = -this.y; - // Wyczyść tylko ten obszar na dużym płótnie maski, który odpowiada - // widocznemu obszarowi wyjściowemu. + this.maskCtx.clearRect(destX, destY, this.canvasInstance.width, this.canvasInstance.height); - // Narysuj nowy obraz maski (który ma rozmiar obszaru wyjściowego) - // dokładnie w tym wyczyszczonym miejscu. + this.maskCtx.drawImage(image, destX, destY); if (this.onStateChange) { diff --git a/js/examples/mask_editor_examples.js b/js/examples/mask_editor_examples.js index 052f13f..2006e82 100644 --- a/js/examples/mask_editor_examples.js +++ b/js/examples/mask_editor_examples.js @@ -15,11 +15,10 @@ import { * Przykład 1: Podstawowe użycie z obrazem maski */ async function example1_basic_usage(canvasInstance) { - // Załaduj obraz maski z URL + const maskImage = await create_mask_from_image_src('/path/to/mask.png'); - - // Uruchom mask editor z predefiniowaną maską - // sendCleanImage = true oznacza że wyślemy czysty obraz bez istniejącej maski + + start_mask_editor_with_predefined_mask(canvasInstance, maskImage, true); } @@ -27,24 +26,21 @@ async function example1_basic_usage(canvasInstance) { * Przykład 2: Użycie z canvas jako maska */ async function example2_canvas_mask(canvasInstance) { - // Stwórz canvas z maską programowo + const maskCanvas = document.createElement('canvas'); maskCanvas.width = 512; maskCanvas.height = 512; const ctx = maskCanvas.getContext('2d'); - - // Narysuj prostą maskę - białe koło na czarnym tle + ctx.fillStyle = 'black'; ctx.fillRect(0, 0, 512, 512); ctx.fillStyle = 'white'; ctx.beginPath(); ctx.arc(256, 256, 100, 0, 2 * Math.PI); ctx.fill(); - - // Konwertuj canvas do Image + const maskImage = await canvas_to_mask_image(maskCanvas); - - // Uruchom mask editor + start_mask_editor_with_predefined_mask(canvasInstance, maskImage, true); } @@ -52,12 +48,11 @@ async function example2_canvas_mask(canvasInstance) { * Przykład 3: Bezpośrednie użycie metody Canvas */ async function example3_direct_canvas_method(canvasInstance) { - // Załaduj maskę + const maskImage = await create_mask_from_image_src('/path/to/mask.png'); - - // Bezpośrednie wywołanie metody Canvas - // Parametr 1: predefiniowana maska - // Parametr 2: czy wysłać czysty obraz (true = tak, false = z istniejącą maską) + + + await canvasInstance.startMaskEditor(maskImage, true); } @@ -65,17 +60,14 @@ async function example3_direct_canvas_method(canvasInstance) { * Przykład 4: Tworzenie maski z danych binarnych */ async function example4_binary_data_mask(canvasInstance, binaryData) { - // Konwertuj dane binarne do data URL + const blob = new Blob([binaryData], { type: 'image/png' }); const dataUrl = URL.createObjectURL(blob); - - // Stwórz obraz z data URL + const maskImage = await create_mask_from_image_src(dataUrl); - - // Uruchom mask editor + start_mask_editor_with_predefined_mask(canvasInstance, maskImage, true); - - // Wyczyść URL po użyciu + URL.revokeObjectURL(dataUrl); } @@ -87,8 +79,7 @@ async function example5_gradient_mask(canvasInstance) { maskCanvas.width = 512; maskCanvas.height = 512; const ctx = maskCanvas.getContext('2d'); - - // Stwórz gradient od przezroczystego do białego + const gradient = ctx.createLinearGradient(0, 0, 512, 0); gradient.addColorStop(0, 'rgba(0, 0, 0, 0)'); // Przezroczysty gradient.addColorStop(1, 'rgba(255, 255, 255, 1)'); // Biały @@ -109,8 +100,7 @@ async function example6_error_handling(canvasInstance) { start_mask_editor_with_predefined_mask(canvasInstance, maskImage, true); } catch (error) { console.error('Błąd podczas ładowania maski:', error); - - // Fallback - uruchom editor bez predefiniowanej maski + await canvasInstance.startMaskEditor(); } } @@ -134,13 +124,11 @@ async function example7_existing_canvas_element(canvasInstance, canvasElementId) */ async function example8_combine_with_existing_mask(canvasInstance) { const maskImage = await create_mask_from_image_src('/path/to/mask.png'); - - // sendCleanImage = false oznacza że wyślemy obraz z istniejącą maską - // Nowa maska zostanie nałożona dodatkowo w edytorze + + start_mask_editor_with_predefined_mask(canvasInstance, maskImage, false); } -// Eksportuj przykłady dla użycia w innych plikach export { example1_basic_usage, example2_canvas_mask, diff --git a/js/utils/mask_utils.js b/js/utils/mask_utils.js index 115f180..8347066 100644 --- a/js/utils/mask_utils.js +++ b/js/utils/mask_utils.js @@ -21,14 +21,13 @@ export function hide_mask_editor() { } function get_mask_editor_cancel_button(app) { - // Główny przycisk Cancel z nowego editora + const cancelButton = document.getElementById("maskEditor_topBarCancelButton"); if (cancelButton) { log.debug("Found cancel button by ID: maskEditor_topBarCancelButton"); return cancelButton; } - - // Sprawdź inne możliwe selektory (bez :contains które nie działają) + const cancelSelectors = [ 'button[onclick*="cancel"]', 'button[onclick*="Cancel"]', @@ -46,8 +45,7 @@ function get_mask_editor_cancel_button(app) { log.warn("Invalid selector:", selector, e); } } - - // Fallback - sprawdź wszystkie przyciski w dokumencie po tekście + const allButtons = document.querySelectorAll('button, input[type="button"]'); for (const button of allButtons) { const text = button.textContent || button.value || ''; @@ -56,8 +54,7 @@ function get_mask_editor_cancel_button(app) { return button; } } - - // Ostatni fallback - stary editor + const editorElement = get_mask_editor_element(app); if (editorElement) { return editorElement?.parentElement?.lastChild?.childNodes[2]; @@ -72,7 +69,7 @@ function get_mask_editor_save_button(app) { } export function mask_editor_listen_for_cancel(app, callback) { - // Spróbuj znaleźć przycisk Cancel wielokrotnie z opóźnieniem + let attempts = 0; const maxAttempts = 50; // 5 sekund @@ -86,12 +83,11 @@ export function mask_editor_listen_for_cancel(app, callback) { cancel_button.filter_listener_added = true; return true; // Znaleziono i podłączono } else if (attempts < maxAttempts) { - // Spróbuj ponownie za 100ms + setTimeout(findAndAttachListener, 100); } else { log.warn("Could not find cancel button after", maxAttempts, "attempts"); - - // Ostatnia próba - nasłuchuj na wszystkie kliknięcia w dokumencie + const globalClickHandler = (event) => { const target = event.target; const text = target.textContent || target.value || ''; @@ -144,10 +140,9 @@ export function start_mask_editor_auto(canvasInstance) { log.error('Canvas instance is required'); return; } - - // Wywołaj bez parametrów - użyje domyślnych wartości (null, true) - // Co oznacza: brak predefiniowanej maski, ale wyślij czysty obraz - // i automatycznie nałóż istniejącą maskę z canvas + + + canvasInstance.startMaskEditor(); }