import { join } from "node:path"; import { APP_NAME } from "./constants.ts"; import { notificationToDesktopText, type NotificationInput } from "./notify-protocol.ts"; import type { Runtime } from "./runtime.ts"; import { $which } from "./which.ts"; export type DesktopNotifierKind = "notify-send" | "gdbus"; export interface DesktopNotifier { kind: DesktopNotifierKind; path: string; } const cache = new WeakMap(); const urgencyByte = { low: 0, normal: 1, critical: 2 } as const; export function hasLinuxDesktopSession(runtime: Runtime): boolean { if (runtime.platform !== "linux" && runtime.platform !== "wsl") return false; if (runtime.env.DBUS_SESSION_BUS_ADDRESS) return true; const runtimeDir = runtime.env.XDG_RUNTIME_DIR; return Boolean(runtimeDir && runtime.existsSync(join(runtimeDir, "bus"))); } export function resetDesktopNotifierCache(runtime?: Runtime): void { if (runtime) cache.delete(runtime); } export function resolveDesktopNotifier(runtime: Runtime): DesktopNotifier | null { const cached = cache.get(runtime); if (cached !== undefined) return cached; const notifySend = $which("notify-send", runtime); if (notifySend) { const notifier = { kind: "notify-send" as const, path: notifySend }; cache.set(runtime, notifier); return notifier; } const gdbus = $which("gdbus", runtime); if (gdbus) { const notifier = { kind: "gdbus" as const, path: gdbus }; cache.set(runtime, notifier); return notifier; } cache.set(runtime, null); return null; } export function buildDesktopNotifyCommand(notifier: DesktopNotifier, input: NotificationInput): string[] { const { title, body, urgency } = notificationToDesktopText(input); if (notifier.kind === "notify-send") { return [notifier.path, "--app-name", APP_NAME, `--urgency=${urgency}`, "--expire-time=5000", title, body]; } return [ notifier.path, "call", "--session", "--dest", "org.freedesktop.Notifications", "--object-path", "/org/freedesktop/Notifications", "--method", "org.freedesktop.Notifications.Notify", APP_NAME, "0", "", title, body, "[]", `{"urgency": }`, "5000", ]; } export function resolveLinuxDesktopCommand(input: NotificationInput, runtime: Runtime): { command: string; args: string[] } | null { if (!hasLinuxDesktopSession(runtime)) return null; const notifier = resolveDesktopNotifier(runtime); if (!notifier) return null; const argv = buildDesktopNotifyCommand(notifier, input); return { command: argv[0]!, args: argv.slice(1) }; } /** Fire-and-forget helper; returns whether a notifier was resolved and attempted. */ export function sendLinuxDesktopNotification(input: NotificationInput, runtime: Runtime): boolean { const resolved = resolveLinuxDesktopCommand(input, runtime); if (!resolved) return false; runtime.spawnDetached(resolved.command, resolved.args); return true; }