From 3493508b8af096f42488f278db36f450d988e803 Mon Sep 17 00:00:00 2001 From: Max Litruv Boonzaayer Date: Fri, 27 Feb 2026 22:12:49 +1100 Subject: [PATCH] notifications --- cinny | 2 +- electron/main.js | 67 +++++++++++++++++++++++++++++++++++++++++++-- electron/preload.js | 5 ++++ package-lock.json | 30 ++++++++++++++++++-- package.json | 3 +- 5 files changed, 101 insertions(+), 6 deletions(-) diff --git a/cinny b/cinny index 58966ae..f8af51b 160000 --- a/cinny +++ b/cinny @@ -1 +1 @@ -Subproject commit 58966aec195ba92e015a9b8f90744eaa09e95450 +Subproject commit f8af51b75cfa9eeae9cb511126833ad491edf351 diff --git a/electron/main.js b/electron/main.js index d22062c..a82c13e 100644 --- a/electron/main.js +++ b/electron/main.js @@ -75,6 +75,17 @@ let tray = null; let isQuitting = false; let apiServer = null; +// Set app name for notifications and system tray +app.setName('Paarrot'); + +// On Windows, this controls the app name shown in toast notifications. +// In dev mode, use process.execPath so Windows can find the shortcut for electron.exe. +// In production, use the proper app ID. +if (process.platform === 'win32') { + const isDevelopment = process.env.NODE_ENV === 'development' || !app.isPackaged; + app.setAppUserModelId(isDevelopment ? process.execPath : 'com.paarrot.app'); +} + // Configure auto-updater autoUpdater.autoDownload = false; autoUpdater.autoInstallOnAppQuit = true; @@ -275,7 +286,7 @@ function createWindow() { // Set up session handlers BEFORE loading the app // Auto-approve media permissions including screen capture mainWindow.webContents.session.setPermissionRequestHandler((webContents, permission, callback) => { - const allowedPermissions = ['media', 'microphone', 'camera', 'display-capture']; + const allowedPermissions = ['media', 'microphone', 'camera', 'display-capture', 'notifications']; if (allowedPermissions.includes(permission)) { console.log(`Paarrot: Auto-approving permission: ${permission}`); @@ -424,11 +435,27 @@ function createWindow() { }); // Open external links in default browser - mainWindow.webContents.setWindowOpenHandler(({ url }) => { + mainWindow.webContents.setWindowOpenHandler(({ url, features }) => { if (url.startsWith('http://') || url.startsWith('https://')) { shell.openExternal(url); return { action: 'deny' }; } + // Allow blank windows for screen share pop-outs with custom features + if (url === '' || url === 'about:blank') { + return { + action: 'allow', + overrideBrowserWindowOptions: { + frame: false, + resizable: true, + backgroundColor: '#000000', + webPreferences: { + contextIsolation: true, + nodeIntegration: false, + backgroundThrottling: false, + } + } + }; + } return { action: 'allow' }; }); @@ -738,6 +765,42 @@ ipcMain.handle('get-desktop-sources', async (event, opts) => { } }); +// Desktop notification handler +ipcMain.handle('show-notification', async (event, { title, body, icon }) => { + try { + console.log('Electron notification handler called:', { title, body }); + const { Notification } = require('electron'); + + if (!Notification.isSupported()) { + console.error('Notifications not supported on this system'); + return { success: false, error: 'Notifications not supported on this system' }; + } + + const notification = new Notification({ + title: title || 'Paarrot', + body: body || '', + icon: icon || getIconPath(process.platform === 'win32' ? 'icon.ico' : 'icon.png'), + silent: false, + }); + + notification.on('click', () => { + console.log('Notification clicked'); + if (mainWindow) { + mainWindow.show(); + mainWindow.focus(); + } + }); + + notification.show(); + console.log('Notification shown successfully'); + + return { success: true }; + } catch (error) { + console.error('Failed to show notification:', error); + return { success: false, error: error.message }; + } +}); + console.log('Paarrot Electron main process started'); console.log('Development mode:', isDev); console.log('Platform:', process.platform); diff --git a/electron/preload.js b/electron/preload.js index d3e856b..0fad9fb 100644 --- a/electron/preload.js +++ b/electron/preload.js @@ -150,6 +150,11 @@ contextBridge.exposeInMainWorld('electron', { sendResponse: (channel, result) => { ipcRenderer.invoke(channel, result); } + }, + + // Desktop notifications + notification: { + show: (options) => ipcRenderer.invoke('show-notification', options) } }); diff --git a/package-lock.json b/package-lock.json index ad7ede0..5fabf87 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "paarrot", - "version": "4.11.20", + "version": "4.11.26", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "paarrot", - "version": "4.11.20", + "version": "4.11.26", "license": "AGPL-3.0-only", "dependencies": { "body-parser": "2.2.2", @@ -21,6 +21,7 @@ "@actions/github": "6.0.0", "@electron/rebuild": "4.0.3", "concurrently": "9.2.1", + "cross-env": "10.1.0", "electron": "40.6.0", "electron-builder": "26.8.1", "node-fetch": "3.3.2", @@ -529,6 +530,13 @@ "tslib": "^2.4.0" } }, + "node_modules/@epic-web/invariant": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@epic-web/invariant/-/invariant-1.0.0.tgz", + "integrity": "sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==", + "dev": true, + "license": "MIT" + }, "node_modules/@fastify/busboy": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.0.0.tgz", @@ -2789,6 +2797,24 @@ "optional": true, "peer": true }, + "node_modules/cross-env": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-10.1.0.tgz", + "integrity": "sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@epic-web/invariant": "^1.0.0", + "cross-spawn": "^7.0.6" + }, + "bin": { + "cross-env": "dist/bin/cross-env.js", + "cross-env-shell": "dist/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", diff --git a/package.json b/package.json index 576bec6..6950c36 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "scripts": { "dev": "concurrently \"npm run dev:vite\" \"npm run dev:electron\"", "dev:vite": "cd cinny && npm start", - "dev:electron": "wait-on http://localhost:8080 && NODE_ENV=development electron .", + "dev:electron": "wait-on http://localhost:8080 && cross-env NODE_ENV=development electron .", "build": "node -e \"require('fs').copyFileSync('config.json', 'cinny/config.json')\" && cd cinny && npm run build && cd .. && electron-builder --publish never", "build:linux": "npm run build -- --linux", "build:win": "npm run build -- --win", @@ -35,6 +35,7 @@ "@actions/github": "6.0.0", "@electron/rebuild": "4.0.3", "concurrently": "9.2.1", + "cross-env": "10.1.0", "electron": "40.6.0", "electron-builder": "26.8.1", "node-fetch": "3.3.2",