Build and Release / build (x64, ubuntu-latest, linux) (push) Has been skipped
Build and Release / build (arm64, macos-latest, darwin) (push) Has been cancelled
Build and Release / build (x64, macos-latest, darwin) (push) Has been cancelled
Build and Release / build (x64, windows-latest, win32) (push) Has been cancelled
Build and Release / release (push) Has been cancelled
726 lines
28 KiB
JavaScript
726 lines
28 KiB
JavaScript
const { BrowserWindow, globalShortcut, ipcMain, screen } = require('electron');
|
|
const path = require('node:path');
|
|
const fs = require('node:fs');
|
|
const os = require('os');
|
|
const storage = require('../storage');
|
|
|
|
let mouseEventsIgnored = false;
|
|
let windowResizing = false;
|
|
let resizeAnimation = null;
|
|
const RESIZE_ANIMATION_DURATION = 500; // milliseconds
|
|
|
|
function createWindow(sendToRenderer, geminiSessionRef) {
|
|
// Get layout preference (default to 'normal')
|
|
let windowWidth = 1100;
|
|
let windowHeight = 800;
|
|
|
|
const mainWindow = new BrowserWindow({
|
|
width: windowWidth,
|
|
height: windowHeight,
|
|
frame: false,
|
|
transparent: true,
|
|
hasShadow: false,
|
|
alwaysOnTop: true,
|
|
webPreferences: {
|
|
nodeIntegration: true,
|
|
contextIsolation: false, // TODO: change to true
|
|
backgroundThrottling: false,
|
|
enableBlinkFeatures: 'GetDisplayMedia',
|
|
webSecurity: true,
|
|
allowRunningInsecureContent: false,
|
|
},
|
|
backgroundColor: '#00000000',
|
|
});
|
|
|
|
const { session, desktopCapturer } = require('electron');
|
|
session.defaultSession.setDisplayMediaRequestHandler(
|
|
(request, callback) => {
|
|
desktopCapturer.getSources({ types: ['screen'] }).then(sources => {
|
|
callback({ video: sources[0], audio: 'loopback' });
|
|
});
|
|
},
|
|
{ useSystemPicker: true }
|
|
);
|
|
|
|
mainWindow.setResizable(false);
|
|
mainWindow.setContentProtection(true);
|
|
mainWindow.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true });
|
|
|
|
// Hide from Windows taskbar
|
|
if (process.platform === 'win32') {
|
|
try {
|
|
mainWindow.setSkipTaskbar(true);
|
|
console.log('Hidden from Windows taskbar');
|
|
} catch (error) {
|
|
console.warn('Could not hide from taskbar:', error.message);
|
|
}
|
|
}
|
|
|
|
// Hide from Mission Control on macOS
|
|
if (process.platform === 'darwin') {
|
|
try {
|
|
mainWindow.setHiddenInMissionControl(true);
|
|
console.log('Hidden from macOS Mission Control');
|
|
} catch (error) {
|
|
console.warn('Could not hide from Mission Control:', error.message);
|
|
}
|
|
}
|
|
|
|
// Center window at the top of the screen
|
|
const primaryDisplay = screen.getPrimaryDisplay();
|
|
const { width: screenWidth } = primaryDisplay.workAreaSize;
|
|
const x = Math.floor((screenWidth - windowWidth) / 2);
|
|
const y = 0;
|
|
mainWindow.setPosition(x, y);
|
|
|
|
if (process.platform === 'win32') {
|
|
mainWindow.setAlwaysOnTop(true, 'screen-saver', 1);
|
|
}
|
|
|
|
mainWindow.loadFile(path.join(__dirname, '../index.html'));
|
|
|
|
// After window is created, initialize keybinds
|
|
mainWindow.webContents.once('dom-ready', () => {
|
|
setTimeout(() => {
|
|
const defaultKeybinds = getDefaultKeybinds();
|
|
let keybinds = defaultKeybinds;
|
|
|
|
// Load keybinds from storage
|
|
const savedKeybinds = storage.getKeybinds();
|
|
if (savedKeybinds) {
|
|
keybinds = { ...defaultKeybinds, ...savedKeybinds };
|
|
}
|
|
|
|
updateGlobalShortcuts(keybinds, mainWindow, sendToRenderer, geminiSessionRef);
|
|
}, 150);
|
|
});
|
|
|
|
setupWindowIpcHandlers(mainWindow, sendToRenderer, geminiSessionRef);
|
|
|
|
return mainWindow;
|
|
}
|
|
|
|
function getDefaultKeybinds() {
|
|
const isMac = process.platform === 'darwin';
|
|
return {
|
|
moveUp: isMac ? 'Alt+Up' : 'Ctrl+Up',
|
|
moveDown: isMac ? 'Alt+Down' : 'Ctrl+Down',
|
|
moveLeft: isMac ? 'Alt+Left' : 'Ctrl+Left',
|
|
moveRight: isMac ? 'Alt+Right' : 'Ctrl+Right',
|
|
toggleVisibility: isMac ? 'Cmd+\\' : 'Ctrl+\\',
|
|
toggleClickThrough: isMac ? 'Cmd+M' : 'Ctrl+M',
|
|
nextStep: isMac ? 'Cmd+Enter' : 'Ctrl+Enter',
|
|
previousResponse: isMac ? 'Cmd+[' : 'Ctrl+[',
|
|
nextResponse: isMac ? 'Cmd+]' : 'Ctrl+]',
|
|
scrollUp: isMac ? 'Cmd+Shift+Up' : 'Ctrl+Shift+Up',
|
|
scrollDown: isMac ? 'Cmd+Shift+Down' : 'Ctrl+Shift+Down',
|
|
emergencyErase: isMac ? 'Cmd+Shift+E' : 'Ctrl+Shift+E',
|
|
};
|
|
}
|
|
|
|
function updateGlobalShortcuts(keybinds, mainWindow, sendToRenderer, geminiSessionRef) {
|
|
console.log('Updating global shortcuts with:', keybinds);
|
|
|
|
// Unregister all existing shortcuts
|
|
globalShortcut.unregisterAll();
|
|
|
|
const primaryDisplay = screen.getPrimaryDisplay();
|
|
const { width, height } = primaryDisplay.workAreaSize;
|
|
const moveIncrement = Math.floor(Math.min(width, height) * 0.1);
|
|
|
|
// Register window movement shortcuts
|
|
const movementActions = {
|
|
moveUp: () => {
|
|
if (!mainWindow.isVisible()) return;
|
|
const [currentX, currentY] = mainWindow.getPosition();
|
|
mainWindow.setPosition(currentX, currentY - moveIncrement);
|
|
},
|
|
moveDown: () => {
|
|
if (!mainWindow.isVisible()) return;
|
|
const [currentX, currentY] = mainWindow.getPosition();
|
|
mainWindow.setPosition(currentX, currentY + moveIncrement);
|
|
},
|
|
moveLeft: () => {
|
|
if (!mainWindow.isVisible()) return;
|
|
const [currentX, currentY] = mainWindow.getPosition();
|
|
mainWindow.setPosition(currentX - moveIncrement, currentY);
|
|
},
|
|
moveRight: () => {
|
|
if (!mainWindow.isVisible()) return;
|
|
const [currentX, currentY] = mainWindow.getPosition();
|
|
mainWindow.setPosition(currentX + moveIncrement, currentY);
|
|
},
|
|
};
|
|
|
|
// Register each movement shortcut
|
|
Object.keys(movementActions).forEach(action => {
|
|
const keybind = keybinds[action];
|
|
if (keybind) {
|
|
try {
|
|
globalShortcut.register(keybind, movementActions[action]);
|
|
console.log(`Registered ${action}: ${keybind}`);
|
|
} catch (error) {
|
|
console.error(`Failed to register ${action} (${keybind}):`, error);
|
|
}
|
|
}
|
|
});
|
|
|
|
// Register toggle visibility shortcut
|
|
if (keybinds.toggleVisibility) {
|
|
try {
|
|
globalShortcut.register(keybinds.toggleVisibility, () => {
|
|
if (mainWindow.isVisible()) {
|
|
mainWindow.hide();
|
|
} else {
|
|
mainWindow.showInactive();
|
|
}
|
|
});
|
|
console.log(`Registered toggleVisibility: ${keybinds.toggleVisibility}`);
|
|
} catch (error) {
|
|
console.error(`Failed to register toggleVisibility (${keybinds.toggleVisibility}):`, error);
|
|
}
|
|
}
|
|
|
|
// Register toggle click-through shortcut
|
|
if (keybinds.toggleClickThrough) {
|
|
try {
|
|
globalShortcut.register(keybinds.toggleClickThrough, () => {
|
|
mouseEventsIgnored = !mouseEventsIgnored;
|
|
if (mouseEventsIgnored) {
|
|
mainWindow.setIgnoreMouseEvents(true, { forward: true });
|
|
console.log('Mouse events ignored');
|
|
} else {
|
|
mainWindow.setIgnoreMouseEvents(false);
|
|
console.log('Mouse events enabled');
|
|
}
|
|
mainWindow.webContents.send('click-through-toggled', mouseEventsIgnored);
|
|
});
|
|
console.log(`Registered toggleClickThrough: ${keybinds.toggleClickThrough}`);
|
|
} catch (error) {
|
|
console.error(`Failed to register toggleClickThrough (${keybinds.toggleClickThrough}):`, error);
|
|
}
|
|
}
|
|
|
|
// Register next step shortcut (either starts session or takes screenshot based on view)
|
|
if (keybinds.nextStep) {
|
|
try {
|
|
globalShortcut.register(keybinds.nextStep, async () => {
|
|
console.log('Next step shortcut triggered');
|
|
try {
|
|
// Determine the shortcut key format
|
|
const isMac = process.platform === 'darwin';
|
|
const shortcutKey = isMac ? 'cmd+enter' : 'ctrl+enter';
|
|
|
|
// Use the new handleShortcut function
|
|
mainWindow.webContents.executeJavaScript(`
|
|
cheatingDaddy.handleShortcut('${shortcutKey}');
|
|
`);
|
|
} catch (error) {
|
|
console.error('Error handling next step shortcut:', error);
|
|
}
|
|
});
|
|
console.log(`Registered nextStep: ${keybinds.nextStep}`);
|
|
} catch (error) {
|
|
console.error(`Failed to register nextStep (${keybinds.nextStep}):`, error);
|
|
}
|
|
}
|
|
|
|
// Register previous response shortcut
|
|
if (keybinds.previousResponse) {
|
|
try {
|
|
globalShortcut.register(keybinds.previousResponse, () => {
|
|
console.log('Previous response shortcut triggered');
|
|
sendToRenderer('navigate-previous-response');
|
|
});
|
|
console.log(`Registered previousResponse: ${keybinds.previousResponse}`);
|
|
} catch (error) {
|
|
console.error(`Failed to register previousResponse (${keybinds.previousResponse}):`, error);
|
|
}
|
|
}
|
|
|
|
// Register next response shortcut
|
|
if (keybinds.nextResponse) {
|
|
try {
|
|
globalShortcut.register(keybinds.nextResponse, () => {
|
|
console.log('Next response shortcut triggered');
|
|
sendToRenderer('navigate-next-response');
|
|
});
|
|
console.log(`Registered nextResponse: ${keybinds.nextResponse}`);
|
|
} catch (error) {
|
|
console.error(`Failed to register nextResponse (${keybinds.nextResponse}):`, error);
|
|
}
|
|
}
|
|
|
|
// Register scroll up shortcut
|
|
if (keybinds.scrollUp) {
|
|
try {
|
|
globalShortcut.register(keybinds.scrollUp, () => {
|
|
console.log('Scroll up shortcut triggered');
|
|
sendToRenderer('scroll-response-up');
|
|
});
|
|
console.log(`Registered scrollUp: ${keybinds.scrollUp}`);
|
|
} catch (error) {
|
|
console.error(`Failed to register scrollUp (${keybinds.scrollUp}):`, error);
|
|
}
|
|
}
|
|
|
|
// Register scroll down shortcut
|
|
if (keybinds.scrollDown) {
|
|
try {
|
|
globalShortcut.register(keybinds.scrollDown, () => {
|
|
console.log('Scroll down shortcut triggered');
|
|
sendToRenderer('scroll-response-down');
|
|
});
|
|
console.log(`Registered scrollDown: ${keybinds.scrollDown}`);
|
|
} catch (error) {
|
|
console.error(`Failed to register scrollDown (${keybinds.scrollDown}):`, error);
|
|
}
|
|
}
|
|
|
|
// Register emergency erase shortcut
|
|
if (keybinds.emergencyErase) {
|
|
try {
|
|
globalShortcut.register(keybinds.emergencyErase, () => {
|
|
console.log('Emergency Erase triggered!');
|
|
if (mainWindow && !mainWindow.isDestroyed()) {
|
|
mainWindow.hide();
|
|
|
|
if (geminiSessionRef.current) {
|
|
geminiSessionRef.current.close();
|
|
geminiSessionRef.current = null;
|
|
}
|
|
|
|
sendToRenderer('clear-sensitive-data');
|
|
|
|
setTimeout(() => {
|
|
const { app } = require('electron');
|
|
app.quit();
|
|
}, 300);
|
|
}
|
|
});
|
|
console.log(`Registered emergencyErase: ${keybinds.emergencyErase}`);
|
|
} catch (error) {
|
|
console.error(`Failed to register emergencyErase (${keybinds.emergencyErase}):`, error);
|
|
}
|
|
}
|
|
}
|
|
|
|
function setupWindowIpcHandlers(mainWindow, sendToRenderer, geminiSessionRef) {
|
|
ipcMain.on('view-changed', (event, view) => {
|
|
if (view !== 'assistant' && !mainWindow.isDestroyed()) {
|
|
mainWindow.setIgnoreMouseEvents(false);
|
|
}
|
|
});
|
|
|
|
ipcMain.handle('window-minimize', () => {
|
|
if (!mainWindow.isDestroyed()) {
|
|
mainWindow.minimize();
|
|
}
|
|
});
|
|
|
|
ipcMain.on('update-keybinds', (event, newKeybinds) => {
|
|
if (!mainWindow.isDestroyed()) {
|
|
updateGlobalShortcuts(newKeybinds, mainWindow, sendToRenderer, geminiSessionRef);
|
|
}
|
|
});
|
|
|
|
ipcMain.handle('toggle-window-visibility', async event => {
|
|
try {
|
|
if (mainWindow.isDestroyed()) {
|
|
return { success: false, error: 'Window has been destroyed' };
|
|
}
|
|
|
|
if (mainWindow.isVisible()) {
|
|
mainWindow.hide();
|
|
} else {
|
|
mainWindow.showInactive();
|
|
}
|
|
return { success: true };
|
|
} catch (error) {
|
|
console.error('Error toggling window visibility:', error);
|
|
return { success: false, error: error.message };
|
|
}
|
|
});
|
|
|
|
function animateWindowResize(mainWindow, targetWidth, targetHeight, layoutMode) {
|
|
return new Promise(resolve => {
|
|
// Check if window is destroyed before starting animation
|
|
if (mainWindow.isDestroyed()) {
|
|
console.log('Cannot animate resize: window has been destroyed');
|
|
resolve();
|
|
return;
|
|
}
|
|
|
|
// Clear any existing animation
|
|
if (resizeAnimation) {
|
|
clearInterval(resizeAnimation);
|
|
resizeAnimation = null;
|
|
}
|
|
|
|
const [startWidth, startHeight] = mainWindow.getSize();
|
|
|
|
// If already at target size, no need to animate
|
|
if (startWidth === targetWidth && startHeight === targetHeight) {
|
|
console.log(`Window already at target size for ${layoutMode} mode`);
|
|
resolve();
|
|
return;
|
|
}
|
|
|
|
console.log(`Starting animated resize from ${startWidth}x${startHeight} to ${targetWidth}x${targetHeight}`);
|
|
|
|
windowResizing = true;
|
|
mainWindow.setResizable(true);
|
|
|
|
const frameRate = 60; // 60 FPS
|
|
const totalFrames = Math.floor(RESIZE_ANIMATION_DURATION / (1000 / frameRate));
|
|
let currentFrame = 0;
|
|
|
|
const widthDiff = targetWidth - startWidth;
|
|
const heightDiff = targetHeight - startHeight;
|
|
|
|
resizeAnimation = setInterval(() => {
|
|
currentFrame++;
|
|
const progress = currentFrame / totalFrames;
|
|
|
|
// Use easing function (ease-out)
|
|
const easedProgress = 1 - Math.pow(1 - progress, 3);
|
|
|
|
const currentWidth = Math.round(startWidth + widthDiff * easedProgress);
|
|
const currentHeight = Math.round(startHeight + heightDiff * easedProgress);
|
|
|
|
if (!mainWindow || mainWindow.isDestroyed()) {
|
|
clearInterval(resizeAnimation);
|
|
resizeAnimation = null;
|
|
windowResizing = false;
|
|
return;
|
|
}
|
|
mainWindow.setSize(currentWidth, currentHeight);
|
|
|
|
// Re-center the window during animation
|
|
const primaryDisplay = screen.getPrimaryDisplay();
|
|
const { width: screenWidth } = primaryDisplay.workAreaSize;
|
|
const x = Math.floor((screenWidth - currentWidth) / 2);
|
|
const y = 0;
|
|
mainWindow.setPosition(x, y);
|
|
|
|
if (currentFrame >= totalFrames) {
|
|
clearInterval(resizeAnimation);
|
|
resizeAnimation = null;
|
|
windowResizing = false;
|
|
|
|
// Check if window is still valid before final operations
|
|
if (!mainWindow.isDestroyed()) {
|
|
mainWindow.setResizable(false);
|
|
|
|
// Ensure final size is exact
|
|
mainWindow.setSize(targetWidth, targetHeight);
|
|
const finalX = Math.floor((screenWidth - targetWidth) / 2);
|
|
mainWindow.setPosition(finalX, 0);
|
|
}
|
|
|
|
console.log(`Animation complete: ${targetWidth}x${targetHeight}`);
|
|
resolve();
|
|
}
|
|
}, 1000 / frameRate);
|
|
});
|
|
}
|
|
|
|
ipcMain.handle('update-sizes', async event => {
|
|
try {
|
|
if (mainWindow.isDestroyed()) {
|
|
return { success: false, error: 'Window has been destroyed' };
|
|
}
|
|
|
|
// Get current view and layout mode from renderer
|
|
let viewName, layoutMode;
|
|
try {
|
|
viewName = await event.sender.executeJavaScript('cheatingDaddy.getCurrentView()');
|
|
layoutMode = await event.sender.executeJavaScript('cheatingDaddy.getLayoutMode()');
|
|
} catch (error) {
|
|
console.warn('Failed to get view/layout from renderer, using defaults:', error);
|
|
viewName = 'main';
|
|
layoutMode = 'normal';
|
|
}
|
|
|
|
console.log('Size update requested for view:', viewName, 'layout:', layoutMode);
|
|
|
|
let targetWidth, targetHeight;
|
|
|
|
// Determine base size from layout mode
|
|
const baseWidth = layoutMode === 'compact' ? 700 : 900;
|
|
const baseHeight = layoutMode === 'compact' ? 500 : 600;
|
|
|
|
// Adjust height based on view
|
|
switch (viewName) {
|
|
case 'main':
|
|
targetWidth = baseWidth;
|
|
targetHeight = layoutMode === 'compact' ? 320 : 400;
|
|
break;
|
|
case 'customize':
|
|
case 'settings':
|
|
targetWidth = baseWidth;
|
|
targetHeight = layoutMode === 'compact' ? 700 : 800;
|
|
break;
|
|
case 'help':
|
|
targetWidth = baseWidth;
|
|
targetHeight = layoutMode === 'compact' ? 650 : 750;
|
|
break;
|
|
case 'history':
|
|
targetWidth = baseWidth;
|
|
targetHeight = layoutMode === 'compact' ? 650 : 750;
|
|
break;
|
|
case 'assistant':
|
|
case 'onboarding':
|
|
default:
|
|
targetWidth = baseWidth;
|
|
targetHeight = baseHeight;
|
|
break;
|
|
}
|
|
|
|
const [currentWidth, currentHeight] = mainWindow.getSize();
|
|
console.log('Current window size:', currentWidth, 'x', currentHeight);
|
|
|
|
// If currently resizing, the animation will start from current position
|
|
if (windowResizing) {
|
|
console.log('Interrupting current resize animation');
|
|
}
|
|
|
|
await animateWindowResize(mainWindow, targetWidth, targetHeight, `${viewName} view (${layoutMode})`);
|
|
|
|
return { success: true };
|
|
} catch (error) {
|
|
console.error('Error updating sizes:', error);
|
|
return { success: false, error: error.message };
|
|
}
|
|
});
|
|
|
|
// Region selection window for capturing areas outside the main window
|
|
let regionSelectionWindow = null;
|
|
|
|
ipcMain.handle('start-region-selection', async (event, { screenshotDataUrl }) => {
|
|
try {
|
|
// Hide main window first
|
|
const wasVisible = mainWindow.isVisible();
|
|
if (wasVisible) {
|
|
mainWindow.hide();
|
|
}
|
|
|
|
// Small delay to ensure window is hidden
|
|
await new Promise(resolve => setTimeout(resolve, 100));
|
|
|
|
// Get all displays to cover all screens
|
|
const displays = screen.getAllDisplays();
|
|
const primaryDisplay = screen.getPrimaryDisplay();
|
|
|
|
// Calculate bounds that cover all displays
|
|
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
displays.forEach(display => {
|
|
minX = Math.min(minX, display.bounds.x);
|
|
minY = Math.min(minY, display.bounds.y);
|
|
maxX = Math.max(maxX, display.bounds.x + display.bounds.width);
|
|
maxY = Math.max(maxY, display.bounds.y + display.bounds.height);
|
|
});
|
|
|
|
const totalWidth = maxX - minX;
|
|
const totalHeight = maxY - minY;
|
|
|
|
// Create fullscreen transparent window for selection
|
|
regionSelectionWindow = new BrowserWindow({
|
|
x: minX,
|
|
y: minY,
|
|
width: totalWidth,
|
|
height: totalHeight,
|
|
frame: false,
|
|
transparent: true,
|
|
alwaysOnTop: true,
|
|
skipTaskbar: true,
|
|
resizable: false,
|
|
movable: false,
|
|
hasShadow: false,
|
|
// Hide from screen capture/sharing
|
|
...(process.platform === 'darwin' ? { type: 'panel' } : {}),
|
|
webPreferences: {
|
|
nodeIntegration: true,
|
|
contextIsolation: false,
|
|
},
|
|
});
|
|
|
|
// Hide window content from screen capture (macOS)
|
|
if (process.platform === 'darwin') {
|
|
regionSelectionWindow.setContentProtection(true);
|
|
}
|
|
|
|
regionSelectionWindow.setAlwaysOnTop(true, 'screen-saver', 1);
|
|
|
|
// Create HTML content for selection overlay
|
|
const htmlContent = `
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<style>
|
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
body {
|
|
width: 100vw;
|
|
height: 100vh;
|
|
cursor: crosshair;
|
|
overflow: hidden;
|
|
position: relative;
|
|
}
|
|
#screenshot {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
}
|
|
#overlay {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: rgba(0, 0, 0, 0.3);
|
|
}
|
|
#selection {
|
|
position: absolute;
|
|
border: 2px dashed #fff;
|
|
background: rgba(255, 255, 255, 0.1);
|
|
box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.5);
|
|
display: none;
|
|
pointer-events: none;
|
|
}
|
|
#hint {
|
|
position: fixed;
|
|
top: 20px;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
background: rgba(0, 0, 0, 0.8);
|
|
color: white;
|
|
padding: 12px 24px;
|
|
border-radius: 8px;
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
font-size: 14px;
|
|
z-index: 10000;
|
|
pointer-events: none;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<img id="screenshot" src="${screenshotDataUrl}" />
|
|
<div id="overlay"></div>
|
|
<div id="selection"></div>
|
|
<div id="hint">Click and drag to select region • ESC to cancel</div>
|
|
<script>
|
|
const { ipcRenderer } = require('electron');
|
|
const selection = document.getElementById('selection');
|
|
const overlay = document.getElementById('overlay');
|
|
let isSelecting = false;
|
|
let startX = 0, startY = 0;
|
|
|
|
document.addEventListener('mousedown', (e) => {
|
|
if (e.button !== 0) return;
|
|
isSelecting = true;
|
|
startX = e.clientX;
|
|
startY = e.clientY;
|
|
selection.style.display = 'block';
|
|
selection.style.left = startX + 'px';
|
|
selection.style.top = startY + 'px';
|
|
selection.style.width = '0px';
|
|
selection.style.height = '0px';
|
|
});
|
|
|
|
document.addEventListener('mousemove', (e) => {
|
|
if (!isSelecting) return;
|
|
const currentX = e.clientX;
|
|
const currentY = e.clientY;
|
|
const left = Math.min(startX, currentX);
|
|
const top = Math.min(startY, currentY);
|
|
const width = Math.abs(currentX - startX);
|
|
const height = Math.abs(currentY - startY);
|
|
selection.style.left = left + 'px';
|
|
selection.style.top = top + 'px';
|
|
selection.style.width = width + 'px';
|
|
selection.style.height = height + 'px';
|
|
});
|
|
|
|
document.addEventListener('mouseup', (e) => {
|
|
if (!isSelecting) return;
|
|
isSelecting = false;
|
|
const rect = {
|
|
left: parseInt(selection.style.left),
|
|
top: parseInt(selection.style.top),
|
|
width: parseInt(selection.style.width),
|
|
height: parseInt(selection.style.height)
|
|
};
|
|
if (rect.width > 10 && rect.height > 10) {
|
|
ipcRenderer.send('region-selected', rect);
|
|
} else {
|
|
ipcRenderer.send('region-selection-cancelled');
|
|
}
|
|
});
|
|
|
|
document.addEventListener('keydown', (e) => {
|
|
if (e.key === 'Escape') {
|
|
ipcRenderer.send('region-selection-cancelled');
|
|
}
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|
|
`;
|
|
|
|
regionSelectionWindow.loadURL(`data:text/html;charset=utf-8,${encodeURIComponent(htmlContent)}`);
|
|
|
|
return new Promise((resolve) => {
|
|
ipcMain.once('region-selected', (event, rect) => {
|
|
if (regionSelectionWindow && !regionSelectionWindow.isDestroyed()) {
|
|
regionSelectionWindow.close();
|
|
regionSelectionWindow = null;
|
|
}
|
|
if (wasVisible) {
|
|
mainWindow.showInactive();
|
|
}
|
|
resolve({ success: true, rect });
|
|
});
|
|
|
|
ipcMain.once('region-selection-cancelled', () => {
|
|
if (regionSelectionWindow && !regionSelectionWindow.isDestroyed()) {
|
|
regionSelectionWindow.close();
|
|
regionSelectionWindow = null;
|
|
}
|
|
if (wasVisible) {
|
|
mainWindow.showInactive();
|
|
}
|
|
resolve({ success: false, cancelled: true });
|
|
});
|
|
|
|
// Also handle window close
|
|
regionSelectionWindow.on('closed', () => {
|
|
regionSelectionWindow = null;
|
|
if (wasVisible && !mainWindow.isDestroyed()) {
|
|
mainWindow.showInactive();
|
|
}
|
|
});
|
|
});
|
|
} catch (error) {
|
|
console.error('Error starting region selection:', error);
|
|
if (regionSelectionWindow && !regionSelectionWindow.isDestroyed()) {
|
|
regionSelectionWindow.close();
|
|
regionSelectionWindow = null;
|
|
}
|
|
if (!mainWindow.isDestroyed()) {
|
|
mainWindow.showInactive();
|
|
}
|
|
return { success: false, error: error.message };
|
|
}
|
|
});
|
|
}
|
|
|
|
module.exports = {
|
|
createWindow,
|
|
getDefaultKeybinds,
|
|
updateGlobalShortcuts,
|
|
setupWindowIpcHandlers,
|
|
};
|