refactor ask
This commit is contained in:
		
							parent
							
								
									0992cd4668
								
							
						
					
					
						commit
						bf13a865ba
					
				@ -57,8 +57,10 @@ module.exports = {
 | 
			
		||||
    ipcMain.handle('ollama:shutdown', async (event, force = false) => await ollamaService.handleShutdown(event, force));
 | 
			
		||||
 | 
			
		||||
    // Ask
 | 
			
		||||
    ipcMain.handle('ask:sendMessage', async (event, userPrompt, conversationHistoryRaw = []) => await askService.sendMessage(userPrompt, conversationHistoryRaw));
 | 
			
		||||
  
 | 
			
		||||
    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());
 | 
			
		||||
 | 
			
		||||
    // Listen
 | 
			
		||||
    ipcMain.handle('send-audio-content', async (event, { data, mimeType }) => await listenService.handleSendAudioContent(data, mimeType));
 | 
			
		||||
    ipcMain.handle('send-system-audio-content', async (event, { data, mimeType }) => {
 | 
			
		||||
@ -71,22 +73,6 @@ module.exports = {
 | 
			
		||||
    ipcMain.handle('start-macos-audio', async () => await listenService.handleStartMacosAudio());
 | 
			
		||||
    ipcMain.handle('stop-macos-audio', async () => await listenService.handleStopMacosAudio());
 | 
			
		||||
    ipcMain.handle('update-google-search-setting', async (event, enabled) => await listenService.handleUpdateGoogleSearchSetting(enabled));
 | 
			
		||||
 | 
			
		||||
     // ModelStateService
 | 
			
		||||
    ipcMain.handle('model:validate-key', async (e, { provider, key }) => await modelStateService.handleValidateKey(provider, key));
 | 
			
		||||
    ipcMain.handle('model:get-all-keys', () => modelStateService.getAllApiKeys());
 | 
			
		||||
    ipcMain.handle('model:set-api-key', async (e, { provider, key }) => await modelStateService.setApiKey(provider, key));
 | 
			
		||||
    ipcMain.handle('model:remove-api-key', async (e, { provider }) => await modelStateService.handleRemoveApiKey(provider));
 | 
			
		||||
    ipcMain.handle('model:get-selected-models', () => modelStateService.getSelectedModels());
 | 
			
		||||
    ipcMain.handle('model:set-selected-model', async (e, { type, modelId }) => await modelStateService.handleSetSelectedModel(type, modelId));
 | 
			
		||||
    ipcMain.handle('model:get-available-models', (e, { type }) => modelStateService.getAvailableModels(type));
 | 
			
		||||
    ipcMain.handle('model:are-providers-configured', () => modelStateService.areProvidersConfigured());
 | 
			
		||||
    ipcMain.handle('model:get-current-model-info', (e, { type }) => modelStateService.getCurrentModelInfo(type));
 | 
			
		||||
    ipcMain.handle('model:get-provider-config', () => modelStateService.getProviderConfig());
 | 
			
		||||
 | 
			
		||||
    console.log('[FeatureBridge] Initialized with all feature handlers.');
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    ipcMain.handle('listen:changeSession', async (event, listenButtonText) => {
 | 
			
		||||
      console.log('[FeatureBridge] listen:changeSession from mainheader', listenButtonText);
 | 
			
		||||
      try {
 | 
			
		||||
@ -100,6 +86,21 @@ module.exports = {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
     // ModelStateService
 | 
			
		||||
    ipcMain.handle('model:validate-key', async (e, { provider, key }) => await modelStateService.handleValidateKey(provider, key));
 | 
			
		||||
    ipcMain.handle('model:get-all-keys', () => modelStateService.getAllApiKeys());
 | 
			
		||||
    ipcMain.handle('model:set-api-key', async (e, { provider, key }) => await modelStateService.setApiKey(provider, key));
 | 
			
		||||
    ipcMain.handle('model:remove-api-key', async (e, { provider }) => await modelStateService.handleRemoveApiKey(provider));
 | 
			
		||||
    ipcMain.handle('model:get-selected-models', () => modelStateService.getSelectedModels());
 | 
			
		||||
    ipcMain.handle('model:set-selected-model', async (e, { type, modelId }) => await modelStateService.handleSetSelectedModel(type, modelId));
 | 
			
		||||
    ipcMain.handle('model:get-available-models', (e, { type }) => modelStateService.getAvailableModels(type));
 | 
			
		||||
    ipcMain.handle('model:are-providers-configured', () => modelStateService.areProvidersConfigured());
 | 
			
		||||
    ipcMain.handle('model:get-current-model-info', (e, { type }) => modelStateService.getCurrentModelInfo(type));
 | 
			
		||||
    ipcMain.handle('model:get-provider-config', () => modelStateService.getProviderConfig());
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    console.log('[FeatureBridge] Initialized with all feature handlers.');
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  // Renderer로 상태를 전송
 | 
			
		||||
 | 
			
		||||
@ -6,37 +6,60 @@ const askRepository = require('./repositories');
 | 
			
		||||
const { getSystemPrompt } = require('../common/prompts/promptBuilder');
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @class AskService
 | 
			
		||||
 * @description 사용자의 질문을 처리하고 AI 모델과 통신하여 응답을 스트리밍하는 모든 로직을 캡슐화합니다.
 | 
			
		||||
 * @class
 | 
			
		||||
 * @description
 | 
			
		||||
 */
 | 
			
		||||
class AskService {
 | 
			
		||||
    /**
 | 
			
		||||
     * AskService의 인스턴스를 생성합니다.
 | 
			
		||||
     */
 | 
			
		||||
    constructor() {
 | 
			
		||||
        this.abortController = null;
 | 
			
		||||
        console.log('[AskService] Service instance created.');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async toggleAskButton() {
 | 
			
		||||
        const { windowPool, updateLayout } = require('../../window/windowManager');
 | 
			
		||||
        const askWindow = windowPool.get('ask');
 | 
			
		||||
        const header = windowPool.get('header');
 | 
			
		||||
        try {
 | 
			
		||||
            if (askWindow.isVisible()) {
 | 
			
		||||
                askWindow.webContents.send('window-hide-animation');
 | 
			
		||||
            } else {
 | 
			
		||||
                console.log('[AskService] Showing hidden Ask window');
 | 
			
		||||
                askWindow.show();
 | 
			
		||||
                updateLayout();
 | 
			
		||||
                askWindow.webContents.send('window-show-animation');
 | 
			
		||||
            }
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            console.error('[AskService] error in toggleAskButton:', error);
 | 
			
		||||
            throw error; 
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 대화 기록 배열을 프롬프트에 적합한 단일 문자열로 변환합니다.
 | 
			
		||||
     * @param {string[]} conversationTexts - 대화 내용 문자열의 배열
 | 
			
		||||
     * @returns {string} 프롬프트에 사용될 형식의 대화 기록
 | 
			
		||||
     * 
 | 
			
		||||
     * @param {string[]} conversationTexts
 | 
			
		||||
     * @returns {string}
 | 
			
		||||
     * @private
 | 
			
		||||
     */
 | 
			
		||||
    _formatConversationForPrompt(conversationTexts) {
 | 
			
		||||
        if (!conversationTexts || conversationTexts.length === 0) {
 | 
			
		||||
            return 'No conversation history available.';
 | 
			
		||||
        }
 | 
			
		||||
        // 최근 30개의 대화만 사용
 | 
			
		||||
        return conversationTexts.slice(-30).join('\n');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 사용자의 프롬프트를 받아 AI 모델에 전송하고, 응답을 스트리밍으로 처리합니다.
 | 
			
		||||
     * @param {string} userPrompt - 사용자가 입력한 질문 또는 메시지
 | 
			
		||||
     * @returns {Promise<{success: boolean, response?: string, error?: string}>} 처리 결과 객체
 | 
			
		||||
     * 
 | 
			
		||||
     * @param {string} userPrompt
 | 
			
		||||
     * @returns {Promise<{success: boolean, response?: string, error?: string}>}
 | 
			
		||||
     */
 | 
			
		||||
    async sendMessage(userPrompt, conversationHistoryRaw=[]) {
 | 
			
		||||
        if (this.abortController) {
 | 
			
		||||
            this.abortController.abort('New request received.');
 | 
			
		||||
        }
 | 
			
		||||
        this.abortController = new AbortController();
 | 
			
		||||
        const { signal } = this.abortController;
 | 
			
		||||
 | 
			
		||||
        if (!userPrompt || userPrompt.trim().length === 0) {
 | 
			
		||||
            console.warn('[AskService] Cannot process empty message');
 | 
			
		||||
            return { success: false, error: 'Empty message' };
 | 
			
		||||
@ -47,7 +70,6 @@ class AskService {
 | 
			
		||||
        try {
 | 
			
		||||
            console.log(`[AskService] 🤖 Processing message: ${userPrompt.substring(0, 50)}...`);
 | 
			
		||||
 | 
			
		||||
            // --- 사용자 메시지 저장 ---
 | 
			
		||||
            sessionId = await sessionRepository.getOrCreateActive('ask');
 | 
			
		||||
            await askRepository.addAiMessage({ sessionId, role: 'user', content: userPrompt.trim() });
 | 
			
		||||
            console.log(`[AskService] DB: Saved user prompt to session ${sessionId}`);
 | 
			
		||||
@ -61,7 +83,6 @@ class AskService {
 | 
			
		||||
            const screenshotResult = await captureScreenshot({ quality: 'medium' });
 | 
			
		||||
            const screenshotBase64 = screenshotResult.success ? screenshotResult.base64 : null;
 | 
			
		||||
 | 
			
		||||
            // const conversationHistoryRaw = this._getConversationHistory();
 | 
			
		||||
            const conversationHistory = this._formatConversationForPrompt(conversationHistoryRaw);
 | 
			
		||||
 | 
			
		||||
            const systemPrompt = getSystemPrompt('pickle_glass_analysis', conversationHistory, false);
 | 
			
		||||
@ -101,14 +122,22 @@ class AskService {
 | 
			
		||||
                return { success: false, error: 'Ask window is not available.' };
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // --- 스트림 처리 ---
 | 
			
		||||
            await this._processStream(response.body, askWin, sessionId);
 | 
			
		||||
            const reader = response.body.getReader();
 | 
			
		||||
            signal.addEventListener('abort', () => {
 | 
			
		||||
                console.log(`[AskService] Aborting stream reader. Reason: ${signal.reason}`);
 | 
			
		||||
                reader.cancel(signal.reason).catch(() => { /* 이미 취소된 경우의 오류는 무시 */ });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            // _processStream 내부에서 전체 응답이 완료되면 반환됩니다.
 | 
			
		||||
            // 하지만 비동기 스트림의 특성상 이 지점에서는 직접 반환 값을 알기 어렵습니다.
 | 
			
		||||
            // 성공/실패 여부는 스트림 처리 로직 내에서 결정됩니다.
 | 
			
		||||
            await this._processStream(reader, askWin, sessionId, signal);
 | 
			
		||||
 | 
			
		||||
            return { success: true };
 | 
			
		||||
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            if (error.name === 'AbortError') {
 | 
			
		||||
                console.log('[AskService] SendMessage operation was successfully aborted.');
 | 
			
		||||
                return { success: true, response: 'Cancelled' };
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            console.error('[AskService] Error processing message:', error);
 | 
			
		||||
            const askWin = windowPool.get('ask');
 | 
			
		||||
            if (askWin && !askWin.isDestroyed()) {
 | 
			
		||||
@ -119,18 +148,17 @@ class AskService {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * AI 모델로부터 받은 응답 스트림을 처리합니다.
 | 
			
		||||
     * @param {ReadableStream} body - 스트리밍 응답의 body
 | 
			
		||||
     * @param {BrowserWindow} askWin - 응답을 보낼 대상 창
 | 
			
		||||
     * @param {number} sessionId - 현재 세션 ID
 | 
			
		||||
     * 
 | 
			
		||||
     * @param {ReadableStreamDefaultReader} reader
 | 
			
		||||
     * @param {BrowserWindow} askWin
 | 
			
		||||
     * @param {number} sessionId 
 | 
			
		||||
     * @param {AbortSignal} signal
 | 
			
		||||
     * @returns {Promise<void>}
 | 
			
		||||
     * @private
 | 
			
		||||
     */
 | 
			
		||||
    async _processStream(body, askWin, sessionId) {
 | 
			
		||||
        const reader = body.getReader();
 | 
			
		||||
    async _processStream(reader, askWin, sessionId, signal) {
 | 
			
		||||
        const decoder = new TextDecoder();
 | 
			
		||||
        let fullResponse = '';
 | 
			
		||||
        let finalResult = { success: false }; // 최종 결과 저장을 위한 변수
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            while (true) {
 | 
			
		||||
@ -144,13 +172,9 @@ class AskService {
 | 
			
		||||
                    if (line.startsWith('data: ')) {
 | 
			
		||||
                        const data = line.substring(6);
 | 
			
		||||
                        if (data === '[DONE]') {
 | 
			
		||||
                            askWin.webContents.send('ask-response-stream-end');
 | 
			
		||||
                            
 | 
			
		||||
                            await askRepository.addAiMessage({ sessionId, role: 'assistant', content: fullResponse });
 | 
			
		||||
                            console.log(`[AskService] DB: Saved assistant response to session ${sessionId}`);
 | 
			
		||||
                            
 | 
			
		||||
                            // 스트림이 성공적으로 완료되었으므로, 최종 결과를 성공으로 설정합니다.
 | 
			
		||||
                            // 실제 반환은 sendMessage에서 이루어지지만, 로직상의 완료를 의미합니다.
 | 
			
		||||
                            if (askWin && !askWin.isDestroyed()) {
 | 
			
		||||
                                askWin.webContents.send('ask-response-stream-end');
 | 
			
		||||
                            }
 | 
			
		||||
                            return; 
 | 
			
		||||
                        }
 | 
			
		||||
                        try {
 | 
			
		||||
@ -158,35 +182,37 @@ class AskService {
 | 
			
		||||
                            const token = json.choices[0]?.delta?.content || '';
 | 
			
		||||
                            if (token) {
 | 
			
		||||
                                fullResponse += token;
 | 
			
		||||
                                askWin.webContents.send('ask-response-chunk', { token });
 | 
			
		||||
                                if (askWin && !askWin.isDestroyed()) {
 | 
			
		||||
                                    askWin.webContents.send('ask-response-chunk', { token });
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        } catch (error) {
 | 
			
		||||
                            // JSON 파싱 오류는 무시하고 계속 진행
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } catch (streamError) {
 | 
			
		||||
            console.error('[AskService] Error while processing stream:', streamError);
 | 
			
		||||
            askWin.webContents.send('ask-response-stream-error', { error: streamError.message });
 | 
			
		||||
            // 스트림 처리 중 에러가 발생했음을 기록
 | 
			
		||||
            if (signal.aborted) {
 | 
			
		||||
                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 {
 | 
			
		||||
            // 스트림이 정상적으로 [DONE]을 받지 못하고 종료된 경우에도
 | 
			
		||||
            // 현재까지의 응답이라도 저장 시도
 | 
			
		||||
            if (fullResponse) {
 | 
			
		||||
                 try {
 | 
			
		||||
                    await askRepository.addAiMessage({ sessionId, role: 'assistant', content: fullResponse });
 | 
			
		||||
                    console.log(`[AskService] DB: Saved partial assistant response to session ${sessionId} after stream interruption.`);
 | 
			
		||||
                    console.log(`[AskService] DB: Saved partial or full assistant response to session ${sessionId} after stream ended.`);
 | 
			
		||||
                } catch(dbError) {
 | 
			
		||||
                    console.error("[AskService] DB: Failed to save assistant response after stream interruption:", dbError);
 | 
			
		||||
                    console.error("[AskService] DB: Failed to save assistant response after stream ended:", dbError);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AskService 클래스의 단일 인스턴스를 생성하여 내보냅니다.
 | 
			
		||||
// 이렇게 하면 애플리케이션 전체에서 동일한 서비스 인스턴스를 공유하게 됩니다.
 | 
			
		||||
const askService = new AskService();
 | 
			
		||||
 | 
			
		||||
module.exports = askService;
 | 
			
		||||
@ -131,7 +131,7 @@ contextBridge.exposeInMainWorld('api', {
 | 
			
		||||
    adjustWindowHeight: (height) => ipcRenderer.invoke('adjust-window-height', height),
 | 
			
		||||
    
 | 
			
		||||
    // Message Handling
 | 
			
		||||
    sendMessage: (text) => ipcRenderer.invoke('ask:sendMessage', text),
 | 
			
		||||
    sendMessage: (text) => ipcRenderer.invoke('ask:sendQuestionFromAsk', text),
 | 
			
		||||
    
 | 
			
		||||
    // Listeners
 | 
			
		||||
    onSendQuestionToRenderer: (callback) => ipcRenderer.on('ask:sendQuestionToRenderer', callback),
 | 
			
		||||
@ -170,7 +170,7 @@ contextBridge.exposeInMainWorld('api', {
 | 
			
		||||
  // src/ui/listen/summary/SummaryView.js
 | 
			
		||||
  summaryView: {
 | 
			
		||||
    // Message Handling
 | 
			
		||||
    sendQuestionToMain: (text) => ipcRenderer.invoke('ask:sendQuestionToMain', text),
 | 
			
		||||
    sendQuestionFromSummary: (text) => ipcRenderer.invoke('ask:sendQuestionFromSummary', text),
 | 
			
		||||
    
 | 
			
		||||
    // Listeners
 | 
			
		||||
    onSummaryUpdate: (callback) => ipcRenderer.on('summary-update', callback),
 | 
			
		||||
 | 
			
		||||
@ -568,6 +568,17 @@ export class MainHeader extends LitElement {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async _handleAskClick() {
 | 
			
		||||
        if (this.wasJustDragged) return;
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            const channel = 'ask:toggleAskButton';
 | 
			
		||||
            await this.invoke(channel);
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            console.error('IPC invoke for ask button failed:', error);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    renderShortcut(accelerator) {
 | 
			
		||||
        if (!accelerator) return html``;
 | 
			
		||||
@ -636,7 +647,7 @@ export class MainHeader extends LitElement {
 | 
			
		||||
                        `}
 | 
			
		||||
                </button>
 | 
			
		||||
 | 
			
		||||
                <div class="header-actions ask-action" @click=${() => this.invoke('toggle-feature', 'ask')}>
 | 
			
		||||
                <div class="header-actions ask-action" @click=${() => this._handleAskClick()}>
 | 
			
		||||
                    <div class="action-text">
 | 
			
		||||
                        <div class="action-text-content">Ask</div>
 | 
			
		||||
                    </div>
 | 
			
		||||
 | 
			
		||||
@ -408,7 +408,7 @@ export class SummaryView extends LitElement {
 | 
			
		||||
 | 
			
		||||
        if (window.api) {
 | 
			
		||||
            try {
 | 
			
		||||
                const result = await window.api.summaryView.sendQuestionToMain(requestText);
 | 
			
		||||
                const result = await window.api.summaryView.sendQuestionFromSummary(requestText);
 | 
			
		||||
 | 
			
		||||
                if (result.success) {
 | 
			
		||||
                    console.log('✅ Question sent to AskView successfully');
 | 
			
		||||
 | 
			
		||||
@ -887,52 +887,13 @@ function setupIpcHandlers(movementManager) {
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    ipcMain.handle('ask:sendQuestionToMain', (event, question) => {
 | 
			
		||||
        console.log('📨 Main process: Sending question to AskView', question);
 | 
			
		||||
        toggleFeature('ask', {ask: { questionText: question }});
 | 
			
		||||
        return { success: true };
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // ipcMain.handle('listen:changeSession', async (event, actionText) => {
 | 
			
		||||
    //     console.log('📨 Main process: Received actionText', actionText);
 | 
			
		||||
    //     const header = windowPool.get('header');
 | 
			
		||||
    //     const listenWindow = windowPool.get('listen');
 | 
			
		||||
 | 
			
		||||
    //     try {
 | 
			
		||||
    //         if (listenService && listenService.isSessionActive()) {
 | 
			
		||||
    //             console.log('[WindowManager] Listen session is active, closing it.');
 | 
			
		||||
    //             // ✨ closeSession도 비동기일 수 있으므로 await 처리 (만약 동기 함수라면 await는 무시됨)
 | 
			
		||||
    //             await listenService.closeSession();
 | 
			
		||||
    //             listenWindow.webContents.send('session-state-changed', { isActive: false });
 | 
			
		||||
    //         } else {
 | 
			
		||||
    //             if (listenWindow.isVisible()) {
 | 
			
		||||
    //                 listenWindow.webContents.send('window-hide-animation');
 | 
			
		||||
    //                 listenWindow.webContents.send('session-state-changed', { isActive: false });
 | 
			
		||||
    //             } else {
 | 
			
		||||
    //                 listenWindow.show();
 | 
			
		||||
    //                 updateLayout();
 | 
			
		||||
    //                 listenWindow.webContents.send('window-show-animation');
 | 
			
		||||
                    
 | 
			
		||||
    //                 // ✨ 핵심: initializeSession 작업이 끝날 때까지 기다림
 | 
			
		||||
    //                 await listenService.initializeSession(); 
 | 
			
		||||
                    
 | 
			
		||||
    //                 listenWindow.webContents.send('session-state-changed', { isActive: true });
 | 
			
		||||
    //             }
 | 
			
		||||
    //         }
 | 
			
		||||
 | 
			
		||||
    //         // ✨ 모든 비동기 작업이 성공적으로 끝난 후 결과 전송
 | 
			
		||||
    //         header.webContents.send('listen:changeSessionResult', { success: true });
 | 
			
		||||
    //         return { success: true };
 | 
			
		||||
 | 
			
		||||
    //     } catch (error) {
 | 
			
		||||
    //         console.error('[WindowManager] Failed to change listen session:', error);
 | 
			
		||||
            
 | 
			
		||||
    //         // ✨ 작업 실패 시 UI에 실패 결과를 알려 로딩 상태를 해제하도록 함
 | 
			
		||||
    //         header.webContents.send('listen:changeSessionResult', { success: false });
 | 
			
		||||
    //         return { success: false, error: error.message };
 | 
			
		||||
    //     }
 | 
			
		||||
    // ipcMain.handle('ask:sendQuestionToMain', (event, question) => {
 | 
			
		||||
    //     console.log('📨 Main process: Sending question to AskView', question);
 | 
			
		||||
    //     toggleFeature('ask', {ask: { questionText: question }});
 | 
			
		||||
    //     return { success: true };
 | 
			
		||||
    // });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -950,33 +911,6 @@ async function toggleFeature(featureName, options = {}) {
 | 
			
		||||
        createFeatureWindows(windowPool.get('header'));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const header = windowPool.get('header');
 | 
			
		||||
    // if (featureName === 'listen') {
 | 
			
		||||
    //     console.log(`[WindowManager] Toggling feature: ${featureName}`);
 | 
			
		||||
    //     const listenWindow = windowPool.get(featureName);
 | 
			
		||||
    //     // const listenService = global.listenService;
 | 
			
		||||
    //     if (listenService && listenService.isSessionActive()) {
 | 
			
		||||
    //         console.log('[WindowManager] Listen session is active, closing it via toggle.');
 | 
			
		||||
    //         await listenService.closeSession();
 | 
			
		||||
    //         listenWindow.webContents.send('session-state-changed', { isActive: false });
 | 
			
		||||
    //         header.webContents.send('session-state-text', 'Done');
 | 
			
		||||
    //         // return;
 | 
			
		||||
    //     } else {
 | 
			
		||||
    //         if (listenWindow.isVisible()) {
 | 
			
		||||
    //             listenWindow.webContents.send('window-hide-animation');
 | 
			
		||||
    //             listenWindow.webContents.send('session-state-changed', { isActive: false });
 | 
			
		||||
    //             header.webContents.send('session-state-text', 'Listen');
 | 
			
		||||
    //         } else {
 | 
			
		||||
    //             listenWindow.show();
 | 
			
		||||
    //             updateLayout();
 | 
			
		||||
    //             listenWindow.webContents.send('window-show-animation');
 | 
			
		||||
    //             await listenService.initializeSession();
 | 
			
		||||
    //             listenWindow.webContents.send('session-state-changed', { isActive: true });
 | 
			
		||||
    //             header.webContents.send('session-state-text', 'Stop');
 | 
			
		||||
    //         }
 | 
			
		||||
    //     }
 | 
			
		||||
    // }
 | 
			
		||||
 | 
			
		||||
    if (featureName === 'ask') {
 | 
			
		||||
        let askWindow = windowPool.get('ask');
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user