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