diff --git a/src/bridge/featureBridge.js b/src/bridge/featureBridge.js index 3cce84b..4986f36 100644 --- a/src/bridge/featureBridge.js +++ b/src/bridge/featureBridge.js @@ -1,10 +1,16 @@ // src/bridge/featureBridge.js const { ipcMain } = require('electron'); const settingsService = require('../features/settings/settingsService'); +const askService = require('../features/ask/askService'); module.exports = { // Renderer로부터의 요청을 수신 initialize() { + // Ask 관련 핸들러 추가 + ipcMain.handle('ask:sendMessage', async (event, userPrompt) => { + return askService.sendMessage(userPrompt); + }); + // 기존 ask 핸들러 유지 ipcMain.handle('feature:ask', (e, query) => { // 실제로는 여기서 Controller -> Service 로직 수행 @@ -61,7 +67,7 @@ module.exports = { return await settingsService.setAutoUpdateSetting(isEnabled); }); - console.log('[FeatureBridge] Initialized with settings handlers.'); + console.log('[FeatureBridge] Initialized with ask and settings handlers.'); }, // Renderer로 상태를 전송 diff --git a/src/features/ask/askService.js b/src/features/ask/askService.js index ceb347d..b2c7d94 100644 --- a/src/features/ask/askService.js +++ b/src/features/ask/askService.js @@ -31,7 +31,7 @@ async function sendMessage(userPrompt) { let sessionId; try { - console.log(`[AskService] 🤖 Processing message: ${userPrompt.substring(0, 50)}...`); + console.log(`[AskService] Processing message: ${userPrompt.substring(0, 50)}...`); // --- Save user's message immediately --- // This ensures the user message is always timestamped before the assistant's response. @@ -139,12 +139,11 @@ async function sendMessage(userPrompt) { } function initialize() { - ipcMain.handle('ask:sendMessage', async (event, userPrompt) => { - return sendMessage(userPrompt); - }); + // IPC 핸들러는 featureBridge.js로 이동됨 console.log('[AskService] Initialized and ready.'); } module.exports = { initialize, + sendMessage, // sendMessage 함수 export 추가 }; \ No newline at end of file diff --git a/src/preload.js b/src/preload.js index 80dd79d..5fe8ac8 100644 --- a/src/preload.js +++ b/src/preload.js @@ -2,8 +2,254 @@ const { contextBridge, ipcRenderer } = require('electron'); contextBridge.exposeInMainWorld('api', { + // Ask + ask: { + // sendMessage + sendMessage: (message) => ipcRenderer.invoke('ask:sendMessage', message), + + // window + adjustWindowHeight: (height) => ipcRenderer.invoke('adjust-window-height', height), + forceCloseWindow: (windowName) => ipcRenderer.invoke('force-close-window', windowName), + closeWindowIfEmpty: () => ipcRenderer.invoke('close-ask-window-if-empty'), + + // event listener + onGlobalSend: (callback) => ipcRenderer.on('ask-global-send', callback), + onReceiveQuestionFromAssistant: (callback) => ipcRenderer.on('receive-question-from-assistant', callback), + onHideTextInput: (callback) => ipcRenderer.on('hide-text-input', callback), + onWindowHideAnimation: (callback) => ipcRenderer.on('window-hide-animation', callback), + onWindowBlur: (callback) => ipcRenderer.on('window-blur', callback), + onWindowDidShow: (callback) => ipcRenderer.on('window-did-show', callback), + onResponseChunk: (callback) => ipcRenderer.on('ask-response-chunk', callback), + onResponseStreamEnd: (callback) => ipcRenderer.on('ask-response-stream-end', callback), + onScrollResponseUp: (callback) => ipcRenderer.on('scroll-response-up', callback), + onScrollResponseDown: (callback) => ipcRenderer.on('scroll-response-down', callback), + + // event listener remove + removeOnGlobalSend: (callback) => ipcRenderer.removeListener('ask-global-send', callback), + removeOnReceiveQuestionFromAssistant: (callback) => ipcRenderer.removeListener('receive-question-from-assistant', callback), + removeOnHideTextInput: (callback) => ipcRenderer.removeListener('hide-text-input', callback), + removeOnWindowHideAnimation: (callback) => ipcRenderer.removeListener('window-hide-animation', callback), + removeOnWindowBlur: (callback) => ipcRenderer.removeListener('window-blur', callback), + removeOnWindowDidShow: (callback) => ipcRenderer.removeListener('window-did-show', callback), + removeOnResponseChunk: (callback) => ipcRenderer.removeListener('ask-response-chunk', callback), + removeOnResponseStreamEnd: (callback) => ipcRenderer.removeListener('ask-response-stream-end', callback), + removeOnScrollResponseUp: (callback) => ipcRenderer.removeListener('scroll-response-up', callback), + removeOnScrollResponseDown: (callback) => ipcRenderer.removeListener('scroll-response-down', callback) + }, + + // Listen + listen: { + // window + adjustWindowHeight: (height) => ipcRenderer.invoke('adjust-window-height', height), + + // event listener + onSessionStateChanged: (callback) => ipcRenderer.on('session-state-changed', callback), + onSttUpdate: (callback) => ipcRenderer.on('stt-update', callback), + onSummaryUpdate: (callback) => ipcRenderer.on('summary-update', callback), + + // remove event listener + removeOnSessionStateChanged: (callback) => ipcRenderer.removeListener('session-state-changed', callback), + removeOnSttUpdate: (callback) => ipcRenderer.removeListener('stt-update', callback), + removeOnSummaryUpdate: (callback) => ipcRenderer.removeListener('summary-update', callback), + + // Ask window + isAskWindowVisible: (windowName) => ipcRenderer.invoke('is-ask-window-visible', windowName), + toggleFeature: (featureName) => ipcRenderer.invoke('toggle-feature', featureName), + sendQuestionToAsk: (question) => ipcRenderer.invoke('send-question-to-ask', question) + }, + + // Audio + audio: { + // audio capture + sendAudioContent: (options) => ipcRenderer.invoke('send-audio-content', options), + sendSystemAudioContent: (options) => ipcRenderer.invoke('send-system-audio-content', options), + + // macOS audio + startMacosAudio: () => ipcRenderer.invoke('start-macos-audio'), + stopMacosAudio: () => ipcRenderer.invoke('stop-macos-audio'), + + // screen capture + startScreenCapture: () => ipcRenderer.invoke('start-screen-capture'), + stopScreenCapture: () => ipcRenderer.invoke('stop-screen-capture'), + captureScreenshot: (options) => ipcRenderer.invoke('capture-screenshot', options), + getCurrentScreenshot: () => ipcRenderer.invoke('get-current-screenshot'), + + // session + isSessionActive: () => ipcRenderer.invoke('is-session-active'), + + // event listener + onChangeListenCaptureState: (callback) => ipcRenderer.on('change-listen-capture-state', callback), + onSystemAudioData: (callback) => ipcRenderer.on('system-audio-data', callback), + + // remove event listener + removeOnChangeListenCaptureState: (callback) => ipcRenderer.removeListener('change-listen-capture-state', callback), + removeOnSystemAudioData: (callback) => ipcRenderer.removeListener('system-audio-data', callback) + }, + + // Settings + settings: { + // shortcut + saveShortcuts: (shortcuts) => ipcRenderer.invoke('save-shortcuts', shortcuts), + getDefaultShortcuts: () => ipcRenderer.invoke('get-default-shortcuts'), + + // shortcut editor + closeShortcutEditor: () => ipcRenderer.send('close-shortcut-editor'), + + // event listener + onLoadShortcuts: (callback) => ipcRenderer.on('load-shortcuts', callback), + + // remove event listener + removeOnLoadShortcuts: (callback) => ipcRenderer.removeListener('load-shortcuts', callback) + }, + + // App + app: { + // quit application + quitApplication: () => ipcRenderer.invoke('quit-application'), + + // session + isSessionActive: () => ipcRenderer.invoke('is-session-active'), + + // event listener + onClickThroughToggled: (callback) => ipcRenderer.on('click-through-toggled', callback), + + // remove event listener + removeOnClickThroughToggled: (callback) => ipcRenderer.removeListener('click-through-toggled', callback), + + // remove all listeners + removeAllListeners: (eventName) => ipcRenderer.removeAllListeners(eventName) + }, + + // API Key Header + apikey: { + // model + getProviderConfig: () => ipcRenderer.invoke('model:get-provider-config'), + validateKey: (options) => ipcRenderer.invoke('model:validate-key', options), + setSelectedModel: (options) => ipcRenderer.invoke('model:set-selected-model', options), + areProvidersConfigured: () => ipcRenderer.invoke('model:are-providers-configured'), + + // Ollama + getOllamaStatus: () => ipcRenderer.invoke('ollama:get-status'), + getModelSuggestions: () => ipcRenderer.invoke('ollama:get-model-suggestions'), + ensureReady: () => ipcRenderer.invoke('ollama:ensure-ready'), + installOllama: () => ipcRenderer.invoke('ollama:install'), + startService: () => ipcRenderer.invoke('ollama:start-service'), + pullModel: (modelName) => ipcRenderer.invoke('ollama:pull-model', modelName), + + // Whisper + downloadModel: (modelId) => ipcRenderer.invoke('whisper:download-model', modelId), + + // position + getHeaderPosition: () => ipcRenderer.invoke('get-header-position'), + moveHeaderTo: (x, y) => ipcRenderer.invoke('move-header-to', x, y), + + // authentication + startFirebaseAuth: () => ipcRenderer.invoke('start-firebase-auth'), + getCurrentUser: () => ipcRenderer.invoke('get-current-user'), + quitApplication: () => ipcRenderer.invoke('quit-application'), + + // event listener + onOllamaInstallProgress: (callback) => ipcRenderer.on('ollama:install-progress', callback), + onOllamaInstallComplete: (callback) => ipcRenderer.on('ollama:install-complete', callback), + onOllamaPullProgress: (callback) => ipcRenderer.on('ollama:pull-progress', callback), + onWhisperDownloadProgress: (callback) => ipcRenderer.on('whisper:download-progress', callback), + + // remove event listener + removeOnOllamaInstallProgress: (callback) => ipcRenderer.removeListener('ollama:install-progress', callback), + removeOnOllamaInstallComplete: (callback) => ipcRenderer.removeListener('ollama:install-complete', callback), + removeOnOllamaPullProgress: (callback) => ipcRenderer.removeListener('ollama:pull-progress', callback), + removeOnWhisperDownloadProgress: (callback) => ipcRenderer.removeListener('whisper:download-progress', callback), + + // remove all listeners + removeAllListeners: (eventName) => ipcRenderer.removeAllListeners(eventName) + }, + + // Controller + controller: { + // user state + getCurrentUser: () => ipcRenderer.invoke('get-current-user'), + + // model + areProvidersConfigured: () => ipcRenderer.invoke('model:are-providers-configured'), + + // permission + checkPermissionsCompleted: () => ipcRenderer.invoke('check-permissions-completed'), + checkSystemPermissions: () => ipcRenderer.invoke('check-system-permissions'), + + // window + resizeHeaderWindow: (options) => ipcRenderer.invoke('resize-header-window', options), + + // state change + sendHeaderStateChanged: (state) => ipcRenderer.send('header-state-changed', state), + + // event listener + onUserStateChanged: (callback) => ipcRenderer.on('user-state-changed', callback), + onAuthFailed: (callback) => ipcRenderer.on('auth-failed', callback), + onForceShowApiKeyHeader: (callback) => ipcRenderer.on('force-show-apikey-header', callback), + + // remove event listener + removeOnUserStateChanged: (callback) => ipcRenderer.removeListener('user-state-changed', callback), + removeOnAuthFailed: (callback) => ipcRenderer.removeListener('auth-failed', callback), + removeOnForceShowApiKeyHeader: (callback) => ipcRenderer.removeListener('force-show-apikey-header', callback) + }, + + // Header + header: { + // position + getHeaderPosition: () => ipcRenderer.invoke('get-header-position'), + moveHeaderTo: (x, y) => ipcRenderer.invoke('move-header-to', x, y), + + // event listener + onSessionStateText: (callback) => ipcRenderer.on('session-state-text', callback), + onShortcutsUpdated: (callback) => ipcRenderer.on('shortcuts-updated', callback), + + // remove event listener + removeOnSessionStateText: (callback) => ipcRenderer.removeListener('session-state-text', callback), + removeOnShortcutsUpdated: (callback) => ipcRenderer.removeListener('shortcuts-updated', callback), + + // animation + sendAnimationFinished: (state) => ipcRenderer.send('header-animation-finished', state), + + // settings window + cancelHideSettingsWindow: () => ipcRenderer.send('cancel-hide-settings-window'), + showSettingsWindow: (options) => ipcRenderer.send('show-settings-window', options), + hideSettingsWindow: () => ipcRenderer.send('hide-settings-window'), + + // invoke + invoke: (channel, ...args) => ipcRenderer.invoke(channel, ...args) + }, + + // Permissions + permissions: { + checkSystemPermissions: () => ipcRenderer.invoke('check-system-permissions'), + requestMicrophonePermission: () => ipcRenderer.invoke('request-microphone-permission'), + openSystemPreferences: (section) => ipcRenderer.invoke('open-system-preferences', section), + markPermissionsCompleted: () => ipcRenderer.invoke('mark-permissions-completed'), + quitApplication: () => ipcRenderer.invoke('quit-application') + }, + + // Animation + animation: { + // send animation finished + sendAnimationFinished: () => ipcRenderer.send('animation-finished'), + + // event listener + onWindowShowAnimation: (callback) => ipcRenderer.on('window-show-animation', callback), + onWindowHideAnimation: (callback) => ipcRenderer.on('window-hide-animation', callback), + onSettingsWindowHideAnimation: (callback) => ipcRenderer.on('settings-window-hide-animation', callback), + onListenWindowMoveToCenter: (callback) => ipcRenderer.on('listen-window-move-to-center', callback), + onListenWindowMoveToLeft: (callback) => ipcRenderer.on('listen-window-move-to-left', callback), + + // remove event listener + removeOnWindowShowAnimation: (callback) => ipcRenderer.removeListener('window-show-animation', callback), + removeOnWindowHideAnimation: (callback) => ipcRenderer.removeListener('window-hide-animation', callback), + removeOnSettingsWindowHideAnimation: (callback) => ipcRenderer.removeListener('settings-window-hide-animation', callback), + removeOnListenWindowMoveToCenter: (callback) => ipcRenderer.removeListener('listen-window-move-to-center', callback), + removeOnListenWindowMoveToLeft: (callback) => ipcRenderer.removeListener('listen-window-move-to-left', callback) + }, + feature: { - // 기존 ask 관련 유지 + // ask submitAsk: (query) => ipcRenderer.invoke('feature:ask', query), onAskProgress: (callback) => ipcRenderer.on('feature:ask:progress', (e, p) => callback(p)), @@ -51,13 +297,13 @@ contextBridge.exposeInMainWorld('api', { hideSettingsWindow: () => ipcRenderer.send('hide-settings-window') } }, - // 기존 window 유지 + // window window: { - // 기존 + // window hide: () => ipcRenderer.send('window:hide'), onFocusChange: (callback) => ipcRenderer.on('window:focus-change', (e, f) => callback(f)), - // 추가 + // settings window showSettingsWindow: (bounds) => ipcRenderer.send('show-settings-window', bounds), hideSettingsWindow: () => ipcRenderer.send('hide-settings-window'), cancelHideSettingsWindow: () => ipcRenderer.send('cancel-hide-settings-window'), @@ -67,7 +313,7 @@ contextBridge.exposeInMainWorld('api', { ollamaShutdown: (graceful) => ipcRenderer.invoke('ollama:shutdown', graceful), startFirebaseAuth: () => ipcRenderer.invoke('start-firebase-auth'), - // on methods (listeners) + // event listener onUserStateChanged: (callback) => ipcRenderer.on('user-state-changed', callback), removeOnUserStateChanged: (callback) => ipcRenderer.removeListener('user-state-changed', callback), onSettingsUpdated: (callback) => ipcRenderer.on('settings-updated', callback), @@ -79,7 +325,7 @@ contextBridge.exposeInMainWorld('api', { onWhisperDownloadProgress: (callback) => ipcRenderer.on('whisper:download-progress', callback), removeOnWhisperDownloadProgress: (callback) => ipcRenderer.removeListener('whisper:download-progress', callback), - // send methods + // send cancelHideSettingsWindow: () => ipcRenderer.send('cancel-hide-settings-window'), hideSettingsWindow: () => ipcRenderer.send('hide-settings-window') } diff --git a/src/ui/app/ApiKeyHeader.js b/src/ui/app/ApiKeyHeader.js index a0ed1fa..7452fcb 100644 --- a/src/ui/app/ApiKeyHeader.js +++ b/src/ui/app/ApiKeyHeader.js @@ -370,13 +370,12 @@ export class ApiKeyHeader extends LitElement { } async loadProviderConfig() { - if (!window.require) return; - const { ipcRenderer } = window.require('electron'); + if (!window.api || !window.api.apikey) return; try { const [config, ollamaStatus] = await Promise.all([ - ipcRenderer.invoke('model:get-provider-config'), - ipcRenderer.invoke('ollama:get-status') + window.api.apikey.getProviderConfig(), + window.api.apikey.getOllamaStatus() ]); const llmProviders = []; @@ -428,8 +427,8 @@ export class ApiKeyHeader extends LitElement { e.preventDefault() - const { ipcRenderer } = window.require("electron") - const initialPosition = await ipcRenderer.invoke("get-header-position") + if (!window.api || !window.api.apikey) return; + const initialPosition = await window.api.apikey.getHeaderPosition() this.dragState = { initialMouseX: e.screenX, @@ -456,8 +455,9 @@ export class ApiKeyHeader extends LitElement { const newWindowX = this.dragState.initialWindowX + (e.screenX - this.dragState.initialMouseX) const newWindowY = this.dragState.initialWindowY + (e.screenY - this.dragState.initialMouseY) - const { ipcRenderer } = window.require("electron") - ipcRenderer.invoke("move-header-to", newWindowX, newWindowY) + if (window.api && window.api.apikey) { + window.api.apikey.moveHeaderTo(newWindowX, newWindowY) + } } handleMouseUp(e) { @@ -652,9 +652,8 @@ export class ApiKeyHeader extends LitElement { try { // Lightweight health check - just ping the service const isHealthy = await this._executeOperation('health_check', async () => { - if (!window.require) return false; - const { ipcRenderer } = window.require('electron'); - const result = await ipcRenderer.invoke('ollama:get-status'); + if (!window.api || !window.api.apikey) return false; + const result = await window.api.apikey.getOllamaStatus(); return result?.success && result?.running; }, { timeout: 5000, priority: 'low' }); @@ -928,14 +927,13 @@ export class ApiKeyHeader extends LitElement { } async refreshOllamaStatus() { - if (!window.require) return; + if (!window.api || !window.api.apikey) return; try { this._updateConnectionState('connecting', 'Checking Ollama status'); const result = await this._executeOperation('ollama_status', async () => { - const { ipcRenderer } = window.require('electron'); - return await ipcRenderer.invoke('ollama:get-status'); + return await window.api.apikey.getOllamaStatus(); }); if (result?.success) { @@ -960,12 +958,11 @@ export class ApiKeyHeader extends LitElement { } async loadModelSuggestions() { - if (!window.require) return; + if (!window.api || !window.api.apikey) return; try { const result = await this._executeOperation('model_suggestions', async () => { - const { ipcRenderer } = window.require('electron'); - return await ipcRenderer.invoke('ollama:get-model-suggestions'); + return await window.api.apikey.getModelSuggestions(); }); if (result?.success) { @@ -988,14 +985,13 @@ export class ApiKeyHeader extends LitElement { } async ensureOllamaReady() { - if (!window.require) return false; + if (!window.api || !window.api.apikey) return false; try { this._updateConnectionState('connecting', 'Ensuring Ollama is ready'); const result = await this._executeOperation('ollama_ensure_ready', async () => { - const { ipcRenderer } = window.require('electron'); - return await ipcRenderer.invoke('ollama:ensure-ready'); + return await window.api.apikey.ensureReady(); }, { timeout: this.operationTimeout }); if (result?.success) { @@ -1015,8 +1011,7 @@ export class ApiKeyHeader extends LitElement { } async ensureOllamaReadyWithUI() { - if (!window.require) return false; - const { ipcRenderer } = window.require("electron"); + if (!window.api || !window.api.apikey) return false; this.installingModel = "Setting up Ollama"; this.installProgress = 0; @@ -1074,21 +1069,21 @@ export class ApiKeyHeader extends LitElement { operationCompleted = true; clearTimeout(completionTimeout); - ipcRenderer.removeListener("ollama:install-progress", progressHandler); + window.api.apikey.removeOnOllamaInstallProgress(progressHandler); await this._handleOllamaSetupCompletion(result.success, result.error); }; - ipcRenderer.once("ollama:install-complete", completionHandler); - ipcRenderer.on("ollama:install-progress", progressHandler); + window.api.apikey.onOllamaInstallComplete(completionHandler); + window.api.apikey.onOllamaInstallProgress(progressHandler); try { let result; if (!this.ollamaStatus.installed) { console.log("[ApiKeyHeader] Ollama not installed. Starting installation."); - result = await ipcRenderer.invoke("ollama:install"); + result = await window.api.apikey.installOllama(); } else { console.log("[ApiKeyHeader] Ollama installed. Starting service."); - result = await ipcRenderer.invoke("ollama:start-service"); + result = await window.api.apikey.startService(); } // If IPC call succeeds but no event received, handle completion manually @@ -1106,8 +1101,8 @@ export class ApiKeyHeader extends LitElement { operationCompleted = true; clearTimeout(completionTimeout); console.error("[ApiKeyHeader] Ollama setup failed:", error); - ipcRenderer.removeListener("ollama:install-progress", progressHandler); - ipcRenderer.removeListener("ollama:install-complete", completionHandler); + window.api.apikey.removeOnOllamaInstallProgress(progressHandler); + window.api.apikey.removeOnOllamaInstallComplete(completionHandler); await this._handleOllamaSetupCompletion(false, error.message); } } @@ -1229,7 +1224,7 @@ export class ApiKeyHeader extends LitElement { this.clearMessages(); this.requestUpdate(); - const { ipcRenderer } = window.require('electron'); + if (!window.api || !window.api.apikey) return; let progressHandler = null; try { @@ -1249,10 +1244,10 @@ export class ApiKeyHeader extends LitElement { }; // Set up progress tracking - ipcRenderer.on('ollama:pull-progress', progressHandler); + window.api.apikey.onOllamaPullProgress(progressHandler); // Execute the model pull with timeout - const installPromise = ipcRenderer.invoke('ollama:pull-model', modelName); + const installPromise = window.api.apikey.pullModel(modelName); const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error('Installation timeout after 10 minutes')), 600000) ); @@ -1281,7 +1276,7 @@ export class ApiKeyHeader extends LitElement { } finally { // Comprehensive cleanup if (progressHandler) { - ipcRenderer.removeListener('ollama:pull-progress', progressHandler); + window.api.apikey.removeOnOllamaPullProgress(progressHandler); } this.installingModel = null; @@ -1307,7 +1302,7 @@ export class ApiKeyHeader extends LitElement { this.clearMessages(); this.requestUpdate(); - const { ipcRenderer } = window.require('electron'); + if (!window.api || !window.api.apikey) return; let progressHandler = null; try { @@ -1321,10 +1316,10 @@ export class ApiKeyHeader extends LitElement { } }; - ipcRenderer.on('whisper:download-progress', progressHandler); + window.api.apikey.onWhisperDownloadProgress(progressHandler); // Start download with timeout protection - const downloadPromise = ipcRenderer.invoke('whisper:download-model', modelId); + const downloadPromise = window.api.apikey.downloadModel(modelId); const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error('Download timeout after 10 minutes')), 600000) ); @@ -1351,7 +1346,7 @@ export class ApiKeyHeader extends LitElement { } finally { // Cleanup if (progressHandler) { - ipcRenderer.removeListener('whisper:download-progress', progressHandler); + window.api.apikey.removeOnWhisperDownloadProgress(progressHandler); } delete this.whisperInstallingModels[modelId]; this.requestUpdate(); @@ -1412,7 +1407,7 @@ export class ApiKeyHeader extends LitElement { this.clearMessages(); this.requestUpdate(); - const { ipcRenderer } = window.require('electron'); + if (!window.api || !window.api.apikey) return; try { // Handle LLM provider @@ -1436,14 +1431,14 @@ export class ApiKeyHeader extends LitElement { } // Validate Ollama is working - llmResult = await ipcRenderer.invoke('model:validate-key', { + llmResult = await window.api.apikey.validateKey({ provider: 'ollama', key: 'local' }); if (llmResult.success) { // Set the selected model - await ipcRenderer.invoke('model:set-selected-model', { + await window.api.apikey.setSelectedModel({ type: 'llm', modelId: this.selectedLlmModel }); @@ -1454,7 +1449,7 @@ export class ApiKeyHeader extends LitElement { throw new Error('Please enter LLM API key'); } - llmResult = await ipcRenderer.invoke('model:validate-key', { + llmResult = await window.api.apikey.validateKey({ provider: this.llmProvider, key: this.llmApiKey.trim() }); @@ -1467,14 +1462,14 @@ export class ApiKeyHeader extends LitElement { sttResult = { success: true }; } else if (this.sttProvider === 'whisper') { // For Whisper, just validate it's enabled (model download already handled in handleSttModelChange) - sttResult = await ipcRenderer.invoke('model:validate-key', { + sttResult = await window.api.apikey.validateKey({ provider: 'whisper', key: 'local' }); if (sttResult.success && this.selectedSttModel) { // Set the selected model - await ipcRenderer.invoke('model:set-selected-model', { + await window.api.apikey.setSelectedModel({ type: 'stt', modelId: this.selectedSttModel }); @@ -1485,7 +1480,7 @@ export class ApiKeyHeader extends LitElement { throw new Error('Please enter STT API key'); } - sttResult = await ipcRenderer.invoke('model:validate-key', { + sttResult = await window.api.apikey.validateKey({ provider: this.sttProvider, key: this.sttApiKey.trim() }); @@ -1522,15 +1517,15 @@ export class ApiKeyHeader extends LitElement { e.preventDefault() console.log("Requesting Firebase authentication from main process...") - if (window.require) { - window.require("electron").ipcRenderer.invoke("start-firebase-auth") + if (window.api && window.api.apikey) { + window.api.apikey.startFirebaseAuth() } } handleClose() { console.log("Close button clicked") - if (window.require) { - window.require("electron").ipcRenderer.invoke("quit-application") + if (window.api && window.api.apikey) { + window.api.apikey.quitApplication() } } @@ -1543,8 +1538,8 @@ export class ApiKeyHeader extends LitElement { console.log('[ApiKeyHeader] handleAnimationEnd: Animation completed, transitioning to next state...'); - if (!window.require) { - console.error('[ApiKeyHeader] handleAnimationEnd: window.require not available'); + if (!window.api || !window.api.apikey) { + console.error('[ApiKeyHeader] handleAnimationEnd: window.api.apikey not available'); return; } @@ -1553,14 +1548,12 @@ export class ApiKeyHeader extends LitElement { return; } - const { ipcRenderer } = window.require('electron'); - - ipcRenderer.invoke('get-current-user') + window.api.apikey.getCurrentUser() .then(userState => { console.log('[ApiKeyHeader] handleAnimationEnd: User state retrieved:', userState); // Additional validation for local providers - return ipcRenderer.invoke('model:are-providers-configured').then(isConfigured => { + return window.api.apikey.areProvidersConfigured().then(isConfigured => { console.log('[ApiKeyHeader] handleAnimationEnd: Providers configured check:', isConfigured); if (!isConfigured) { @@ -1624,12 +1617,11 @@ export class ApiKeyHeader extends LitElement { } // Cleanup event listeners - if (window.require) { - const { ipcRenderer } = window.require('electron'); - ipcRenderer.removeAllListeners('whisper:download-progress'); - ipcRenderer.removeAllListeners('ollama:install-progress'); - ipcRenderer.removeAllListeners('ollama:pull-progress'); - ipcRenderer.removeAllListeners('ollama:install-complete'); + if (window.api && window.api.apikey) { + window.api.apikey.removeAllListeners('whisper:download-progress'); + window.api.apikey.removeAllListeners('ollama:install-progress'); + window.api.apikey.removeAllListeners('ollama:pull-progress'); + window.api.apikey.removeAllListeners('ollama:install-complete'); } // Cancel any ongoing downloads diff --git a/src/ui/app/HeaderController.js b/src/ui/app/HeaderController.js index e2c5fe8..a6e37f0 100644 --- a/src/ui/app/HeaderController.js +++ b/src/ui/app/HeaderController.js @@ -50,22 +50,20 @@ class HeaderTransitionManager { this._bootstrap(); - if (window.require) { - const { ipcRenderer } = window.require('electron'); - - ipcRenderer.on('user-state-changed', (event, userState) => { + if (window.api && window.api.controller) { + window.api.controller.onUserStateChanged((event, userState) => { console.log('[HeaderController] Received user state change:', userState); this.handleStateUpdate(userState); }); - ipcRenderer.on('auth-failed', (event, { message }) => { + window.api.controller.onAuthFailed((event, { message }) => { console.error('[HeaderController] Received auth failure from main process:', message); if (this.apiKeyHeader) { this.apiKeyHeader.errorMessage = 'Authentication failed. Please try again.'; this.apiKeyHeader.isLoading = false; } }); - ipcRenderer.on('force-show-apikey-header', async () => { + window.api.controller.onForceShowApiKeyHeader(async () => { console.log('[HeaderController] Received broadcast to show apikey header. Switching now.'); await this._resizeForApiKey(); this.ensureHeader('apikey'); @@ -75,16 +73,16 @@ class HeaderTransitionManager { notifyHeaderState(stateOverride) { const state = stateOverride || this.currentHeaderType || 'apikey'; - if (window.require) { - window.require('electron').ipcRenderer.send('header-state-changed', state); + if (window.api && window.api.controller) { + window.api.controller.sendHeaderStateChanged(state); } } async _bootstrap() { // The initial state will be sent by the main process via 'user-state-changed' // We just need to request it. - if (window.require) { - const userState = await window.require('electron').ipcRenderer.invoke('get-current-user'); + if (window.api && window.api.controller) { + const userState = await window.api.controller.getCurrentUser(); console.log('[HeaderController] Bootstrapping with initial user state:', userState); this.handleStateUpdate(userState); } else { @@ -96,8 +94,8 @@ class HeaderTransitionManager { //////// after_modelStateService //////// async handleStateUpdate(userState) { - const { ipcRenderer } = window.require('electron'); - const isConfigured = await ipcRenderer.invoke('model:are-providers-configured'); + if (!window.api || !window.api.controller) return; + const isConfigured = await window.api.controller.areProvidersConfigured(); if (isConfigured) { const { isLoggedIn } = userState; @@ -126,10 +124,9 @@ class HeaderTransitionManager { } // Check if permissions were previously completed - if (window.require) { - const { ipcRenderer } = window.require('electron'); + if (window.api && window.api.controller) { try { - const permissionsCompleted = await ipcRenderer.invoke('check-permissions-completed'); + const permissionsCompleted = await window.api.controller.checkPermissionsCompleted(); if (permissionsCompleted) { console.log('[HeaderController] Permissions were previously completed, checking current status...'); @@ -162,38 +159,30 @@ class HeaderTransitionManager { } _resizeForMain() { - if (!window.require) return; - return window - .require('electron') - .ipcRenderer.invoke('resize-header-window', { width: 353, height: 47 }) + if (!window.api || !window.api.controller) return; + return window.api.controller.resizeHeaderWindow({ width: 353, height: 47 }) .catch(() => {}); } async _resizeForApiKey() { - if (!window.require) return; - return window - .require('electron') - .ipcRenderer.invoke('resize-header-window', { width: 350, height: 300 }) + if (!window.api || !window.api.controller) return; + return window.api.controller.resizeHeaderWindow({ width: 350, height: 300 }) .catch(() => {}); } async _resizeForPermissionHeader() { - if (!window.require) return; - return window - .require('electron') - .ipcRenderer.invoke('resize-header-window', { width: 285, height: 220 }) + if (!window.api || !window.api.controller) return; + return window.api.controller.resizeHeaderWindow({ width: 285, height: 220 }) .catch(() => {}); } async checkPermissions() { - if (!window.require) { + if (!window.api || !window.api.controller) { return { success: true }; } - - const { ipcRenderer } = window.require('electron'); try { - const permissions = await ipcRenderer.invoke('check-system-permissions'); + const permissions = await window.api.controller.checkSystemPermissions(); console.log('[HeaderController] Current permissions:', permissions); if (!permissions.needsSetup) { diff --git a/src/ui/app/MainHeader.js b/src/ui/app/MainHeader.js index 3f78f67..561d064 100644 --- a/src/ui/app/MainHeader.js +++ b/src/ui/app/MainHeader.js @@ -362,8 +362,8 @@ export class MainHeader extends LitElement { async handleMouseDown(e) { e.preventDefault(); - const { ipcRenderer } = window.require('electron'); - const initialPosition = await ipcRenderer.invoke('get-header-position'); + if (!window.api || !window.api.header) return; + const initialPosition = await window.api.header.getHeaderPosition(); this.dragState = { initialMouseX: e.screenX, @@ -390,8 +390,9 @@ export class MainHeader extends LitElement { const newWindowX = this.dragState.initialWindowX + (e.screenX - this.dragState.initialMouseX); const newWindowY = this.dragState.initialWindowY + (e.screenY - this.dragState.initialMouseY); - const { ipcRenderer } = window.require('electron'); - ipcRenderer.invoke('move-header-to', newWindowX, newWindowY); + if (window.api && window.api.header) { + window.api.header.moveHeaderTo(newWindowX, newWindowY); + } } handleMouseUp(e) { @@ -447,12 +448,12 @@ export class MainHeader extends LitElement { if (this.classList.contains('hiding')) { this.classList.add('hidden'); - if (window.require) { - window.require('electron').ipcRenderer.send('header-animation-finished', 'hidden'); + if (window.api && window.api.header) { + window.api.header.sendAnimationFinished('hidden'); } } else if (this.classList.contains('showing')) { - if (window.require) { - window.require('electron').ipcRenderer.send('header-animation-finished', 'visible'); + if (window.api && window.api.header) { + window.api.header.sendAnimationFinished('visible'); } } } @@ -466,14 +467,12 @@ export class MainHeader extends LitElement { super.connectedCallback(); this.addEventListener('animationend', this.handleAnimationEnd); - if (window.require) { - const { ipcRenderer } = window.require('electron'); - + if (window.api && window.api.header) { this._sessionStateTextListener = (event, text) => { this.actionText = text; this.isTogglingSession = false; }; - ipcRenderer.on('session-state-text', this._sessionStateTextListener); + window.api.header.onSessionStateText(this._sessionStateTextListener); // this._sessionStateListener = (event, { isActive }) => { @@ -485,7 +484,7 @@ export class MainHeader extends LitElement { console.log('[MainHeader] Received updated shortcuts:', keybinds); this.shortcuts = keybinds; }; - ipcRenderer.on('shortcuts-updated', this._shortcutListener); + window.api.header.onShortcutsUpdated(this._shortcutListener); } } @@ -498,39 +497,37 @@ export class MainHeader extends LitElement { this.animationEndTimer = null; } - if (window.require) { - const { ipcRenderer } = window.require('electron'); + if (window.api && window.api.header) { if (this._sessionStateTextListener) { - ipcRenderer.removeListener('session-state-text', this._sessionStateTextListener); + window.api.header.removeOnSessionStateText(this._sessionStateTextListener); } // if (this._sessionStateListener) { // ipcRenderer.removeListener('session-state-changed', this._sessionStateListener); // } if (this._shortcutListener) { - ipcRenderer.removeListener('shortcuts-updated', this._shortcutListener); + window.api.header.removeOnShortcutsUpdated(this._shortcutListener); } } } invoke(channel, ...args) { if (this.wasJustDragged) return; - if (window.require) { - window.require('electron').ipcRenderer.invoke(channel, ...args); + if (window.api && window.api.header) { + window.api.header.invoke(channel, ...args); } // return Promise.resolve(); } showSettingsWindow(element) { if (this.wasJustDragged) return; - if (window.require) { - const { ipcRenderer } = window.require('electron'); + if (window.api && window.api.header) { console.log(`[MainHeader] showSettingsWindow called at ${Date.now()}`); - ipcRenderer.send('cancel-hide-settings-window'); + window.api.header.cancelHideSettingsWindow(); if (element) { const { left, top, width, height } = element.getBoundingClientRect(); - ipcRenderer.send('show-settings-window', { + window.api.header.showSettingsWindow({ x: left, y: top, width, @@ -542,9 +539,9 @@ export class MainHeader extends LitElement { hideSettingsWindow() { if (this.wasJustDragged) return; - if (window.require) { + if (window.api && window.api.header) { console.log(`[MainHeader] hideSettingsWindow called at ${Date.now()}`); - window.require('electron').ipcRenderer.send('hide-settings-window'); + window.api.header.hideSettingsWindow(); } } diff --git a/src/ui/app/PermissionHeader.js b/src/ui/app/PermissionHeader.js index 96a01c1..719d804 100644 --- a/src/ui/app/PermissionHeader.js +++ b/src/ui/app/PermissionHeader.js @@ -288,13 +288,12 @@ export class PermissionHeader extends LitElement { } async checkPermissions() { - if (!window.require || this.isChecking) return; - + if (!window.api || !window.api.permissions || this.isChecking) return; + this.isChecking = true; - const { ipcRenderer } = window.require('electron'); try { - const permissions = await ipcRenderer.invoke('check-system-permissions'); + const permissions = await window.api.permissions.checkSystemPermissions(); console.log('[PermissionHeader] Permission check result:', permissions); const prevMic = this.microphoneGranted; @@ -324,13 +323,12 @@ export class PermissionHeader extends LitElement { } async handleMicrophoneClick() { - if (!window.require || this.microphoneGranted === 'granted') return; + if (!window.api || !window.api.permissions || this.microphoneGranted === 'granted') return; console.log('[PermissionHeader] Requesting microphone permission...'); - const { ipcRenderer } = window.require('electron'); try { - const result = await ipcRenderer.invoke('check-system-permissions'); + const result = await window.api.permissions.checkSystemPermissions(); console.log('[PermissionHeader] Microphone permission result:', result); if (result.microphone === 'granted') { @@ -340,7 +338,7 @@ export class PermissionHeader extends LitElement { } if (result.microphone === 'not-determined' || result.microphone === 'denied' || result.microphone === 'unknown' || result.microphone === 'restricted') { - const res = await ipcRenderer.invoke('request-microphone-permission'); + const res = await window.api.permissions.requestMicrophonePermission(); if (res.status === 'granted' || res.success === true) { this.microphoneGranted = 'granted'; this.requestUpdate(); @@ -357,13 +355,12 @@ export class PermissionHeader extends LitElement { } async handleScreenClick() { - if (!window.require || this.screenGranted === 'granted') return; + if (!window.api || !window.api.permissions || this.screenGranted === 'granted') return; console.log('[PermissionHeader] Checking screen recording permission...'); - const { ipcRenderer } = window.require('electron'); try { - const permissions = await ipcRenderer.invoke('check-system-permissions'); + const permissions = await window.api.permissions.checkSystemPermissions(); console.log('[PermissionHeader] Screen permission check result:', permissions); if (permissions.screen === 'granted') { @@ -373,7 +370,7 @@ export class PermissionHeader extends LitElement { } if (permissions.screen === 'not-determined' || permissions.screen === 'denied' || permissions.screen === 'unknown' || permissions.screen === 'restricted') { console.log('[PermissionHeader] Opening screen recording preferences...'); - await ipcRenderer.invoke('open-system-preferences', 'screen-recording'); + await window.api.permissions.openSystemPreferences('screen-recording'); } // Check permissions again after a delay @@ -389,10 +386,9 @@ export class PermissionHeader extends LitElement { this.microphoneGranted === 'granted' && this.screenGranted === 'granted') { // Mark permissions as completed - if (window.require) { - const { ipcRenderer } = window.require('electron'); + if (window.api && window.api.permissions) { try { - await ipcRenderer.invoke('mark-permissions-completed'); + await window.api.permissions.markPermissionsCompleted(); console.log('[PermissionHeader] Marked permissions as completed'); } catch (error) { console.error('[PermissionHeader] Error marking permissions as completed:', error); @@ -405,8 +401,8 @@ export class PermissionHeader extends LitElement { handleClose() { console.log('Close button clicked'); - if (window.require) { - window.require('electron').ipcRenderer.invoke('quit-application'); + if (window.api && window.api.permissions) { + window.api.permissions.quitApplication(); } } diff --git a/src/ui/app/PickleGlassApp.js b/src/ui/app/PickleGlassApp.js index c1a7f72..16439ec 100644 --- a/src/ui/app/PickleGlassApp.js +++ b/src/ui/app/PickleGlassApp.js @@ -74,13 +74,11 @@ export class PickleGlassApp extends LitElement { connectedCallback() { super.connectedCallback(); - if (window.require) { - const { ipcRenderer } = window.require('electron'); - - ipcRenderer.on('click-through-toggled', (_, isEnabled) => { + if (window.api && window.api.app) { + window.api.app.onClickThroughToggled((_, isEnabled) => { this._isClickThrough = isEnabled; }); - // ipcRenderer.on('start-listening-session', () => { + // window.api.app.onStartListeningSession(() => { // console.log('Received start-listening-session command, calling handleListenClick.'); // this.handleListenClick(); // }); @@ -89,10 +87,9 @@ export class PickleGlassApp extends LitElement { disconnectedCallback() { super.disconnectedCallback(); - if (window.require) { - const { ipcRenderer } = window.require('electron'); - ipcRenderer.removeAllListeners('click-through-toggled'); - // ipcRenderer.removeAllListeners('start-listening-session'); + if (window.api && window.api.app) { + window.api.app.removeAllListeners('click-through-toggled'); + // window.api.app.removeAllListeners('start-listening-session'); } } @@ -160,9 +157,8 @@ export class PickleGlassApp extends LitElement { // } async handleClose() { - if (window.require) { - const { ipcRenderer } = window.require('electron'); - await ipcRenderer.invoke('quit-application'); + if (window.api && window.api.app) { + await window.api.app.quitApplication(); } } diff --git a/src/ui/app/content.html b/src/ui/app/content.html index 868e0db..3cba228 100644 --- a/src/ui/app/content.html +++ b/src/ui/app/content.html @@ -238,15 +238,13 @@ window.addEventListener('DOMContentLoaded', () => { const app = document.getElementById('pickle-glass'); - if (window.require) { - const { ipcRenderer } = window.require('electron'); - + if (window.api && window.api.animation) { // --- REFACTORED: Event-driven animation handling --- app.addEventListener('animationend', (event) => { // 숨김 애니메이션이 끝나면 main 프로세스에 알려 창을 실제로 숨깁니다. if (event.animationName === 'slideUpToHeader' || event.animationName === 'settingsCollapseToButton') { console.log(`Animation finished: ${event.animationName}. Notifying main process.`); - ipcRenderer.send('animation-finished'); + window.api.animation.sendAnimationFinished(); // 완료 후 애니메이션 클래스 정리 app.classList.remove('window-sliding-up', 'settings-window-hide'); @@ -257,26 +255,26 @@ } }); - ipcRenderer.on('window-show-animation', () => { + window.api.animation.onWindowShowAnimation(() => { console.log('Starting window show animation'); app.classList.remove('window-hidden', 'window-sliding-up', 'settings-window-hide'); app.classList.add('window-sliding-down'); }); - ipcRenderer.on('window-hide-animation', () => { + window.api.animation.onWindowHideAnimation(() => { console.log('Starting window hide animation'); app.classList.remove('window-sliding-down', 'settings-window-show'); app.classList.add('window-sliding-up'); }); - ipcRenderer.on('settings-window-hide-animation', () => { + window.api.animation.onSettingsWindowHideAnimation(() => { console.log('Starting settings window hide animation'); app.classList.remove('window-sliding-down', 'settings-window-show'); app.classList.add('settings-window-hide'); }); // --- UNCHANGED: Existing logic for listen window movement --- - ipcRenderer.on('listen-window-move-to-center', () => { + window.api.animation.onListenWindowMoveToCenter(() => { console.log('Moving listen window to center'); app.classList.add('listen-window-moving'); app.classList.remove('listen-window-left'); @@ -287,7 +285,7 @@ }, 350); }); - ipcRenderer.on('listen-window-move-to-left', () => { + window.api.animation.onListenWindowMoveToLeft(() => { console.log('Moving listen window to left'); app.classList.add('listen-window-moving'); app.classList.remove('listen-window-center'); diff --git a/src/ui/ask/AskView.js b/src/ui/ask/AskView.js index 39270ba..83fc62a 100644 --- a/src/ui/ask/AskView.js +++ b/src/ui/ask/AskView.js @@ -822,17 +822,15 @@ export class AskView extends LitElement { handleWindowBlur() { if (!this.currentResponse && !this.isLoading && !this.isStreaming) { // If there's no active content, ask the main process to close this window. - if (window.require) { - const { ipcRenderer } = window.require('electron'); - ipcRenderer.invoke('close-ask-window-if-empty'); + if (window.api && window.api.ask) { + window.api.ask.closeWindowIfEmpty(); } } } closeIfNoContent() { - if (window.require) { - const { ipcRenderer } = window.require('electron'); - ipcRenderer.invoke('force-close-window', 'ask'); + if (window.api && window.api.ask) { + window.api.ask.forceCloseWindow('ask'); } } @@ -912,34 +910,34 @@ export class AskView extends LitElement { this.processAssistantQuestion(question); }; - if (window.require) { - const { ipcRenderer } = window.require('electron'); - ipcRenderer.on('ask-global-send', this.handleGlobalSendRequest); - ipcRenderer.on('receive-question-from-assistant', this.handleQuestionFromAssistant); - ipcRenderer.on('hide-text-input', () => { + if (window.api && window.api.ask) { + // preload API를 사용하여 이벤트 리스너 등록 + window.api.ask.onGlobalSend(this.handleGlobalSendRequest); + window.api.ask.onReceiveQuestionFromAssistant(this.handleQuestionFromAssistant); + window.api.ask.onHideTextInput(() => { console.log('📤 Hide text input signal received'); this.showTextInput = false; this.requestUpdate(); }); - ipcRenderer.on('window-hide-animation', () => { + window.api.ask.onWindowHideAnimation(() => { console.log('📤 Ask window hiding - clearing response content'); setTimeout(() => { this.clearResponseContent(); }, 250); }); - ipcRenderer.on('window-blur', this.handleWindowBlur); - ipcRenderer.on('window-did-show', () => { + window.api.ask.onWindowBlur(this.handleWindowBlur); + window.api.ask.onWindowDidShow(() => { if (!this.currentResponse && !this.isLoading && !this.isStreaming) { this.focusTextInput(); } }); - ipcRenderer.on('ask-response-chunk', this.handleStreamChunk); - ipcRenderer.on('ask-response-stream-end', this.handleStreamEnd); + window.api.ask.onResponseChunk(this.handleStreamChunk); + window.api.ask.onResponseStreamEnd(this.handleStreamEnd); - ipcRenderer.on('scroll-response-up', () => this.handleScroll('up')); - ipcRenderer.on('scroll-response-down', () => this.handleScroll('down')); - console.log('✅ AskView: IPC 이벤트 리스너 등록 완료'); + window.api.ask.onScrollResponseUp(() => this.handleScroll('up')); + window.api.ask.onScrollResponseDown(() => this.handleScroll('down')); + console.log('✅ AskView: preload API 이벤트 리스너 등록 완료'); } } @@ -966,19 +964,19 @@ export class AskView extends LitElement { Object.values(this.lineCopyTimeouts).forEach(timeout => clearTimeout(timeout)); - if (window.require) { - const { ipcRenderer } = window.require('electron'); - ipcRenderer.removeListener('ask-global-send', this.handleGlobalSendRequest); - ipcRenderer.removeListener('hide-text-input', () => { }); - ipcRenderer.removeListener('window-hide-animation', () => { }); - ipcRenderer.removeListener('window-blur', this.handleWindowBlur); + if (window.api && window.api.ask) { + // preload API를 사용하여 이벤트 리스너 제거 + window.api.ask.removeOnGlobalSend(this.handleGlobalSendRequest); + window.api.ask.removeOnHideTextInput(() => { }); + window.api.ask.removeOnWindowHideAnimation(() => { }); + window.api.ask.removeOnWindowBlur(this.handleWindowBlur); - ipcRenderer.removeListener('ask-response-chunk', this.handleStreamChunk); - ipcRenderer.removeListener('ask-response-stream-end', this.handleStreamEnd); + window.api.ask.removeOnResponseChunk(this.handleStreamChunk); + window.api.ask.removeOnResponseStreamEnd(this.handleStreamEnd); - ipcRenderer.removeListener('scroll-response-up', () => this.handleScroll('up')); - ipcRenderer.removeListener('scroll-response-down', () => this.handleScroll('down')); - console.log('✅ AskView: IPC 이벤트 리스너 제거 완료'); + window.api.ask.removeOnScrollResponseUp(() => this.handleScroll('up')); + window.api.ask.removeOnScrollResponseDown(() => this.handleScroll('down')); + console.log('✅ AskView: preload API 이벤트 리스너 제거 완료'); } } @@ -1138,9 +1136,8 @@ export class AskView extends LitElement { requestWindowResize(targetHeight) { - if (window.require) { - const { ipcRenderer } = window.require('electron'); - ipcRenderer.invoke('adjust-window-height', targetHeight); + if (window.api && window.api.ask) { + window.api.ask.adjustWindowHeight(targetHeight); } } @@ -1181,9 +1178,8 @@ export class AskView extends LitElement { } closeResponsePanel() { - if (window.require) { - const { ipcRenderer } = window.require('electron'); - ipcRenderer.invoke('force-close-window', 'ask'); + if (window.api && window.api.ask) { + window.api.ask.forceCloseWindow('ask'); } } @@ -1236,9 +1232,8 @@ export class AskView extends LitElement { this.requestUpdate(); this.renderContent(); - if (window.require) { - const { ipcRenderer } = window.require('electron'); - ipcRenderer.invoke('ask:sendMessage', question).catch(error => { + if (window.api && window.api.ask) { + window.api.ask.sendMessage(question).catch(error => { console.error('Error processing assistant question:', error); this.isLoading = false; this.isStreaming = false; @@ -1335,9 +1330,8 @@ export class AskView extends LitElement { this.requestUpdate(); this.renderContent(); - if (window.require) { - const { ipcRenderer } = window.require('electron'); - ipcRenderer.invoke('ask:sendMessage', text).catch(error => { + if (window.api && window.api.ask) { + window.api.ask.sendMessage(text).catch(error => { console.error('Error sending text:', error); this.isLoading = false; this.isStreaming = false; @@ -1511,7 +1505,7 @@ export class AskView extends LitElement { // Dynamically resize the BrowserWindow to fit current content adjustWindowHeight() { - if (!window.require) return; + if (!window.api || !window.api.ask) return; this.updateComplete.then(() => { const headerEl = this.shadowRoot.querySelector('.response-header'); @@ -1528,8 +1522,7 @@ export class AskView extends LitElement { const targetHeight = Math.min(700, idealHeight); - const { ipcRenderer } = window.require('electron'); - ipcRenderer.invoke('adjust-window-height', targetHeight); + window.api.ask.adjustWindowHeight(targetHeight); }).catch(err => console.error('AskView adjustWindowHeight error:', err)); } diff --git a/src/ui/listen/ListenView.js b/src/ui/listen/ListenView.js index 3339300..159cb4f 100644 --- a/src/ui/listen/ListenView.js +++ b/src/ui/listen/ListenView.js @@ -453,9 +453,8 @@ export class ListenView extends LitElement { if (this.isSessionActive) { this.startTimer(); } - if (window.require) { - const { ipcRenderer } = window.require('electron'); - ipcRenderer.on('session-state-changed', (event, { isActive }) => { + if (window.api && window.api.listen) { + window.api.listen.onSessionStateChanged((event, { isActive }) => { const wasActive = this.isSessionActive; this.isSessionActive = isActive; @@ -514,7 +513,7 @@ export class ListenView extends LitElement { } adjustWindowHeight() { - if (!window.require) return; + if (!window.api || !window.api.listen) return; this.updateComplete .then(() => { @@ -537,8 +536,7 @@ export class ListenView extends LitElement { `[Height Adjusted] Mode: ${this.viewMode}, TopBar: ${topBarHeight}px, Content: ${contentHeight}px, Ideal: ${idealHeight}px, Target: ${targetHeight}px` ); - const { ipcRenderer } = window.require('electron'); - ipcRenderer.invoke('adjust-window-height', targetHeight); + window.api.listen.adjustWindowHeight(targetHeight); }) .catch(error => { console.error('Error in adjustWindowHeight:', error); diff --git a/src/ui/listen/audioCore/listenCapture.js b/src/ui/listen/audioCore/listenCapture.js index b2d80bf..8da61ba 100644 --- a/src/ui/listen/audioCore/listenCapture.js +++ b/src/ui/listen/audioCore/listenCapture.js @@ -198,7 +198,7 @@ function runAecSync(micF32, sysF32) { // System audio data handler -ipcRenderer.on('system-audio-data', (event, { data }) => { + window.api.audio.onSystemAudioData((event, { data }) => { systemAudioBuffer.push({ data: data, timestamp: Date.now(), @@ -336,7 +336,7 @@ async function setupMicProcessing(micStream) { const pcm16 = convertFloat32ToInt16(processedChunk); const b64 = arrayBufferToBase64(pcm16.buffer); - ipcRenderer.invoke('send-audio-content', { + window.api.audio.sendAudioContent({ data: b64, mimeType: 'audio/pcm;rate=24000', }); @@ -369,7 +369,7 @@ function setupLinuxMicProcessing(micStream) { const pcmData16 = convertFloat32ToInt16(chunk); const base64Data = arrayBufferToBase64(pcmData16.buffer); - await ipcRenderer.invoke('send-audio-content', { + await window.api.audio.sendAudioContent({ data: base64Data, mimeType: 'audio/pcm;rate=24000', }); @@ -403,7 +403,7 @@ function setupSystemAudioProcessing(systemStream) { const base64Data = arrayBufferToBase64(pcmData16.buffer); try { - await ipcRenderer.invoke('send-system-audio-content', { + await window.api.audio.sendSystemAudioContent({ data: base64Data, mimeType: 'audio/pcm;rate=24000', }); @@ -433,7 +433,7 @@ async function captureScreenshot(imageQuality = 'medium', isManual = false) { try { // Request screenshot from main process - const result = await ipcRenderer.invoke('capture-screenshot', { + const result = await window.api.audio.captureScreenshot({ quality: imageQuality, }); @@ -470,7 +470,7 @@ async function captureManualScreenshot(imageQuality = null) { async function getCurrentScreenshot() { try { // First try to get a fresh screenshot from main process - const result = await ipcRenderer.invoke('get-current-screenshot'); + const result = await window.api.audio.getCurrentScreenshot(); if (result.success && result.base64) { console.log('📸 Got fresh screenshot from main process'); @@ -479,7 +479,7 @@ async function getCurrentScreenshot() { // If no screenshot available, capture one now console.log('📸 No screenshot available, capturing new one'); - const captureResult = await ipcRenderer.invoke('capture-screenshot', { + const captureResult = await window.api.audio.captureScreenshot({ quality: currentImageQuality, }); @@ -518,15 +518,15 @@ async function startCapture(screenshotIntervalSeconds = 5, imageQuality = 'mediu console.log('Starting macOS capture with SystemAudioDump...'); // Start macOS audio capture - const audioResult = await ipcRenderer.invoke('start-macos-audio'); + const audioResult = await window.api.audio.startMacosAudio(); if (!audioResult.success) { console.warn('[listenCapture] macOS audio start failed:', audioResult.error); // 이미 실행 중 → stop 후 재시도 if (audioResult.error === 'already_running') { - await ipcRenderer.invoke('stop-macos-audio'); + await window.api.audio.stopMacosAudio(); await new Promise(r => setTimeout(r, 500)); - const retry = await ipcRenderer.invoke('start-macos-audio'); + const retry = await window.api.audio.startMacosAudio(); if (!retry.success) { throw new Error('Retry failed: ' + retry.error); } @@ -536,7 +536,7 @@ async function startCapture(screenshotIntervalSeconds = 5, imageQuality = 'mediu } // Initialize screen capture in main process - const screenResult = await ipcRenderer.invoke('start-screen-capture'); + const screenResult = await window.api.audio.startScreenCapture(); if (!screenResult.success) { throw new Error('Failed to start screen capture: ' + screenResult.error); } @@ -604,13 +604,13 @@ async function startCapture(screenshotIntervalSeconds = 5, imageQuality = 'mediu console.log('Starting Windows capture with native loopback audio...'); // Start screen capture in main process for screenshots - const screenResult = await ipcRenderer.invoke('start-screen-capture'); + const screenResult = await window.api.audio.startScreenCapture(); if (!screenResult.success) { throw new Error('Failed to start screen capture: ' + screenResult.error); } // Ensure STT sessions are initialized before starting audio capture - const sessionActive = await ipcRenderer.invoke('is-session-active'); + const sessionActive = await window.api.audio.isSessionActive(); if (!sessionActive) { throw new Error('STT sessions not initialized - please wait for initialization to complete'); } @@ -715,13 +715,13 @@ function stopCapture() { } // Stop screen capture in main process - ipcRenderer.invoke('stop-screen-capture').catch(err => { + window.api.audio.stopScreenCapture().catch(err => { console.error('Error stopping screen capture:', err); }); // Stop macOS audio capture if running if (isMacOS) { - ipcRenderer.invoke('stop-macos-audio').catch(err => { + window.api.audio.stopMacosAudio().catch(err => { console.error('Error stopping macOS audio:', err); }); } diff --git a/src/ui/listen/audioCore/renderer.js b/src/ui/listen/audioCore/renderer.js index fe26e66..6ff58b1 100644 --- a/src/ui/listen/audioCore/renderer.js +++ b/src/ui/listen/audioCore/renderer.js @@ -15,7 +15,7 @@ window.pickleGlass = { }; -ipcRenderer.on('change-listen-capture-state', (_event, { status }) => { +window.api.audio.onChangeListenCaptureState((_event, { status }) => { if (!isListenView) { console.log('[Renderer] Non-listen view: ignoring capture-state change'); return; diff --git a/src/ui/listen/stt/SttView.js b/src/ui/listen/stt/SttView.js index 1b18b9e..3b663c5 100644 --- a/src/ui/listen/stt/SttView.js +++ b/src/ui/listen/stt/SttView.js @@ -95,17 +95,15 @@ export class SttView extends LitElement { connectedCallback() { super.connectedCallback(); - if (window.require) { - const { ipcRenderer } = window.require('electron'); - ipcRenderer.on('stt-update', this.handleSttUpdate); + if (window.api && window.api.listen) { + window.api.listen.onSttUpdate(this.handleSttUpdate); } } disconnectedCallback() { super.disconnectedCallback(); - if (window.require) { - const { ipcRenderer } = window.require('electron'); - ipcRenderer.removeListener('stt-update', this.handleSttUpdate); + if (window.api && window.api.listen) { + window.api.listen.removeOnSttUpdate(this.handleSttUpdate); } } diff --git a/src/ui/listen/summary/SummaryView.js b/src/ui/listen/summary/SummaryView.js index b8394b5..c6c63ca 100644 --- a/src/ui/listen/summary/SummaryView.js +++ b/src/ui/listen/summary/SummaryView.js @@ -262,9 +262,8 @@ export class SummaryView extends LitElement { connectedCallback() { super.connectedCallback(); - if (window.require) { - const { ipcRenderer } = window.require('electron'); - ipcRenderer.on('summary-update', (event, data) => { + if (window.api && window.api.listen) { + window.api.listen.onSummaryUpdate((event, data) => { this.structuredData = data; this.requestUpdate(); }); @@ -273,9 +272,8 @@ export class SummaryView extends LitElement { disconnectedCallback() { super.disconnectedCallback(); - if (window.require) { - const { ipcRenderer } = window.require('electron'); - ipcRenderer.removeAllListeners('summary-update'); + if (window.api && window.api.listen) { + window.api.listen.removeOnSummaryUpdate(() => {}); } } @@ -408,18 +406,16 @@ export class SummaryView extends LitElement { async handleRequestClick(requestText) { console.log('🔥 Analysis request clicked:', requestText); - if (window.require) { - const { ipcRenderer } = window.require('electron'); - + if (window.api && window.api.listen) { try { - const isAskViewVisible = await ipcRenderer.invoke('is-ask-window-visible', 'ask'); + const isAskViewVisible = await window.api.listen.isAskWindowVisible('ask'); if (!isAskViewVisible) { - await ipcRenderer.invoke('toggle-feature', 'ask'); + await window.api.listen.toggleFeature('ask'); await new Promise(resolve => setTimeout(resolve, 100)); } - const result = await ipcRenderer.invoke('send-question-to-ask', requestText); + const result = await window.api.listen.sendQuestionToAsk(requestText); if (result.success) { console.log('✅ Question sent to AskView successfully'); diff --git a/src/ui/settings/ShortCutSettingsView.js b/src/ui/settings/ShortCutSettingsView.js index 4bf6511..22acf9b 100644 --- a/src/ui/settings/ShortCutSettingsView.js +++ b/src/ui/settings/ShortCutSettingsView.js @@ -102,23 +102,23 @@ export class ShortcutSettingsView extends LitElement { this.feedback = {}; this.isLoading = true; this.capturingKey = null; - this.ipcRenderer = window.require ? window.require('electron').ipcRenderer : null; + this.hasAPI = window.api && window.api.settings; } connectedCallback() { super.connectedCallback(); - if (!this.ipcRenderer) return; + if (!this.hasAPI) return; this.loadShortcutsHandler = (event, keybinds) => { this.shortcuts = keybinds; this.isLoading = false; }; - this.ipcRenderer.on('load-shortcuts', this.loadShortcutsHandler); + window.api.settings.onLoadShortcuts(this.loadShortcutsHandler); } disconnectedCallback() { super.disconnectedCallback(); - if (this.ipcRenderer && this.loadShortcutsHandler) { - this.ipcRenderer.removeListener('load-shortcuts', this.loadShortcutsHandler); + if (this.hasAPI && this.loadShortcutsHandler) { + window.api.settings.removeOnLoadShortcuts(this.loadShortcutsHandler); } } @@ -171,25 +171,25 @@ export class ShortcutSettingsView extends LitElement { } async handleSave() { - if (!this.ipcRenderer) return; - const result = await this.ipcRenderer.invoke('save-shortcuts', this.shortcuts); + if (!this.hasAPI) return; + const result = await window.api.settings.saveShortcuts(this.shortcuts); if (!result.success) { alert('Failed to save shortcuts: ' + result.error); } } handleClose() { - if (!this.ipcRenderer) return; - this.ipcRenderer.send('close-shortcut-editor'); + if (!this.hasAPI) return; + window.api.settings.closeShortcutEditor(); } async handleResetToDefault() { - if (!this.ipcRenderer) return; + if (!this.hasAPI) return; const confirmation = confirm("Are you sure you want to reset all shortcuts to their default values?"); if (!confirmation) return; try { - const defaultShortcuts = await this.ipcRenderer.invoke('get-default-shortcuts'); + const defaultShortcuts = await window.api.settings.getDefaultShortcuts(); this.shortcuts = defaultShortcuts; } catch (error) { alert('Failed to load default settings.');