diff --git a/electron/entitlements.mac.plist b/electron/entitlements.mac.plist new file mode 100644 index 0000000..8fd2022 --- /dev/null +++ b/electron/entitlements.mac.plist @@ -0,0 +1,14 @@ + + + + + com.apple.security.device.audio-input + + com.apple.security.cs.allow-jit + + com.apple.security.cs.allow-unsigned-executable-memory + + com.apple.security.cs.disable-library-validation + + + \ No newline at end of file diff --git a/electron/main.js b/electron/main.js index c5eb290..7bb0e12 100644 --- a/electron/main.js +++ b/electron/main.js @@ -1,4 +1,4 @@ -import { app, BrowserWindow } from 'electron'; +import { app, BrowserWindow, systemPreferences } from 'electron'; import path from 'path'; import { fileURLToPath } from 'url'; @@ -6,6 +6,26 @@ const isDev = !app.isPackaged; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); +async function handlePermissions() { + // macOS requires an explicit request for microphone access in packaged apps + if (process.platform === 'darwin') { + try { + const status = systemPreferences.getMediaAccessStatus('microphone'); + console.log(`[Main Process] Current Microphone Status: ${status}`); + + if (status !== 'granted') { + const granted = await systemPreferences.askForMediaAccess('microphone'); + console.log(`[Main Process] Microphone permission granted after request: ${granted}`); + } + } catch (err) { + console.error('[Main Process] Error checking/requesting microphone access:', err); + } + } else { + // Windows/Linux typically grant access by default or via the Renderer process + console.log(`[Main Process] Platform is ${process.platform}, skipping native macOS permission prompt.`); + } +} + function createWindow() { const win = new BrowserWindow({ width: 1024, @@ -13,20 +33,28 @@ function createWindow() { webPreferences: { preload: path.join(__dirname, 'preload.cjs'), contextIsolation: true, - nodeIntegration: false + nodeIntegration: false, + // Ensure the browser engine treats the app as "secure" to allow microphone access + sandbox: false } }); if (isDev) { const devUrl = process.env.ELECTRON_START_URL || 'http://localhost:5173'; win.loadURL(devUrl); + // Open DevTools automatically in dev win.webContents.openDevTools(); } else { + // In production, load the built index.html from the dist folder + // This path assumes your structure is /electron/main.js and /dist/index.html win.loadFile(path.join(__dirname, '..', 'dist', 'index.html')); } } -app.whenReady().then(createWindow); +app.whenReady().then(async () => { + await handlePermissions(); + createWindow(); +}); app.on('window-all-closed', () => { if (process.platform !== 'darwin') app.quit(); @@ -34,4 +62,4 @@ app.on('window-all-closed', () => { app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) createWindow(); -}); +}); \ No newline at end of file diff --git a/package.json b/package.json index 885ac53..9d76447 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,39 @@ "build": { "appId": "com.whattheflat.app", "productName": "WhatTheFlat", + "mac": { + "hardenedRuntime": true, + "entitlements": "electron/entitlements.mac.plist", + "entitlementsInherit": "electron/entitlements.mac.plist", + "extendInfo": { + "NSMicrophoneUsageDescription": "WhatTheFlat needs access to your microphone to detect pitch and provide the tuner." + } + }, + "win": { + "target": [ + { + "target": "nsis", + "arch": [ + "x64", + "ia32" + ] + }, + "zip" + ] + }, + "nsis": { + "oneClick": false, + "perMachine": false, + "allowElevation": true, + "allowToChangeInstallationDirectory": true + }, + "linux": { + "target": [ + "AppImage", + "deb" + ], + "category": "Audio" + }, "files": [ "dist/**/*", "electron/**/*", @@ -45,4 +78,4 @@ "output": "release" } } -} +} \ No newline at end of file