feat: add notification system with deduplication

Implemented a comprehensive notification system with smart deduplication for LayerForge's "Paste Image" operations. The system prevents duplicate error/warning notifications while providing clear feedback for all clipboard operations including success, failure, and edge cases.
This commit is contained in:
Dariusz L
2025-08-14 14:30:51 +02:00
parent 0f4f2cb1b0
commit 868221b285
4 changed files with 255 additions and 41 deletions

View File

@@ -1,5 +1,5 @@
import { createModuleLogger } from "./LoggerUtils.js";
import { showNotification, showInfoNotification } from "./NotificationUtils.js";
import { showNotification, showInfoNotification, showErrorNotification, showWarningNotification } from "./NotificationUtils.js";
import { withErrorHandling, createValidationError, createNetworkError, createFileError } from "../ErrorHandler.js";
import { safeClipspacePaste } from "./ClipspaceUtils.js";
// @ts-ignore
@@ -18,6 +18,7 @@ export class ClipboardManager {
if (this.canvas.canvasLayers.internalClipboard.length > 0) {
log.info("Found layers in internal clipboard, pasting layers");
this.canvas.canvasLayers.pasteLayers();
showInfoNotification("Layers pasted from internal clipboard");
return true;
}
if (preference === 'clipspace') {
@@ -27,9 +28,20 @@ export class ClipboardManager {
return true;
}
log.info("No image found in ComfyUI Clipspace");
// Don't show error here, will try system clipboard next
}
log.info("Attempting paste from system clipboard");
return await this.trySystemClipboardPaste(addMode);
const systemSuccess = await this.trySystemClipboardPaste(addMode);
if (!systemSuccess) {
// No valid image found in any clipboard
if (preference === 'clipspace') {
showWarningNotification("No valid image found in Clipspace or system clipboard");
}
else {
showWarningNotification("No valid image found in clipboard");
}
}
return systemSuccess;
}, 'ClipboardManager.handlePaste');
/**
* Attempts to paste from ComfyUI Clipspace
@@ -51,6 +63,7 @@ export class ClipboardManager {
const img = new Image();
img.onload = async () => {
await this.canvas.canvasLayers.addLayerWithImage(img, {}, addMode);
showInfoNotification("Image pasted from Clipspace");
};
img.src = clipspaceImage.src;
return true;
@@ -96,6 +109,7 @@ export class ClipboardManager {
img.onload = async () => {
log.info("Successfully loaded image from backend response");
await this.canvas.canvasLayers.addLayerWithImage(img, {}, addMode);
showInfoNotification("Image loaded from file path");
resolve(true);
};
img.onerror = () => {
@@ -131,6 +145,7 @@ export class ClipboardManager {
img.onload = async () => {
log.info("Successfully loaded image from system clipboard");
await this.canvas.canvasLayers.addLayerWithImage(img, {}, addMode);
showInfoNotification("Image pasted from system clipboard");
};
if (event.target?.result) {
img.src = event.target.result;
@@ -252,10 +267,12 @@ export class ClipboardManager {
img.onload = async () => {
log.info("Successfully loaded image from URL");
await this.canvas.canvasLayers.addLayerWithImage(img, {}, addMode);
showInfoNotification("Image loaded from URL");
resolve(true);
};
img.onerror = () => {
log.warn("Failed to load image from URL:", filePath);
showErrorNotification(`Failed to load image from URL\nThe link might be incorrect or may not point to an image file.: ${filePath}`, 5000, true);
resolve(false);
};
img.src = filePath;
@@ -313,6 +330,7 @@ export class ClipboardManager {
img.onload = async () => {
log.info("Successfully loaded image from file picker");
await this.canvas.canvasLayers.addLayerWithImage(img, {}, addMode);
showInfoNotification("Image loaded from selected file");
resolve(true);
};
img.onerror = () => {