screenshot moved from windowManager
This commit is contained in:
		
							parent
							
								
									9ec8df0548
								
							
						
					
					
						commit
						5c2f9c1eb7
					
				@ -67,6 +67,7 @@ module.exports = {
 | 
			
		||||
    ipcMain.handle('ask:sendQuestionFromAsk', async (event, userPrompt) => await askService.sendMessage(userPrompt));
 | 
			
		||||
    ipcMain.handle('ask:sendQuestionFromSummary', async (event, userPrompt) => await askService.sendMessage(userPrompt));
 | 
			
		||||
    ipcMain.handle('ask:toggleAskButton', async () => await askService.toggleAskButton());
 | 
			
		||||
    ipcMain.handle('stop-screen-capture', async () => askService.handleStopScreenCapture());
 | 
			
		||||
 | 
			
		||||
    // Listen
 | 
			
		||||
    ipcMain.handle('send-audio-content', async (event, { data, mimeType }) => await listenService.handleSendAudioContent(data, mimeType));
 | 
			
		||||
 | 
			
		||||
@ -1,10 +1,111 @@
 | 
			
		||||
const { BrowserWindow } = require('electron');
 | 
			
		||||
const { createStreamingLLM } = require('../common/ai/factory');
 | 
			
		||||
const { getCurrentModelInfo, windowPool, captureScreenshot } = require('../../window/windowManager');
 | 
			
		||||
const { getCurrentModelInfo, windowPool, updateLayout } = require('../../window/windowManager');
 | 
			
		||||
const sessionRepository = require('../common/repositories/session');
 | 
			
		||||
const askRepository = require('./repositories');
 | 
			
		||||
const { getSystemPrompt } = require('../common/prompts/promptBuilder');
 | 
			
		||||
const listenService = require('../listen/listenService');
 | 
			
		||||
const path = require('node:path');
 | 
			
		||||
const fs = require('node:fs');
 | 
			
		||||
const os = require('os');
 | 
			
		||||
const util = require('util');
 | 
			
		||||
const execFile = util.promisify(require('child_process').execFile);
 | 
			
		||||
const { desktopCapturer } = require('electron');
 | 
			
		||||
 | 
			
		||||
// Try to load sharp, but don't fail if it's not available
 | 
			
		||||
let sharp;
 | 
			
		||||
try {
 | 
			
		||||
    sharp = require('sharp');
 | 
			
		||||
    console.log('[AskService] Sharp module loaded successfully');
 | 
			
		||||
} catch (error) {
 | 
			
		||||
    console.warn('[AskService] Sharp module not available:', error.message);
 | 
			
		||||
    console.warn('[AskService] Screenshot functionality will work with reduced image processing capabilities');
 | 
			
		||||
    sharp = null;
 | 
			
		||||
}
 | 
			
		||||
let lastScreenshot = null;
 | 
			
		||||
 | 
			
		||||
async function captureScreenshot(options = {}) {
 | 
			
		||||
    if (process.platform === 'darwin') {
 | 
			
		||||
        try {
 | 
			
		||||
            const tempPath = path.join(os.tmpdir(), `screenshot-${Date.now()}.jpg`);
 | 
			
		||||
 | 
			
		||||
            await execFile('screencapture', ['-x', '-t', 'jpg', tempPath]);
 | 
			
		||||
 | 
			
		||||
            const imageBuffer = await fs.promises.readFile(tempPath);
 | 
			
		||||
            await fs.promises.unlink(tempPath);
 | 
			
		||||
 | 
			
		||||
            if (sharp) {
 | 
			
		||||
                try {
 | 
			
		||||
                    // Try using sharp for optimal image processing
 | 
			
		||||
                    const resizedBuffer = await sharp(imageBuffer)
 | 
			
		||||
                        .resize({ height: 384 })
 | 
			
		||||
                        .jpeg({ quality: 80 })
 | 
			
		||||
                        .toBuffer();
 | 
			
		||||
 | 
			
		||||
                    const base64 = resizedBuffer.toString('base64');
 | 
			
		||||
                    const metadata = await sharp(resizedBuffer).metadata();
 | 
			
		||||
 | 
			
		||||
                    lastScreenshot = {
 | 
			
		||||
                        base64,
 | 
			
		||||
                        width: metadata.width,
 | 
			
		||||
                        height: metadata.height,
 | 
			
		||||
                        timestamp: Date.now(),
 | 
			
		||||
                    };
 | 
			
		||||
 | 
			
		||||
                    return { success: true, base64, width: metadata.width, height: metadata.height };
 | 
			
		||||
                } catch (sharpError) {
 | 
			
		||||
                    console.warn('Sharp module failed, falling back to basic image processing:', sharpError.message);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            // Fallback: Return the original image without resizing
 | 
			
		||||
            console.log('[AskService] Using fallback image processing (no resize/compression)');
 | 
			
		||||
            const base64 = imageBuffer.toString('base64');
 | 
			
		||||
            
 | 
			
		||||
            lastScreenshot = {
 | 
			
		||||
                base64,
 | 
			
		||||
                width: null, // We don't have metadata without sharp
 | 
			
		||||
                height: null,
 | 
			
		||||
                timestamp: Date.now(),
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            return { success: true, base64, width: null, height: null };
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            console.error('Failed to capture screenshot:', error);
 | 
			
		||||
            return { success: false, error: error.message };
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        const sources = await desktopCapturer.getSources({
 | 
			
		||||
            types: ['screen'],
 | 
			
		||||
            thumbnailSize: {
 | 
			
		||||
                width: 1920,
 | 
			
		||||
                height: 1080,
 | 
			
		||||
            },
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        if (sources.length === 0) {
 | 
			
		||||
            throw new Error('No screen sources available');
 | 
			
		||||
        }
 | 
			
		||||
        const source = sources[0];
 | 
			
		||||
        const buffer = source.thumbnail.toJPEG(70);
 | 
			
		||||
        const base64 = buffer.toString('base64');
 | 
			
		||||
        const size = source.thumbnail.getSize();
 | 
			
		||||
 | 
			
		||||
        return {
 | 
			
		||||
            success: true,
 | 
			
		||||
            base64,
 | 
			
		||||
            width: size.width,
 | 
			
		||||
            height: size.height,
 | 
			
		||||
        };
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
        console.error('Failed to capture screenshot using desktopCapturer:', error);
 | 
			
		||||
        return {
 | 
			
		||||
            success: false,
 | 
			
		||||
            error: error.message,
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @class
 | 
			
		||||
@ -32,15 +133,17 @@ class AskService {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async toggleAskButton() {
 | 
			
		||||
        const { windowPool, updateLayout } = require('../../window/windowManager');
 | 
			
		||||
        const askWindow = windowPool.get('ask');
 | 
			
		||||
 | 
			
		||||
        // 답변이 있거나 스트리밍 중일 때
 | 
			
		||||
        const hasContent = this.state.isStreaming || (this.state.currentResponse && this.state.currentResponse.length > 0);
 | 
			
		||||
 | 
			
		||||
        if (askWindow.isVisible() && hasContent) {
 | 
			
		||||
            // 창을 닫는 대신, 텍스트 입력창만 토글합니다.
 | 
			
		||||
            this.state.showTextInput = !this.state.showTextInput;
 | 
			
		||||
            this._broadcastState();
 | 
			
		||||
            this._broadcastState(); // 변경된 상태 전파
 | 
			
		||||
        } else {
 | 
			
		||||
            // 기존의 창 보이기/숨기기 로직
 | 
			
		||||
            if (askWindow.isVisible()) {
 | 
			
		||||
                askWindow.webContents.send('window-hide-animation');
 | 
			
		||||
                this.state.isVisible = false;
 | 
			
		||||
@ -51,6 +154,7 @@ class AskService {
 | 
			
		||||
                updateLayout();
 | 
			
		||||
                askWindow.webContents.send('window-show-animation');
 | 
			
		||||
            }
 | 
			
		||||
            // 창이 다시 열릴 때를 대비해 상태를 초기화하고 전파합니다.
 | 
			
		||||
            if (this.state.isVisible) {
 | 
			
		||||
                this.state.showTextInput = true;
 | 
			
		||||
                this._broadcastState();
 | 
			
		||||
@ -77,7 +181,7 @@ class AskService {
 | 
			
		||||
     * @param {string} userPrompt
 | 
			
		||||
     * @returns {Promise<{success: boolean, response?: string, error?: string}>}
 | 
			
		||||
     */
 | 
			
		||||
    async sendMessage(userPrompt) {
 | 
			
		||||
    async sendMessage(userPrompt, conversationHistoryRaw=[]) {
 | 
			
		||||
        if (this.abortController) {
 | 
			
		||||
            this.abortController.abort('New request received.');
 | 
			
		||||
        }
 | 
			
		||||
@ -117,20 +221,7 @@ class AskService {
 | 
			
		||||
            const screenshotResult = await captureScreenshot({ quality: 'medium' });
 | 
			
		||||
            const screenshotBase64 = screenshotResult.success ? screenshotResult.base64 : null;
 | 
			
		||||
 | 
			
		||||
            let conversationHistoryRaw = [];
 | 
			
		||||
            try {
 | 
			
		||||
                const history = listenService.getConversationHistory();
 | 
			
		||||
                if (history && Array.isArray(history) && history.length > 0) {
 | 
			
		||||
                    console.log(`[AskService] Using conversation history from ListenService ${history.length} items).`);
 | 
			
		||||
                    conversationHistoryRaw = history;
 | 
			
		||||
                } else {
 | 
			
		||||
                    console.log('[AskService] No active conversation history found in ListenService.');
 | 
			
		||||
                }
 | 
			
		||||
            } catch (error) {
 | 
			
		||||
                console.error('[AskService] Failed to get conversation history from ListenService:', error);
 | 
			
		||||
            }
 | 
			
		||||
            const conversationHistory = this._formatConversationForPrompt(conversationHistoryRaw);
 | 
			
		||||
            console.log(`[AskService] Using conversation history (${conversationHistory}`);
 | 
			
		||||
 | 
			
		||||
            const systemPrompt = getSystemPrompt('pickle_glass_analysis', conversationHistory, false);
 | 
			
		||||
 | 
			
		||||
@ -241,6 +332,9 @@ class AskService {
 | 
			
		||||
                console.log(`[AskService] Stream reading was intentionally cancelled. Reason: ${signal.reason}`);
 | 
			
		||||
            } else {
 | 
			
		||||
                console.error('[AskService] Error while processing stream:', streamError);
 | 
			
		||||
                if (askWin && !askWin.isDestroyed()) {
 | 
			
		||||
                    askWin.webContents.send('ask-response-stream-error', { error: streamError.message });
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } finally {
 | 
			
		||||
            this.state.isStreaming = false;
 | 
			
		||||
@ -256,6 +350,12 @@ class AskService {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    handleStopScreenCapture() {
 | 
			
		||||
        lastScreenshot = null;
 | 
			
		||||
        console.log('[AskService] Stopped screen capture and cleared cache.');
 | 
			
		||||
        return { success: true };
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const askService = new AskService();
 | 
			
		||||
 | 
			
		||||
@ -2,23 +2,9 @@ const { BrowserWindow, globalShortcut, ipcMain, screen, app, shell, desktopCaptu
 | 
			
		||||
const WindowLayoutManager = require('./windowLayoutManager');
 | 
			
		||||
const SmoothMovementManager = require('./smoothMovementManager');
 | 
			
		||||
const path = require('node:path');
 | 
			
		||||
const fs = require('node:fs');
 | 
			
		||||
const os = require('os');
 | 
			
		||||
const util = require('util');
 | 
			
		||||
const execFile = util.promisify(require('child_process').execFile);
 | 
			
		||||
const shortcutsService = require('../features/shortcuts/shortcutsService');
 | 
			
		||||
const internalBridge = require('../bridge/internalBridge');
 | 
			
		||||
 | 
			
		||||
// Try to load sharp, but don't fail if it's not available
 | 
			
		||||
let sharp;
 | 
			
		||||
try {
 | 
			
		||||
    sharp = require('sharp');
 | 
			
		||||
    console.log('[WindowManager] Sharp module loaded successfully');
 | 
			
		||||
} catch (error) {
 | 
			
		||||
    console.warn('[WindowManager] Sharp module not available:', error.message);
 | 
			
		||||
    console.warn('[WindowManager] Screenshot functionality will work with reduced image processing capabilities');
 | 
			
		||||
    sharp = null;
 | 
			
		||||
}
 | 
			
		||||
const permissionRepository = require('../features/common/repositories/permission');
 | 
			
		||||
 | 
			
		||||
/* ────────────────[ GLASS BYPASS ]─────────────── */
 | 
			
		||||
@ -53,7 +39,6 @@ const DEFAULT_WINDOW_WIDTH = 353;
 | 
			
		||||
let currentHeaderState = 'apikey';
 | 
			
		||||
const windowPool = new Map();
 | 
			
		||||
let fixedYPosition = 0;
 | 
			
		||||
let lastScreenshot = null;
 | 
			
		||||
 | 
			
		||||
let settingsHideTimer = null;
 | 
			
		||||
 | 
			
		||||
@ -719,59 +704,6 @@ function setupIpcHandlers(movementManager) {
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    ipcMain.handle('start-screen-capture', async () => {
 | 
			
		||||
        try {
 | 
			
		||||
            isCapturing = true;
 | 
			
		||||
            console.log('Starting screen capture in main process');
 | 
			
		||||
            return { success: true };
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            console.error('Failed to start screen capture:', error);
 | 
			
		||||
            return { success: false, error: error.message };
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    ipcMain.handle('stop-screen-capture', async () => {
 | 
			
		||||
        try {
 | 
			
		||||
            isCapturing = false;
 | 
			
		||||
            lastScreenshot = null;
 | 
			
		||||
            console.log('Stopped screen capture in main process');
 | 
			
		||||
            return { success: true };
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            console.error('Failed to stop screen capture:', error);
 | 
			
		||||
            return { success: false, error: error.message };
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    ipcMain.handle('capture-screenshot', async (event, options = {}) => {
 | 
			
		||||
        return captureScreenshot(options);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    ipcMain.handle('get-current-screenshot', async event => {
 | 
			
		||||
        try {
 | 
			
		||||
            if (lastScreenshot && Date.now() - lastScreenshot.timestamp < 1000) {
 | 
			
		||||
                console.log('Returning cached screenshot');
 | 
			
		||||
                return {
 | 
			
		||||
                    success: true,
 | 
			
		||||
                    base64: lastScreenshot.base64,
 | 
			
		||||
                    width: lastScreenshot.width,
 | 
			
		||||
                    height: lastScreenshot.height,
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
            return {
 | 
			
		||||
                success: false,
 | 
			
		||||
                error: 'No screenshot available',
 | 
			
		||||
            };
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            console.error('Failed to get current screenshot:', error);
 | 
			
		||||
            return {
 | 
			
		||||
                success: false,
 | 
			
		||||
                error: error.message,
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // firebase-logout handler moved to windowBridge.js to avoid duplication
 | 
			
		||||
 | 
			
		||||
    ipcMain.handle('check-system-permissions', async () => {
 | 
			
		||||
        const { systemPreferences } = require('electron');
 | 
			
		||||
        const permissions = {
 | 
			
		||||
@ -1041,91 +973,6 @@ function setupApiKeyIPC() {
 | 
			
		||||
//////// after_modelStateService ////////
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async function captureScreenshot(options = {}) {
 | 
			
		||||
    if (process.platform === 'darwin') {
 | 
			
		||||
        try {
 | 
			
		||||
            const tempPath = path.join(os.tmpdir(), `screenshot-${Date.now()}.jpg`);
 | 
			
		||||
 | 
			
		||||
            await execFile('screencapture', ['-x', '-t', 'jpg', tempPath]);
 | 
			
		||||
 | 
			
		||||
            const imageBuffer = await fs.promises.readFile(tempPath);
 | 
			
		||||
            await fs.promises.unlink(tempPath);
 | 
			
		||||
 | 
			
		||||
            if (sharp) {
 | 
			
		||||
                try {
 | 
			
		||||
                    // Try using sharp for optimal image processing
 | 
			
		||||
                    const resizedBuffer = await sharp(imageBuffer)
 | 
			
		||||
                        // .resize({ height: 1080 })
 | 
			
		||||
                        .resize({ height: 384 })
 | 
			
		||||
                        .jpeg({ quality: 80 })
 | 
			
		||||
                        .toBuffer();
 | 
			
		||||
 | 
			
		||||
                    const base64 = resizedBuffer.toString('base64');
 | 
			
		||||
                    const metadata = await sharp(resizedBuffer).metadata();
 | 
			
		||||
 | 
			
		||||
                    lastScreenshot = {
 | 
			
		||||
                        base64,
 | 
			
		||||
                        width: metadata.width,
 | 
			
		||||
                        height: metadata.height,
 | 
			
		||||
                        timestamp: Date.now(),
 | 
			
		||||
                    };
 | 
			
		||||
 | 
			
		||||
                    return { success: true, base64, width: metadata.width, height: metadata.height };
 | 
			
		||||
                } catch (sharpError) {
 | 
			
		||||
                    console.warn('Sharp module failed, falling back to basic image processing:', sharpError.message);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            // Fallback: Return the original image without resizing
 | 
			
		||||
            console.log('[WindowManager] Using fallback image processing (no resize/compression)');
 | 
			
		||||
            const base64 = imageBuffer.toString('base64');
 | 
			
		||||
            
 | 
			
		||||
            lastScreenshot = {
 | 
			
		||||
                base64,
 | 
			
		||||
                width: null, // We don't have metadata without sharp
 | 
			
		||||
                height: null,
 | 
			
		||||
                timestamp: Date.now(),
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            return { success: true, base64, width: null, height: null };
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            console.error('Failed to capture screenshot:', error);
 | 
			
		||||
            return { success: false, error: error.message };
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        const sources = await desktopCapturer.getSources({
 | 
			
		||||
            types: ['screen'],
 | 
			
		||||
            thumbnailSize: {
 | 
			
		||||
                width: 1920,
 | 
			
		||||
                height: 1080,
 | 
			
		||||
            },
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        if (sources.length === 0) {
 | 
			
		||||
            throw new Error('No screen sources available');
 | 
			
		||||
        }
 | 
			
		||||
        const source = sources[0];
 | 
			
		||||
        const buffer = source.thumbnail.toJPEG(70);
 | 
			
		||||
        const base64 = buffer.toString('base64');
 | 
			
		||||
        const size = source.thumbnail.getSize();
 | 
			
		||||
 | 
			
		||||
        return {
 | 
			
		||||
            success: true,
 | 
			
		||||
            base64,
 | 
			
		||||
            width: size.width,
 | 
			
		||||
            height: size.height,
 | 
			
		||||
        };
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
        console.error('Failed to capture screenshot using desktopCapturer:', error);
 | 
			
		||||
        return {
 | 
			
		||||
            success: false,
 | 
			
		||||
            error: error.message,
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const closeWindow = (windowName) => {
 | 
			
		||||
    const win = windowPool.get(windowName);
 | 
			
		||||
    if (win && !win.isDestroyed()) {
 | 
			
		||||
@ -1141,7 +988,6 @@ module.exports = {
 | 
			
		||||
    getStoredApiKey,
 | 
			
		||||
    getStoredProvider,
 | 
			
		||||
    getCurrentModelInfo,
 | 
			
		||||
    captureScreenshot,
 | 
			
		||||
    toggleFeature, // Export toggleFeature so shortcutsService can use it
 | 
			
		||||
    toggleContentProtection,
 | 
			
		||||
    resizeHeaderWindow,
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user