prettier fix

This commit is contained in:
Илья Глазунов
2026-01-15 23:26:09 +03:00
parent 528dfe01a1
commit 669c019fd8
21 changed files with 4857 additions and 4639 deletions
+4 -6
View File
@@ -52,9 +52,7 @@ function buildContextMessage() {
if (validTurns.length === 0) return null;
const contextLines = validTurns.map(turn =>
`[Interviewer]: ${turn.transcription.trim()}\n[Your answer]: ${turn.ai_response.trim()}`
);
const contextLines = validTurns.map(turn => `[Interviewer]: ${turn.transcription.trim()}\n[Your answer]: ${turn.ai_response.trim()}`);
return `Session reconnected. Here's the conversation so far:\n\n${contextLines.join('\n\n')}\n\nContinue from here.`;
}
@@ -74,7 +72,7 @@ function initializeNewSession(profile = null, customPrompt = null) {
sendToRenderer('save-session-context', {
sessionId: currentSessionId,
profile: profile,
customPrompt: customPrompt || ''
customPrompt: customPrompt || '',
});
}
}
@@ -110,7 +108,7 @@ function saveScreenAnalysis(prompt, response, model) {
timestamp: Date.now(),
prompt: prompt,
response: response.trim(),
model: model
model: model,
};
screenAnalysisHistory.push(analysisEntry);
@@ -122,7 +120,7 @@ function saveScreenAnalysis(prompt, response, model) {
analysis: analysisEntry,
fullHistory: screenAnalysisHistory,
profile: currentProfile,
customPrompt: currentCustomPrompt
customPrompt: currentCustomPrompt,
});
}
+24 -22
View File
@@ -7,19 +7,19 @@ let logPath = null;
function getLogPath() {
if (logPath) return logPath;
const userDataPath = app.getPath('userData');
const logsDir = path.join(userDataPath, 'logs');
// Create logs directory if it doesn't exist
if (!fs.existsSync(logsDir)) {
fs.mkdirSync(logsDir, { recursive: true });
}
// Create log file with timestamp
const timestamp = new Date().toISOString().split('T')[0];
logPath = path.join(logsDir, `app-${timestamp}.log`);
return logPath;
}
@@ -27,32 +27,32 @@ function initLogger() {
try {
const filePath = getLogPath();
logFile = fs.createWriteStream(filePath, { flags: 'a' });
const startMsg = `\n${'='.repeat(60)}\nApp started at ${new Date().toISOString()}\nPlatform: ${process.platform}, Arch: ${process.arch}\nElectron: ${process.versions.electron}, Node: ${process.versions.node}\nPackaged: ${app.isPackaged}\n${'='.repeat(60)}\n`;
logFile.write(startMsg);
// Override console methods to also write to file
const originalLog = console.log;
const originalError = console.error;
const originalWarn = console.warn;
console.log = (...args) => {
originalLog.apply(console, args);
writeLog('INFO', args);
};
console.error = (...args) => {
originalError.apply(console, args);
writeLog('ERROR', args);
};
console.warn = (...args) => {
originalWarn.apply(console, args);
writeLog('WARN', args);
};
console.log('Logger initialized, writing to:', filePath);
return filePath;
} catch (err) {
console.error('Failed to initialize logger:', err);
@@ -62,20 +62,22 @@ function initLogger() {
function writeLog(level, args) {
if (!logFile) return;
try {
const timestamp = new Date().toISOString();
const message = args.map(arg => {
if (typeof arg === 'object') {
try {
return JSON.stringify(arg, null, 2);
} catch {
return String(arg);
const message = args
.map(arg => {
if (typeof arg === 'object') {
try {
return JSON.stringify(arg, null, 2);
} catch {
return String(arg);
}
}
}
return String(arg);
}).join(' ');
return String(arg);
})
.join(' ');
logFile.write(`[${timestamp}] [${level}] ${message}\n`);
} catch (err) {
// Silently fail - don't want logging errors to crash the app
+3 -1
View File
@@ -311,7 +311,9 @@ async function sendImageToOpenAI(base64Data, prompt, config) {
const { apiKey, baseUrl, model } = config;
// OpenAI doesn't support images in Realtime API yet, use standard Chat Completions
const apiEndpoint = baseUrl ? `${baseUrl.replace('wss://', 'https://').replace('/v1/realtime', '')}/v1/chat/completions` : 'https://api.openai.com/v1/chat/completions';
const apiEndpoint = baseUrl
? `${baseUrl.replace('wss://', 'https://').replace('/v1/realtime', '')}/v1/chat/completions`
: 'https://api.openai.com/v1/chat/completions';
try {
const response = await fetch(apiEndpoint, {
+6 -6
View File
@@ -297,7 +297,7 @@ async function processAudioChunk(base64Audio, mimeType) {
// Track first chunk time for duration-based flushing
if (audioChunks.length === 0) {
firstChunkTime = now;
// Start periodic transcription timer (Windows needs this)
if (!windowsTranscriptionTimer && process.platform === 'win32') {
console.log('Starting Windows periodic transcription timer...');
@@ -355,7 +355,7 @@ async function flushAudioAndTranscribe() {
// Calculate audio duration
const bytesPerSample = 2;
const audioDurationMs = (combinedBuffer.length / bytesPerSample / SAMPLE_RATE) * 1000;
console.log(`Transcribing ${chunkCount} chunks (${audioDurationMs.toFixed(0)}ms of audio)...`);
// Transcribe
@@ -384,7 +384,7 @@ function clearConversation() {
const systemMessage = conversationMessages.find(m => m.role === 'system');
conversationMessages = systemMessage ? [systemMessage] : [];
audioChunks = [];
// Clear timers
if (silenceCheckTimer) {
clearTimeout(silenceCheckTimer);
@@ -403,7 +403,7 @@ function closeOpenAISDK() {
conversationMessages = [];
audioChunks = [];
isProcessing = false;
// Clear timers
if (silenceCheckTimer) {
clearTimeout(silenceCheckTimer);
@@ -413,7 +413,7 @@ function closeOpenAISDK() {
clearInterval(windowsTranscriptionTimer);
windowsTranscriptionTimer = null;
}
sendToRenderer('update-status', 'Disconnected');
}
@@ -600,7 +600,7 @@ async function startMacOSAudioCapture() {
// Add to audio buffer for transcription
audioBuffer = Buffer.concat([audioBuffer, monoChunk]);
chunkCount++;
if (chunkCount % 100 === 0) {
console.log(`Audio: ${chunkCount} chunks processed, buffer size: ${audioBuffer.length}`);
+134 -92
View File
@@ -22,14 +22,20 @@ const isWindows = process.platform === 'win32';
// Send logs to main process for file logging
function logToMain(level, ...args) {
const message = args.map(arg => {
if (typeof arg === 'object') {
try { return JSON.stringify(arg); } catch { return String(arg); }
}
return String(arg);
}).join(' ');
const message = args
.map(arg => {
if (typeof arg === 'object') {
try {
return JSON.stringify(arg);
} catch {
return String(arg);
}
}
return String(arg);
})
.join(' ');
ipcRenderer.send('renderer-log', { level, message });
// Also log to console
if (level === 'error') console.error(...args);
else if (level === 'warn') console.warn(...args);
@@ -130,7 +136,7 @@ const storage = {
async getTodayLimits() {
const result = await ipcRenderer.invoke('storage:get-today-limits');
return result.success ? result.data : { flash: { count: 0 }, flashLite: { count: 0 } };
}
},
};
// Cache for preferences to avoid async calls in hot paths
@@ -297,18 +303,18 @@ async function startCapture(screenshotIntervalSeconds = 5, imageQuality = 'mediu
// Windows - show custom screen picker first
logToMain('info', '=== Starting Windows audio capture ===');
cheatingDaddy.setStatus('Choose screen to share...');
// Show screen picker dialog
const appElement = document.querySelector('cheating-daddy-app');
const pickerResult = await appElement.showScreenPickerDialog();
if (pickerResult.cancelled) {
cheatingDaddy.setStatus('Cancelled');
return;
}
cheatingDaddy.setStatus('Starting capture...');
mediaStream = await navigator.mediaDevices.getDisplayMedia({
video: {
frameRate: 1,
@@ -326,16 +332,16 @@ async function startCapture(screenshotIntervalSeconds = 5, imageQuality = 'mediu
const audioTracks = mediaStream.getAudioTracks();
const videoTracks = mediaStream.getVideoTracks();
logToMain('info', 'Windows capture result:', {
hasVideo: videoTracks.length > 0,
hasAudio: audioTracks.length > 0,
audioTrackInfo: audioTracks.map(t => ({
label: t.label,
enabled: t.enabled,
audioTrackInfo: audioTracks.map(t => ({
label: t.label,
enabled: t.enabled,
muted: t.muted,
readyState: t.readyState,
settings: t.getSettings()
settings: t.getSettings(),
})),
});
@@ -379,10 +385,10 @@ 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);
// 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')) {
@@ -392,7 +398,7 @@ async function startCapture(screenshotIntervalSeconds = 5, imageQuality = 'mediu
} else if (errorMessage.toLowerCase().includes('aborted') || errorMessage.toLowerCase().includes('cancel')) {
errorMessage = 'Screen selection was cancelled. Please try again.';
}
cheatingDaddy.setStatus('Error: ' + errorMessage);
}
}
@@ -463,25 +469,28 @@ function setupLinuxSystemAudioProcessing() {
function setupWindowsLoopbackProcessing() {
// Setup audio processing for Windows loopback audio only
logToMain('info', 'Setting up Windows loopback audio processing...');
try {
audioContext = new AudioContext({ sampleRate: SAMPLE_RATE });
logToMain('info', 'AudioContext created:', {
state: audioContext.state,
sampleRate: audioContext.sampleRate,
});
// Resume AudioContext if suspended (Chrome policy)
if (audioContext.state === 'suspended') {
logToMain('warn', 'AudioContext suspended, attempting resume...');
audioContext.resume().then(() => {
logToMain('info', 'AudioContext resumed successfully');
}).catch(err => {
logToMain('error', 'Failed to resume AudioContext:', err.message);
});
audioContext
.resume()
.then(() => {
logToMain('info', 'AudioContext resumed successfully');
})
.catch(err => {
logToMain('error', 'Failed to resume AudioContext:', err.message);
});
}
const source = audioContext.createMediaStreamSource(mediaStream);
audioProcessor = audioContext.createScriptProcessor(BUFFER_SIZE, 1, 1);
@@ -505,9 +514,9 @@ function setupWindowsLoopbackProcessing() {
data: base64Data,
mimeType: 'audio/pcm;rate=24000',
});
chunkCount++;
// Log progress every 100 chunks (~10 seconds)
if (chunkCount === 1) {
logToMain('info', 'First audio chunk sent to AI');
@@ -522,9 +531,8 @@ function setupWindowsLoopbackProcessing() {
source.connect(audioProcessor);
audioProcessor.connect(audioContext.destination);
logToMain('info', 'Windows audio processing pipeline connected');
} catch (err) {
logToMain('error', 'Error setting up Windows audio:', err.message, err.stack);
cheatingDaddy.setStatus('Audio error: ' + err.message);
@@ -760,17 +768,7 @@ async function captureRegionFromScreenshot(rect, screenshotDataUrl) {
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
);
cropContext.drawImage(img, scaledRect.left, scaledRect.top, scaledRect.width, scaledRect.height, 0, 0, scaledRect.width, scaledRect.height);
// Convert to blob and send
cropCanvas.toBlob(
@@ -983,7 +981,7 @@ ipcRenderer.on('save-session-context', async (event, data) => {
try {
await storage.saveSession(data.sessionId, {
profile: data.profile,
customPrompt: data.customPrompt
customPrompt: data.customPrompt,
});
console.log('Session context saved:', data.sessionId, 'profile:', data.profile);
} catch (error) {
@@ -997,7 +995,7 @@ ipcRenderer.on('save-screen-analysis', async (event, data) => {
await storage.saveSession(data.sessionId, {
screenAnalysisHistory: data.fullHistory,
profile: data.profile,
customPrompt: data.customPrompt
customPrompt: data.customPrompt,
});
console.log('Screen analysis saved:', data.sessionId);
} catch (error) {
@@ -1032,60 +1030,102 @@ const theme = {
themes: {
dark: {
background: '#1e1e1e',
text: '#e0e0e0', textSecondary: '#a0a0a0', textMuted: '#6b6b6b',
border: '#333333', accent: '#ffffff',
btnPrimaryBg: '#ffffff', btnPrimaryText: '#000000', btnPrimaryHover: '#e0e0e0',
tooltipBg: '#1a1a1a', tooltipText: '#ffffff',
keyBg: 'rgba(255,255,255,0.1)'
text: '#e0e0e0',
textSecondary: '#a0a0a0',
textMuted: '#6b6b6b',
border: '#333333',
accent: '#ffffff',
btnPrimaryBg: '#ffffff',
btnPrimaryText: '#000000',
btnPrimaryHover: '#e0e0e0',
tooltipBg: '#1a1a1a',
tooltipText: '#ffffff',
keyBg: 'rgba(255,255,255,0.1)',
},
light: {
background: '#ffffff',
text: '#1a1a1a', textSecondary: '#555555', textMuted: '#888888',
border: '#e0e0e0', accent: '#000000',
btnPrimaryBg: '#1a1a1a', btnPrimaryText: '#ffffff', btnPrimaryHover: '#333333',
tooltipBg: '#1a1a1a', tooltipText: '#ffffff',
keyBg: 'rgba(0,0,0,0.1)'
text: '#1a1a1a',
textSecondary: '#555555',
textMuted: '#888888',
border: '#e0e0e0',
accent: '#000000',
btnPrimaryBg: '#1a1a1a',
btnPrimaryText: '#ffffff',
btnPrimaryHover: '#333333',
tooltipBg: '#1a1a1a',
tooltipText: '#ffffff',
keyBg: 'rgba(0,0,0,0.1)',
},
midnight: {
background: '#0d1117',
text: '#c9d1d9', textSecondary: '#8b949e', textMuted: '#6e7681',
border: '#30363d', accent: '#58a6ff',
btnPrimaryBg: '#58a6ff', btnPrimaryText: '#0d1117', btnPrimaryHover: '#79b8ff',
tooltipBg: '#161b22', tooltipText: '#c9d1d9',
keyBg: 'rgba(88,166,255,0.15)'
text: '#c9d1d9',
textSecondary: '#8b949e',
textMuted: '#6e7681',
border: '#30363d',
accent: '#58a6ff',
btnPrimaryBg: '#58a6ff',
btnPrimaryText: '#0d1117',
btnPrimaryHover: '#79b8ff',
tooltipBg: '#161b22',
tooltipText: '#c9d1d9',
keyBg: 'rgba(88,166,255,0.15)',
},
sepia: {
background: '#f4ecd8',
text: '#5c4b37', textSecondary: '#7a6a56', textMuted: '#998875',
border: '#d4c8b0', accent: '#8b4513',
btnPrimaryBg: '#5c4b37', btnPrimaryText: '#f4ecd8', btnPrimaryHover: '#7a6a56',
tooltipBg: '#5c4b37', tooltipText: '#f4ecd8',
keyBg: 'rgba(92,75,55,0.15)'
text: '#5c4b37',
textSecondary: '#7a6a56',
textMuted: '#998875',
border: '#d4c8b0',
accent: '#8b4513',
btnPrimaryBg: '#5c4b37',
btnPrimaryText: '#f4ecd8',
btnPrimaryHover: '#7a6a56',
tooltipBg: '#5c4b37',
tooltipText: '#f4ecd8',
keyBg: 'rgba(92,75,55,0.15)',
},
nord: {
background: '#2e3440',
text: '#eceff4', textSecondary: '#d8dee9', textMuted: '#4c566a',
border: '#3b4252', accent: '#88c0d0',
btnPrimaryBg: '#88c0d0', btnPrimaryText: '#2e3440', btnPrimaryHover: '#8fbcbb',
tooltipBg: '#3b4252', tooltipText: '#eceff4',
keyBg: 'rgba(136,192,208,0.15)'
text: '#eceff4',
textSecondary: '#d8dee9',
textMuted: '#4c566a',
border: '#3b4252',
accent: '#88c0d0',
btnPrimaryBg: '#88c0d0',
btnPrimaryText: '#2e3440',
btnPrimaryHover: '#8fbcbb',
tooltipBg: '#3b4252',
tooltipText: '#eceff4',
keyBg: 'rgba(136,192,208,0.15)',
},
dracula: {
background: '#282a36',
text: '#f8f8f2', textSecondary: '#bd93f9', textMuted: '#6272a4',
border: '#44475a', accent: '#ff79c6',
btnPrimaryBg: '#ff79c6', btnPrimaryText: '#282a36', btnPrimaryHover: '#ff92d0',
tooltipBg: '#44475a', tooltipText: '#f8f8f2',
keyBg: 'rgba(255,121,198,0.15)'
text: '#f8f8f2',
textSecondary: '#bd93f9',
textMuted: '#6272a4',
border: '#44475a',
accent: '#ff79c6',
btnPrimaryBg: '#ff79c6',
btnPrimaryText: '#282a36',
btnPrimaryHover: '#ff92d0',
tooltipBg: '#44475a',
tooltipText: '#f8f8f2',
keyBg: 'rgba(255,121,198,0.15)',
},
abyss: {
background: '#0a0a0a',
text: '#d4d4d4', textSecondary: '#808080', textMuted: '#505050',
border: '#1a1a1a', accent: '#ffffff',
btnPrimaryBg: '#ffffff', btnPrimaryText: '#0a0a0a', btnPrimaryHover: '#d4d4d4',
tooltipBg: '#141414', tooltipText: '#d4d4d4',
keyBg: 'rgba(255,255,255,0.08)'
}
text: '#d4d4d4',
textSecondary: '#808080',
textMuted: '#505050',
border: '#1a1a1a',
accent: '#ffffff',
btnPrimaryBg: '#ffffff',
btnPrimaryText: '#0a0a0a',
btnPrimaryHover: '#d4d4d4',
tooltipBg: '#141414',
tooltipText: '#d4d4d4',
keyBg: 'rgba(255,255,255,0.08)',
},
},
current: 'dark',
@@ -1102,29 +1142,31 @@ const theme = {
sepia: 'Sepia',
nord: 'Nord',
dracula: 'Dracula',
abyss: 'Abyss'
abyss: 'Abyss',
};
return Object.keys(this.themes).map(key => ({
value: key,
name: names[key] || key,
colors: this.themes[key]
colors: this.themes[key],
}));
},
hexToRgb(hex) {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
} : { r: 30, g: 30, b: 30 };
return result
? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16),
}
: { r: 30, g: 30, b: 30 };
},
lightenColor(rgb, amount) {
return {
r: Math.min(255, rgb.r + amount),
g: Math.min(255, rgb.g + amount),
b: Math.min(255, rgb.b + amount)
b: Math.min(255, rgb.b + amount),
};
},
@@ -1132,7 +1174,7 @@ const theme = {
return {
r: Math.max(0, rgb.r - amount),
g: Math.max(0, rgb.g - amount),
b: Math.max(0, rgb.b - amount)
b: Math.max(0, rgb.b - amount),
};
},
@@ -1212,7 +1254,7 @@ const theme = {
async save(themeName) {
await storage.updatePreference('theme', themeName);
this.apply(themeName);
}
},
};
// Consolidated cheatingDaddy object - all functions in one place
+7 -4
View File
@@ -33,10 +33,10 @@ function createWindow(sendToRenderer, geminiSessionRef) {
});
const { session, desktopCapturer } = require('electron');
// Store selected source for Windows custom picker
let selectedSourceId = null;
// Setup display media handler based on platform
if (process.platform === 'darwin') {
// macOS: Use native system picker
@@ -553,7 +553,10 @@ function setupWindowIpcHandlers(mainWindow, sendToRenderer, geminiSessionRef) {
const primaryDisplay = screen.getPrimaryDisplay();
// Calculate bounds that cover all displays
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
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);
@@ -712,7 +715,7 @@ function setupWindowIpcHandlers(mainWindow, sendToRenderer, geminiSessionRef) {
regionSelectionWindow.loadURL(`data:text/html;charset=utf-8,${encodeURIComponent(htmlContent)}`);
return new Promise((resolve) => {
return new Promise(resolve => {
ipcMain.once('region-selected', (event, rect) => {
if (regionSelectionWindow && !regionSelectionWindow.isDestroyed()) {
regionSelectionWindow.close();
+1 -1
View File
@@ -12,4 +12,4 @@ export async function resizeLayout() {
} catch (error) {
console.error('Error resizing window:', error);
}
}
}