Refactor notifications and improve matting UX

Refactored notification utilities for a more modern, reusable notification system and added info/success/error notifications to the background removal (matting) workflow. Removed the custom error dialog in favor of notifications, and exposed all notification types for debugging. Updated imports and cleaned up notification-related code.
This commit is contained in:
Dariusz L
2025-07-28 00:58:04 +02:00
parent bfea0cdbab
commit 5e9869f827
5 changed files with 188 additions and 362 deletions

View File

@@ -14,7 +14,7 @@ import {clearAllCanvasStates} from "./db.js";
import {ImageCache} from "./ImageCache.js";
import {generateUniqueFileName, createCanvas} from "./utils/CommonUtils.js";
import {createModuleLogger} from "./utils/LoggerUtils.js";
import {showErrorNotification, showSuccessNotification} from "./utils/NotificationUtils.js";
import {showErrorNotification, showSuccessNotification, showInfoNotification, showWarningNotification} 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';
@@ -341,9 +341,13 @@ async function createCanvasWidget(node: ComfyNode, widget: any, app: ComfyApp):
const spinner = $el("div.matting-spinner") as HTMLDivElement;
button.appendChild(spinner);
button.classList.add('loading');
showInfoNotification("Starting background removal process...", 2000);
try {
if (canvas.canvasSelection.selectedLayers.length !== 1) throw new Error("Please select exactly one image layer for matting.");
if (canvas.canvasSelection.selectedLayers.length !== 1) {
throw new Error("Please select exactly one image layer for matting.");
}
const selectedLayer = canvas.canvasSelection.selectedLayers[0];
const selectedLayerIndex = canvas.layers.indexOf(selectedLayer);
@@ -359,24 +363,28 @@ async function createCanvasWidget(node: ComfyNode, widget: any, app: ComfyApp):
if (!response.ok) {
let errorMsg = `Server error: ${response.status} - ${response.statusText}`;
if (result && result.error) {
errorMsg = `Error: ${result.error}\n\nDetails: ${result.details}`;
errorMsg = `Error: ${result.error}. Details: ${result.details || 'Check console'}`;
}
throw new Error(errorMsg);
}
const mattedImage = new Image();
mattedImage.src = result.matted_image;
await mattedImage.decode();
const newLayer = {...selectedLayer, image: mattedImage, flipH: false, flipV: false} as Layer;
delete (newLayer as any).imageId;
canvas.layers[selectedLayerIndex] = newLayer;
canvas.canvasSelection.updateSelection([newLayer]);
canvas.render();
canvas.saveState();
showSuccessNotification("Background removed successfully!");
} catch (error: any) {
log.error("Matting error:", error);
const errorMessage = error.message || "An unknown error occurred.";
const errorDetails = error.stack || (error.details ? JSON.stringify(error.details, null, 2) : "No details available.");
showErrorDialog(errorMessage, errorDetails);
showErrorNotification(`Matting Failed: ${errorMessage}`);
} finally {
button.classList.remove('loading');
if (button.contains(spinner)) {
@@ -944,57 +952,6 @@ async function createCanvasWidget(node: ComfyNode, widget: any, app: ComfyApp):
};
}
function showErrorDialog(message: string, details: string) {
const dialog = $el("div.painter-dialog.error-dialog", {
style: {
position: 'fixed',
left: '50%',
top: '50%',
transform: 'translate(-50%, -50%)',
zIndex: '9999',
padding: '20px',
background: '#282828',
border: '1px solid #ff4444',
borderRadius: '8px',
minWidth: '400px',
maxWidth: '80vw',
}
}, [
$el("h3", { textContent: "Matting Error", style: { color: "#ff4444", marginTop: "0" } }),
$el("p", { textContent: message, style: { color: "white" } }),
$el("pre.error-details", {
textContent: details,
style: {
background: "#1e1e1e",
border: "1px solid #444",
padding: "10px",
maxHeight: "300px",
overflowY: "auto",
whiteSpace: "pre-wrap",
wordBreak: "break-all",
color: "#ccc"
}
}),
$el("div.dialog-buttons", { style: { textAlign: "right", marginTop: "20px" } }, [
$el("button", {
textContent: "Copy Details",
onclick: () => {
navigator.clipboard.writeText(details)
.then(() => showSuccessNotification("Error details copied to clipboard!"))
.catch(err => showErrorNotification("Failed to copy details: " + err));
}
}),
$el("button", {
textContent: "Close",
style: { marginLeft: "10px" },
onclick: () => document.body.removeChild(dialog)
})
])
]);
document.body.appendChild(dialog);
}
const canvasNodeInstances = new Map<number, CanvasWidget>();
app.registerExtension({