diff --git a/js/CanvasIO.js b/js/CanvasIO.js index eea43a3..5cb776b 100644 --- a/js/CanvasIO.js +++ b/js/CanvasIO.js @@ -1,5 +1,6 @@ import { createCanvas } from "./utils/CommonUtils.js"; import { createModuleLogger } from "./utils/LoggerUtils.js"; +import { showErrorNotification } from "./utils/NotificationUtils.js"; import { webSocketManager } from "./utils/WebSocketManager.js"; const log = createModuleLogger('CanvasIO'); export class CanvasIO { @@ -525,7 +526,7 @@ export class CanvasIO { } catch (error) { log.error("Error importing latest image:", error); - alert(`Failed to import latest image: ${error.message}`); + showErrorNotification(`Failed to import latest image: ${error.message}`); return false; } } @@ -565,7 +566,7 @@ export class CanvasIO { } catch (error) { log.error("Error importing latest images:", error); - alert(`Failed to import latest images: ${error.message}`); + showErrorNotification(`Failed to import latest images: ${error.message}`); return []; } } diff --git a/js/CanvasLayers.js b/js/CanvasLayers.js index 197c8c1..d61c8ee 100644 --- a/js/CanvasLayers.js +++ b/js/CanvasLayers.js @@ -1,7 +1,8 @@ import { saveImage } from "./db.js"; import { createModuleLogger } from "./utils/LoggerUtils.js"; -import { generateUUID, generateUniqueFileName } from "./utils/CommonUtils.js"; +import { generateUUID, generateUniqueFileName, createCanvas } from "./utils/CommonUtils.js"; import { withErrorHandling, createValidationError } from "./ErrorHandler.js"; +import { showErrorNotification } from "./utils/NotificationUtils.js"; // @ts-ignore import { app } from "../../scripts/app.js"; // @ts-ignore @@ -63,17 +64,11 @@ export class CanvasLayers { ...layerProps }; if (layer.mask) { - const tempCanvas = document.createElement('canvas'); - const tempCtx = tempCanvas.getContext('2d'); + const { canvas: tempCanvas, ctx: tempCtx } = createCanvas(layer.width, layer.height); if (tempCtx) { - tempCanvas.width = layer.width; - tempCanvas.height = layer.height; tempCtx.drawImage(layer.image, 0, 0, layer.width, layer.height); - const maskCanvas = document.createElement('canvas'); - const maskCtx = maskCanvas.getContext('2d'); + const { canvas: maskCanvas, ctx: maskCtx } = createCanvas(layer.width, layer.height); if (maskCtx) { - maskCanvas.width = layer.width; - maskCanvas.height = layer.height; const maskImageData = maskCtx.createImageData(layer.width, layer.height); for (let i = 0; i < layer.mask.length; i++) { maskImageData.data[i * 4] = 255; @@ -371,10 +366,7 @@ export class CanvasLayers { let maskCanvas = this.getDistanceFieldMask(layer.image, blendArea); if (maskCanvas) { // Create a temporary canvas for the masked layer - const tempCanvas = document.createElement('canvas'); - tempCanvas.width = layer.width; - tempCanvas.height = layer.height; - const tempCtx = tempCanvas.getContext('2d'); + const { canvas: tempCanvas, ctx: tempCtx } = createCanvas(layer.width, layer.height); if (tempCtx) { // Draw the original image tempCtx.drawImage(layer.image, 0, 0, layer.width, layer.height); @@ -978,7 +970,7 @@ export class CanvasLayers { } async fuseLayers() { if (this.canvas.canvasSelection.selectedLayers.length < 2) { - alert("Please select at least 2 layers to fuse."); + showErrorNotification("Please select at least 2 layers to fuse."); return; } log.info(`Fusing ${this.canvas.canvasSelection.selectedLayers.length} selected layers`); @@ -1012,7 +1004,7 @@ export class CanvasLayers { const fusedHeight = Math.ceil(maxY - minY); if (fusedWidth <= 0 || fusedHeight <= 0) { log.warn("Calculated fused layer dimensions are invalid"); - alert("Cannot fuse layers: invalid dimensions calculated."); + showErrorNotification("Cannot fuse layers: invalid dimensions calculated."); return; } const tempCanvas = document.createElement('canvas'); @@ -1070,7 +1062,7 @@ export class CanvasLayers { } catch (error) { log.error("Error during layer fusion:", error); - alert(`Error fusing layers: ${error.message}`); + showErrorNotification(`Error fusing layers: ${error.message}`); } } } diff --git a/js/CanvasSelection.js b/js/CanvasSelection.js index 0836435..f0d3fd2 100644 --- a/js/CanvasSelection.js +++ b/js/CanvasSelection.js @@ -1,4 +1,5 @@ import { createModuleLogger } from "./utils/LoggerUtils.js"; +import { generateUUID } from "./utils/CommonUtils.js"; const log = createModuleLogger('CanvasSelection'); export class CanvasSelection { constructor(canvas) { @@ -18,7 +19,7 @@ export class CanvasSelection { sortedLayers.forEach(layer => { const newLayer = { ...layer, - id: `layer_${+new Date()}_${Math.random().toString(36).substr(2, 9)}`, + id: generateUUID(), zIndex: this.canvas.layers.length, // Nowa warstwa zawsze na wierzchu }; this.canvas.layers.push(newLayer); diff --git a/js/CanvasView.js b/js/CanvasView.js index 019405b..5c2ef8b 100644 --- a/js/CanvasView.js +++ b/js/CanvasView.js @@ -7,6 +7,7 @@ import { Canvas } from "./Canvas.js"; import { clearAllCanvasStates } from "./db.js"; import { ImageCache } from "./ImageCache.js"; import { createModuleLogger } from "./utils/LoggerUtils.js"; +import { showErrorNotification, showSuccessNotification } from "./utils/NotificationUtils.js"; import { iconLoader, LAYERFORGE_TOOLS } from "./utils/IconLoader.js"; import { setupSAMDetectorHook } from "./SAMDetectorIntegration.js"; const log = createModuleLogger('Canvas_view'); @@ -488,11 +489,11 @@ async function createCanvasWidget(node, widget, app) { await canvas.imageReferenceManager.manualGarbageCollection(); const newStats = canvas.imageReferenceManager.getStats(); log.info("GC Stats after cleanup:", newStats); - alert(`Garbage collection completed!\nTracked images: ${newStats.trackedImages}\nTotal references: ${newStats.totalReferences}\nOperations: ${canvas.imageReferenceManager.operationCount}/${canvas.imageReferenceManager.operationThreshold}`); + showSuccessNotification(`Garbage collection completed!\nTracked images: ${newStats.trackedImages}\nTotal references: ${newStats.totalReferences}\nOperations: ${canvas.imageReferenceManager.operationCount}/${canvas.imageReferenceManager.operationThreshold}`); } catch (e) { log.error("Failed to run garbage collection:", e); - alert("Error running garbage collection. Check the console for details."); + showErrorNotification("Error running garbage collection. Check the console for details."); } } }), @@ -504,11 +505,11 @@ async function createCanvasWidget(node, widget, app) { if (confirm("Are you sure you want to clear all saved canvas states? This action cannot be undone.")) { try { await clearAllCanvasStates(); - alert("Canvas cache cleared successfully!"); + showSuccessNotification("Canvas cache cleared successfully!"); } catch (e) { log.error("Failed to clear canvas cache:", e); - alert("Error clearing canvas cache. Check the console for details."); + showErrorNotification("Error clearing canvas cache. Check the console for details."); } } } @@ -845,8 +846,8 @@ function showErrorDialog(message, details) { textContent: "Copy Details", onclick: () => { navigator.clipboard.writeText(details) - .then(() => alert("Error details copied to clipboard!")) - .catch(err => alert("Failed to copy details: " + err)); + .then(() => showSuccessNotification("Error details copied to clipboard!")) + .catch(err => showErrorNotification("Failed to copy details: " + err)); } }), $el("button", { @@ -885,7 +886,7 @@ app.registerExtension({ } catch (error) { log.error("Failed to send canvas data for one or more nodes. Aborting prompt.", error); - alert(`CanvasNode Error: ${error.message}`); + showErrorNotification(`CanvasNode Error: ${error.message}`); return; } } @@ -1043,12 +1044,12 @@ app.registerExtension({ } else { log.error("Canvas widget not available"); - alert("Canvas not ready. Please try again."); + showErrorNotification("Canvas not ready. Please try again."); } } catch (e) { log.error("Error opening MaskEditor:", e); - alert(`Failed to open MaskEditor: ${e.message}`); + showErrorNotification(`Failed to open MaskEditor: ${e.message}`); } }, }, @@ -1103,7 +1104,7 @@ app.registerExtension({ } catch (e) { log.error("Error copying image:", e); - alert("Failed to copy image to clipboard."); + showErrorNotification("Failed to copy image to clipboard."); } }, }, @@ -1122,7 +1123,7 @@ app.registerExtension({ } catch (e) { log.error("Error copying image with mask:", e); - alert("Failed to copy image with mask to clipboard."); + showErrorNotification("Failed to copy image with mask to clipboard."); } }, }, diff --git a/js/MaskEditorIntegration.js b/js/MaskEditorIntegration.js index 8daa8e1..63e4eb6 100644 --- a/js/MaskEditorIntegration.js +++ b/js/MaskEditorIntegration.js @@ -3,6 +3,7 @@ import { app } from "../../scripts/app.js"; // @ts-ignore import { ComfyApp } from "../../scripts/app.js"; import { createModuleLogger } from "./utils/LoggerUtils.js"; +import { showErrorNotification } from "./utils/NotificationUtils.js"; import { uploadImageBlob } from "./utils/ImageUploadUtils.js"; import { processImageToMask, processMaskForViewport } from "./utils/MaskProcessingUtils.js"; import { convertToImage } from "./utils/ImageUtils.js"; @@ -77,7 +78,7 @@ export class MaskEditorIntegration { } catch (error) { log.error("Error preparing image for mask editor:", error); - alert(`Error: ${error.message}`); + showErrorNotification(`Error: ${error.message}`); } } /** diff --git a/js/MaskTool.js b/js/MaskTool.js index 3f1053b..e91c8e4 100644 --- a/js/MaskTool.js +++ b/js/MaskTool.js @@ -1,4 +1,5 @@ import { createModuleLogger } from "./utils/LoggerUtils.js"; +import { createCanvas } from "./utils/CommonUtils.js"; const log = createModuleLogger('Mask_tool'); export class MaskTool { constructor(canvasInstance, callbacks = {}) { @@ -396,10 +397,7 @@ export class MaskTool { * Creates a canvas with specified dimensions and returns both canvas and context */ createCanvas(width, height) { - const canvas = document.createElement('canvas'); - canvas.width = width; - canvas.height = height; - const ctx = canvas.getContext('2d', { willReadFrequently: true }); + const { canvas, ctx } = createCanvas(width, height, '2d', { willReadFrequently: true }); if (!ctx) { throw new Error("Failed to get 2D context for canvas"); } diff --git a/src/CanvasIO.ts b/src/CanvasIO.ts index 4ff2e0b..13199e1 100644 --- a/src/CanvasIO.ts +++ b/src/CanvasIO.ts @@ -1,5 +1,6 @@ import { createCanvas } from "./utils/CommonUtils.js"; import { createModuleLogger } from "./utils/LoggerUtils.js"; +import { showErrorNotification } from "./utils/NotificationUtils.js"; import { webSocketManager } from "./utils/WebSocketManager.js"; import type { Canvas } from './Canvas'; import type { Layer, Shape } from './types'; @@ -611,7 +612,7 @@ export class CanvasIO { } } catch (error: any) { log.error("Error importing latest image:", error); - alert(`Failed to import latest image: ${error.message}`); + showErrorNotification(`Failed to import latest image: ${error.message}`); return false; } } @@ -655,7 +656,7 @@ export class CanvasIO { } } catch (error: any) { log.error("Error importing latest images:", error); - alert(`Failed to import latest images: ${error.message}`); + showErrorNotification(`Failed to import latest images: ${error.message}`); return []; } } diff --git a/src/CanvasLayers.ts b/src/CanvasLayers.ts index 38d2408..7508299 100644 --- a/src/CanvasLayers.ts +++ b/src/CanvasLayers.ts @@ -1,7 +1,8 @@ import {saveImage, removeImage} from "./db.js"; import {createModuleLogger} from "./utils/LoggerUtils.js"; -import {generateUUID, generateUniqueFileName} from "./utils/CommonUtils.js"; +import {generateUUID, generateUniqueFileName, createCanvas} from "./utils/CommonUtils.js"; import {withErrorHandling, createValidationError} from "./ErrorHandler.js"; +import {showErrorNotification, showSuccessNotification} from "./utils/NotificationUtils.js"; // @ts-ignore import {app} from "../../scripts/app.js"; // @ts-ignore @@ -223,18 +224,12 @@ export class CanvasLayers { }; if (layer.mask) { - const tempCanvas = document.createElement('canvas'); - const tempCtx = tempCanvas.getContext('2d'); + const { canvas: tempCanvas, ctx: tempCtx } = createCanvas(layer.width, layer.height); if(tempCtx) { - tempCanvas.width = layer.width; - tempCanvas.height = layer.height; tempCtx.drawImage(layer.image, 0, 0, layer.width, layer.height); - const maskCanvas = document.createElement('canvas'); - const maskCtx = maskCanvas.getContext('2d'); + const { canvas: maskCanvas, ctx: maskCtx } = createCanvas(layer.width, layer.height); if(maskCtx) { - maskCanvas.width = layer.width; - maskCanvas.height = layer.height; const maskImageData = maskCtx.createImageData(layer.width, layer.height); for (let i = 0; i < layer.mask.length; i++) { maskImageData.data[i * 4] = 255; @@ -432,10 +427,7 @@ export class CanvasLayers { if (maskCanvas) { // Create a temporary canvas for the masked layer - const tempCanvas = document.createElement('canvas'); - tempCanvas.width = layer.width; - tempCanvas.height = layer.height; - const tempCtx = tempCanvas.getContext('2d'); + const { canvas: tempCanvas, ctx: tempCtx } = createCanvas(layer.width, layer.height); if (tempCtx) { // Draw the original image @@ -1136,7 +1128,7 @@ export class CanvasLayers { async fuseLayers(): Promise { if (this.canvas.canvasSelection.selectedLayers.length < 2) { - alert("Please select at least 2 layers to fuse."); + showErrorNotification("Please select at least 2 layers to fuse."); return; } @@ -1178,7 +1170,7 @@ export class CanvasLayers { if (fusedWidth <= 0 || fusedHeight <= 0) { log.warn("Calculated fused layer dimensions are invalid"); - alert("Cannot fuse layers: invalid dimensions calculated."); + showErrorNotification("Cannot fuse layers: invalid dimensions calculated."); return; } @@ -1245,7 +1237,7 @@ export class CanvasLayers { } catch (error: any) { log.error("Error during layer fusion:", error); - alert(`Error fusing layers: ${error.message}`); + showErrorNotification(`Error fusing layers: ${error.message}`); } } } diff --git a/src/CanvasSelection.ts b/src/CanvasSelection.ts index a56ccae..f0de041 100644 --- a/src/CanvasSelection.ts +++ b/src/CanvasSelection.ts @@ -1,4 +1,5 @@ import { createModuleLogger } from "./utils/LoggerUtils.js"; +import { generateUUID } from "./utils/CommonUtils.js"; const log = createModuleLogger('CanvasSelection'); @@ -26,7 +27,7 @@ export class CanvasSelection { sortedLayers.forEach(layer => { const newLayer = { ...layer, - id: `layer_${+new Date()}_${Math.random().toString(36).substr(2, 9)}`, + id: generateUUID(), zIndex: this.canvas.layers.length, // Nowa warstwa zawsze na wierzchu }; this.canvas.layers.push(newLayer); diff --git a/src/CanvasView.ts b/src/CanvasView.ts index daaeae1..dd4af9f 100644 --- a/src/CanvasView.ts +++ b/src/CanvasView.ts @@ -14,6 +14,7 @@ import {clearAllCanvasStates} from "./db.js"; import {ImageCache} from "./ImageCache.js"; import {generateUniqueFileName} from "./utils/CommonUtils.js"; import {createModuleLogger} from "./utils/LoggerUtils.js"; +import {showErrorNotification, showSuccessNotification} from "./utils/NotificationUtils.js"; import { iconLoader, LAYERFORGE_TOOLS } from "./utils/IconLoader.js"; import { registerImageInClipspace, startSAMDetectorMonitoring, setupSAMDetectorHook } from "./SAMDetectorIntegration.js"; import type { ComfyNode, Layer, AddMode } from './types'; @@ -524,10 +525,10 @@ async function createCanvasWidget(node: ComfyNode, widget: any, app: ComfyApp): const newStats = canvas.imageReferenceManager.getStats(); log.info("GC Stats after cleanup:", newStats); - alert(`Garbage collection completed!\nTracked images: ${newStats.trackedImages}\nTotal references: ${newStats.totalReferences}\nOperations: ${canvas.imageReferenceManager.operationCount}/${canvas.imageReferenceManager.operationThreshold}`); + showSuccessNotification(`Garbage collection completed!\nTracked images: ${newStats.trackedImages}\nTotal references: ${newStats.totalReferences}\nOperations: ${canvas.imageReferenceManager.operationCount}/${canvas.imageReferenceManager.operationThreshold}`); } catch (e) { log.error("Failed to run garbage collection:", e); - alert("Error running garbage collection. Check the console for details."); + showErrorNotification("Error running garbage collection. Check the console for details."); } } }), @@ -539,10 +540,10 @@ async function createCanvasWidget(node: ComfyNode, widget: any, app: ComfyApp): if (confirm("Are you sure you want to clear all saved canvas states? This action cannot be undone.")) { try { await clearAllCanvasStates(); - alert("Canvas cache cleared successfully!"); + showSuccessNotification("Canvas cache cleared successfully!"); } catch (e) { log.error("Failed to clear canvas cache:", e); - alert("Error clearing canvas cache. Check the console for details."); + showErrorNotification("Error clearing canvas cache. Check the console for details."); } } } @@ -918,8 +919,8 @@ function showErrorDialog(message: string, details: string) { textContent: "Copy Details", onclick: () => { navigator.clipboard.writeText(details) - .then(() => alert("Error details copied to clipboard!")) - .catch(err => alert("Failed to copy details: " + err)); + .then(() => showSuccessNotification("Error details copied to clipboard!")) + .catch(err => showErrorNotification("Failed to copy details: " + err)); } }), $el("button", { @@ -964,7 +965,7 @@ app.registerExtension({ log.info("All canvas data has been sent and acknowledged by the server."); } catch (error: any) { log.error("Failed to send canvas data for one or more nodes. Aborting prompt.", error); - alert(`CanvasNode Error: ${error.message}`); + showErrorNotification(`CanvasNode Error: ${error.message}`); return; } } @@ -1152,11 +1153,11 @@ app.registerExtension({ await (self as any).canvasWidget.startMaskEditor(null, true); } else { log.error("Canvas widget not available"); - alert("Canvas not ready. Please try again."); + showErrorNotification("Canvas not ready. Please try again."); } } catch (e: any) { log.error("Error opening MaskEditor:", e); - alert(`Failed to open MaskEditor: ${e.message}`); + showErrorNotification(`Failed to open MaskEditor: ${e.message}`); } }, }, @@ -1202,7 +1203,7 @@ app.registerExtension({ log.info("Image copied to clipboard."); } catch (e) { log.error("Error copying image:", e); - alert("Failed to copy image to clipboard."); + showErrorNotification("Failed to copy image to clipboard."); } }, }, @@ -1218,7 +1219,7 @@ app.registerExtension({ log.info("Image with mask alpha copied to clipboard."); } catch (e) { log.error("Error copying image with mask:", e); - alert("Failed to copy image with mask to clipboard."); + showErrorNotification("Failed to copy image with mask to clipboard."); } }, }, diff --git a/src/MaskEditorIntegration.ts b/src/MaskEditorIntegration.ts index e494309..ff0dc4f 100644 --- a/src/MaskEditorIntegration.ts +++ b/src/MaskEditorIntegration.ts @@ -5,6 +5,7 @@ import {ComfyApp} from "../../scripts/app.js"; // @ts-ignore import {api} from "../../scripts/api.js"; import { createModuleLogger } from "./utils/LoggerUtils.js"; +import { showErrorNotification } from "./utils/NotificationUtils.js"; import { uploadCanvasAsImage, uploadCanvasWithMaskAsImage, uploadImageBlob } from "./utils/ImageUploadUtils.js"; import { processImageToMask, processMaskForViewport } from "./utils/MaskProcessingUtils.js"; import { convertToImage } from "./utils/ImageUtils.js"; @@ -100,7 +101,7 @@ export class MaskEditorIntegration { } catch (error) { log.error("Error preparing image for mask editor:", error); - alert(`Error: ${(error as Error).message}`); + showErrorNotification(`Error: ${(error as Error).message}`); } } diff --git a/src/MaskTool.ts b/src/MaskTool.ts index d1b4ef0..e6bfd43 100644 --- a/src/MaskTool.ts +++ b/src/MaskTool.ts @@ -1,4 +1,5 @@ import {createModuleLogger} from "./utils/LoggerUtils.js"; +import { createCanvas } from "./utils/CommonUtils.js"; import type { Canvas } from './Canvas'; import type { Point, CanvasState } from './types'; @@ -540,10 +541,7 @@ export class MaskTool { * Creates a canvas with specified dimensions and returns both canvas and context */ private createCanvas(width: number, height: number): { canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D } { - const canvas = document.createElement('canvas'); - canvas.width = width; - canvas.height = height; - const ctx = canvas.getContext('2d', { willReadFrequently: true }); + const { canvas, ctx } = createCanvas(width, height, '2d', { willReadFrequently: true }); if (!ctx) { throw new Error("Failed to get 2D context for canvas"); }