diff --git a/src/extensions/core/colorPalette.ts b/src/extensions/core/colorPalette.ts index 0db3ce95b..1f0647729 100644 --- a/src/extensions/core/colorPalette.ts +++ b/src/extensions/core/colorPalette.ts @@ -1,3 +1,4 @@ +import { useToastStore } from '@/stores/toastStore' import { app } from '../../scripts/app' import { $el } from '../../scripts/ui' import type { ColorPalettes, Palette } from '@/types/colorPalette' @@ -588,22 +589,22 @@ app.registerExtension({ const addCustomColorPalette = async (colorPalette) => { if (typeof colorPalette !== 'object') { - alert('Invalid color palette.') + useToastStore().addAlert('Invalid color palette.') return } if (!colorPalette.id) { - alert('Color palette missing id.') + useToastStore().addAlert('Color palette missing id.') return } if (!colorPalette.name) { - alert('Color palette missing name.') + useToastStore().addAlert('Color palette missing name.') return } if (!colorPalette.colors) { - alert('Color palette missing colors.') + useToastStore().addAlert('Color palette missing colors.') return } @@ -611,7 +612,7 @@ app.registerExtension({ colorPalette.colors.node_slot && typeof colorPalette.colors.node_slot !== 'object' ) { - alert('Invalid color palette colors.node_slot.') + useToastStore().addAlert('Invalid color palette colors.node_slot.') return } @@ -842,7 +843,9 @@ app.registerExtension({ ) if (colorPalettes[colorPaletteId]) { - alert('You cannot delete a built-in color palette.') + useToastStore().addAlert( + 'You cannot delete a built-in color palette.' + ) return } diff --git a/src/extensions/core/groupNode.ts b/src/extensions/core/groupNode.ts index f1100b5aa..c38a44337 100644 --- a/src/extensions/core/groupNode.ts +++ b/src/extensions/core/groupNode.ts @@ -6,6 +6,7 @@ import type { LGraphNode } from '@comfyorg/litegraph' import { LGraphCanvas, LiteGraph } from '@comfyorg/litegraph' import { useNodeDefStore } from '@/stores/nodeDefStore' import { ComfyLink, ComfyNode, ComfyWorkflowJSON } from '@/types/comfyWorkflow' +import { useToastStore } from '@/stores/toastStore' type GroupNodeWorkflowData = { external: ComfyLink[] @@ -75,7 +76,7 @@ class GroupNodeBuilder { const used = Workflow.isInUseGroupNode(name) switch (used) { case Workflow.InUse.InWorkflow: - alert( + useToastStore().addAlert( 'An in use group node with this name already exists embedded in this workflow, please remove any instances or use a new name.' ) return diff --git a/src/extensions/core/groupNodeManage.ts b/src/extensions/core/groupNodeManage.ts index 7d6b54602..4f0efd3b8 100644 --- a/src/extensions/core/groupNodeManage.ts +++ b/src/extensions/core/groupNodeManage.ts @@ -8,6 +8,7 @@ import { type LGraphNode, type LGraphNodeConstructor } from '@comfyorg/litegraph' +import { useToastStore } from '@/stores/toastStore' const ORDER: symbol = Symbol() const PREFIX = 'workflow' @@ -396,7 +397,7 @@ export class ManageGroupDialog extends ComfyDialog { (n) => n.type === `${PREFIX}${SEPARATOR}` + this.selectedGroup ) if (node) { - alert( + useToastStore().addAlert( 'This group node is in use in the current workflow, please first remove these.' ) return diff --git a/src/extensions/core/nodeTemplates.ts b/src/extensions/core/nodeTemplates.ts index ae5aa105d..02da9f860 100644 --- a/src/extensions/core/nodeTemplates.ts +++ b/src/extensions/core/nodeTemplates.ts @@ -3,6 +3,7 @@ import { api } from '../../scripts/api' import { ComfyDialog, $el } from '../../scripts/ui' import { GroupNodeConfig, GroupNodeHandler } from './groupNode' import { LGraphCanvas } from '@comfyorg/litegraph' +import { useToastStore } from '@/stores/toastStore' // Adds the ability to save and add multiple nodes as a template // To save: @@ -118,7 +119,7 @@ class ManageTemplates extends ComfyDialog { await api.storeUserData(file, templates, { stringify: false }) } catch (error) { console.error(error) - alert(error.message) + useToastStore().addAlert(error.message) } } else { localStorage.setItem(id, JSON.stringify(this.templates)) @@ -151,7 +152,7 @@ class ManageTemplates extends ComfyDialog { exportAll() { if (this.templates.length == 0) { - alert('No templates to export.') + useToastStore().addAlert('No templates to export.') return } diff --git a/src/extensions/core/uploadAudio.ts b/src/extensions/core/uploadAudio.ts index 07a411213..5dc729bac 100644 --- a/src/extensions/core/uploadAudio.ts +++ b/src/extensions/core/uploadAudio.ts @@ -3,6 +3,7 @@ import { api } from '../../scripts/api' import type { IWidget } from '@comfyorg/litegraph' import type { DOMWidget } from '@/scripts/domWidget' import { ComfyNodeDef } from '@/types/apiTypes' +import { useToastStore } from '@/stores/toastStore' type FolderType = 'input' | 'output' | 'temp' @@ -66,10 +67,10 @@ async function uploadFile( audioWidget.value = path } } else { - alert(resp.status + ' - ' + resp.statusText) + useToastStore().addAlert(resp.status + ' - ' + resp.statusText) } } catch (error) { - alert(error) + useToastStore().addAlert(error) } } diff --git a/src/extensions/core/webcamCapture.ts b/src/extensions/core/webcamCapture.ts index 1aea0f6a7..86c461a1c 100644 --- a/src/extensions/core/webcamCapture.ts +++ b/src/extensions/core/webcamCapture.ts @@ -1,5 +1,6 @@ import { app } from '../../scripts/app' import { api } from '../../scripts/api' +import { useToastStore } from '@/stores/toastStore' const WEBCAM_READY = Symbol() @@ -102,7 +103,7 @@ app.registerExtension({ capture() } else if (!node.imgs?.length) { const err = `No webcam image captured` - alert(err) + useToastStore().addAlert(err) throw new Error(err) } @@ -120,7 +121,7 @@ app.registerExtension({ }) if (resp.status !== 200) { const err = `Error uploading camera image: ${resp.status} - ${resp.statusText}` - alert(err) + useToastStore().addAlert(err) throw new Error(err) } return `webcam/${name} [temp]` diff --git a/src/scripts/app.ts b/src/scripts/app.ts index 9eb32ac89..56f2d6f4f 100644 --- a/src/scripts/app.ts +++ b/src/scripts/app.ts @@ -29,8 +29,7 @@ import { LGraphCanvas, LGraph, LGraphNode, - LiteGraph, - LGraphGroup + LiteGraph } from '@comfyorg/litegraph' import { StorageLocation } from '@/types/settingTypes' import { ExtensionManager } from '@/types/extensionTypes' @@ -48,7 +47,7 @@ import { } from '@/services/dialogService' import { useSettingStore } from '@/stores/settingStore' import { useToastStore } from '@/stores/toastStore' -import { ModelStore, useModelStore } from '@/stores/modelStore' +import { useModelStore } from '@/stores/modelStore' import type { ToastMessageOptions } from 'primevue/toast' import { useWorkspaceStore } from '@/stores/workspaceStateStore' import { useExecutionStore } from '@/stores/executionStore' @@ -504,7 +503,9 @@ export class ComfyApp { throw error } } catch (error) { - alert('Error copying image: ' + (error.message ?? error)) + useToastStore().addAlert( + 'Error copying image: ' + (error.message ?? error) + ) } } } @@ -2301,7 +2302,9 @@ export class ComfyApp { // TODO: Show validation error in a dialog. const validatedGraphData = await validateComfyWorkflow( graphData, - /* onError=*/ alert + /* onError=*/ (err) => { + useToastStore().addAlert(err) + } ) // If the validation failed, use the original graph data. // Ideally we should not block users from loading the workflow. diff --git a/src/scripts/logging.ts b/src/scripts/logging.ts index b25d65881..21750e920 100644 --- a/src/scripts/logging.ts +++ b/src/scripts/logging.ts @@ -1,6 +1,7 @@ import { $el, ComfyDialog } from './ui' import { api } from './api' import type { ComfyApp } from './app' +import { useToastStore } from '@/stores/toastStore' $el('style', { textContent: ` @@ -130,7 +131,7 @@ class ComfyLoggingDialog extends ComfyDialog { throw new Error('Invalid file selected.') } } catch (error) { - alert('Unable to load logs: ' + error.message) + useToastStore().addAlert('Unable to load logs: ' + error.message) } } reader.readAsText(fileInput.files[0]) diff --git a/src/scripts/ui/settings.ts b/src/scripts/ui/settings.ts index b4f6ad0e6..720f4bd04 100644 --- a/src/scripts/ui/settings.ts +++ b/src/scripts/ui/settings.ts @@ -5,6 +5,7 @@ import type { ComfyApp } from '../app' import type { Setting, SettingParams } from '@/types/settingTypes' import { useSettingStore } from '@/stores/settingStore' import { Settings } from '@/types/apiTypes' +import { useToastStore } from '@/stores/toastStore' export class ComfySettingsDialog extends ComfyDialog { app: ComfyApp @@ -157,8 +158,7 @@ export class ComfySettingsDialog extends ComfyDialog { setSettingValue(id: K, value: Settings[K]) { this.setSettingValueAsync(id, value).catch((err) => { - alert(`Error saving setting '${id}'`) - console.error(err) + useToastStore().addAlert(`Error saving setting '${id}': ${err}`) }) } diff --git a/src/scripts/widgets.ts b/src/scripts/widgets.ts index 5b12aaae1..5beaa75ea 100644 --- a/src/scripts/widgets.ts +++ b/src/scripts/widgets.ts @@ -4,6 +4,7 @@ import type { ComfyApp } from './app' import type { IWidget, LGraphNode } from '@comfyorg/litegraph' import { InputSpec } from '@/types/apiTypes' import { useSettingStore } from '@/stores/settingStore' +import { useToastStore } from '@/stores/toastStore' export type ComfyWidgetConstructor = ( node: LGraphNode, @@ -578,10 +579,10 @@ export const ComfyWidgets: Record = { imageWidget.value = path } } else { - alert(resp.status + ' - ' + resp.statusText) + useToastStore().addAlert(resp.status + ' - ' + resp.statusText) } } catch (error) { - alert(error) + useToastStore().addAlert(error) } } diff --git a/src/scripts/workflows.ts b/src/scripts/workflows.ts index 8ad88c807..65c77ba5d 100644 --- a/src/scripts/workflows.ts +++ b/src/scripts/workflows.ts @@ -12,6 +12,7 @@ import { import { useExecutionStore } from '@/stores/executionStore' import { markRaw, toRaw } from 'vue' import { UserDataFullInfo } from '@/types/apiTypes' +import { useToastStore } from '@/stores/toastStore' export class ComfyWorkflowManager extends EventTarget { executionStore: ReturnType | null @@ -76,7 +77,9 @@ export class ComfyWorkflowManager extends EventTarget { } }) } catch (error) { - alert('Error loading workflows: ' + (error.message ?? error)) + useToastStore().addAlert( + 'Error loading workflows: ' + (error.message ?? error) + ) } } @@ -227,7 +230,7 @@ export class ComfyWorkflow { async getWorkflowData() { const resp = await api.getUserData('workflows/' + this.path) if (resp.status !== 200) { - alert( + useToastStore().addAlert( `Error loading workflow file '${this.path}': ${resp.status} ${resp.statusText}` ) return @@ -268,7 +271,7 @@ export class ComfyWorkflow { this.manager.workflowBookmarkStore?.setBookmarked(this.path, value) this.manager.dispatchEvent(new CustomEvent('favorite', { detail: this })) } catch (error) { - alert( + useToastStore().addAlert( 'Error favoriting workflow ' + this.path + '\n' + @@ -299,7 +302,7 @@ export class ComfyWorkflow { } if (resp.status !== 200) { - alert( + useToastStore().addAlert( `Error renaming workflow file '${this.path}': ${resp.status} ${resp.statusText}` ) return @@ -343,7 +346,7 @@ export class ComfyWorkflow { } const resp = await api.deleteUserData('workflows/' + this.path) if (resp.status !== 204) { - alert( + useToastStore().addAlert( `Error removing user data file '${this.path}': ${resp.status} ${resp.statusText}` ) } @@ -395,7 +398,7 @@ export class ComfyWorkflow { } if (resp.status !== 200) { - alert( + useToastStore().addAlert( `Error saving workflow '${this.path}': ${resp.status} ${resp.statusText}` ) return diff --git a/src/stores/toastStore.ts b/src/stores/toastStore.ts index 9265a2a3b..1629dfc23 100644 --- a/src/stores/toastStore.ts +++ b/src/stores/toastStore.ts @@ -20,6 +20,9 @@ export const useToastStore = defineStore('toast', { }, removeAll() { this.removeAllRequested = true + }, + addAlert(message: string) { + this.add({ severity: 'warn', summary: 'Alert', detail: message }) } } }) diff --git a/tests-ui/globalSetup.ts b/tests-ui/globalSetup.ts index 5ad73025f..d8ff2d2ea 100644 --- a/tests-ui/globalSetup.ts +++ b/tests-ui/globalSetup.ts @@ -28,6 +28,14 @@ module.exports = async function () { } }) + jest.mock('@/stores/toastStore', () => { + return { + useToastStore: () => ({ + addAlert: jest.fn() + }) + } + }) + jest.mock('vue-i18n', () => { return { useI18n: jest.fn()