diff --git a/js/Canvas.js b/js/Canvas.js index 34234f8..16b2468 100644 --- a/js/Canvas.js +++ b/js/Canvas.js @@ -1,5 +1,5 @@ -import { app, ComfyApp } from "../../scripts/app.js"; -import { api } from "../../scripts/api.js"; +import {app, ComfyApp} from "../../scripts/app.js"; +import {api} from "../../scripts/api.js"; import {removeImage} from "./db.js"; import {MaskTool} from "./MaskTool.js"; import {CanvasState} from "./CanvasState.js"; @@ -9,13 +9,13 @@ import {CanvasRenderer} from "./CanvasRenderer.js"; import {CanvasIO} from "./CanvasIO.js"; import {ImageReferenceManager} from "./ImageReferenceManager.js"; import {createModuleLogger} from "./utils/LoggerUtils.js"; -import { mask_editor_showing, mask_editor_listen_for_cancel } from "./utils/mask_utils.js"; +import {mask_editor_showing, mask_editor_listen_for_cancel} from "./utils/mask_utils.js"; const log = createModuleLogger('Canvas'); /** * Canvas - Fasada dla systemu rysowania - * + * * Klasa Canvas pełni rolę fasady, oferując uproszczony interfejs wysokiego poziomu * dla złożonego systemu rysowania. Zamiast eksponować wszystkie metody modułów, * udostępnia tylko kluczowe operacje i umożliwia bezpośredni dostęp do modułów @@ -69,21 +69,21 @@ export class Canvas { return new Promise((resolve, reject) => { const check = () => { - const widget = node.widgets.find(w => w.name === name); - if (widget) { - resolve(widget); - } else if (Date.now() - startTime > timeout) { - reject(new Error(`Widget "${name}" not found within timeout.`)); - } else { - setTimeout(check, interval); - } + const widget = node.widgets.find(w => w.name === name); + if (widget) { + resolve(widget); + } else if (Date.now() - startTime > timeout) { + reject(new Error(`Widget "${name}" not found within timeout.`)); + } else { + setTimeout(check, interval); + } }; check(); }); } - + /** * Kontroluje widoczność podglądu canvas * @param {boolean} visible - Czy podgląd ma być widoczny @@ -95,7 +95,7 @@ export class Canvas { 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 ==="); @@ -110,10 +110,10 @@ export class Canvas { } console.log("Setting computeSize to fixed height 250"); - imagePreviewWidget.computeSize = function() { + imagePreviewWidget.computeSize = function () { return [0, 250]; // Szerokość 0 (auto), wysokość 250 }; - + console.log("ImagePreviewWidget shown"); } else { console.log("=== HIDING WIDGET ==="); @@ -127,14 +127,14 @@ export class Canvas { if ('hidden' in imagePreviewWidget) { imagePreviewWidget.hidden = true; } - - imagePreviewWidget.computeSize = function() { + + imagePreviewWidget.computeSize = function () { return [0, 0]; // Szerokość 0, wysokość 0 - }; - + }; + console.log("ImagePreviewWidget hidden with zero size"); } - + console.log("=== FINAL WIDGET STATE ==="); this.render() } else { @@ -173,8 +173,6 @@ export class Canvas { } - - /** * Ładuje stan canvas z bazy danych */ @@ -292,8 +290,6 @@ export class Canvas { } - - /** * Uruchamia edytor masek * @param {Image|HTMLCanvasElement|null} predefinedMask - Opcjonalna maska do nałożenia po otwarciu editora @@ -322,7 +318,7 @@ export class Canvas { blob = await this.canvasLayers.getFlattenedCanvasForMaskEditor(); } - + if (!blob) { log.warn("Canvas is empty, cannot open mask editor."); return; @@ -344,28 +340,28 @@ export class Canvas { throw new Error(`Failed to upload image: ${response.statusText}`); } const data = await response.json(); - + const img = new Image(); img.src = api.apiURL(`/view?filename=${encodeURIComponent(data.name)}&type=${data.type}&subfolder=${data.subfolder}`); await new Promise((res, rej) => { img.onload = res; img.onerror = rej; }); - + this.node.imgs = [img]; - ComfyApp.copyToClipspace(this.node); - ComfyApp.clipspace_return_node = this.node; - ComfyApp.open_maskeditor(); - - this.editorWasShowing = false; - this.waitWhileMaskEditing(); + ComfyApp.copyToClipspace(this.node); + ComfyApp.clipspace_return_node = this.node; + ComfyApp.open_maskeditor(); - this.setupCancelListener(); + this.editorWasShowing = false; + this.waitWhileMaskEditing(); - if (predefinedMask) { - this.waitForMaskEditorAndApplyMask(); - } + this.setupCancelListener(); + + if (predefinedMask) { + this.waitForMaskEditorAndApplyMask(); + } } catch (error) { log.error("Error preparing image for mask editor:", error); @@ -374,8 +370,6 @@ export class Canvas { } - - /** * Inicjalizuje podstawowe właściwości canvas */ @@ -428,7 +422,7 @@ export class Canvas { const mouseX_Canvas = mouseX_DOM * scaleX; const mouseY_Canvas = mouseY_DOM * scaleY; - return { x: mouseX_Canvas, y: mouseY_Canvas }; + return {x: mouseX_Canvas, y: mouseY_Canvas}; } /** @@ -488,23 +482,21 @@ export class Canvas { } - - /** * Czeka na otwarcie mask editora i automatycznie nakłada predefiniowaną maskę */ waitForMaskEditorAndApplyMask() { let attempts = 0; const maxAttempts = 100; // Zwiększone do 10 sekund oczekiwania - + const checkEditor = () => { attempts++; - + if (mask_editor_showing(app)) { const useNewEditor = app.ui.settings.getSettingValue('Comfy.MaskEditor.UseNewEditor'); let editorReady = false; - + if (useNewEditor) { const MaskEditorDialog = window.MaskEditorDialog; @@ -541,7 +533,7 @@ export class Canvas { log.info("Old mask editor detected as ready"); } } - + if (editorReady) { log.info("Applying mask to editor after", attempts * 100, "ms wait"); @@ -572,7 +564,7 @@ export class Canvas { this.pendingMask = null; } }; - + checkEditor(); } @@ -584,7 +576,7 @@ export class Canvas { try { const useNewEditor = app.ui.settings.getSettingValue('Comfy.MaskEditor.UseNewEditor'); - + if (useNewEditor) { const MaskEditorDialog = window.MaskEditorDialog; @@ -599,7 +591,7 @@ export class 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); @@ -653,7 +645,7 @@ export class Canvas { const maskCtx = maskCanvas.getContext('2d'); - const maskColor = { r: 255, g: 255, b: 255 }; + const maskColor = {r: 255, g: 255, b: 255}; const processedMask = await this.processMaskForEditor(maskData, maskCanvas.width, maskCanvas.height, maskColor); maskCtx.clearRect(0, 0, maskCanvas.width, maskCanvas.height); @@ -671,11 +663,11 @@ export class Canvas { async processMaskForEditor(maskData, targetWidth, targetHeight, maskColor) { const originalWidth = maskData.width || maskData.naturalWidth || this.width; const originalHeight = maskData.height || maskData.naturalHeight || this.height; - + log.info("Processing mask for editor:", { - originalSize: { width: originalWidth, height: originalHeight }, - targetSize: { width: targetWidth, height: targetHeight }, - canvasSize: { width: this.width, height: this.height } + originalSize: {width: originalWidth, height: originalHeight}, + targetSize: {width: targetWidth, height: targetHeight}, + canvasSize: {width: this.width, height: this.height} }); const tempCanvas = document.createElement('canvas'); @@ -693,16 +685,16 @@ export class Canvas { const offsetX = (targetWidth - scaledWidth) / 2; const offsetY = (targetHeight - scaledHeight) / 2; - + tempCtx.drawImage(maskData, offsetX, offsetY, scaledWidth, scaledHeight); - - log.info("Mask drawn scaled to original image size:", { - originalSize: { width: originalWidth, height: originalHeight }, - targetSize: { width: targetWidth, height: targetHeight }, - canvasSize: { width: this.width, height: this.height }, + + log.info("Mask drawn scaled to original image size:", { + originalSize: {width: originalWidth, height: originalHeight}, + targetSize: {width: targetWidth, height: targetHeight}, + canvasSize: {width: this.width, height: this.height}, scaleToOriginal: scaleToOriginal, - finalSize: { width: scaledWidth, height: scaledHeight }, - offset: { x: offsetX, y: offsetY } + finalSize: {width: scaledWidth, height: scaledHeight}, + offset: {x: offsetX, y: offsetY} }); const imageData = tempCtx.getImageData(0, 0, targetWidth, targetHeight); @@ -718,7 +710,7 @@ export class Canvas { } tempCtx.putImageData(imageData, 0, 0); - + log.info("Mask processing completed - full size scaling applied"); return tempCanvas; } @@ -744,10 +736,10 @@ export class Canvas { if (mask_editor_showing(app)) { this.editorWasShowing = true; } - + if (!mask_editor_showing(app) && this.editorWasShowing) { - this.editorWasShowing = false; - setTimeout(() => this.handleMaskEditorClose(), 100); + this.editorWasShowing = false; + setTimeout(() => this.handleMaskEditorClose(), 100); } else { setTimeout(this.waitWhileMaskEditing.bind(this), 100); } @@ -778,7 +770,7 @@ export class Canvas { tempCanvas.width = this.width; tempCanvas.height = this.height; const tempCtx = tempCanvas.getContext('2d'); - + tempCtx.drawImage(resultImage, 0, 0, this.width, this.height); const imageData = tempCtx.getImageData(0, 0, this.width, this.height); @@ -786,7 +778,7 @@ export class Canvas { for (let i = 0; i < data.length; i += 4) { const originalAlpha = data[i + 3]; - data[i] = 255; + data[i] = 255; data[i + 1] = 255; data[i + 2] = 255; data[i + 3] = 255 - originalAlpha; @@ -797,7 +789,7 @@ export class Canvas { const maskAsImage = new Image(); maskAsImage.src = tempCanvas.toDataURL(); await new Promise(resolve => maskAsImage.onload = resolve); - + const maskCtx = this.maskTool.maskCtx; const destX = -this.maskTool.x; const destY = -this.maskTool.y; @@ -807,10 +799,10 @@ export class Canvas { maskCtx.clearRect(destX, destY, this.width, this.height); maskCtx.drawImage(maskAsImage, destX, destY); - + this.render(); this.saveState(); - + const new_preview = new Image(); const blob = await this.canvasLayers.getFlattenedCanvasWithMaskAsBlob(); @@ -826,8 +818,6 @@ export class Canvas { } - - /** * Zapisuje obecny stan maski przed otwarciem editora * @returns {Object} Zapisany stan maski @@ -929,7 +919,7 @@ export class Canvas { tempCanvas.width = this.width; tempCanvas.height = this.height; const tempCtx = tempCanvas.getContext('2d'); - + tempCtx.drawImage(resultImage, 0, 0, this.width, this.height); const imageData = tempCtx.getImageData(0, 0, this.width, this.height); @@ -937,7 +927,7 @@ export class Canvas { for (let i = 0; i < data.length; i += 4) { const originalAlpha = data[i + 3]; - data[i] = 255; + data[i] = 255; data[i + 1] = 255; data[i + 2] = 255; data[i + 3] = 255 - originalAlpha; @@ -948,7 +938,7 @@ export class Canvas { const maskAsImage = new Image(); maskAsImage.src = tempCanvas.toDataURL(); await new Promise(resolve => maskAsImage.onload = resolve); - + const maskCtx = this.maskTool.maskCtx; const destX = -this.maskTool.x; const destY = -this.maskTool.y; @@ -958,10 +948,10 @@ export class Canvas { maskCtx.clearRect(destX, destY, this.width, this.height); maskCtx.drawImage(maskAsImage, destX, destY); - + this.render(); this.saveState(); - + const new_preview = new Image(); const blob = await this.canvasLayers.getFlattenedCanvasWithMaskAsBlob(); diff --git a/js/CanvasLayers.js b/js/CanvasLayers.js index 5a9c8a2..0c4a1c2 100644 --- a/js/CanvasLayers.js +++ b/js/CanvasLayers.js @@ -2,7 +2,7 @@ import {saveImage, removeImage} from "./db.js"; import {createModuleLogger} from "./utils/LoggerUtils.js"; import {generateUUID, generateUniqueFileName} from "./utils/CommonUtils.js"; import {withErrorHandling, createValidationError} from "./ErrorHandler.js"; -import { app, ComfyApp } from "../../scripts/app.js"; +import {app, ComfyApp} from "../../scripts/app.js"; const log = createModuleLogger('CanvasLayers'); @@ -141,7 +141,7 @@ export class CanvasLayers { try { log.info("Attempting to paste from system clipboard"); const clipboardItems = await navigator.clipboard.read(); - + for (const item of clipboardItems) { const imageType = item.types.find(type => type.startsWith('image/')); @@ -160,7 +160,7 @@ export class CanvasLayers { return true; } } - + log.info("No image found in system clipboard"); return false; } catch (error) { @@ -178,7 +178,7 @@ export class CanvasLayers { const imageId = generateUUID(); await saveImage(imageId, image.src); this.canvas.imageCache.set(imageId, image.src); - + let finalWidth = image.width; let finalHeight = image.height; let finalX, finalY; @@ -268,7 +268,7 @@ export class CanvasLayers { */ resizeLayer(scale) { if (this.canvas.selectedLayers.length === 0) return; - + this.canvas.selectedLayers.forEach(layer => { layer.width *= scale; layer.height *= scale; @@ -283,7 +283,7 @@ export class CanvasLayers { */ rotateLayer(angle) { if (this.canvas.selectedLayers.length === 0) return; - + this.canvas.selectedLayers.forEach(layer => { layer.rotation += angle; }); diff --git a/js/CanvasView.js b/js/CanvasView.js index 417f666..ff16e62 100644 --- a/js/CanvasView.js +++ b/js/CanvasView.js @@ -488,7 +488,7 @@ async function createCanvasWidget(node, widget, app) { helpTooltip.style.visibility = 'hidden'; helpTooltip.style.display = 'block'; - + const buttonRect = e.target.getBoundingClientRect(); const tooltipRect = helpTooltip.getBoundingClientRect(); const viewportWidth = window.innerWidth; @@ -1004,7 +1004,7 @@ async function createCanvasWidget(node, widget, app) { log.debug("Image object loaded from dropped data:URL."); const fitOnAddWidget = node.widgets.find(w => w.name === "fit_on_add"); const addMode = fitOnAddWidget && fitOnAddWidget.value ? 'fit' : 'center'; - + await canvas.addLayer(img, {}, addMode); log.info("Dropped layer added and state saved."); }; @@ -1105,8 +1105,8 @@ async function createCanvasWidget(node, widget, app) { const showPreviewWidget = node.widgets.find(w => w.name === "show_preview"); if (showPreviewWidget) { const originalCallback = showPreviewWidget.callback; - - showPreviewWidget.callback = function(value) { + + showPreviewWidget.callback = function (value) { if (originalCallback) { originalCallback.call(this, value); } @@ -1124,7 +1124,6 @@ async function createCanvasWidget(node, widget, app) { } - return { canvas: canvas, panel: controlPanel @@ -1251,13 +1250,13 @@ app.registerExtension({ const self = this; - const maskEditorIndex = options.findIndex(option => + const maskEditorIndex = options.findIndex(option => option && option.content === "Open in MaskEditor" ); if (maskEditorIndex !== -1) { options.splice(maskEditorIndex, 1); } - + const newOptions = [ { content: "Open in MaskEditor", diff --git a/js/MaskTool.js b/js/MaskTool.js index 1d9d2d2..3aa0ac0 100644 --- a/js/MaskTool.js +++ b/js/MaskTool.js @@ -283,7 +283,6 @@ export class MaskTool { setMask(image) { - const destX = -this.x; const destY = -this.y; diff --git a/js/examples/mask_editor_examples.js b/js/examples/mask_editor_examples.js deleted file mode 100644 index 2006e82..0000000 --- a/js/examples/mask_editor_examples.js +++ /dev/null @@ -1,141 +0,0 @@ -/** - * Przykłady użycia automatycznego nakładania masek w mask editorze - * - * Te przykłady pokazują jak używać nowej funkcjonalności do automatycznego - * nakładania predefiniowanych masek po otwarciu mask editora ComfyUI. - */ - -import { - start_mask_editor_with_predefined_mask, - create_mask_from_image_src, - canvas_to_mask_image -} from '../utils/mask_utils.js'; - -/** - * Przykład 1: Podstawowe użycie z obrazem maski - */ -async function example1_basic_usage(canvasInstance) { - - const maskImage = await create_mask_from_image_src('/path/to/mask.png'); - - - start_mask_editor_with_predefined_mask(canvasInstance, maskImage, true); -} - -/** - * Przykład 2: Użycie z canvas jako maska - */ -async function example2_canvas_mask(canvasInstance) { - - const maskCanvas = document.createElement('canvas'); - maskCanvas.width = 512; - maskCanvas.height = 512; - const ctx = maskCanvas.getContext('2d'); - - 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(); - - const maskImage = await canvas_to_mask_image(maskCanvas); - - start_mask_editor_with_predefined_mask(canvasInstance, maskImage, true); -} - -/** - * Przykład 3: Bezpośrednie użycie metody Canvas - */ -async function example3_direct_canvas_method(canvasInstance) { - - const maskImage = await create_mask_from_image_src('/path/to/mask.png'); - - - - await canvasInstance.startMaskEditor(maskImage, true); -} - -/** - * Przykład 4: Tworzenie maski z danych binarnych - */ -async function example4_binary_data_mask(canvasInstance, binaryData) { - - 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(canvasInstance, maskImage, true); - - URL.revokeObjectURL(dataUrl); -} - -/** - * Przykład 5: Maska z gradientem - */ -async function example5_gradient_mask(canvasInstance) { - const maskCanvas = document.createElement('canvas'); - maskCanvas.width = 512; - maskCanvas.height = 512; - const ctx = maskCanvas.getContext('2d'); - - 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 - - ctx.fillStyle = gradient; - ctx.fillRect(0, 0, 512, 512); - - const maskImage = await canvas_to_mask_image(maskCanvas); - start_mask_editor_with_predefined_mask(canvasInstance, maskImage, true); -} - -/** - * Przykład 6: Obsługa błędów - */ -async function example6_error_handling(canvasInstance) { - try { - const maskImage = await create_mask_from_image_src('/path/to/nonexistent.png'); - start_mask_editor_with_predefined_mask(canvasInstance, maskImage, true); - } catch (error) { - console.error('Błąd podczas ładowania maski:', error); - - await canvasInstance.startMaskEditor(); - } -} - -/** - * Przykład 7: Maska z istniejącego elementu canvas na stronie - */ -async function example7_existing_canvas_element(canvasInstance, canvasElementId) { - const existingCanvas = document.getElementById(canvasElementId); - if (!existingCanvas) { - console.error('Canvas element not found:', canvasElementId); - return; - } - - const maskImage = await canvas_to_mask_image(existingCanvas); - start_mask_editor_with_predefined_mask(canvasInstance, maskImage, true); -} - -/** - * Przykład 8: Kombinowanie z istniejącą maską - */ -async function example8_combine_with_existing_mask(canvasInstance) { - const maskImage = await create_mask_from_image_src('/path/to/mask.png'); - - - start_mask_editor_with_predefined_mask(canvasInstance, maskImage, false); -} - -export { - example1_basic_usage, - example2_canvas_mask, - example3_direct_canvas_method, - example4_binary_data_mask, - example5_gradient_mask, - example6_error_handling, - example7_existing_canvas_element, - example8_combine_with_existing_mask -}; diff --git a/js/utils/CommonUtils.js b/js/utils/CommonUtils.js index 9b59295..7779b91 100644 --- a/js/utils/CommonUtils.js +++ b/js/utils/CommonUtils.js @@ -8,7 +8,7 @@ * @returns {string} UUID w formacie xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx */ export function generateUUID() { - return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { const r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8); return v.toString(16); }); @@ -42,7 +42,7 @@ export function getSnapAdjustment(layer, gridSize = 64, snapThreshold = 10) { top: layer.y, bottom: layer.y + layer.height }; - + const x_adjustments = [ {type: 'x', delta: snapToGrid(layerEdges.left, gridSize) - layerEdges.left}, {type: 'x', delta: snapToGrid(layerEdges.right, gridSize) - layerEdges.right} @@ -52,17 +52,17 @@ export function getSnapAdjustment(layer, gridSize = 64, snapThreshold = 10) { {type: 'y', delta: snapToGrid(layerEdges.top, gridSize) - layerEdges.top}, {type: 'y', delta: snapToGrid(layerEdges.bottom, gridSize) - layerEdges.bottom} ]; - + x_adjustments.forEach(adj => adj.abs = Math.abs(adj.delta)); y_adjustments.forEach(adj => adj.abs = Math.abs(adj.delta)); - + const bestXSnap = x_adjustments .filter(adj => adj.abs < snapThreshold && adj.abs > 1e-9) .sort((a, b) => a.abs - b.abs)[0]; const bestYSnap = y_adjustments .filter(adj => adj.abs < snapThreshold && adj.abs > 1e-9) .sort((a, b) => a.abs - b.abs)[0]; - + return { dx: bestXSnap ? bestXSnap.delta : 0, dy: bestYSnap ? bestYSnap.delta : 0 @@ -145,7 +145,7 @@ export function getStateSignature(layers) { if (layer.image && layer.image.src) { sig.imageSrc = layer.image.src.substring(0, 100); // First 100 chars to avoid huge signatures } - + return sig; })); } @@ -179,7 +179,7 @@ export function debounce(func, wait, immediate) { */ export function throttle(func, limit) { let inThrottle; - return function(...args) { + return function (...args) { if (!inThrottle) { func.apply(this, args); inThrottle = true; @@ -241,7 +241,7 @@ export function createCanvas(width, height, contextType = '2d', contextOptions = if (width) canvas.width = width; if (height) canvas.height = height; const ctx = canvas.getContext(contextType, contextOptions); - return { canvas, ctx }; + return {canvas, ctx}; } /** @@ -284,5 +284,5 @@ export function generateUniqueFileName(baseName, nodeId) { */ export function isPointInRect(pointX, pointY, rectX, rectY, rectWidth, rectHeight) { return pointX >= rectX && pointX <= rectX + rectWidth && - pointY >= rectY && pointY <= rectY + rectHeight; + pointY >= rectY && pointY <= rectY + rectHeight; } diff --git a/js/utils/ImageUtils.js b/js/utils/ImageUtils.js index b5392a1..324a3ce 100644 --- a/js/utils/ImageUtils.js +++ b/js/utils/ImageUtils.js @@ -1,5 +1,6 @@ import {createModuleLogger} from "./LoggerUtils.js"; import {withErrorHandling, createValidationError} from "../ErrorHandler.js"; + const log = createModuleLogger('ImageUtils'); export function validateImageData(data) { @@ -114,7 +115,7 @@ export function applyMaskToImageData(imageData, maskData) { }; } -export const prepareImageForCanvas = withErrorHandling(function(inputImage) { +export const prepareImageForCanvas = withErrorHandling(function (inputImage) { log.info("Preparing image for canvas:", inputImage); if (Array.isArray(inputImage)) { @@ -122,7 +123,7 @@ export const prepareImageForCanvas = withErrorHandling(function(inputImage) { } if (!inputImage || !inputImage.shape || !inputImage.data) { - throw createValidationError("Invalid input image format", { inputImage }); + throw createValidationError("Invalid input image format", {inputImage}); } const shape = inputImage.shape; @@ -161,29 +162,29 @@ export const prepareImageForCanvas = withErrorHandling(function(inputImage) { * @param {HTMLImageElement|HTMLCanvasElement} image - Obraz do konwersji * @returns {Promise} Tensor z danymi obrazu */ -export const imageToTensor = withErrorHandling(async function(image) { +export const imageToTensor = withErrorHandling(async function (image) { if (!image) { throw createValidationError("Image is required"); } const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); - + canvas.width = image.width || image.naturalWidth; canvas.height = image.height || image.naturalHeight; - + ctx.drawImage(image, 0, 0); - + const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const data = new Float32Array(canvas.width * canvas.height * 3); - + for (let i = 0; i < imageData.data.length; i += 4) { const pixelIndex = i / 4; data[pixelIndex * 3] = imageData.data[i] / 255; data[pixelIndex * 3 + 1] = imageData.data[i + 1] / 255; data[pixelIndex * 3 + 2] = imageData.data[i + 2] / 255; } - + return { data: data, shape: [1, canvas.height, canvas.width, 3], @@ -197,33 +198,33 @@ export const imageToTensor = withErrorHandling(async function(image) { * @param {Object} tensor - Tensor z danymi obrazu * @returns {Promise} Obraz HTML */ -export const tensorToImage = withErrorHandling(async function(tensor) { +export const tensorToImage = withErrorHandling(async function (tensor) { if (!tensor || !tensor.data || !tensor.shape) { - throw createValidationError("Invalid tensor format", { tensor }); + throw createValidationError("Invalid tensor format", {tensor}); } const [, height, width, channels] = tensor.shape; const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); - + canvas.width = width; canvas.height = height; - + const imageData = ctx.createImageData(width, height); const data = tensor.data; - + for (let i = 0; i < width * height; i++) { const pixelIndex = i * 4; const tensorIndex = i * channels; - + imageData.data[pixelIndex] = Math.round(data[tensorIndex] * 255); imageData.data[pixelIndex + 1] = Math.round(data[tensorIndex + 1] * 255); imageData.data[pixelIndex + 2] = Math.round(data[tensorIndex + 2] * 255); imageData.data[pixelIndex + 3] = 255; } - + ctx.putImageData(imageData, 0, 0); - + return new Promise((resolve, reject) => { const img = new Image(); img.onload = () => resolve(img); @@ -239,27 +240,27 @@ export const tensorToImage = withErrorHandling(async function(tensor) { * @param {number} maxHeight - Maksymalna wysokość * @returns {Promise} Przeskalowany obraz */ -export const resizeImage = withErrorHandling(async function(image, maxWidth, maxHeight) { +export const resizeImage = withErrorHandling(async function (image, maxWidth, maxHeight) { if (!image) { throw createValidationError("Image is required"); } const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); - + const originalWidth = image.width || image.naturalWidth; const originalHeight = image.height || image.naturalHeight; const scale = Math.min(maxWidth / originalWidth, maxHeight / originalHeight); const newWidth = Math.round(originalWidth * scale); const newHeight = Math.round(originalHeight * scale); - + canvas.width = newWidth; canvas.height = newHeight; ctx.imageSmoothingEnabled = true; ctx.imageSmoothingQuality = 'high'; - + ctx.drawImage(image, 0, 0, newWidth, newHeight); - + return new Promise((resolve, reject) => { const img = new Image(); img.onload = () => resolve(img); @@ -274,7 +275,7 @@ export const resizeImage = withErrorHandling(async function(image, maxWidth, max * @param {number} size - Rozmiar miniatury (kwadrat) * @returns {Promise} Miniatura */ -export const createThumbnail = withErrorHandling(async function(image, size = 128) { +export const createThumbnail = withErrorHandling(async function (image, size = 128) { return resizeImage(image, size, size); }, 'createThumbnail'); @@ -285,19 +286,19 @@ export const createThumbnail = withErrorHandling(async function(image, size = 12 * @param {number} quality - Jakość (0-1) dla formatów stratnych * @returns {string} Base64 string */ -export const imageToBase64 = withErrorHandling(function(image, format = 'png', quality = 0.9) { +export const imageToBase64 = withErrorHandling(function (image, format = 'png', quality = 0.9) { if (!image) { throw createValidationError("Image is required"); } const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); - + canvas.width = image.width || image.naturalWidth; canvas.height = image.height || image.naturalHeight; - + ctx.drawImage(image, 0, 0); - + const mimeType = `image/${format}`; return canvas.toDataURL(mimeType, quality); }, 'imageToBase64'); @@ -307,7 +308,7 @@ export const imageToBase64 = withErrorHandling(function(image, format = 'png', q * @param {string} base64 - Base64 string * @returns {Promise} Obraz */ -export const base64ToImage = withErrorHandling(function(base64) { +export const base64ToImage = withErrorHandling(function (base64) { if (!base64) { throw createValidationError("Base64 string is required"); } @@ -326,10 +327,10 @@ export const base64ToImage = withErrorHandling(function(base64) { * @returns {boolean} Czy obraz jest prawidłowy */ export function isValidImage(image) { - return image && - (image instanceof HTMLImageElement || image instanceof HTMLCanvasElement) && - image.width > 0 && - image.height > 0; + return image && + (image instanceof HTMLImageElement || image instanceof HTMLCanvasElement) && + image.width > 0 && + image.height > 0; } /** @@ -371,18 +372,18 @@ export function createImageFromSource(source) { * @param {string} color - Kolor tła (CSS color) * @returns {Promise} Pusty obraz */ -export const createEmptyImage = withErrorHandling(function(width, height, color = 'transparent') { +export const createEmptyImage = withErrorHandling(function (width, height, color = 'transparent') { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); - + canvas.width = width; canvas.height = height; - + if (color !== 'transparent') { ctx.fillStyle = color; ctx.fillRect(0, 0, width, height); } - + return new Promise((resolve, reject) => { const img = new Image(); img.onload = () => resolve(img); diff --git a/js/utils/LoggerUtils.js b/js/utils/LoggerUtils.js index d1cbdbc..622a488 100644 --- a/js/utils/LoggerUtils.js +++ b/js/utils/LoggerUtils.js @@ -13,7 +13,7 @@ import {logger, LogLevel} from "../logger.js"; */ export function createModuleLogger(moduleName, level = LogLevel.DEBUG) { logger.setModuleLevel(moduleName, level); - + return { debug: (...args) => logger.debug(moduleName, ...args), info: (...args) => logger.info(moduleName, ...args), @@ -31,7 +31,7 @@ export function createAutoLogger(level = LogLevel.DEBUG) { const stack = new Error().stack; const match = stack.match(/\/([^\/]+)\.js/); const moduleName = match ? match[1] : 'Unknown'; - + return createModuleLogger(moduleName, level); } @@ -43,7 +43,7 @@ export function createAutoLogger(level = LogLevel.DEBUG) { * @returns {Function} Opakowana funkcja */ export function withErrorLogging(operation, log, operationName) { - return async function(...args) { + return async function (...args) { try { log.debug(`Starting ${operationName}`); const result = await operation.apply(this, args); @@ -62,10 +62,10 @@ export function withErrorLogging(operation, log, operationName) { * @param {string} methodName - Nazwa metody */ export function logMethod(log, methodName) { - return function(target, propertyKey, descriptor) { + return function (target, propertyKey, descriptor) { const originalMethod = descriptor.value; - - descriptor.value = async function(...args) { + + descriptor.value = async function (...args) { try { log.debug(`${methodName || propertyKey} started`); const result = await originalMethod.apply(this, args); @@ -76,7 +76,7 @@ export function logMethod(log, methodName) { throw error; } }; - + return descriptor; }; } diff --git a/js/utils/WebSocketManager.js b/js/utils/WebSocketManager.js index 6314bd2..1d261d2 100644 --- a/js/utils/WebSocketManager.js +++ b/js/utils/WebSocketManager.js @@ -45,7 +45,7 @@ class WebSocketManager { try { const data = JSON.parse(event.data); log.debug("Received message:", data); - + if (data.type === 'ack' && data.nodeId) { const callback = this.ackCallbacks.get(data.nodeId); if (callback) { @@ -130,7 +130,6 @@ class WebSocketManager { log.warn("WebSocket not open. Queuing message."); - this.messageQueue.push(message); if (!this.isConnecting) { this.connect(); @@ -147,7 +146,6 @@ class WebSocketManager { log.debug(`Flushing ${this.messageQueue.length} queued messages.`); - while (this.messageQueue.length > 0) { const message = this.messageQueue.shift(); this.socket.send(message); diff --git a/js/utils/mask_utils.js b/js/utils/mask_utils.js index 8347066..ff531bb 100644 --- a/js/utils/mask_utils.js +++ b/js/utils/mask_utils.js @@ -33,7 +33,7 @@ function get_mask_editor_cancel_button(app) { 'button[onclick*="Cancel"]', 'input[value="Cancel"]' ]; - + for (const selector of cancelSelectors) { try { const button = document.querySelector(selector); @@ -59,7 +59,7 @@ function get_mask_editor_cancel_button(app) { if (editorElement) { return editorElement?.parentElement?.lastChild?.childNodes[2]; } - + return null; } @@ -72,11 +72,11 @@ export function mask_editor_listen_for_cancel(app, callback) { let attempts = 0; const maxAttempts = 50; // 5 sekund - + const findAndAttachListener = () => { attempts++; const cancel_button = get_mask_editor_cancel_button(app); - + if (cancel_button && !cancel_button.filter_listener_added) { log.info("Cancel button found, attaching listener"); cancel_button.addEventListener('click', callback); @@ -91,7 +91,7 @@ export function mask_editor_listen_for_cancel(app, callback) { const globalClickHandler = (event) => { const target = event.target; const text = target.textContent || target.value || ''; - if (text.toLowerCase().includes('cancel') || + if (text.toLowerCase().includes('cancel') || target.id.toLowerCase().includes('cancel') || target.className.toLowerCase().includes('cancel')) { log.info("Cancel detected via global click handler"); @@ -99,12 +99,12 @@ export function mask_editor_listen_for_cancel(app, callback) { document.removeEventListener('click', globalClickHandler); } }; - + document.addEventListener('click', globalClickHandler); log.debug("Added global click handler for cancel detection"); } }; - + findAndAttachListener(); } @@ -127,7 +127,7 @@ export function start_mask_editor_with_predefined_mask(canvasInstance, maskImage log.error('Canvas instance and mask image are required'); return; } - + canvasInstance.startMaskEditor(maskImage, sendCleanImage); } @@ -142,7 +142,6 @@ export function start_mask_editor_auto(canvasInstance) { } - canvasInstance.startMaskEditor(); }