import { exec } from 'child_process';
import { promisify } from 'util';
import type { NotificationOptions, NotificationResult, NotificationType } from '../types/index.js';
import { getProjectName } from '../utils/project.js';
import { logNotification, getLogPath } from '../utils/logger.js';
const execAsync = promisify(exec);
function getNotificationIcon(type: NotificationType): string {
switch (type) {
case 'success':
return '✅';
case 'error':
return '❌';
case 'warning':
return '⚠️';
case 'info':
default:
return 'ℹ️';
}
}
function escapeForAppleScript(text: string): string {
return text
.replace(/\\/g, '\\\\')
.replace(/"/g, '\\"')
.replace(/\n/g, '\\n')
.replace(/\r/g, '\\r')
.replace(/\t/g, '\\t');
}
export async function sendNotification(options: NotificationOptions): Promise<NotificationResult> {
const projectName = getProjectName();
const type = options.type || 'info';
const icon = getNotificationIcon(type);
// Build the notification title with icon and project name
const fullTitle = `${icon} ${options.title}`;
const subtitle = options.subtitle || `[${projectName}]`;
// Escape strings for AppleScript
const escapedTitle = escapeForAppleScript(fullTitle);
const escapedMessage = escapeForAppleScript(options.message);
const escapedSubtitle = escapeForAppleScript(subtitle);
// Build AppleScript command
let script = `display notification "${escapedMessage}" with title "${escapedTitle}"`;
if (subtitle) {
script += ` subtitle "${escapedSubtitle}"`;
}
if (options.sound !== false) {
script += ' sound name "default"';
}
const command = `osascript -e '${script}'`;
try {
await execAsync(command);
await logNotification(options, projectName);
return {
success: true,
timestamp: new Date().toISOString(),
logPath: getLogPath(),
};
} catch (error) {
const err = error as Error;
await logNotification(options, projectName, err);
return {
success: false,
timestamp: new Date().toISOString(),
logPath: getLogPath(),
error: err.message,
};
}
}