diff --git a/src/lib/components/ComfyApp.ts b/src/lib/components/ComfyApp.ts index d7e696c..6eb6d7f 100644 --- a/src/lib/components/ComfyApp.ts +++ b/src/lib/components/ComfyApp.ts @@ -990,7 +990,7 @@ export default class ComfyApp { tag = null; if (targetWorkflow.attrs.showDefaultNotifications) { - notify("Prompt queued.", { type: "info" }); + notify("Prompt queued.", { type: "info", showOn: "web" }); } this.processingQueue = true; diff --git a/src/lib/nodes/actions/ComfyNotifyAction.ts b/src/lib/nodes/actions/ComfyNotifyAction.ts index 5c495f0..22533cb 100644 --- a/src/lib/nodes/actions/ComfyNotifyAction.ts +++ b/src/lib/nodes/actions/ComfyNotifyAction.ts @@ -3,17 +3,20 @@ import notify from "$lib/notify"; import { convertComfyOutputToGradio, type SerializedPromptOutput } from "$lib/utils"; import { BuiltInSlotType, LiteGraph, type SlotLayout } from "@litegraph-ts/core"; import ComfyGraphNode, { type ComfyGraphNodeProperties } from "../ComfyGraphNode"; +import configState from "$lib/stores/configState"; export interface ComfyNotifyActionProperties extends ComfyGraphNodeProperties { message: string, - type: string + type: string, + alwaysShow: boolean } export default class ComfyNotifyAction extends ComfyGraphNode { override properties: ComfyNotifyActionProperties = { tags: [], message: "Nya.", - type: "info" + type: "info", + alwaysShow: false } static slotLayout: SlotLayout = { @@ -24,6 +27,9 @@ export default class ComfyNotifyAction extends ComfyGraphNode { } override onAction(action: any, param: any) { + if (!configState.canShowNotificationText() && !this.properties.alwaysShow) + return; + const message = this.getInputData(0) || this.properties.message; if (!message) return; diff --git a/src/lib/nodes/actions/ComfyPlaySoundAction.ts b/src/lib/nodes/actions/ComfyPlaySoundAction.ts index 10659db..85178d8 100644 --- a/src/lib/nodes/actions/ComfyPlaySoundAction.ts +++ b/src/lib/nodes/actions/ComfyPlaySoundAction.ts @@ -1,6 +1,7 @@ import { BuiltInSlotType, LiteGraph, type SlotLayout } from "@litegraph-ts/core"; import ComfyGraphNode, { type ComfyGraphNodeProperties } from "../ComfyGraphNode"; import { playSound } from "$lib/utils"; +import configState from "$lib/stores/configState"; export interface ComfyPlaySoundActionProperties extends ComfyGraphNodeProperties { sound: string, @@ -20,6 +21,9 @@ export default class ComfyPlaySoundAction extends ComfyGraphNode { } override onAction(action: any, param: any) { + if (!configState.canPlayNotificationSound()) + return; + const sound = this.getInputData(0) || this.properties.sound; if (sound) { playSound(sound) diff --git a/src/lib/stores/configDefs.ts b/src/lib/stores/configDefs.ts index 0962f4f..e9ab20e 100644 --- a/src/lib/stores/configDefs.ts +++ b/src/lib/stores/configDefs.ts @@ -84,6 +84,41 @@ const defComfyUIPort: ConfigDefNumber<"comfyUIPort"> = { } }; +export enum NotificationState { + MessageAndSound, + MessageOnly, + SoundOnly, + None +} + +const defNotifications: ConfigDefEnum<"notifications", NotificationState> = { + name: "notifications", + type: "enum", + defaultValue: NotificationState.MessageAndSound, + category: "ui", + description: "Controls how notifications are shown", + options: { + values: [ + { + value: NotificationState.MessageAndSound, + label: "Message & sound" + }, + { + value: NotificationState.MessageOnly, + label: "Message only" + }, + { + value: NotificationState.SoundOnly, + label: "Sound only" + }, + { + value: NotificationState.None, + label: "None" + }, + ] + } +}; + const defAlwaysStripUserState: ConfigDefBoolean<"alwaysStripUserState"> = { name: "alwaysStripUserState", type: "boolean", @@ -158,6 +193,7 @@ const defBuiltInTemplates: ConfigDefStringArray<"builtInTemplates"> = { export const CONFIG_DEFS = [ defComfyUIHostname, defComfyUIPort, + defNotifications, defAlwaysStripUserState, defPromptForWorkflowName, defConfirmWhenUnloadingUnsavedChanges, diff --git a/src/lib/stores/configState.ts b/src/lib/stores/configState.ts index 4ecf4ce..e6fe1e9 100644 --- a/src/lib/stores/configState.ts +++ b/src/lib/stores/configState.ts @@ -2,10 +2,12 @@ import { debounce } from '$lib/utils'; import { toHashMap } from '@litegraph-ts/core'; import { get, writable } from 'svelte/store'; import type { Writable } from 'svelte/store'; -import { defaultConfig, type ConfigState, type ConfigDefAny, CONFIG_DEFS_BY_NAME, validateConfigOption } from './configDefs'; +import { defaultConfig, type ConfigState, type ConfigDefAny, CONFIG_DEFS_BY_NAME, validateConfigOption, NotificationState } from './configDefs'; type ConfigStateOps = { getBackendURL: () => string, + canShowNotificationText: () => boolean, + canPlayNotificationSound: () => boolean, load: (data: any, runOnChanged?: boolean) => ConfigState loadDefault: (runOnChanged?: boolean) => ConfigState @@ -25,6 +27,17 @@ function getBackendURL(): string { return `${window.location.protocol}//${state.comfyUIHostname}:${state.comfyUIPort}` } +function canShowNotificationText(): boolean { + const state = get(store).notifications; + return state === NotificationState.MessageAndSound || state === NotificationState.MessageOnly; +} + +function canPlayNotificationSound(): boolean { + const state = get(store).notifications; + return state === NotificationState.MessageAndSound || state === NotificationState.SoundOnly; +} + + function setConfigOption(def: ConfigDefAny, v: any, runOnChanged: boolean): boolean { let valid = false; store.update(state => { @@ -112,6 +125,9 @@ const configStateStore: WritableConfigStateStore = { ...store, getBackendURL, + canShowNotificationText, + canPlayNotificationSound, + validateConfigOption, setConfigOption, load, diff --git a/src/lib/stores/queueState.ts b/src/lib/stores/queueState.ts index 82712d9..56cf239 100644 --- a/src/lib/stores/queueState.ts +++ b/src/lib/stores/queueState.ts @@ -7,6 +7,7 @@ import { playSound } from "$lib/utils"; import { get, writable, type Writable } from "svelte/store"; import { v4 as uuidv4 } from "uuid"; import workflowState, { type WorkflowError, type WorkflowExecutionError, type WorkflowInstID, type WorkflowValidationError } from "./workflowState"; +import configState from "./configState"; export type QueueEntryStatus = "success" | "validation_failed" | "error" | "interrupted" | "all_cached" | "unknown"; @@ -283,14 +284,18 @@ function executingUpdated(promptID: PromptID, runningNodeID: ComfyNodeID | null) if (entry != null) { const totalNodesInPrompt = Object.keys(entry.prompt).length if (entry.cachedNodes.size >= Object.keys(entry.prompt).length) { - notify("Prompt was cached, nothing to run.", { type: "warning" }) + notify("Prompt was cached, nothing to run.", { type: "warning", showOn: "web" }) moveToCompleted(index, queue, "all_cached", "(Execution was cached)"); } else if (entry.nodesRan.size >= totalNodesInPrompt) { const workflow = workflowState.getWorkflow(entry.extraData.workflowID); if (workflow?.attrs.showDefaultNotifications) { - notify("Prompt finished!", { type: "success" }); - playSound("notification.mp3") + if (configState.canShowNotificationText()) { + notify("Prompt finished!", { type: "success" }); + } + if (configState.canPlayNotificationSound()) { + playSound("notification.mp3") + } } moveToCompleted(index, queue, "success") } diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 559349d..e340256 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -635,6 +635,9 @@ export function nextLetter(s: string): string { } export function playSound(sound: string) { + if (!configState.canPlayNotificationSound()) + return; + const url = `${location.origin}/sound/${sound}`; const audio = new Audio(url); audio.play();