shikarno epta

This commit is contained in:
Илья Глазунов
2026-01-15 04:15:23 +03:00
parent beb9034cd4
commit 0011bacfa3
6 changed files with 670 additions and 33 deletions
+3 -2
View File
@@ -265,8 +265,9 @@ async function initializeGeminiSession(apiKey, customPrompt = '', profile = 'int
}
},
onerror: function (e) {
console.log('Session error:', e.message);
sendToRenderer('update-status', 'Error: ' + e.message);
const errorMsg = e?.message || e?.error?.message || 'Session error occurred';
console.log('Session error:', errorMsg, e);
sendToRenderer('update-status', 'Error: ' + errorMsg);
},
onclose: function (e) {
console.log('Session closed:', e.reason);
+53
View File
@@ -219,7 +219,60 @@ function getSystemPrompt(profile, customPrompt = '', googleSearchEnabled = true)
return buildSystemPrompt(promptParts, customPrompt, googleSearchEnabled);
}
// Comprehensive prompt for Vision/Image analysis
const VISION_ANALYSIS_PROMPT = `You are an expert AI assistant analyzing a screenshot. Your task is to understand what the user needs help with and provide the most useful response.
**ANALYSIS APPROACH:**
1. First, identify what's shown on the screen (code editor, math problem, website, document, exam, etc.)
2. Determine what the user likely needs (explanation, solution, answer, debugging help, etc.)
3. Provide a direct, actionable response
**RESPONSE GUIDELINES BY CONTEXT:**
**If it's CODE (LeetCode, HackerRank, coding interview, IDE):**
- Identify the programming language and problem type
- Provide a brief explanation of the approach (2-3 bullet points max)
- Give the complete, working code solution
- Include time/space complexity if relevant
- If there's an error, explain the fix
**If it's MATH or SCIENCE:**
- Show step-by-step solution
- Use proper mathematical notation with LaTeX ($..$ for inline, $$...$$ for blocks)
- Provide the final answer clearly marked
- Include any relevant formulas used
**If it's MCQ/EXAM/QUIZ:**
- State the correct answer immediately and clearly (e.g., "**Answer: B**")
- Provide brief justification (1-2 sentences)
- If multiple questions visible, answer all of them
**If it's a DOCUMENT/ARTICLE/WEBSITE:**
- Summarize the key information
- Answer any specific questions if apparent
- Highlight important points
**If it's a FORM/APPLICATION:**
- Help fill in the required information
- Suggest appropriate responses
- Point out any issues or missing fields
**If it's an ERROR/DEBUG scenario:**
- Identify the error type and cause
- Provide the fix immediately
- Explain briefly why it occurred
**FORMAT REQUIREMENTS:**
- Use **markdown** for formatting
- Use **bold** for key answers and important points
- Use code blocks with language specification for code
- Be concise but complete - no unnecessary explanations
- No pleasantries or filler text - get straight to the answer
**CRITICAL:** Provide the complete answer. Don't ask for clarification - make reasonable assumptions and deliver value immediately.`;
module.exports = {
profilePrompts,
getSystemPrompt,
VISION_ANALYSIS_PROMPT,
};
+203 -6
View File
@@ -153,7 +153,7 @@ async function initializeGemini(profile = 'interview', language = 'en-US') {
if (success) {
cheatingDaddy.setStatus('Live');
} else {
cheatingDaddy.setStatus('error');
cheatingDaddy.setStatus('Error: Failed to initialize AI session Gemini');
}
}
@@ -329,7 +329,21 @@ async function startCapture(screenshotIntervalSeconds = 5, imageQuality = 'mediu
console.log('Manual mode enabled - screenshots will be captured on demand only');
} catch (err) {
console.error('Error starting capture:', err);
cheatingDaddy.setStatus('error');
// Provide more helpful error messages based on error type
let errorMessage = err.message || 'Failed to start capture';
if (errorMessage.toLowerCase().includes('timeout')) {
errorMessage = 'Screen capture timed out. Please try again and select a screen quickly.';
} else if (errorMessage.toLowerCase().includes('permission') || errorMessage.toLowerCase().includes('denied')) {
errorMessage = 'Screen capture permission denied. Please grant screen recording permission in System Settings.';
} else if (errorMessage.toLowerCase().includes('not found') || errorMessage.toLowerCase().includes('no sources')) {
errorMessage = 'No screen sources found. Please ensure a display is connected.';
} else if (errorMessage.toLowerCase().includes('aborted') || errorMessage.toLowerCase().includes('cancel')) {
errorMessage = 'Screen selection was cancelled. Please try again.';
}
cheatingDaddy.setStatus('Error: ' + errorMessage);
}
}
@@ -518,10 +532,193 @@ async function captureScreenshot(imageQuality = 'medium', isManual = false) {
);
}
const MANUAL_SCREENSHOT_PROMPT = `Help me on this page, give me the answer no bs, complete answer.
So if its a code question, give me the approach in few bullet points, then the entire code. Also if theres anything else i need to know, tell me.
If its a question about the website, give me the answer no bs, complete answer.
If its a mcq question, give me the answer no bs, complete answer.`;
const MANUAL_SCREENSHOT_PROMPT = `You are an expert AI assistant analyzing a screenshot. Your task is to understand what the user needs help with and provide the most useful response.
**ANALYSIS APPROACH:**
1. First, identify what's shown on the screen (code editor, math problem, website, document, exam, etc.)
2. Determine what the user likely needs (explanation, solution, answer, debugging help, etc.)
3. Provide a direct, actionable response
**RESPONSE GUIDELINES BY CONTEXT:**
**If it's CODE (LeetCode, HackerRank, coding interview, IDE):**
- Identify the programming language and problem type
- Provide a brief explanation of the approach (2-3 bullet points max)
- Give the complete, working code solution
- Include time/space complexity if relevant
- If there's an error, explain the fix
**If it's MATH or SCIENCE:**
- Show step-by-step solution
- Use proper mathematical notation with LaTeX ($..$ for inline, $$...$$ for blocks)
- Provide the final answer clearly marked
- Include any relevant formulas used
**If it's MCQ/EXAM/QUIZ:**
- State the correct answer immediately and clearly (e.g., "**Answer: B**")
- Provide brief justification (1-2 sentences)
- If multiple questions visible, answer all of them
**If it's a DOCUMENT/ARTICLE/WEBSITE:**
- Summarize the key information
- Answer any specific questions if apparent
- Highlight important points
**If it's a FORM/APPLICATION:**
- Help fill in the required information
- Suggest appropriate responses
- Point out any issues or missing fields
**If it's an ERROR/DEBUG scenario:**
- Identify the error type and cause
- Provide the fix immediately
- Explain briefly why it occurred
**FORMAT REQUIREMENTS:**
- Use **markdown** for formatting
- Use **bold** for key answers and important points
- Use code blocks with language specification for code
- Be concise but complete - no unnecessary explanations
- No pleasantries or filler text - get straight to the answer
**CRITICAL:** Provide the complete answer. Don't ask for clarification - make reasonable assumptions and deliver value immediately.`;
// ============ REGION SELECTION ============
// Uses a separate fullscreen window to allow selection outside the app window
async function startRegionSelection() {
console.log('Starting region selection...');
if (!mediaStream) {
console.error('No media stream available. Please start capture first.');
cheatingDaddy?.addNewResponse('Please start screen capture first before selecting a region.');
return;
}
// Ensure video is ready
if (!hiddenVideo) {
hiddenVideo = document.createElement('video');
hiddenVideo.srcObject = mediaStream;
hiddenVideo.muted = true;
hiddenVideo.playsInline = true;
await hiddenVideo.play();
await new Promise(resolve => {
if (hiddenVideo.readyState >= 2) return resolve();
hiddenVideo.onloadedmetadata = () => resolve();
});
// Initialize canvas
offscreenCanvas = document.createElement('canvas');
offscreenCanvas.width = hiddenVideo.videoWidth;
offscreenCanvas.height = hiddenVideo.videoHeight;
offscreenContext = offscreenCanvas.getContext('2d');
}
if (hiddenVideo.readyState < 2) {
console.warn('Video not ready yet');
return;
}
// Capture current screen to show in selection window
offscreenContext.drawImage(hiddenVideo, 0, 0, offscreenCanvas.width, offscreenCanvas.height);
const screenshotDataUrl = offscreenCanvas.toDataURL('image/jpeg', 0.9);
// Request main process to create selection window
const result = await ipcRenderer.invoke('start-region-selection', { screenshotDataUrl });
if (result.success && result.rect) {
// Capture the selected region from the screenshot
await captureRegionFromScreenshot(result.rect, screenshotDataUrl);
} else if (result.cancelled) {
console.log('Region selection cancelled');
} else if (result.error) {
console.error('Region selection error:', result.error);
}
}
async function captureRegionFromScreenshot(rect, screenshotDataUrl) {
console.log('Capturing region from screenshot:', rect);
// Load the screenshot into an image
const img = new Image();
img.src = screenshotDataUrl;
await new Promise((resolve, reject) => {
img.onload = resolve;
img.onerror = reject;
});
// Calculate scale factor (screenshot might have different resolution than display)
// The selection coordinates are in screen pixels, we need to map to image pixels
const scaleX = img.naturalWidth / window.screen.width;
const scaleY = img.naturalHeight / window.screen.height;
// Scale the selection rectangle
const scaledRect = {
left: Math.round(rect.left * scaleX),
top: Math.round(rect.top * scaleY),
width: Math.round(rect.width * scaleX),
height: Math.round(rect.height * scaleY),
};
// Create canvas for the cropped region
const cropCanvas = document.createElement('canvas');
cropCanvas.width = scaledRect.width;
cropCanvas.height = scaledRect.height;
const cropContext = cropCanvas.getContext('2d');
// Draw only the selected region
cropContext.drawImage(
img,
scaledRect.left,
scaledRect.top,
scaledRect.width,
scaledRect.height,
0,
0,
scaledRect.width,
scaledRect.height
);
// Convert to blob and send
cropCanvas.toBlob(
async blob => {
if (!blob) {
console.error('Failed to create blob from cropped region');
return;
}
const reader = new FileReader();
reader.onloadend = async () => {
const base64data = reader.result.split(',')[1];
if (!base64data || base64data.length < 100) {
console.error('Invalid base64 data generated');
return;
}
const result = await ipcRenderer.invoke('send-image-content', {
data: base64data,
prompt: MANUAL_SCREENSHOT_PROMPT,
});
if (result.success) {
console.log(`Region capture response completed from ${result.model}`);
} else {
console.error('Failed to get region capture response:', result.error);
cheatingDaddy.addNewResponse(`Error: ${result.error}`);
}
};
reader.readAsDataURL(blob);
},
'image/jpeg',
0.9
);
}
// Expose to global scope
window.startRegionSelection = startRegionSelection;
async function captureManualScreenshot(imageQuality = null) {
console.log('Manual screenshot triggered');
+268 -8
View File
@@ -33,14 +33,59 @@ function createWindow(sendToRenderer, geminiSessionRef) {
});
const { session, desktopCapturer } = require('electron');
session.defaultSession.setDisplayMediaRequestHandler(
(request, callback) => {
desktopCapturer.getSources({ types: ['screen'] }).then(sources => {
callback({ video: sources[0], audio: 'loopback' });
});
},
{ useSystemPicker: true }
);
// Setup display media request handler for screen capture
// On macOS, use system picker for better UX
if (process.platform === 'darwin') {
session.defaultSession.setDisplayMediaRequestHandler(
async (request, callback) => {
try {
const sources = await desktopCapturer.getSources({
types: ['screen'],
thumbnailSize: { width: 0, height: 0 } // Skip thumbnail generation for speed
});
if (sources.length === 0) {
console.error('No screen sources available');
callback(null);
return;
}
// On macOS, directly use the first screen (system already granted permission)
console.log('Screen capture source:', sources[0].name);
callback({ video: sources[0], audio: 'loopback' });
} catch (error) {
console.error('Error getting screen sources:', error);
callback(null);
}
},
{ useSystemPicker: false } // Disable system picker, use our source directly
);
} else {
// On other platforms, use the system picker
session.defaultSession.setDisplayMediaRequestHandler(
async (request, callback) => {
try {
const sources = await desktopCapturer.getSources({
types: ['screen'],
thumbnailSize: { width: 0, height: 0 }
});
if (sources.length === 0) {
console.error('No screen sources available');
callback(null);
return;
}
callback({ video: sources[0], audio: 'loopback' });
} catch (error) {
console.error('Error getting screen sources:', error);
callback(null);
}
},
{ useSystemPicker: true }
);
}
mainWindow.setResizable(false);
mainWindow.setContentProtection(true);
@@ -493,6 +538,221 @@ function setupWindowIpcHandlers(mainWindow, sendToRenderer, geminiSessionRef) {
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,
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
},
});
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 = {