diff --git a/src/components/SettingsPage.tsx b/src/components/SettingsPage.tsx index ed930f3b1..4a06bb1f9 100644 --- a/src/components/SettingsPage.tsx +++ b/src/components/SettingsPage.tsx @@ -1030,7 +1030,7 @@ export default function SettingsPage({ activeSection = "general" }: SettingsPage title: t("settingsPage.permissions.resetAccessibility.title"), description: message, onConfirm: () => { - permissionsHook.openAccessibilitySettings(); + permissionsHook.requestAccessibilityPermission(); }, }); }; @@ -3324,8 +3324,7 @@ EOF`, description={t("settingsPage.permissions.microphoneDescription")} granted={permissionsHook.micPermissionGranted} onRequest={permissionsHook.requestMicPermission} - buttonText={t("settingsPage.permissions.test")} - onOpenSettings={permissionsHook.openMicPrivacySettings} + buttonText={t("settingsPage.permissions.grantAccess")} /> {platform === "darwin" && ( @@ -3335,9 +3334,8 @@ EOF`, title={t("settingsPage.permissions.accessibilityTitle")} description={t("settingsPage.permissions.accessibilityDescription")} granted={permissionsHook.accessibilityPermissionGranted} - onRequest={permissionsHook.testAccessibilityPermission} - buttonText={t("settingsPage.permissions.testAndGrant")} - onOpenSettings={permissionsHook.openAccessibilitySettings} + onRequest={permissionsHook.requestAccessibilityPermission} + buttonText={t("settingsPage.permissions.grantAccess")} /> {systemAudio.mode === "native" && ( )} diff --git a/src/components/ui/PermissionCard.tsx b/src/components/ui/PermissionCard.tsx index 3413f0fbc..161644492 100644 --- a/src/components/ui/PermissionCard.tsx +++ b/src/components/ui/PermissionCard.tsx @@ -1,5 +1,5 @@ import { Button } from "./button"; -import { Check, ExternalLink, LucideIcon, Settings } from "lucide-react"; +import { Check, LucideIcon } from "lucide-react"; import { cn } from "../lib/utils"; interface PermissionCardProps { @@ -9,9 +9,7 @@ interface PermissionCardProps { granted: boolean; onRequest: () => void; buttonText?: string; - onOpenSettings?: () => void; badge?: string; - openSettingsText?: string; hint?: string; } @@ -22,9 +20,7 @@ export default function PermissionCard({ granted, onRequest, buttonText = "Grant Access", - onOpenSettings, badge, - openSettingsText, hint, }: PermissionCardProps) { return ( @@ -38,7 +34,6 @@ export default function PermissionCard({ )} >
- {/* Icon container */}
- {/* Content */}

{title} @@ -67,36 +61,13 @@ export default function PermissionCard({

{description}

- {/* Actions - only when not granted */} {!granted && ( -
- {onOpenSettings && openSettingsText ? ( - - ) : ( - <> - - {onOpenSettings && ( - - )} - - )} -
+ )}
- {/* Troubleshooting hint */} {hint && !granted && (

{hint}

)} diff --git a/src/components/ui/PermissionsSection.tsx b/src/components/ui/PermissionsSection.tsx index 607375a13..9f4df7434 100644 --- a/src/components/ui/PermissionsSection.tsx +++ b/src/components/ui/PermissionsSection.tsx @@ -11,7 +11,6 @@ interface PermissionsSectionProps { granted: boolean; mode: "native" | "unsupported"; request: () => Promise; - openSettings: () => Promise; }; } @@ -29,7 +28,7 @@ export default function PermissionsSection({ permissions, systemAudio }: Permiss description={t("onboarding.permissions.microphoneDescription")} granted={permissions.micPermissionGranted} onRequest={permissions.requestMicPermission} - buttonText={t("onboarding.permissions.grant")} + buttonText={t("onboarding.permissions.grantAccess")} /> {isMacOS && ( @@ -39,10 +38,8 @@ export default function PermissionsSection({ permissions, systemAudio }: Permiss title={t("onboarding.permissions.accessibilityTitle")} description={t("onboarding.permissions.accessibilityDescription")} granted={permissions.accessibilityPermissionGranted} - onRequest={permissions.testAccessibilityPermission} - buttonText={t("onboarding.permissions.testAndGrant")} - onOpenSettings={permissions.openAccessibilitySettings} - openSettingsText={t("onboarding.permissions.openSystemSettings")} + onRequest={permissions.requestAccessibilityPermission} + buttonText={t("onboarding.permissions.grantAccess")} badge={t("onboarding.permissions.recommended")} hint={ permissions.accessibilityTroubleshooting @@ -57,8 +54,7 @@ export default function PermissionsSection({ permissions, systemAudio }: Permiss description={t("onboarding.permissions.systemAudioDescription")} granted={systemAudio.granted} onRequest={systemAudio.request} - buttonText={t("onboarding.permissions.grant")} - onOpenSettings={systemAudio.openSettings} + buttonText={t("onboarding.permissions.grantAccess")} badge={t("onboarding.permissions.optional")} /> )} diff --git a/src/helpers/audioTapManager.js b/src/helpers/audioTapManager.js index 25f7950cd..29e797282 100644 --- a/src/helpers/audioTapManager.js +++ b/src/helpers/audioTapManager.js @@ -11,7 +11,7 @@ const ARCH_CPU_TYPE = { const DEFAULT_SAMPLE_RATE = 24000; const DEFAULT_CHUNK_MS = 100; const START_TIMEOUT_MS = 3000; -const REQUEST_TIMEOUT_MS = 15000; +const REQUEST_TIMEOUT_MS = 60000; const STOP_TIMEOUT_MS = 5000; function compareVersions(left, right) { diff --git a/src/helpers/clipboard.js b/src/helpers/clipboard.js index 0d3d0e51e..c436d76f2 100644 --- a/src/helpers/clipboard.js +++ b/src/helpers/clipboard.js @@ -162,9 +162,7 @@ class ClipboardManager { } const possiblePaths = [ - ...(process.resourcesPath - ? [path.join(process.resourcesPath, "bin", "nircmd.exe")] - : []), + ...(process.resourcesPath ? [path.join(process.resourcesPath, "bin", "nircmd.exe")] : []), path.join(__dirname, "..", "..", "resources", "bin", "nircmd.exe"), path.join(process.cwd(), "resources", "bin", "nircmd.exe"), ]; diff --git a/src/helpers/hotkeyManager.js b/src/helpers/hotkeyManager.js index 0b5f12983..96600ff25 100644 --- a/src/helpers/hotkeyManager.js +++ b/src/helpers/hotkeyManager.js @@ -211,9 +211,7 @@ class HotkeyManager { slot.accelerator = null; this.slots.set(slotName, slot); - debugLogger.log( - `[HotkeyManager] KDE slot "${slotName}" set to "${hotkey}"` - ); + debugLogger.log(`[HotkeyManager] KDE slot "${slotName}" set to "${hotkey}"`); return { success: true, hotkey }; } @@ -483,7 +481,11 @@ class HotkeyManager { } async initializeKDEShortcuts(callback) { - if (process.platform !== "linux" || !KDEShortcutManager.isWayland() || !KDEShortcutManager.isKDE()) { + if ( + process.platform !== "linux" || + !KDEShortcutManager.isWayland() || + !KDEShortcutManager.isKDE() + ) { return false; } @@ -606,9 +608,7 @@ class HotkeyManager { const success = await this.kdeManager.registerKeybinding(hotkey, "dictation", callback); if (success) { this.currentHotkey = hotkey; - debugLogger.log( - `[HotkeyManager] KDE hotkey "${hotkey}" registered successfully` - ); + debugLogger.log(`[HotkeyManager] KDE hotkey "${hotkey}" registered successfully`); } else { debugLogger.log( "[HotkeyManager] KDE keybinding failed, falling back to globalShortcut" diff --git a/src/helpers/ipcHandlers.js b/src/helpers/ipcHandlers.js index 8d6c32a19..754899242 100644 --- a/src/helpers/ipcHandlers.js +++ b/src/helpers/ipcHandlers.js @@ -2066,12 +2066,14 @@ class IPCHandlers { return { granted: true, status: "granted", mode: "unsupported" }; } - if (this.audioTapManager?.isSupported()) { - const status = systemPreferences.getMediaAccessStatus("screen"); - return { granted: status === "granted", status, mode: "native" }; + if (!this.audioTapManager?.isSupported()) { + return { granted: false, status: "unsupported", mode: "unsupported" }; } - return { granted: false, status: "unsupported", mode: "unsupported" }; + const screenStatus = systemPreferences.getMediaAccessStatus("screen"); + const tapStatus = this.audioTapManager.getPermissionStatus(); + const granted = screenStatus === "granted" || tapStatus === "granted"; + return { granted, status: granted ? "granted" : screenStatus, mode: "native" }; }); ipcMain.handle("request-system-audio-access", async () => { @@ -2083,16 +2085,24 @@ class IPCHandlers { return { granted: false, status: "unsupported", mode: "unsupported" }; } - const status = systemPreferences.getMediaAccessStatus("screen"); - if (status === "granted") { + const screenStatus = systemPreferences.getMediaAccessStatus("screen"); + if (screenStatus === "granted") { return { granted: true, status: "granted", mode: "native" }; } - // Permission not yet granted — open System Settings so the user can toggle it manually. - // Unlike microphone, macOS does not show a system prompt for screen/audio recording; - // the user must enable it in System Settings. + // Probe the binary — AudioHardwareCreateProcessTap triggers the native consent dialog + try { + const result = await this.audioTapManager.requestAccess(); + if (result.granted) { + return { granted: true, status: "granted", mode: "native" }; + } + } catch { + // Falls through to opening System Settings + } + + // Fallback for older macOS or if the native prompt was denied await openSystemSettings("systemAudio"); - return { granted: false, status, mode: "native" }; + return { granted: false, status: screenStatus, mode: "native" }; }); // Auth: clear all session cookies for sign-out. @@ -2487,7 +2497,13 @@ class IPCHandlers { { ref: "_meetingSystemStreaming", secret: secrets[1], source: "system" }, ]; } else { - pairs = [{ ref: "_meetingMicStreaming", secret: await fetchRealtimeToken(event, options), source: "mic" }]; + pairs = [ + { + ref: "_meetingMicStreaming", + secret: await fetchRealtimeToken(event, options), + source: "mic", + }, + ]; } for (const { ref, source } of pairs) { diff --git a/src/helpers/kdeShortcut.js b/src/helpers/kdeShortcut.js index 9ebf01864..e1d511030 100644 --- a/src/helpers/kdeShortcut.js +++ b/src/helpers/kdeShortcut.js @@ -25,23 +25,75 @@ const QT_MODIFIERS = { }; const QT_KEYS = { - a: 0x41, b: 0x42, c: 0x43, d: 0x44, e: 0x45, f: 0x46, g: 0x47, h: 0x48, - i: 0x49, j: 0x4a, k: 0x4b, l: 0x4c, m: 0x4d, n: 0x4e, o: 0x4f, p: 0x50, - q: 0x51, r: 0x52, s: 0x53, t: 0x54, u: 0x55, v: 0x56, w: 0x57, x: 0x58, - y: 0x59, z: 0x5a, - 0: 0x30, 1: 0x31, 2: 0x32, 3: 0x33, 4: 0x34, - 5: 0x35, 6: 0x36, 7: 0x37, 8: 0x38, 9: 0x39, - f1: 0x01000030, f2: 0x01000031, f3: 0x01000032, f4: 0x01000033, - f5: 0x01000034, f6: 0x01000035, f7: 0x01000036, f8: 0x01000037, - f9: 0x01000038, f10: 0x01000039, f11: 0x0100003a, f12: 0x0100003b, - space: 0x20, tab: 0x01000001, escape: 0x01000000, backspace: 0x01000003, - enter: 0x01000005, return: 0x01000004, - insert: 0x01000006, delete: 0x01000007, - home: 0x01000010, end: 0x01000011, - pageup: 0x01000016, pagedown: 0x01000017, - up: 0x01000013, down: 0x01000015, left: 0x01000012, right: 0x01000014, - "`": 0x60, grave: 0x60, - print: 0x01000009, scrolllock: 0x01000026, pause: 0x01000008, + a: 0x41, + b: 0x42, + c: 0x43, + d: 0x44, + e: 0x45, + f: 0x46, + g: 0x47, + h: 0x48, + i: 0x49, + j: 0x4a, + k: 0x4b, + l: 0x4c, + m: 0x4d, + n: 0x4e, + o: 0x4f, + p: 0x50, + q: 0x51, + r: 0x52, + s: 0x53, + t: 0x54, + u: 0x55, + v: 0x56, + w: 0x57, + x: 0x58, + y: 0x59, + z: 0x5a, + 0: 0x30, + 1: 0x31, + 2: 0x32, + 3: 0x33, + 4: 0x34, + 5: 0x35, + 6: 0x36, + 7: 0x37, + 8: 0x38, + 9: 0x39, + f1: 0x01000030, + f2: 0x01000031, + f3: 0x01000032, + f4: 0x01000033, + f5: 0x01000034, + f6: 0x01000035, + f7: 0x01000036, + f8: 0x01000037, + f9: 0x01000038, + f10: 0x01000039, + f11: 0x0100003a, + f12: 0x0100003b, + space: 0x20, + tab: 0x01000001, + escape: 0x01000000, + backspace: 0x01000003, + enter: 0x01000005, + return: 0x01000004, + insert: 0x01000006, + delete: 0x01000007, + home: 0x01000010, + end: 0x01000011, + pageup: 0x01000016, + pagedown: 0x01000017, + up: 0x01000013, + down: 0x01000015, + left: 0x01000012, + right: 0x01000014, + "`": 0x60, + grave: 0x60, + print: 0x01000009, + scrolllock: 0x01000026, + pause: 0x01000008, }; const COMPONENT_NAME = "openwhispr"; @@ -92,10 +144,7 @@ class KDEShortcutManager { try { this.bus = dbusModule.sessionBus(); - const proxy = await this.bus.getProxyObject( - "org.kde.kglobalaccel", - "/kglobalaccel" - ); + const proxy = await this.bus.getProxyObject("org.kde.kglobalaccel", "/kglobalaccel"); this.kglobalaccel = proxy.getInterface("org.kde.KGlobalAccel"); debugLogger.log("[KDEShortcut] Connected to KGlobalAccel D-Bus"); @@ -113,10 +162,7 @@ class KDEShortcutManager { try { const componentPath = `/component/${COMPONENT_NAME}`; - const proxy = await this.bus.getProxyObject( - "org.kde.kglobalaccel", - componentPath - ); + const proxy = await this.bus.getProxyObject("org.kde.kglobalaccel", componentPath); const iface = proxy.getInterface("org.kde.kglobalaccel.Component"); iface.on("globalShortcutPressed", (componentUnique, shortcutUnique, timestamp) => { @@ -124,12 +170,15 @@ class KDEShortcutManager { // Tested on KDE Plasma 6 (Fedora 43): signal sends the actionFriendly // name ("OpenWhispr") not the actionUnique ("dictation"). The fallback // maps friendly names back to slot names. - const callback = this.callbacks.get(shortcutUnique) - || this._findCallbackByFriendlyName(shortcutUnique); + const callback = + this.callbacks.get(shortcutUnique) || this._findCallbackByFriendlyName(shortcutUnique); if (callback) { callback(); } else { - debugLogger.log("[KDEShortcut] No callback found for", { shortcutUnique, registered: [...this.callbacks.keys()] }); + debugLogger.log("[KDEShortcut] No callback found for", { + shortcutUnique, + registered: [...this.callbacks.keys()], + }); } }); @@ -168,12 +217,7 @@ class KDEShortcutManager { } // actionId: [componentUnique, componentFriendly, actionUnique, actionFriendly] - const actionId = [ - COMPONENT_NAME, - "OpenWhispr", - slotName, - `OpenWhispr ${slotName}`, - ]; + const actionId = [COMPONENT_NAME, "OpenWhispr", slotName, `OpenWhispr ${slotName}`]; try { // Register action then set shortcut @@ -186,7 +230,9 @@ class KDEShortcutManager { // Start listening for press events on the component const listening = await this._listenForComponent(); if (!listening) { - debugLogger.log(`[KDEShortcut] Keybinding registered but listener failed for "${slotName}"`); + debugLogger.log( + `[KDEShortcut] Keybinding registered but listener failed for "${slotName}"` + ); return false; } @@ -206,12 +252,7 @@ class KDEShortcutManager { async unregisterKeybinding(slotName = "dictation") { if (!this.kglobalaccel) return; - const actionId = [ - COMPONENT_NAME, - "OpenWhispr", - slotName, - `OpenWhispr ${slotName}`, - ]; + const actionId = [COMPONENT_NAME, "OpenWhispr", slotName, `OpenWhispr ${slotName}`]; try { await this.kglobalaccel.unRegister(actionId); @@ -237,7 +278,9 @@ class KDEShortcutManager { Promise.allSettled(promises).finally(() => { if (this.bus) { - try { this.bus.disconnect(); } catch {} + try { + this.bus.disconnect(); + } catch {} this.bus = null; this.kglobalaccel = null; this.componentProxy = null; diff --git a/src/helpers/meetingDetectionEngine.js b/src/helpers/meetingDetectionEngine.js index e6aec1398..69bcdf806 100644 --- a/src/helpers/meetingDetectionEngine.js +++ b/src/helpers/meetingDetectionEngine.js @@ -202,7 +202,11 @@ class MeetingDetectionEngine { } } catch (error) { this._meetingModeActive = false; - debugLogger.error("Error handling notification response", { error: error?.message, detectionId, action }, "meeting"); + debugLogger.error( + "Error handling notification response", + { error: error?.message, detectionId, action }, + "meeting" + ); } finally { this.windowManager.dismissMeetingNotification(); } diff --git a/src/hooks/usePermissions.ts b/src/hooks/usePermissions.ts index d1f18f38b..e17f36a2e 100644 --- a/src/hooks/usePermissions.ts +++ b/src/hooks/usePermissions.ts @@ -15,11 +15,10 @@ export interface UsePermissionsReturn { accessibilityTroubleshooting: boolean; requestMicPermission: () => Promise; - testAccessibilityPermission: () => Promise; + requestAccessibilityPermission: () => Promise; checkPasteToolsAvailability: () => Promise; openMicPrivacySettings: () => Promise; openSoundInputSettings: () => Promise; - openAccessibilitySettings: () => Promise; setMicPermissionGranted: (granted: boolean) => void; setAccessibilityPermissionGranted: (granted: boolean) => void; } @@ -128,7 +127,6 @@ export const usePermissions = ( const [isCheckingPasteTools, setIsCheckingPasteTools] = useState(false); const [accessibilityTroubleshooting, setAccessibilityTroubleshooting] = useState(false); const accessibilityPollCount = useRef(0); - const hasPromptedAccessibility = useRef(false); const openSystemSettings = useCallback( async ( @@ -171,19 +169,6 @@ export const usePermissions = ( [openSystemSettings] ); - const openAccessibilitySettings = useCallback(async () => { - // On first click, trigger the macOS system prompt to create a TCC entry - if (!hasPromptedAccessibility.current) { - hasPromptedAccessibility.current = true; - try { - await window.electronAPI?.promptAccessibilityPermission?.(); - } catch { - // Ignore — falls through to opening System Settings - } - } - await openSystemSettings("accessibility", window.electronAPI?.openAccessibilitySettings); - }, [openSystemSettings]); - const requestMicPermission = useCallback(async () => { if (!navigator?.mediaDevices || typeof navigator.mediaDevices.getUserMedia !== "function") { const message = t("hooks.permissions.micUnavailable"); @@ -254,6 +239,46 @@ export const usePermissions = ( } }, [setAccessibilityPermissionGranted]); + const requestAccessibilityPermission = useCallback(async () => { + const platform = getPlatform(); + + if (platform === "darwin") { + // Check if already granted + const alreadyGranted = await window.electronAPI?.checkAccessibilityPermission?.(true); + if (alreadyGranted) { + setAccessibilityPermissionGranted(true); + return; + } + + // Trigger the macOS TCC prompt (creates entry in System Settings) + try { + const granted = await window.electronAPI?.promptAccessibilityPermission?.(); + if (granted) { + setAccessibilityPermissionGranted(true); + return; + } + } catch { + // Falls through to opening System Settings + } + + // Open System Settings for manual toggle + await openSystemSettings("accessibility", window.electronAPI?.openAccessibilitySettings); + return; + } + + // On Windows, PowerShell SendKeys is always available + if (platform === "win32") { + setAccessibilityPermissionGranted(true); + return; + } + + // On Linux, auto-paste is optional — grant regardless of paste tool availability + if (platform === "linux") { + await checkPasteToolsAvailability(); + setAccessibilityPermissionGranted(true); + } + }, [openSystemSettings, checkPasteToolsAvailability, setAccessibilityPermissionGranted]); + // Check paste tools on mount useEffect(() => { checkPasteToolsAvailability(); @@ -305,97 +330,6 @@ export const usePermissions = ( return () => clearInterval(interval); }, [accessibilityPermissionGranted, setAccessibilityPermissionGranted]); - const testAccessibilityPermission = useCallback(async () => { - const platform = getPlatform(); - - // On macOS, actually test the accessibility permission - if (platform === "darwin") { - try { - await window.electronAPI.pasteText(t("hooks.permissions.accessibilityTestText")); - setAccessibilityPermissionGranted(true); - } catch (err) { - logger.error("Accessibility permission test failed:", err); - if (showAlertDialog) { - showAlertDialog({ - title: t("hooks.permissions.titles.accessibilityNeeded"), - description: t("hooks.permissions.descriptions.accessibilityNeeded"), - }); - } else { - alert(t("hooks.permissions.alerts.accessibilityNeeded")); - } - } - return; - } - - // On Windows, PowerShell SendKeys is always available - if (platform === "win32") { - setAccessibilityPermissionGranted(true); - if (showAlertDialog) { - showAlertDialog({ - title: t("hooks.permissions.titles.readyToGo"), - description: t("hooks.permissions.descriptions.windowsReady"), - }); - } - return; - } - - // On Linux, check if paste tools are available - if (platform === "linux") { - const result = await checkPasteToolsAvailability(); - - if (result?.available) { - setAccessibilityPermissionGranted(true); - if (showAlertDialog) { - const method = result.method || t("hooks.permissions.labels.defaultPasteTool"); - const methodLabel = - result.isWayland && method === "xdotool" - ? t("hooks.permissions.labels.xdotoolXwayland") - : method; - showAlertDialog({ - title: t("hooks.permissions.titles.readyToGo"), - description: t("hooks.permissions.descriptions.linuxReadyWithMethod", { - method: methodLabel, - }), - }); - } - } else { - // Don't block, but inform the user - const isWayland = result?.isWayland; - const xwaylandAvailable = result?.xwaylandAvailable; - const recommendedTool = result?.recommendedInstall; - const installCmd = - recommendedTool === "wtype" - ? "sudo dnf install wtype # Fedora\nsudo apt install wtype # Debian/Ubuntu" - : "sudo apt install xdotool # Debian/Ubuntu/Mint\nsudo dnf install xdotool # Fedora"; - - if (showAlertDialog) { - if (isWayland && !xwaylandAvailable && !recommendedTool) { - showAlertDialog({ - title: t("hooks.permissions.titles.waylandClipboardMode"), - description: t("hooks.permissions.descriptions.waylandClipboardMode"), - }); - } else { - const waylandNote = isWayland - ? recommendedTool === "wtype" - ? t("hooks.permissions.notes.waylandWtype") - : t("hooks.permissions.notes.waylandXwaylandOnly") - : ""; - showAlertDialog({ - title: t("hooks.permissions.titles.optionalPasteTool"), - description: t("hooks.permissions.descriptions.optionalPasteTool", { - tool: recommendedTool || t("hooks.permissions.labels.defaultPasteTool"), - installCmd, - waylandNote, - }), - }); - } - } - // Still allow proceeding - this is optional - setAccessibilityPermissionGranted(true); - } - } - }, [showAlertDialog, checkPasteToolsAvailability, setAccessibilityPermissionGranted, t]); - return { micPermissionGranted, accessibilityPermissionGranted, @@ -404,11 +338,10 @@ export const usePermissions = ( isCheckingPasteTools, accessibilityTroubleshooting, requestMicPermission, - testAccessibilityPermission, + requestAccessibilityPermission, checkPasteToolsAvailability, openMicPrivacySettings, openSoundInputSettings, - openAccessibilitySettings, setMicPermissionGranted, setAccessibilityPermissionGranted, }; diff --git a/src/locales/de/translation.json b/src/locales/de/translation.json index 332515e4b..9134726f4 100644 --- a/src/locales/de/translation.json +++ b/src/locales/de/translation.json @@ -157,8 +157,9 @@ "microphoneTitle": "Mikrofon", "microphoneDescription": "Nimmt Ihre Stimme für die Transkription auf", "grant": "Erteilen", + "grantAccess": "Zugriff gewähren", "accessibilityTitle": "Bedienungshilfen", - "accessibilityDescription": "In Systemeinstellungen aktivieren", + "accessibilityDescription": "Ermöglicht das automatische Einfügen transkribierter Texte in jede App", "testAndGrant": "Testen & erteilen", "requiredForApp": "Erforderlich, damit OpenWhispr funktioniert", "microphoneRequired": "Mikrofonzugriff erforderlich", @@ -1335,6 +1336,7 @@ "optional": "Optional", "systemAudioTitle": "Systemaudio", "systemAudioDescription": "Nimmt den Ton anderer Teilnehmer aus Anrufen und Meetings auf", + "grantAccess": "Zugriff gewähren", "test": "Testen", "testAndGrant": "Testen & erteilen", "title": "Berechtigungen", diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index f87322023..ec47cd742 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -157,8 +157,9 @@ "microphoneTitle": "Microphone", "microphoneDescription": "Captures your voice for transcription", "grant": "Grant", + "grantAccess": "Grant Access", "accessibilityTitle": "Accessibility", - "accessibilityDescription": "Enable in System Settings by toggling it on", + "accessibilityDescription": "Allows auto-pasting transcribed text into any app", "testAndGrant": "Test & Grant", "requiredForApp": "Required for OpenWhispr to work", "microphoneRequired": "Microphone access required", @@ -1383,6 +1384,7 @@ "optional": "Optional", "systemAudioTitle": "System Audio", "systemAudioDescription": "Captures other participants' audio from calls and meetings", + "grantAccess": "Grant Access", "test": "Test", "testAndGrant": "Test & Grant", "title": "Permissions", diff --git a/src/locales/es/translation.json b/src/locales/es/translation.json index baeee075b..91f60ea1c 100644 --- a/src/locales/es/translation.json +++ b/src/locales/es/translation.json @@ -157,8 +157,9 @@ "microphoneTitle": "Micrófono", "microphoneDescription": "Captura tu voz para la transcripción", "grant": "Conceder", + "grantAccess": "Conceder acceso", "accessibilityTitle": "Accesibilidad", - "accessibilityDescription": "Actívalo en Ajustes del Sistema", + "accessibilityDescription": "Permite pegar automáticamente el texto transcrito en cualquier aplicación", "testAndGrant": "Probar y conceder", "requiredForApp": "Necesario para que OpenWhispr funcione", "microphoneRequired": "Se requiere acceso al micrófono", @@ -1335,6 +1336,7 @@ "optional": "Opcional", "systemAudioTitle": "Audio del Sistema", "systemAudioDescription": "Captura el audio de otros participantes en llamadas y reuniones", + "grantAccess": "Conceder acceso", "test": "Probar", "testAndGrant": "Probar y conceder", "title": "Permisos", diff --git a/src/locales/fr/translation.json b/src/locales/fr/translation.json index de02ec63c..36c6f02db 100644 --- a/src/locales/fr/translation.json +++ b/src/locales/fr/translation.json @@ -157,8 +157,9 @@ "microphoneTitle": "Microphone", "microphoneDescription": "Capture votre voix pour la transcription", "grant": "Autoriser", + "grantAccess": "Accorder l'accès", "accessibilityTitle": "Accessibilité", - "accessibilityDescription": "Activez dans les Réglages Système", + "accessibilityDescription": "Permet de coller automatiquement le texte transcrit dans n'importe quelle application", "testAndGrant": "Tester et autoriser", "requiredForApp": "Nécessaire pour le bon fonctionnement d'OpenWhispr", "microphoneRequired": "Accès microphone requis", @@ -1335,6 +1336,7 @@ "optional": "Facultatif", "systemAudioTitle": "Audio Système", "systemAudioDescription": "Capture l'audio des autres participants lors d'appels et de réunions", + "grantAccess": "Accorder l'accès", "test": "Tester", "testAndGrant": "Tester et autoriser", "title": "Permissions", diff --git a/src/locales/it/translation.json b/src/locales/it/translation.json index 96f3e6b79..9b0d20064 100644 --- a/src/locales/it/translation.json +++ b/src/locales/it/translation.json @@ -157,8 +157,9 @@ "microphoneTitle": "Microfono", "microphoneDescription": "Cattura la tua voce per la trascrizione", "grant": "Concedi", + "grantAccess": "Concedi accesso", "accessibilityTitle": "Accessibilità", - "accessibilityDescription": "Attiva nelle Impostazioni di Sistema", + "accessibilityDescription": "Permette di incollare automaticamente il testo trascritto in qualsiasi app", "testAndGrant": "Testa e concedi", "requiredForApp": "Necessario per usare OpenWhispr", "microphoneRequired": "Accesso al microfono richiesto", @@ -1335,6 +1336,7 @@ "optional": "Facoltativo", "systemAudioTitle": "Audio di Sistema", "systemAudioDescription": "Cattura l'audio degli altri partecipanti durante chiamate e riunioni", + "grantAccess": "Concedi accesso", "test": "Test", "testAndGrant": "Testa e concedi", "title": "Permessi", diff --git a/src/locales/ja/translation.json b/src/locales/ja/translation.json index abe86df3c..e05ed8811 100644 --- a/src/locales/ja/translation.json +++ b/src/locales/ja/translation.json @@ -157,8 +157,9 @@ "microphoneTitle": "マイク", "microphoneDescription": "文字起こしのためにあなたの声を録音します", "grant": "許可", + "grantAccess": "アクセスを許可", "accessibilityTitle": "アクセシビリティ", - "accessibilityDescription": "システム設定でオンに切り替えてください", + "accessibilityDescription": "文字起こしテキストを任意のアプリに自動貼り付けします", "testAndGrant": "テストして許可", "requiredForApp": "OpenWhispr の動作に必要です", "microphoneRequired": "マイクへのアクセスが必要です", @@ -1335,6 +1336,7 @@ "optional": "任意", "systemAudioTitle": "システムオーディオ", "systemAudioDescription": "通話や会議で他の参加者の音声を録音します", + "grantAccess": "アクセスを許可", "test": "テスト", "testAndGrant": "テストして許可", "title": "権限", diff --git a/src/locales/pt/translation.json b/src/locales/pt/translation.json index 14bca2e7a..f032757a0 100644 --- a/src/locales/pt/translation.json +++ b/src/locales/pt/translation.json @@ -129,8 +129,9 @@ "microphoneTitle": "Microfone", "microphoneDescription": "Captura a sua voz para transcrição", "grant": "Conceder", + "grantAccess": "Conceder acesso", "accessibilityTitle": "Acessibilidade", - "accessibilityDescription": "Ative nas Configurações do Sistema", + "accessibilityDescription": "Permite colar automaticamente o texto transcrito em qualquer aplicativo", "testAndGrant": "Testar e conceder", "requiredForApp": "Necessário para o OpenWhispr funcionar", "microphoneRequired": "Acesso ao microfone necessário", @@ -1307,6 +1308,7 @@ "optional": "Opcional", "systemAudioTitle": "Áudio do Sistema", "systemAudioDescription": "Captura o áudio dos outros participantes em chamadas e reuniões", + "grantAccess": "Conceder acesso", "test": "Testar", "testAndGrant": "Testar e conceder", "title": "Permissões", diff --git a/src/locales/ru/translation.json b/src/locales/ru/translation.json index 167b1c81a..7d11631f9 100644 --- a/src/locales/ru/translation.json +++ b/src/locales/ru/translation.json @@ -157,8 +157,9 @@ "microphoneTitle": "Микрофон", "microphoneDescription": "Записывает ваш голос для транскрипции", "grant": "Предоставить", + "grantAccess": "Предоставить доступ", "accessibilityTitle": "Универсальный доступ", - "accessibilityDescription": "Включите в Системных настройках", + "accessibilityDescription": "Позволяет автоматически вставлять транскрибированный текст в любое приложение", "testAndGrant": "Проверить и предоставить", "requiredForApp": "Необходимо для работы OpenWhispr", "microphoneRequired": "Требуется доступ к микрофону", @@ -1335,6 +1336,7 @@ "optional": "Необязательно", "systemAudioTitle": "Системное аудио", "systemAudioDescription": "Записывает аудио других участников звонков и совещаний", + "grantAccess": "Предоставить доступ", "test": "Тест", "testAndGrant": "Проверить и предоставить", "title": "Разрешения", diff --git a/src/locales/zh-CN/translation.json b/src/locales/zh-CN/translation.json index 0115def16..188a5c885 100644 --- a/src/locales/zh-CN/translation.json +++ b/src/locales/zh-CN/translation.json @@ -157,8 +157,9 @@ "microphoneTitle": "麦克风", "microphoneDescription": "录制您的声音用于转录", "grant": "授权", + "grantAccess": "授予访问权限", "accessibilityTitle": "辅助功能", - "accessibilityDescription": "在系统设置中开启此权限", + "accessibilityDescription": "允许将转录文本自动粘贴到任何应用程序", "testAndGrant": "测试并授权", "requiredForApp": "OpenWhispr 运行所必需的权限", "microphoneRequired": "需要麦克风访问权限", @@ -1335,6 +1336,7 @@ "optional": "可选", "systemAudioTitle": "系统音频", "systemAudioDescription": "录制通话和会议中其他参与者的音频", + "grantAccess": "授予访问权限", "test": "测试", "testAndGrant": "测试并授权", "title": "权限", diff --git a/src/locales/zh-TW/translation.json b/src/locales/zh-TW/translation.json index 0447dd8b8..9fe0164ed 100644 --- a/src/locales/zh-TW/translation.json +++ b/src/locales/zh-TW/translation.json @@ -157,8 +157,9 @@ "microphoneTitle": "麥克風", "microphoneDescription": "錄製您的聲音用於轉錄", "grant": "授權", + "grantAccess": "授予存取權限", "accessibilityTitle": "輔助使用", - "accessibilityDescription": "在系統設定中開啟此權限", + "accessibilityDescription": "允許將轉錄文字自動貼上到任何應用程式", "testAndGrant": "測試並授權", "requiredForApp": "OpenWhispr 運作所需", "microphoneRequired": "需要麥克風存取權限", @@ -1335,6 +1336,7 @@ "optional": "可選", "systemAudioTitle": "系統音訊", "systemAudioDescription": "錄製通話和會議中其他參與者的音訊", + "grantAccess": "授予存取權限", "test": "測試", "testAndGrant": "測試並授權", "title": "權限",