diff --git a/surfsense_desktop/src/modules/keyboard.ts b/surfsense_desktop/src/modules/keyboard.ts new file mode 100644 index 000000000..1fca34b79 --- /dev/null +++ b/surfsense_desktop/src/modules/keyboard.ts @@ -0,0 +1,33 @@ +import { execSync } from 'child_process'; +import { systemPreferences } from 'electron'; + +export function getFrontmostApp(): string { + try { + if (process.platform === 'darwin') { + return execSync( + 'osascript -e \'tell application "System Events" to get name of first application process whose frontmost is true\'' + ).toString().trim(); + } + if (process.platform === 'win32') { + return execSync( + 'powershell -command "Add-Type \'using System; using System.Runtime.InteropServices; public class W { [DllImport(\\\"user32.dll\\\")] public static extern IntPtr GetForegroundWindow(); }\'; (Get-Process | Where-Object { $_.MainWindowHandle -eq [W]::GetForegroundWindow() }).ProcessName"' + ).toString().trim(); + } + } catch { + return ''; + } + return ''; +} + +export function simulatePaste(): void { + if (process.platform === 'darwin') { + execSync('osascript -e \'tell application "System Events" to keystroke "v" using command down\''); + } else if (process.platform === 'win32') { + execSync('powershell -command "Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.SendKeys]::SendWait(\'^v\')"'); + } +} + +export function checkAccessibilityPermission(): boolean { + if (process.platform !== 'darwin') return true; + return systemPreferences.isTrustedAccessibilityClient(true); +} diff --git a/surfsense_desktop/src/modules/quick-ask.ts b/surfsense_desktop/src/modules/quick-ask.ts index 1f1d7f313..b3aa10e3a 100644 --- a/surfsense_desktop/src/modules/quick-ask.ts +++ b/surfsense_desktop/src/modules/quick-ask.ts @@ -1,7 +1,7 @@ -import { BrowserWindow, clipboard, globalShortcut, ipcMain, screen, shell, systemPreferences } from 'electron'; -import { execSync } from 'child_process'; +import { BrowserWindow, clipboard, globalShortcut, ipcMain, screen, shell } from 'electron'; import path from 'path'; import { IPC_CHANNELS } from '../ipc/channels'; +import { checkAccessibilityPermission, getFrontmostApp, simulatePaste } from './keyboard'; import { getServerPort } from './server'; const SHORTCUT = 'CommandOrControl+Option+S'; @@ -10,17 +10,6 @@ let pendingText = ''; let sourceApp = ''; let savedClipboard = ''; -function getFrontmostApp(): string { - if (process.platform !== 'darwin') return ''; - try { - return execSync( - 'osascript -e \'tell application "System Events" to get name of first application process whose frontmost is true\'' - ).toString().trim(); - } catch { - return ''; - } -} - function destroyQuickAsk(): void { if (quickAskWindow && !quickAskWindow.isDestroyed()) { quickAskWindow.close(); @@ -116,16 +105,16 @@ export function registerQuickAsk(): void { }); ipcMain.handle(IPC_CHANNELS.REPLACE_TEXT, async (_event, text: string) => { - if (process.platform !== 'darwin' || !sourceApp) return; + if (!sourceApp) return; - if (!systemPreferences.isTrustedAccessibilityClient(true)) return; + if (!checkAccessibilityPermission()) return; clipboard.writeText(text); destroyQuickAsk(); try { await new Promise((r) => setTimeout(r, 50)); - execSync('osascript -e \'tell application "System Events" to keystroke "v" using command down\''); + simulatePaste(); await new Promise((r) => setTimeout(r, 100)); clipboard.writeText(savedClipboard); } catch {