diff --git a/packages/electron/main.mjs b/packages/electron/main.mjs index ca94f381..3d7b5d92 100644 --- a/packages/electron/main.mjs +++ b/packages/electron/main.mjs @@ -785,6 +785,7 @@ const spawnLocalServer = async () => { attachSignals: false, exitOnShutdown: false, onDesktopNotification: (payload) => maybeShowNativeNotification(payload), + getIsWindowFocused: isAnyWindowFocused, }); const port = handle.getPort(); diff --git a/packages/web/server/index.js b/packages/web/server/index.js index fef16793..19e5c33e 100644 --- a/packages/web/server/index.js +++ b/packages/web/server/index.js @@ -1073,6 +1073,9 @@ async function main(options = {}) { if (typeof options.onDesktopNotification === 'function') { notificationEmitterRuntime.setOnDesktopNotification(options.onDesktopNotification); } + if (typeof options.getIsWindowFocused === 'function') { + notificationTriggerRuntime.setGetIsWindowFocused(options.getIsWindowFocused); + } console.log(`Starting OpenChamber on port ${port === 0 ? 'auto' : port}`); diff --git a/packages/web/server/lib/notifications/runtime.js b/packages/web/server/lib/notifications/runtime.js index 276c57f2..c7b4357d 100644 --- a/packages/web/server/lib/notifications/runtime.js +++ b/packages/web/server/lib/notifications/runtime.js @@ -16,6 +16,14 @@ export const createNotificationTriggerRuntime = (deps) => { getOpenCodeAuthHeaders, } = deps; + let getIsWindowFocused = typeof deps.getIsWindowFocused === 'function' + ? deps.getIsWindowFocused + : null; + + const setGetIsWindowFocused = (cb) => { + getIsWindowFocused = typeof cb === 'function' ? cb : null; + }; + const PUSH_READY_COOLDOWN_MS = 5000; const PUSH_QUESTION_DEBOUNCE_MS = 500; const PUSH_PERMISSION_DEBOUNCE_MS = 500; @@ -211,6 +219,10 @@ export const createNotificationTriggerRuntime = (deps) => { return; } + if (settings.notificationMode !== 'always' && getIsWindowFocused?.()) { + return; + } + const now = Date.now(); const lastAt = lastReadyNotificationAt.get(sessionId) ?? 0; if (now - lastAt < PUSH_READY_COOLDOWN_MS) { @@ -283,6 +295,10 @@ export const createNotificationTriggerRuntime = (deps) => { const settings = await readSettingsFromDisk(); if (settings.notifyOnError === false) return; + if (settings.notificationMode !== 'always' && getIsWindowFocused?.()) { + return; + } + let title = 'Tool error'; let body = 'An error occurred'; @@ -355,6 +371,10 @@ export const createNotificationTriggerRuntime = (deps) => { return; } + if (settings.notificationMode !== 'always' && getIsWindowFocused?.()) { + return; + } + const firstQuestion = payload.properties?.questions?.[0]; const header = typeof firstQuestion?.header === 'string' ? firstQuestion.header.trim() : ''; const questionText = typeof firstQuestion?.question === 'string' ? firstQuestion.question.trim() : ''; @@ -473,6 +493,10 @@ export const createNotificationTriggerRuntime = (deps) => { return; } + if (settings.notificationMode !== 'always' && getIsWindowFocused?.()) { + return; + } + const sessionTitle = payload.properties?.sessionTitle; const permissionText = typeof permission === 'string' && permission.length > 0 ? permission : ''; const fallbackMessage = typeof sessionTitle === 'string' && sessionTitle.trim().length > 0 @@ -543,5 +567,6 @@ export const createNotificationTriggerRuntime = (deps) => { return { maybeSendPushForTrigger, setAutoAcceptSession, + setGetIsWindowFocused, }; };