diff --git a/src/areas/workflows/workflowRunStore.ts b/src/areas/workflows/workflowRunStore.ts index 0f0341d..c4f3eb8 100644 --- a/src/areas/workflows/workflowRunStore.ts +++ b/src/areas/workflows/workflowRunStore.ts @@ -2,6 +2,7 @@ import { create } from 'zustand' import axios, { AxiosInstance } from 'axios' import { useAppStore } from '@shared/stores/appStore' import { getWorkflowExtension } from './mockExtensions' +import { playCompletionSound } from '@shared/utils/sound' import type { WorkflowExtension } from './mockExtensions' import type { Workflow, WFNode, WFEdge } from '@shared/types/electron.d' import { isBranchStarter, isSceneOutput, resolveDataSource, reachesSceneOutput, nearestUpstreamWaits } from './nodeBehaviors' @@ -392,6 +393,7 @@ export const useWorkflowRunStore = create((set, get) => { }, })) useAppStore.getState().updateCurrentJob({ status: 'done', progress: 100, outputUrl }) + playCompletionSound() } return { diff --git a/src/shared/hooks/useGeneration.ts b/src/shared/hooks/useGeneration.ts index 8031789..e1f884b 100644 --- a/src/shared/hooks/useGeneration.ts +++ b/src/shared/hooks/useGeneration.ts @@ -1,6 +1,7 @@ import { useCallback, useRef } from 'react' import { useAppStore } from '@shared/stores/appStore' import { useApi } from './useApi' +import { playCompletionSound } from '@shared/utils/sound' export function useGeneration() { const { currentJob, setCurrentJob, updateCurrentJob, generationOptions, selectedImageData, pushMeshUrl, clearMeshHistory } = useAppStore() @@ -77,6 +78,7 @@ export function useGeneration() { if (result.status === 'done') { updateCurrentJob({ status: 'done', progress: 100, outputUrl: result.outputUrl, originalOutputUrl: result.outputUrl }) if (result.outputUrl) pushMeshUrl(result.outputUrl) + playCompletionSound() break } diff --git a/src/shared/utils/sound.ts b/src/shared/utils/sound.ts new file mode 100644 index 0000000..d47c27d --- /dev/null +++ b/src/shared/utils/sound.ts @@ -0,0 +1,17 @@ +export function playCompletionSound() { + try { + const ctx = new AudioContext() + const oscillator = ctx.createOscillator() + const gain = ctx.createGain() + oscillator.connect(gain) + gain.connect(ctx.destination) + oscillator.type = 'sine' + oscillator.frequency.setValueAtTime(880, ctx.currentTime) + gain.gain.setValueAtTime(0.3, ctx.currentTime) + gain.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + 0.3) + oscillator.start(ctx.currentTime) + oscillator.stop(ctx.currentTime + 0.3) + } catch { + // Audio not available + } +}