// OS notifications via the Electron preload bridge. The exported API // stays identical to the Tauri-era version so callers don't change. // // Permission under Electron is implicit ('granted' always). The legacy // ASKED marker in localStorage is kept so we don't re-prompt after a // previously-denied run; we just fast-succeed now. import { isTauriRuntime } from './globalShortcut'; let permissionChecked = false; let permissionGranted = false; const ASKED_KEY = 'chatapp.notif.asked'; function writeAskedMarker(): void { try { window.localStorage.setItem(ASKED_KEY, '1'); } catch { /* quota / private mode */ } } export async function ensureNotificationPermission(): Promise { if (permissionChecked) return permissionGranted; permissionChecked = true; if (!isTauriRuntime()) { permissionGranted = false; return false; } try { const state = await window.electronAPI.getNotificationPermission(); permissionGranted = state === 'granted'; writeAskedMarker(); } catch (err: unknown) { permissionGranted = false; console.warn('notification permission check failed', err); } return permissionGranted; } export function isAppFocused(): boolean { return typeof document !== 'undefined' && !document.hidden && document.hasFocus(); } interface NotifyOpts { title: string; body?: string; /** Force notification even when app is focused. Default: suppress if focused. */ force?: boolean; } export async function notify({ title, body, force = false }: NotifyOpts): Promise { if (!force && isAppFocused()) return; if (!isTauriRuntime()) return; const granted = await ensureNotificationPermission(); if (!granted) return; try { await window.electronAPI.notify({ title, body: body ?? '', silent: true, }); } catch (err: unknown) { console.error('notify failed', err); } }