diff --git a/electron-builder.json5 b/electron-builder.json5 index b8073db..960484d 100644 --- a/electron-builder.json5 +++ b/electron-builder.json5 @@ -28,6 +28,7 @@ ], "mac": { + "hardenedRuntime": false, "target": [ { "target": "dmg", @@ -38,7 +39,9 @@ "artifactName": "${productName}-Mac-${arch}-${version}-Installer.${ext}", "extendInfo": { "NSAudioCaptureUsageDescription": "OpenScreen needs audio capture permission to record system audio.", - "NSCameraUseContinuityCameraDeviceType": true + "NSMicrophoneUsageDescription": "OpenScreen needs microphone access to record voice audio.", + "NSCameraUseContinuityCameraDeviceType": true, + "com.apple.security.device.audio-input": true } }, "linux": { diff --git a/electron/main.ts b/electron/main.ts index 93c7590..d26dbcc 100644 --- a/electron/main.ts +++ b/electron/main.ts @@ -1,7 +1,17 @@ import fs from "node:fs/promises"; import path from "node:path"; import { fileURLToPath } from "node:url"; -import { app, BrowserWindow, dialog, ipcMain, Menu, nativeImage, Tray } from "electron"; +import { + app, + BrowserWindow, + dialog, + ipcMain, + Menu, + nativeImage, + session, + systemPreferences, + Tray, +} from "electron"; import { registerIpcHandlers } from "./ipc/handlers"; import { createEditorWindow, createHudOverlayWindow, createSourceSelectorWindow } from "./windows"; @@ -291,6 +301,25 @@ app.on("activate", () => { // Register all IPC handlers when app is ready app.whenReady().then(async () => { + // Allow microphone/media permission checks + session.defaultSession.setPermissionCheckHandler((_webContents, permission) => { + const allowed = ["media", "audioCapture", "microphone"]; + return allowed.includes(permission); + }); + + session.defaultSession.setPermissionRequestHandler((_webContents, permission, callback) => { + const allowed = ["media", "audioCapture", "microphone"]; + callback(allowed.includes(permission)); + }); + + // Request microphone permission from macOS + if (process.platform === "darwin") { + const micStatus = systemPreferences.getMediaAccessStatus("microphone"); + if (micStatus !== "granted") { + await systemPreferences.askForMediaAccess("microphone"); + } + } + // Listen for HUD overlay quit event (macOS only) ipcMain.on("hud-overlay-close", () => { app.quit(); diff --git a/src/hooks/useAudioLevelMeter.ts b/src/hooks/useAudioLevelMeter.ts index 893d308..12ff54b 100644 --- a/src/hooks/useAudioLevelMeter.ts +++ b/src/hooks/useAudioLevelMeter.ts @@ -39,6 +39,9 @@ export function useAudioLevelMeter(options: AudioLevelMeterOptions) { streamRef.current = stream; const audioContext = new AudioContext(); + if (audioContext.state === "suspended") { + await audioContext.resume(); + } audioContextRef.current = audioContext; const analyser = audioContext.createAnalyser();