change askservice to class
This commit is contained in:
parent
27f6f0e68e
commit
b5b6f40995
@ -1,145 +1,203 @@
|
|||||||
const { ipcMain, BrowserWindow } = require('electron');
|
const { ipcMain, BrowserWindow } = require('electron');
|
||||||
const { createStreamingLLM } = require('../common/ai/factory');
|
const { createStreamingLLM } = require('../common/ai/factory');
|
||||||
const { getStoredApiKey, getStoredProvider, getCurrentModelInfo, windowPool, captureScreenshot } = require('../../window/windowManager');
|
const { getCurrentModelInfo, windowPool, captureScreenshot } = require('../../window/windowManager');
|
||||||
const authService = require('../common/services/authService');
|
|
||||||
const sessionRepository = require('../common/repositories/session');
|
const sessionRepository = require('../common/repositories/session');
|
||||||
const askRepository = require('./repositories');
|
const askRepository = require('./repositories');
|
||||||
const { getSystemPrompt } = require('../common/prompts/promptBuilder');
|
const { getSystemPrompt } = require('../common/prompts/promptBuilder');
|
||||||
|
|
||||||
function formatConversationForPrompt(conversationTexts) {
|
/**
|
||||||
if (!conversationTexts || conversationTexts.length === 0) return 'No conversation history available.';
|
* @class AskService
|
||||||
return conversationTexts.slice(-30).join('\n');
|
* @description 사용자의 질문을 처리하고 AI 모델과 통신하여 응답을 스트리밍하는 모든 로직을 캡슐화합니다.
|
||||||
}
|
*/
|
||||||
|
class AskService {
|
||||||
// Access conversation history via the global listenService instance created in index.js
|
/**
|
||||||
function getConversationHistory() {
|
* AskService의 인스턴스를 생성합니다.
|
||||||
const listenService = global.listenService;
|
*/
|
||||||
return listenService ? listenService.getConversationHistory() : [];
|
constructor() {
|
||||||
}
|
console.log('[AskService] Service instance created.');
|
||||||
|
|
||||||
async function sendMessage(userPrompt) {
|
|
||||||
if (!userPrompt || userPrompt.trim().length === 0) {
|
|
||||||
console.warn('[AskService] Cannot process empty message');
|
|
||||||
return { success: false, error: 'Empty message' };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let sessionId;
|
/**
|
||||||
|
* IPC 리스너를 등록하여 렌더러 프로세스로부터의 요청을 처리합니다.
|
||||||
try {
|
* Electron 애플리케이션의 메인 프로세스에서 한 번만 호출되어야 합니다.
|
||||||
console.log(`[AskService] 🤖 Processing message: ${userPrompt.substring(0, 50)}...`);
|
*/
|
||||||
|
initialize() {
|
||||||
// --- Save user's message immediately ---
|
ipcMain.handle('ask:sendMessage', async (event, userPrompt, conversationHistoryRaw=[]) => {
|
||||||
// This ensures the user message is always timestamped before the assistant's response.
|
return this.sendMessage(userPrompt, conversationHistoryRaw);
|
||||||
sessionId = await sessionRepository.getOrCreateActive('ask');
|
|
||||||
await askRepository.addAiMessage({ sessionId, role: 'user', content: userPrompt.trim() });
|
|
||||||
console.log(`[AskService] DB: Saved user prompt to session ${sessionId}`);
|
|
||||||
// --- End of user message saving ---
|
|
||||||
|
|
||||||
const modelInfo = await getCurrentModelInfo(null, { type: 'llm' });
|
|
||||||
if (!modelInfo || !modelInfo.apiKey) {
|
|
||||||
throw new Error('AI model or API key not configured.');
|
|
||||||
}
|
|
||||||
console.log(`[AskService] Using model: ${modelInfo.model} for provider: ${modelInfo.provider}`);
|
|
||||||
|
|
||||||
const screenshotResult = await captureScreenshot({ quality: 'medium' });
|
|
||||||
const screenshotBase64 = screenshotResult.success ? screenshotResult.base64 : null;
|
|
||||||
|
|
||||||
const conversationHistoryRaw = getConversationHistory();
|
|
||||||
const conversationHistory = formatConversationForPrompt(conversationHistoryRaw);
|
|
||||||
|
|
||||||
const systemPrompt = getSystemPrompt('pickle_glass_analysis', conversationHistory, false);
|
|
||||||
|
|
||||||
|
|
||||||
const messages = [
|
|
||||||
{ role: 'system', content: systemPrompt },
|
|
||||||
{
|
|
||||||
role: 'user',
|
|
||||||
content: [
|
|
||||||
{ type: 'text', text: `User Request: ${userPrompt.trim()}` },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
if (screenshotBase64) {
|
|
||||||
messages[1].content.push({
|
|
||||||
type: 'image_url',
|
|
||||||
image_url: { url: `data:image/jpeg;base64,${screenshotBase64}` },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const streamingLLM = createStreamingLLM(modelInfo.provider, {
|
|
||||||
apiKey: modelInfo.apiKey,
|
|
||||||
model: modelInfo.model,
|
|
||||||
temperature: 0.7,
|
|
||||||
maxTokens: 2048,
|
|
||||||
usePortkey: modelInfo.provider === 'openai-glass',
|
|
||||||
portkeyVirtualKey: modelInfo.provider === 'openai-glass' ? modelInfo.apiKey : undefined,
|
|
||||||
});
|
});
|
||||||
|
console.log('[AskService] Initialized and ready.');
|
||||||
|
}
|
||||||
|
|
||||||
const response = await streamingLLM.streamChat(messages);
|
/**
|
||||||
|
* 대화 기록 배열을 프롬프트에 적합한 단일 문자열로 변환합니다.
|
||||||
|
* @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');
|
||||||
|
}
|
||||||
|
|
||||||
// --- Stream Processing ---
|
/**
|
||||||
const reader = response.body.getReader();
|
* 사용자의 프롬프트를 받아 AI 모델에 전송하고, 응답을 스트리밍으로 처리합니다.
|
||||||
|
* @param {string} userPrompt - 사용자가 입력한 질문 또는 메시지
|
||||||
|
* @returns {Promise<{success: boolean, response?: string, error?: string}>} 처리 결과 객체
|
||||||
|
*/
|
||||||
|
async sendMessage(userPrompt, conversationHistoryRaw=[]) {
|
||||||
|
if (!userPrompt || userPrompt.trim().length === 0) {
|
||||||
|
console.warn('[AskService] Cannot process empty message');
|
||||||
|
return { success: false, error: 'Empty message' };
|
||||||
|
}
|
||||||
|
|
||||||
|
let sessionId;
|
||||||
|
|
||||||
|
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}`);
|
||||||
|
|
||||||
|
const modelInfo = await getCurrentModelInfo(null, { type: 'llm' });
|
||||||
|
if (!modelInfo || !modelInfo.apiKey) {
|
||||||
|
throw new Error('AI model or API key not configured.');
|
||||||
|
}
|
||||||
|
console.log(`[AskService] Using model: ${modelInfo.model} for provider: ${modelInfo.provider}`);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
const messages = [
|
||||||
|
{ role: 'system', content: systemPrompt },
|
||||||
|
{
|
||||||
|
role: 'user',
|
||||||
|
content: [
|
||||||
|
{ type: 'text', text: `User Request: ${userPrompt.trim()}` },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
if (screenshotBase64) {
|
||||||
|
messages[1].content.push({
|
||||||
|
type: 'image_url',
|
||||||
|
image_url: { url: `data:image/jpeg;base64,${screenshotBase64}` },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const streamingLLM = createStreamingLLM(modelInfo.provider, {
|
||||||
|
apiKey: modelInfo.apiKey,
|
||||||
|
model: modelInfo.model,
|
||||||
|
temperature: 0.7,
|
||||||
|
maxTokens: 2048,
|
||||||
|
usePortkey: modelInfo.provider === 'openai-glass',
|
||||||
|
portkeyVirtualKey: modelInfo.provider === 'openai-glass' ? modelInfo.apiKey : undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
const response = await streamingLLM.streamChat(messages);
|
||||||
|
const askWin = windowPool.get('ask');
|
||||||
|
|
||||||
|
if (!askWin || askWin.isDestroyed()) {
|
||||||
|
console.error("[AskService] Ask window is not available to send stream to.");
|
||||||
|
response.body.getReader().cancel();
|
||||||
|
return { success: false, error: 'Ask window is not available.' };
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- 스트림 처리 ---
|
||||||
|
await this._processStream(response.body, askWin, sessionId);
|
||||||
|
|
||||||
|
// _processStream 내부에서 전체 응답이 완료되면 반환됩니다.
|
||||||
|
// 하지만 비동기 스트림의 특성상 이 지점에서는 직접 반환 값을 알기 어렵습니다.
|
||||||
|
// 성공/실패 여부는 스트림 처리 로직 내에서 결정됩니다.
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[AskService] Error processing message:', error);
|
||||||
|
const askWin = windowPool.get('ask');
|
||||||
|
if (askWin && !askWin.isDestroyed()) {
|
||||||
|
askWin.webContents.send('ask-response-stream-error', { error: error.message });
|
||||||
|
}
|
||||||
|
return { success: false, error: error.message };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AI 모델로부터 받은 응답 스트림을 처리합니다.
|
||||||
|
* @param {ReadableStream} body - 스트리밍 응답의 body
|
||||||
|
* @param {BrowserWindow} askWin - 응답을 보낼 대상 창
|
||||||
|
* @param {number} sessionId - 현재 세션 ID
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
async _processStream(body, askWin, sessionId) {
|
||||||
|
const reader = body.getReader();
|
||||||
const decoder = new TextDecoder();
|
const decoder = new TextDecoder();
|
||||||
let fullResponse = '';
|
let fullResponse = '';
|
||||||
|
let finalResult = { success: false }; // 최종 결과 저장을 위한 변수
|
||||||
|
|
||||||
const askWin = windowPool.get('ask');
|
try {
|
||||||
if (!askWin || askWin.isDestroyed()) {
|
while (true) {
|
||||||
console.error("[AskService] Ask window is not available to send stream to.");
|
const { done, value } = await reader.read();
|
||||||
reader.cancel();
|
if (done) break;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (true) {
|
const chunk = decoder.decode(value);
|
||||||
const { done, value } = await reader.read();
|
const lines = chunk.split('\n').filter(line => line.trim() !== '');
|
||||||
if (done) break;
|
|
||||||
|
|
||||||
const chunk = decoder.decode(value);
|
for (const line of lines) {
|
||||||
const lines = chunk.split('\n').filter(line => line.trim() !== '');
|
if (line.startsWith('data: ')) {
|
||||||
|
const data = line.substring(6);
|
||||||
for (const line of lines) {
|
if (data === '[DONE]') {
|
||||||
if (line.startsWith('data: ')) {
|
askWin.webContents.send('ask-response-stream-end');
|
||||||
const data = line.substring(6);
|
|
||||||
if (data === '[DONE]') {
|
|
||||||
askWin.webContents.send('ask-response-stream-end');
|
|
||||||
|
|
||||||
// Save assistant's message to DB
|
|
||||||
try {
|
|
||||||
// sessionId is already available from when we saved the user prompt
|
|
||||||
await askRepository.addAiMessage({ sessionId, role: 'assistant', content: fullResponse });
|
await askRepository.addAiMessage({ sessionId, role: 'assistant', content: fullResponse });
|
||||||
console.log(`[AskService] DB: Saved assistant response to session ${sessionId}`);
|
console.log(`[AskService] DB: Saved assistant response to session ${sessionId}`);
|
||||||
} catch(dbError) {
|
|
||||||
console.error("[AskService] DB: Failed to save assistant response:", dbError);
|
// 스트림이 성공적으로 완료되었으므로, 최종 결과를 성공으로 설정합니다.
|
||||||
|
// 실제 반환은 sendMessage에서 이루어지지만, 로직상의 완료를 의미합니다.
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
return { success: true, response: fullResponse };
|
const json = JSON.parse(data);
|
||||||
}
|
const token = json.choices[0]?.delta?.content || '';
|
||||||
try {
|
if (token) {
|
||||||
const json = JSON.parse(data);
|
fullResponse += token;
|
||||||
const token = json.choices[0]?.delta?.content || '';
|
askWin.webContents.send('ask-response-chunk', { token });
|
||||||
if (token) {
|
}
|
||||||
fullResponse += token;
|
} catch (error) {
|
||||||
askWin.webContents.send('ask-response-chunk', { token });
|
// JSON 파싱 오류는 무시하고 계속 진행
|
||||||
}
|
}
|
||||||
} catch (error) {
|
|
||||||
// Ignore parsing errors for now
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (streamError) {
|
||||||
|
console.error('[AskService] Error while processing stream:', streamError);
|
||||||
|
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.`);
|
||||||
|
} catch(dbError) {
|
||||||
|
console.error("[AskService] DB: Failed to save assistant response after stream interruption:", dbError);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
|
||||||
console.error('[AskService] Error processing message:', error);
|
|
||||||
return { success: false, error: error.message };
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function initialize() {
|
// AskService 클래스의 단일 인스턴스를 생성하여 내보냅니다.
|
||||||
ipcMain.handle('ask:sendMessage', async (event, userPrompt) => {
|
// 이렇게 하면 애플리케이션 전체에서 동일한 서비스 인스턴스를 공유하게 됩니다.
|
||||||
return sendMessage(userPrompt);
|
const askService = new AskService();
|
||||||
});
|
|
||||||
console.log('[AskService] Initialized and ready.');
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = askService;
|
||||||
initialize,
|
|
||||||
};
|
|
@ -1,4 +1,4 @@
|
|||||||
const { BrowserWindow, app } = require('electron');
|
const { ipcMain, BrowserWindow } = require('electron');
|
||||||
const SttService = require('./stt/sttService');
|
const SttService = require('./stt/sttService');
|
||||||
const SummaryService = require('./summary/summaryService');
|
const SummaryService = require('./summary/summaryService');
|
||||||
const authService = require('../common/services/authService');
|
const authService = require('../common/services/authService');
|
||||||
@ -11,8 +11,9 @@ class ListenService {
|
|||||||
this.summaryService = new SummaryService();
|
this.summaryService = new SummaryService();
|
||||||
this.currentSessionId = null;
|
this.currentSessionId = null;
|
||||||
this.isInitializingSession = false;
|
this.isInitializingSession = false;
|
||||||
|
|
||||||
this.setupServiceCallbacks();
|
this.setupServiceCallbacks();
|
||||||
|
console.log('[ListenService] Service instance created.');
|
||||||
}
|
}
|
||||||
|
|
||||||
setupServiceCallbacks() {
|
setupServiceCallbacks() {
|
||||||
@ -45,6 +46,11 @@ class ListenService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
initialize() {
|
||||||
|
this.setupIpcHandlers();
|
||||||
|
console.log('[ListenService] Initialized and ready.');
|
||||||
|
}
|
||||||
|
|
||||||
async handleTranscriptionComplete(speaker, text) {
|
async handleTranscriptionComplete(speaker, text) {
|
||||||
console.log(`[ListenService] Transcription complete: ${speaker} - ${text}`);
|
console.log(`[ListenService] Transcription complete: ${speaker} - ${text}`);
|
||||||
|
|
||||||
@ -183,6 +189,8 @@ class ListenService {
|
|||||||
// Close STT sessions
|
// Close STT sessions
|
||||||
await this.sttService.closeSessions();
|
await this.sttService.closeSessions();
|
||||||
|
|
||||||
|
await this.stopMacOSAudioCapture();
|
||||||
|
|
||||||
// End database session
|
// End database session
|
||||||
if (this.currentSessionId) {
|
if (this.currentSessionId) {
|
||||||
await sessionRepository.end(this.currentSessionId);
|
await sessionRepository.end(this.currentSessionId);
|
||||||
@ -215,8 +223,6 @@ class ListenService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setupIpcHandlers() {
|
setupIpcHandlers() {
|
||||||
const { ipcMain } = require('electron');
|
|
||||||
|
|
||||||
ipcMain.handle('send-audio-content', async (event, { data, mimeType }) => {
|
ipcMain.handle('send-audio-content', async (event, { data, mimeType }) => {
|
||||||
try {
|
try {
|
||||||
await this.sendAudioContent(data, mimeType);
|
await this.sendAudioContent(data, mimeType);
|
||||||
@ -282,4 +288,5 @@ class ListenService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = ListenService;
|
const listenService = new ListenService();
|
||||||
|
module.exports = listenService;
|
17
src/index.js
17
src/index.js
@ -13,7 +13,7 @@ if (require('electron-squirrel-startup')) {
|
|||||||
|
|
||||||
const { app, BrowserWindow, shell, ipcMain, dialog, desktopCapturer, session } = require('electron');
|
const { app, BrowserWindow, shell, ipcMain, dialog, desktopCapturer, session } = require('electron');
|
||||||
const { createWindows } = require('./window/windowManager.js');
|
const { createWindows } = require('./window/windowManager.js');
|
||||||
const ListenService = require('./features/listen/listenService');
|
const listenService = require('./features/listen/listenService');
|
||||||
const { initializeFirebase } = require('./features/common/services/firebaseClient');
|
const { initializeFirebase } = require('./features/common/services/firebaseClient');
|
||||||
const databaseInitializer = require('./features/common/services/databaseInitializer');
|
const databaseInitializer = require('./features/common/services/databaseInitializer');
|
||||||
const authService = require('./features/common/services/authService');
|
const authService = require('./features/common/services/authService');
|
||||||
@ -34,10 +34,6 @@ const eventBridge = new EventEmitter();
|
|||||||
let WEB_PORT = 3000;
|
let WEB_PORT = 3000;
|
||||||
let isShuttingDown = false; // Flag to prevent infinite shutdown loop
|
let isShuttingDown = false; // Flag to prevent infinite shutdown loop
|
||||||
|
|
||||||
const listenService = new ListenService();
|
|
||||||
// Make listenService globally accessible so other modules (e.g., windowManager, askService) can reuse the same instance
|
|
||||||
global.listenService = listenService;
|
|
||||||
|
|
||||||
//////// after_modelStateService ////////
|
//////// after_modelStateService ////////
|
||||||
const modelStateService = new ModelStateService(authService);
|
const modelStateService = new ModelStateService(authService);
|
||||||
global.modelStateService = modelStateService;
|
global.modelStateService = modelStateService;
|
||||||
@ -203,7 +199,7 @@ app.whenReady().then(async () => {
|
|||||||
await modelStateService.initialize();
|
await modelStateService.initialize();
|
||||||
//////// after_modelStateService ////////
|
//////// after_modelStateService ////////
|
||||||
|
|
||||||
listenService.setupIpcHandlers();
|
listenService.initialize();
|
||||||
askService.initialize();
|
askService.initialize();
|
||||||
settingsService.initialize();
|
settingsService.initialize();
|
||||||
featureBridge.initialize(); // 추가: featureBridge 초기화
|
featureBridge.initialize(); // 추가: featureBridge 초기화
|
||||||
@ -250,13 +246,6 @@ app.whenReady().then(async () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.on('window-all-closed', () => {
|
|
||||||
listenService.stopMacOSAudioCapture();
|
|
||||||
if (process.platform !== 'darwin') {
|
|
||||||
app.quit();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
app.on('before-quit', async (event) => {
|
app.on('before-quit', async (event) => {
|
||||||
// Prevent infinite loop by checking if shutdown is already in progress
|
// Prevent infinite loop by checking if shutdown is already in progress
|
||||||
if (isShuttingDown) {
|
if (isShuttingDown) {
|
||||||
@ -274,7 +263,7 @@ app.on('before-quit', async (event) => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// 1. Stop audio capture first (immediate)
|
// 1. Stop audio capture first (immediate)
|
||||||
listenService.stopMacOSAudioCapture();
|
await listenService.closeSession();
|
||||||
console.log('[Shutdown] Audio capture stopped');
|
console.log('[Shutdown] Audio capture stopped');
|
||||||
|
|
||||||
// 2. End all active sessions (database operations) - with error handling
|
// 2. End all active sessions (database operations) - with error handling
|
||||||
|
@ -6,6 +6,7 @@ const fs = require('node:fs');
|
|||||||
const os = require('os');
|
const os = require('os');
|
||||||
const util = require('util');
|
const util = require('util');
|
||||||
const execFile = util.promisify(require('child_process').execFile);
|
const execFile = util.promisify(require('child_process').execFile);
|
||||||
|
const listenService = require('../features/listen/listenService');
|
||||||
|
|
||||||
// Try to load sharp, but don't fail if it's not available
|
// Try to load sharp, but don't fail if it's not available
|
||||||
let sharp;
|
let sharp;
|
||||||
@ -94,7 +95,7 @@ async function toggleFeature(featureName, options = {}) {
|
|||||||
if (featureName === 'listen') {
|
if (featureName === 'listen') {
|
||||||
console.log(`[WindowManager] Toggling feature: ${featureName}`);
|
console.log(`[WindowManager] Toggling feature: ${featureName}`);
|
||||||
const listenWindow = windowPool.get(featureName);
|
const listenWindow = windowPool.get(featureName);
|
||||||
const listenService = global.listenService;
|
// const listenService = global.listenService;
|
||||||
if (listenService && listenService.isSessionActive()) {
|
if (listenService && listenService.isSessionActive()) {
|
||||||
console.log('[WindowManager] Listen session is active, closing it via toggle.');
|
console.log('[WindowManager] Listen session is active, closing it via toggle.');
|
||||||
await listenService.closeSession();
|
await listenService.closeSession();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user