mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-04-25 00:36:31 +02:00
141 lines
3.7 KiB
TypeScript
141 lines
3.7 KiB
TypeScript
import { BrowserWindow, clipboard, globalShortcut, ipcMain, screen, shell } from 'electron';
|
|
import path from 'path';
|
|
import { IPC_CHANNELS } from '../ipc/channels';
|
|
import { checkAccessibilityPermission, getFrontmostApp, simulatePaste } from './platform';
|
|
import { getServerPort } from './server';
|
|
|
|
const SHORTCUT = 'CommandOrControl+Option+S';
|
|
let quickAskWindow: BrowserWindow | null = null;
|
|
let pendingText = '';
|
|
let pendingMode = '';
|
|
let sourceApp = '';
|
|
let savedClipboard = '';
|
|
|
|
function destroyQuickAsk(): void {
|
|
if (quickAskWindow && !quickAskWindow.isDestroyed()) {
|
|
quickAskWindow.close();
|
|
}
|
|
quickAskWindow = null;
|
|
pendingMode = '';
|
|
}
|
|
|
|
function clampToScreen(x: number, y: number, w: number, h: number): { x: number; y: number } {
|
|
const display = screen.getDisplayNearestPoint({ x, y });
|
|
const { x: dx, y: dy, width: dw, height: dh } = display.workArea;
|
|
return {
|
|
x: Math.max(dx, Math.min(x, dx + dw - w)),
|
|
y: Math.max(dy, Math.min(y, dy + dh - h)),
|
|
};
|
|
}
|
|
|
|
function createQuickAskWindow(x: number, y: number): BrowserWindow {
|
|
destroyQuickAsk();
|
|
|
|
quickAskWindow = new BrowserWindow({
|
|
width: 450,
|
|
height: 750,
|
|
x,
|
|
y,
|
|
...(process.platform === 'darwin'
|
|
? { type: 'panel' as const }
|
|
: { type: 'toolbar' as const, alwaysOnTop: true }),
|
|
resizable: true,
|
|
fullscreenable: false,
|
|
maximizable: false,
|
|
webPreferences: {
|
|
preload: path.join(__dirname, 'preload.js'),
|
|
contextIsolation: true,
|
|
nodeIntegration: false,
|
|
sandbox: true,
|
|
},
|
|
show: false,
|
|
skipTaskbar: true,
|
|
});
|
|
|
|
quickAskWindow.loadURL(`http://localhost:${getServerPort()}/dashboard`);
|
|
|
|
quickAskWindow.once('ready-to-show', () => {
|
|
quickAskWindow?.show();
|
|
});
|
|
|
|
quickAskWindow.webContents.on('before-input-event', (_event, input) => {
|
|
if (input.key === 'Escape') destroyQuickAsk();
|
|
});
|
|
|
|
quickAskWindow.webContents.setWindowOpenHandler(({ url }) => {
|
|
if (url.startsWith('http://localhost')) {
|
|
return { action: 'allow' };
|
|
}
|
|
shell.openExternal(url);
|
|
return { action: 'deny' };
|
|
});
|
|
|
|
quickAskWindow.on('closed', () => {
|
|
quickAskWindow = null;
|
|
});
|
|
|
|
return quickAskWindow;
|
|
}
|
|
|
|
export function registerQuickAsk(): void {
|
|
const ok = globalShortcut.register(SHORTCUT, () => {
|
|
if (quickAskWindow && !quickAskWindow.isDestroyed()) {
|
|
destroyQuickAsk();
|
|
return;
|
|
}
|
|
|
|
sourceApp = getFrontmostApp();
|
|
savedClipboard = clipboard.readText();
|
|
|
|
const text = savedClipboard.trim();
|
|
if (!text) return;
|
|
|
|
pendingText = text;
|
|
const cursor = screen.getCursorScreenPoint();
|
|
const pos = clampToScreen(cursor.x, cursor.y, 450, 750);
|
|
createQuickAskWindow(pos.x, pos.y);
|
|
});
|
|
|
|
if (!ok) {
|
|
console.log(`Quick-ask: failed to register ${SHORTCUT}`);
|
|
}
|
|
|
|
ipcMain.handle(IPC_CHANNELS.QUICK_ASK_TEXT, () => {
|
|
const text = pendingText;
|
|
pendingText = '';
|
|
return text;
|
|
});
|
|
|
|
ipcMain.handle(IPC_CHANNELS.SET_QUICK_ASK_MODE, (_event, mode: string) => {
|
|
pendingMode = mode;
|
|
});
|
|
|
|
ipcMain.handle(IPC_CHANNELS.GET_QUICK_ASK_MODE, (event) => {
|
|
if (quickAskWindow && !quickAskWindow.isDestroyed() && event.sender.id === quickAskWindow.webContents.id) {
|
|
return pendingMode;
|
|
}
|
|
return '';
|
|
});
|
|
|
|
ipcMain.handle(IPC_CHANNELS.REPLACE_TEXT, async (_event, text: string) => {
|
|
if (!sourceApp) return;
|
|
|
|
if (!checkAccessibilityPermission()) return;
|
|
|
|
clipboard.writeText(text);
|
|
destroyQuickAsk();
|
|
|
|
try {
|
|
await new Promise((r) => setTimeout(r, 50));
|
|
simulatePaste();
|
|
await new Promise((r) => setTimeout(r, 100));
|
|
clipboard.writeText(savedClipboard);
|
|
} catch {
|
|
clipboard.writeText(savedClipboard);
|
|
}
|
|
});
|
|
}
|
|
|
|
export function unregisterQuickAsk(): void {
|
|
globalShortcut.unregister(SHORTCUT);
|
|
}
|