diff --git a/aec b/aec index 3be088c..f00bb1f 160000 --- a/aec +++ b/aec @@ -1 +1 @@ -Subproject commit 3be088c6cff8021c74eca714150e68e2cc74bee0 +Subproject commit f00bb1fb948053c752b916adfee19f90644a0b2f diff --git a/src/bridge/featureBridge.js b/src/bridge/featureBridge.js new file mode 100644 index 0000000..3cce84b --- /dev/null +++ b/src/bridge/featureBridge.js @@ -0,0 +1,71 @@ +// src/bridge/featureBridge.js +const { ipcMain } = require('electron'); +const settingsService = require('../features/settings/settingsService'); + +module.exports = { + // Renderer로부터의 요청을 수신 + initialize() { + // 기존 ask 핸들러 유지 + ipcMain.handle('feature:ask', (e, query) => { + // 실제로는 여기서 Controller -> Service 로직 수행 + return `"${query}"에 대한 답변입니다.`; + }); + + // settings 관련 핸들러 추가 + ipcMain.handle('settings:getSettings', async () => { + return await settingsService.getSettings(); + }); + + ipcMain.handle('settings:saveSettings', async (event, settings) => { + return await settingsService.saveSettings(settings); + }); + + ipcMain.handle('settings:getPresets', async () => { + return await settingsService.getPresets(); + }); + + ipcMain.handle('settings:getPresetTemplates', async () => { + return await settingsService.getPresetTemplates(); + }); + + ipcMain.handle('settings:createPreset', async (event, title, prompt) => { + return await settingsService.createPreset(title, prompt); + }); + + ipcMain.handle('settings:updatePreset', async (event, id, title, prompt) => { + return await settingsService.updatePreset(id, title, prompt); + }); + + ipcMain.handle('settings:deletePreset', async (event, id) => { + return await settingsService.deletePreset(id); + }); + + ipcMain.handle('settings:saveApiKey', async (event, apiKey, provider) => { + return await settingsService.saveApiKey(apiKey, provider); + }); + + ipcMain.handle('settings:removeApiKey', async () => { + return await settingsService.removeApiKey(); + }); + + ipcMain.handle('settings:updateContentProtection', async (event, enabled) => { + return await settingsService.updateContentProtection(enabled); + }); + + ipcMain.handle('settings:get-auto-update', async () => { + return await settingsService.getAutoUpdateSetting(); + }); + + ipcMain.handle('settings:set-auto-update', async (event, isEnabled) => { + console.log('[SettingsService] Setting auto update setting:', isEnabled); + return await settingsService.setAutoUpdateSetting(isEnabled); + }); + + console.log('[FeatureBridge] Initialized with settings handlers.'); + }, + + // Renderer로 상태를 전송 + sendAskProgress(win, progress) { + win.webContents.send('feature:ask:progress', progress); + }, +}; \ No newline at end of file diff --git a/src/bridge/internalBridge.js b/src/bridge/internalBridge.js new file mode 100644 index 0000000..28c41cf --- /dev/null +++ b/src/bridge/internalBridge.js @@ -0,0 +1,10 @@ +// src/bridge/internalBridge.js +const { EventEmitter } = require('events'); + +// FeatureCore와 WindowCore를 잇는 내부 이벤트 버스 +module.exports = new EventEmitter(); + +// 예시 이벤트 +internalBridge.on('content-protection-changed', (enabled) => { + // windowManager에서 처리 +}); \ No newline at end of file diff --git a/src/bridge/windowBridge.js b/src/bridge/windowBridge.js new file mode 100644 index 0000000..15aa91f --- /dev/null +++ b/src/bridge/windowBridge.js @@ -0,0 +1,74 @@ +// src/bridge/windowBridge.js +const { ipcMain, BrowserWindow } = require('electron'); +const { windowPool, settingsHideTimer, app, shell } = require('../electron/windowManager'); // 필요 변수 require + +module.exports = { + // Renderer로부터의 요청을 수신 + initialize() { + // 기존 + ipcMain.on('window:hide', (e) => BrowserWindow.fromWebContents(e.sender)?.hide()); + + // windowManager 관련 추가 + ipcMain.handle('toggle-content-protection', () => { + // windowManager의 toggle-content-protection 로직 + isContentProtectionOn = !isContentProtectionOn; + windowPool.forEach(win => { + if (win && !win.isDestroyed()) { + win.setContentProtection(isContentProtectionOn); + } + }); + return isContentProtectionOn; + }); + + ipcMain.handle('get-content-protection-status', () => { + return isContentProtectionOn; + }); + + ipcMain.handle('open-shortcut-editor', () => { + // open-shortcut-editor 로직 (windowPool 등 필요시 require) + const header = windowPool.get('header'); + if (!header) return; + globalShortcut.unregisterAll(); + createFeatureWindows(header, 'shortcut-settings'); + }); + + // 다른 관련 핸들러 추가 (quit-application, etc.) + ipcMain.handle('quit-application', () => { + app.quit(); + }); + + // 추가: show-settings-window + ipcMain.on('show-settings-window', (event, bounds) => { + if (!bounds) return; + const win = windowPool.get('settings'); + if (win && !win.isDestroyed()) { + if (settingsHideTimer) clearTimeout(settingsHideTimer); + // 위치 조정 로직 (기존 복사) + const header = windowPool.get('header'); + const headerBounds = header?.getBounds() ?? { x: 0, y: 0 }; + const settingsBounds = win.getBounds(); + const disp = getCurrentDisplay(header); + const { x: waX, y: waY, width: waW, height: waH } = disp.workArea; + let x = Math.round(headerBounds.x + (bounds?.x ?? 0) + (bounds?.width ?? 0) / 2 - settingsBounds.width / 2); + let y = Math.round(headerBounds.y + (bounds?.y ?? 0) + (bounds?.height ?? 0) + 31); + x = Math.max(waX + 10, Math.min(waX + waW - settingsBounds.width - 10, x)); + y = Math.max(waY + 10, Math.min(waY + waH - settingsBounds.height - 10, y)); + win.setBounds({ x, y }); + win.__lockedByButton = true; + win.show(); + win.moveTop(); + win.setAlwaysOnTop(true); + } + }); + + // 추가: hide-settings-window 등 다른 핸들러 복사 + // ... (hide-settings-window, cancel-hide-settings-window, quit-application, open-login-page, firebase-logout, move-window-step 등) + + // 예: ipcMain.handle('open-login-page', () => { shell.openExternal(...); }); + }, + + // Renderer로 상태를 전송 + notifyFocusChange(win, isFocused) { + win.webContents.send('window:focus-change', isFocused); + }, +}; \ No newline at end of file diff --git a/src/electron/windowManager.js b/src/electron/windowManager.js index c4bbd81..57f2eca 100644 --- a/src/electron/windowManager.js +++ b/src/electron/windowManager.js @@ -76,6 +76,7 @@ function updateLayout() { } let movementManager = null; +const windowBridge = require('../bridge/windowBridge'); async function toggleFeature(featureName) { diff --git a/src/features/settings/SettingsView.js b/src/features/settings/SettingsView.js index c0b40ea..a2339ad 100644 --- a/src/features/settings/SettingsView.js +++ b/src/features/settings/SettingsView.js @@ -543,11 +543,9 @@ export class SettingsView extends LitElement { } async loadAutoUpdateSetting() { - if (!window.require) return; - const { ipcRenderer } = window.require('electron'); this.autoUpdateLoading = true; try { - const enabled = await ipcRenderer.invoke('settings:get-auto-update'); + const enabled = await window.api.feature.settings.getAutoUpdate(); this.autoUpdateEnabled = enabled; console.log('Auto-update setting loaded:', enabled); } catch (e) { @@ -559,13 +557,12 @@ export class SettingsView extends LitElement { } async handleToggleAutoUpdate() { - if (!window.require || this.autoUpdateLoading) return; - const { ipcRenderer } = window.require('electron'); + if (this.autoUpdateLoading) return; this.autoUpdateLoading = true; this.requestUpdate(); try { const newValue = !this.autoUpdateEnabled; - const result = await ipcRenderer.invoke('settings:set-auto-update', newValue); + const result = await window.api.feature.settings.setAutoUpdate(newValue); if (result && result.success) { this.autoUpdateEnabled = newValue; } else { @@ -580,22 +577,20 @@ export class SettingsView extends LitElement { //////// after_modelStateService //////// async loadInitialData() { - if (!window.require) return; this.isLoading = true; - const { ipcRenderer } = window.require('electron'); try { const [userState, config, storedKeys, availableLlm, availableStt, selectedModels, presets, contentProtection, shortcuts, ollamaStatus, whisperModelsResult] = await Promise.all([ - ipcRenderer.invoke('get-current-user'), - ipcRenderer.invoke('model:get-provider-config'), // Provider 설정 로드 - ipcRenderer.invoke('model:get-all-keys'), - ipcRenderer.invoke('model:get-available-models', { type: 'llm' }), - ipcRenderer.invoke('model:get-available-models', { type: 'stt' }), - ipcRenderer.invoke('model:get-selected-models'), - ipcRenderer.invoke('settings:getPresets'), - ipcRenderer.invoke('get-content-protection-status'), - ipcRenderer.invoke('get-current-shortcuts'), - ipcRenderer.invoke('ollama:get-status'), - ipcRenderer.invoke('whisper:get-installed-models') + window.api.feature.settings.getCurrentUser(), + window.api.feature.settings.getProviderConfig(), // Provider 설정 로드 + window.api.feature.settings.getAllKeys(), + window.api.feature.settings.getAvailableModels({ type: 'llm' }), + window.api.feature.settings.getAvailableModels({ type: 'stt' }), + window.api.feature.settings.getSelectedModels(), + window.api.feature.settings.getPresets(), + window.api.feature.settings.getContentProtectionStatus(), + window.api.feature.settings.getCurrentShortcuts(), + window.api.feature.settings.getOllamaStatus(), + window.api.feature.settings.getWhisperInstalledModels() ]); if (userState && userState.isLoggedIn) this.firebaseUser = userState; @@ -644,10 +639,9 @@ export class SettingsView extends LitElement { // For Ollama, we need to ensure it's ready first if (provider === 'ollama') { this.saving = true; - const { ipcRenderer } = window.require('electron'); // First ensure Ollama is installed and running - const ensureResult = await ipcRenderer.invoke('ollama:ensure-ready'); + const ensureResult = await window.api.feature.settings.ensureOllamaReady(); if (!ensureResult.success) { alert(`Failed to setup Ollama: ${ensureResult.error}`); this.saving = false; @@ -655,7 +649,7 @@ export class SettingsView extends LitElement { } // Now validate (which will check if service is running) - const result = await ipcRenderer.invoke('model:validate-key', { provider, key: 'local' }); + const result = await window.api.feature.settings.validateKey({ provider, key: 'local' }); if (result.success) { this.apiKeys = { ...this.apiKeys, [provider]: 'local' }; @@ -671,8 +665,7 @@ export class SettingsView extends LitElement { // For Whisper, just enable it if (provider === 'whisper') { this.saving = true; - const { ipcRenderer } = window.require('electron'); - const result = await ipcRenderer.invoke('model:validate-key', { provider, key: 'local' }); + const result = await window.api.feature.settings.validateKey({ provider, key: 'local' }); if (result.success) { this.apiKeys = { ...this.apiKeys, [provider]: 'local' }; @@ -686,8 +679,7 @@ export class SettingsView extends LitElement { // For other providers, use the normal flow this.saving = true; - const { ipcRenderer } = window.require('electron'); - const result = await ipcRenderer.invoke('model:validate-key', { provider, key }); + const result = await window.api.feature.settings.validateKey({ provider, key }); if (result.success) { this.apiKeys = { ...this.apiKeys, [provider]: key }; @@ -701,20 +693,18 @@ export class SettingsView extends LitElement { async handleClearKey(provider) { this.saving = true; - const { ipcRenderer } = window.require('electron'); - await ipcRenderer.invoke('model:remove-api-key', { provider }); + await window.api.feature.settings.removeApiKey({ provider }); this.apiKeys = { ...this.apiKeys, [provider]: '' }; await this.refreshModelData(); this.saving = false; } async refreshModelData() { - const { ipcRenderer } = window.require('electron'); const [availableLlm, availableStt, selected, storedKeys] = await Promise.all([ - ipcRenderer.invoke('model:get-available-models', { type: 'llm' }), - ipcRenderer.invoke('model:get-available-models', { type: 'stt' }), - ipcRenderer.invoke('model:get-selected-models'), - ipcRenderer.invoke('model:get-all-keys') + window.api.feature.settings.getAvailableModels({ type: 'llm' }), + window.api.feature.settings.getAvailableModels({ type: 'stt' }), + window.api.feature.settings.getSelectedModels(), + window.api.feature.settings.getAllKeys() ]); this.availableLlmModels = availableLlm; this.availableSttModels = availableStt; @@ -765,8 +755,7 @@ export class SettingsView extends LitElement { } this.saving = true; - const { ipcRenderer } = window.require('electron'); - await ipcRenderer.invoke('model:set-selected-model', { type, modelId }); + await window.api.feature.settings.setSelectedModel({ type, modelId }); if (type === 'llm') this.selectedLlm = modelId; if (type === 'stt') this.selectedStt = modelId; this.isLlmListVisible = false; @@ -776,8 +765,7 @@ export class SettingsView extends LitElement { } async refreshOllamaStatus() { - const { ipcRenderer } = window.require('electron'); - const ollamaStatus = await ipcRenderer.invoke('ollama:get-status'); + const ollamaStatus = await window.api.feature.settings.getOllamaStatus(); if (ollamaStatus?.success) { this.ollamaStatus = { installed: ollamaStatus.installed, running: ollamaStatus.running }; this.ollamaModels = ollamaStatus.models || []; @@ -821,8 +809,6 @@ export class SettingsView extends LitElement { this.requestUpdate(); try { - const { ipcRenderer } = window.require('electron'); - // Set up progress listener const progressHandler = (event, { modelId: id, progress }) => { if (id === modelId) { @@ -831,10 +817,10 @@ export class SettingsView extends LitElement { } }; - ipcRenderer.on('whisper:download-progress', progressHandler); + window.api.feature.settings.onWhisperDownloadProgress(progressHandler); // Start download - const result = await ipcRenderer.invoke('whisper:download-model', modelId); + const result = await window.api.feature.settings.downloadWhisperModel(modelId); if (result.success) { // Auto-select the model after download @@ -844,7 +830,7 @@ export class SettingsView extends LitElement { } // Cleanup - ipcRenderer.removeListener('whisper:download-progress', progressHandler); + window.api.feature.settings.removeOnWhisperDownloadProgress(progressHandler); } catch (error) { console.error(`[SettingsView] Error downloading Whisper model ${modelId}:`, error); alert(`Error downloading ${modelId}: ${error.message}`); @@ -876,17 +862,12 @@ export class SettingsView extends LitElement { if (this.wasJustDragged) return console.log("Requesting Firebase authentication from main process...") - if (window.require) { - window.require("electron").ipcRenderer.invoke("start-firebase-auth") - } - } + window.api.feature.settings.startFirebaseAuth(); + } //////// after_modelStateService //////// openShortcutEditor() { - if (window.require) { - const { ipcRenderer } = window.require('electron'); - ipcRenderer.invoke('open-shortcut-editor'); - } + window.api.feature.settings.openShortcutEditor(); } connectedCallback() { @@ -924,10 +905,6 @@ export class SettingsView extends LitElement { } setupIpcListeners() { - if (!window.require) return; - - const { ipcRenderer } = window.require('electron'); - this._userStateListener = (event, userState) => { console.log('[SettingsView] Received user-state-changed:', userState); if (userState && userState.isLoggedIn) { @@ -949,7 +926,7 @@ export class SettingsView extends LitElement { this._presetsUpdatedListener = async (event) => { console.log('[SettingsView] Received presets-updated, refreshing presets'); try { - const presets = await ipcRenderer.invoke('settings:getPresets'); + const presets = await window.api.feature.settings.getPresets(); this.presets = presets || []; // 현재 선택된 프리셋이 삭제되었는지 확인 (사용자 프리셋만 고려) @@ -968,28 +945,24 @@ export class SettingsView extends LitElement { this.shortcuts = keybinds; }; - ipcRenderer.on('user-state-changed', this._userStateListener); - ipcRenderer.on('settings-updated', this._settingsUpdatedListener); - ipcRenderer.on('presets-updated', this._presetsUpdatedListener); - ipcRenderer.on('shortcuts-updated', this._shortcutListener); + window.api.feature.settings.onUserStateChanged(this._userStateListener); + window.api.feature.settings.onSettingsUpdated(this._settingsUpdatedListener); + window.api.feature.settings.onPresetsUpdated(this._presetsUpdatedListener); + window.api.feature.settings.onShortcutsUpdated(this._shortcutListener); } cleanupIpcListeners() { - if (!window.require) return; - - const { ipcRenderer } = window.require('electron'); - if (this._userStateListener) { - ipcRenderer.removeListener('user-state-changed', this._userStateListener); + window.api.feature.settings.removeOnUserStateChanged(this._userStateListener); } if (this._settingsUpdatedListener) { - ipcRenderer.removeListener('settings-updated', this._settingsUpdatedListener); + window.api.feature.settings.removeOnSettingsUpdated(this._settingsUpdatedListener); } if (this._presetsUpdatedListener) { - ipcRenderer.removeListener('presets-updated', this._presetsUpdatedListener); + window.api.feature.settings.removeOnPresetsUpdated(this._presetsUpdatedListener); } if (this._shortcutListener) { - ipcRenderer.removeListener('shortcuts-updated', this._shortcutListener); + window.api.feature.settings.removeOnShortcutsUpdated(this._shortcutListener); } } @@ -1023,17 +996,11 @@ export class SettingsView extends LitElement { } handleMouseEnter = () => { - if (window.require) { - const { ipcRenderer } = window.require('electron'); - ipcRenderer.send('cancel-hide-settings-window'); - } + window.api.window.cancelHideSettingsWindow(); } handleMouseLeave = () => { - if (window.require) { - const { ipcRenderer } = window.require('electron'); - ipcRenderer.send('hide-settings-window'); - } + window.api.window.hideSettingsWindow(); } // getMainShortcuts() { @@ -1083,39 +1050,27 @@ export class SettingsView extends LitElement { handleMoveLeft() { console.log('Move Left clicked'); - if (window.require) { - const { ipcRenderer } = window.require('electron'); - ipcRenderer.invoke('move-window-step', 'left'); - } + window.api.feature.settings.moveWindowStep('left'); } handleMoveRight() { console.log('Move Right clicked'); - if (window.require) { - const { ipcRenderer } = window.require('electron'); - ipcRenderer.invoke('move-window-step', 'right'); - } + window.api.feature.settings.moveWindowStep('right'); } async handlePersonalize() { console.log('Personalize clicked'); - if (window.require) { - const { ipcRenderer } = window.require('electron'); - try { - await ipcRenderer.invoke('open-login-page'); - } catch (error) { - console.error('Failed to open personalize page:', error); - } + try { + await window.api.window.openLoginPage(); + } catch (error) { + console.error('Failed to open personalize page:', error); } } async handleToggleInvisibility() { console.log('Toggle Invisibility clicked'); - if (window.require) { - const { ipcRenderer } = window.require('electron'); - this.isContentProtectionOn = await ipcRenderer.invoke('toggle-content-protection'); - this.requestUpdate(); - } + this.isContentProtectionOn = await window.api.window.toggleContentProtection(); + this.requestUpdate(); } async handleSaveApiKey() { @@ -1123,62 +1078,46 @@ export class SettingsView extends LitElement { if (!input || !input.value) return; const newApiKey = input.value; - if (window.require) { - const { ipcRenderer } = window.require('electron'); - try { - const result = await ipcRenderer.invoke('settings:saveApiKey', newApiKey); - if (result.success) { - console.log('API Key saved successfully via IPC.'); - this.apiKey = newApiKey; - this.requestUpdate(); - } else { - console.error('Failed to save API Key via IPC:', result.error); - } - } catch(e) { - console.error('Error invoking save-api-key IPC:', e); + try { + const result = await window.api.feature.settings.saveApiKey(newApiKey); + if (result.success) { + console.log('API Key saved successfully via IPC.'); + this.apiKey = newApiKey; + this.requestUpdate(); + } else { + console.error('Failed to save API Key via IPC:', result.error); } + } catch(e) { + console.error('Error invoking save-api-key IPC:', e); } } async handleClearApiKey() { console.log('Clear API Key clicked'); - if (window.require) { - const { ipcRenderer } = window.require('electron'); - await ipcRenderer.invoke('settings:removeApiKey'); - this.apiKey = null; - this.requestUpdate(); - } + await window.api.feature.settings.removeApiKey(); + this.apiKey = null; + this.requestUpdate(); } handleQuit() { console.log('Quit clicked'); - if (window.require) { - const { ipcRenderer } = window.require('electron'); - ipcRenderer.invoke('quit-application'); - } + window.api.window.quitApplication(); } handleFirebaseLogout() { console.log('Firebase Logout clicked'); - if (window.require) { - const { ipcRenderer } = window.require('electron'); - ipcRenderer.invoke('firebase-logout'); - } + window.api.window.firebaseLogout(); } async handleOllamaShutdown() { console.log('[SettingsView] Shutting down Ollama service...'); - if (!window.require) return; - - const { ipcRenderer } = window.require('electron'); - try { // Show loading state this.ollamaStatus = { ...this.ollamaStatus, running: false }; this.requestUpdate(); - const result = await ipcRenderer.invoke('ollama:shutdown', false); // Graceful shutdown + const result = await window.api.feature.settings.shutdownOllama(false); // Graceful shutdown if (result.success) { console.log('[SettingsView] Ollama shut down successfully'); @@ -1330,329 +1269,6 @@ export class SettingsView extends LitElement { //////// before_modelStateService //////// //////// after_modelStateService //////// - render() { - if (this.isLoading) { - return html` -
-
-
- Loading... -
-
- `; - } - - const loggedIn = !!this.firebaseUser; - - const apiKeyManagementHTML = html` -
- ${Object.entries(this.providerConfig) - .filter(([id, config]) => !id.includes('-glass')) - .map(([id, config]) => { - if (id === 'ollama') { - // Special UI for Ollama - return html` -
- - ${this.ollamaStatus.installed && this.ollamaStatus.running ? html` -
- ✓ Ollama is running -
- - ` : this.ollamaStatus.installed ? html` -
- ⚠ Ollama installed but not running -
- - ` : html` -
- ✗ Ollama not installed -
- - `} -
- `; - } - - if (id === 'whisper') { - // Special UI for Whisper with model selection - const whisperModels = config.sttModels || []; - const selectedWhisperModel = this.selectedStt && this.getProviderForModel('stt', this.selectedStt) === 'whisper' - ? this.selectedStt - : null; - - return html` -
- - ${this.apiKeys[id] === 'local' ? html` -
- ✓ Whisper is enabled -
- - - - - - ${Object.entries(this.installingModels).map(([modelId, progress]) => { - if (modelId.startsWith('whisper-') && progress !== undefined) { - return html` -
-
- Downloading ${modelId}... -
-
-
-
-
- `; - } - return null; - })} - - - ` : html` - - `} -
- `; - } - - // Regular providers - return html` -
- - -
- - -
-
- `; - })} -
- `; - - const getModelName = (type, id) => { - const models = type === 'llm' ? this.availableLlmModels : this.availableSttModels; - const model = models.find(m => m.id === id); - return model ? model.name : id; - } - - const modelSelectionHTML = html` -
-
- - - ${this.isLlmListVisible ? html` -
- ${this.availableLlmModels.map(model => { - const isOllama = this.getProviderForModel('llm', model.id) === 'ollama'; - const ollamaModel = isOllama ? this.ollamaModels.find(m => m.name === model.id) : null; - const isInstalling = this.installingModels[model.id] !== undefined; - const installProgress = this.installingModels[model.id] || 0; - - return html` -
this.selectModel('llm', model.id)}> - ${model.name} - ${isOllama ? html` - ${isInstalling ? html` -
-
-
- ` : ollamaModel?.installed ? html` - ✓ Installed - ` : html` - Click to install - `} - ` : ''} -
- `; - })} -
- ` : ''} -
-
- - - ${this.isSttListVisible ? html` -
- ${this.availableSttModels.map(model => { - const isWhisper = this.getProviderForModel('stt', model.id) === 'whisper'; - const isInstalling = this.installingModels[model.id] !== undefined; - const installProgress = this.installingModels[model.id] || 0; - - return html` -
this.selectModel('stt', model.id)}> - ${model.name} - ${isWhisper && isInstalling ? html` -
-
-
- ` : ''} -
- `; - })} -
- ` : ''} -
-
- `; - - return html` -
-
-
-

Pickle Glass

- -
-
- - - -
-
- - ${apiKeyManagementHTML} - ${modelSelectionHTML} - -
- -
- - -
- ${this.getMainShortcuts().map(shortcut => html` -
- ${shortcut.name} -
- ${this.renderShortcutKeys(shortcut.accelerator)} -
-
- `)} -
- -
-
- - My Presets - (${this.presets.filter(p => p.is_default === 0).length}) - - - ${this.showPresets ? '▼' : '▶'} - -
- -
- ${this.presets.filter(p => p.is_default === 0).length === 0 ? html` -
- No custom presets yet.
- - Create your first preset - -
- ` : this.presets.filter(p => p.is_default === 0).map(preset => html` -
this.handlePresetSelect(preset)}> - ${preset.title} - ${this.selectedPreset?.id === preset.id ? html`Selected` : ''} -
- `)} -
-
- -
- - - -
- - -
- - - -
- ${this.firebaseUser - ? html` - - ` - : html` - - ` - } - -
-
-
- `; - } - //////// after_modelStateService //////// } customElements.define('settings-view', SettingsView); \ No newline at end of file diff --git a/src/features/settings/settingsService.js b/src/features/settings/settingsService.js index 069c41d..64357b1 100644 --- a/src/features/settings/settingsService.js +++ b/src/features/settings/settingsService.js @@ -373,56 +373,7 @@ function initialize() { // cleanup windowNotificationManager.cleanup(); - // IPC handlers for settings - ipcMain.handle('settings:getSettings', async () => { - return await getSettings(); - }); - - ipcMain.handle('settings:saveSettings', async (event, settings) => { - return await saveSettings(settings); - }); - - // IPC handlers for presets - ipcMain.handle('settings:getPresets', async () => { - return await getPresets(); - }); - - ipcMain.handle('settings:getPresetTemplates', async () => { - return await getPresetTemplates(); - }); - - ipcMain.handle('settings:createPreset', async (event, title, prompt) => { - return await createPreset(title, prompt); - }); - - ipcMain.handle('settings:updatePreset', async (event, id, title, prompt) => { - return await updatePreset(id, title, prompt); - }); - - ipcMain.handle('settings:deletePreset', async (event, id) => { - return await deletePreset(id); - }); - - ipcMain.handle('settings:saveApiKey', async (event, apiKey, provider) => { - return await saveApiKey(apiKey, provider); - }); - - ipcMain.handle('settings:removeApiKey', async () => { - return await removeApiKey(); - }); - - ipcMain.handle('settings:updateContentProtection', async (event, enabled) => { - return await updateContentProtection(enabled); - }); - - ipcMain.handle('settings:get-auto-update', async () => { - return await getAutoUpdateSetting(); - }); - - ipcMain.handle('settings:set-auto-update', async (event, isEnabled) => { - console.log('[SettingsService] Setting auto update setting:', isEnabled); - return await setAutoUpdateSetting(isEnabled); - }); + // IPC handlers 제거 (featureBridge로 이동) console.log('[SettingsService] Initialized and ready.'); } diff --git a/src/index.js b/src/index.js index c0e1a6c..63ca17d 100644 --- a/src/index.js +++ b/src/index.js @@ -27,6 +27,7 @@ const settingsService = require('./features/settings/settingsService'); const sessionRepository = require('./common/repositories/session'); const ModelStateService = require('./common/services/modelStateService'); const sqliteClient = require('./common/services/sqliteClient'); +const featureBridge = require('./bridge/featureBridge'); // Global variables const eventBridge = new EventEmitter(); @@ -205,6 +206,7 @@ app.whenReady().then(async () => { listenService.setupIpcHandlers(); askService.initialize(); settingsService.initialize(); + featureBridge.initialize(); // 추가: featureBridge 초기화 setupGeneralIpcHandlers(); setupOllamaIpcHandlers(); setupWhisperIpcHandlers(); diff --git a/src/preload.js b/src/preload.js index 5e9d369..80dd79d 100644 --- a/src/preload.js +++ b/src/preload.js @@ -1,2 +1,86 @@ -// See the Electron documentation for details on how to use preload scripts: -// https://www.electronjs.org/docs/latest/tutorial/process-model#preload-scripts +// src/preload.js +const { contextBridge, ipcRenderer } = require('electron'); + +contextBridge.exposeInMainWorld('api', { + feature: { + // 기존 ask 관련 유지 + submitAsk: (query) => ipcRenderer.invoke('feature:ask', query), + onAskProgress: (callback) => ipcRenderer.on('feature:ask:progress', (e, p) => callback(p)), + + settings: { + // invoke methods + getCurrentUser: () => ipcRenderer.invoke('get-current-user'), + getProviderConfig: () => ipcRenderer.invoke('model:get-provider-config'), + getAllKeys: () => ipcRenderer.invoke('model:get-all-keys'), + getAvailableModels: (type) => ipcRenderer.invoke('model:get-available-models', type), + getSelectedModels: () => ipcRenderer.invoke('model:get-selected-models'), + getPresets: () => ipcRenderer.invoke('settings:getPresets'), + getContentProtectionStatus: () => ipcRenderer.invoke('get-content-protection-status'), + getCurrentShortcuts: () => ipcRenderer.invoke('get-current-shortcuts'), + getOllamaStatus: () => ipcRenderer.invoke('ollama:get-status'), + getWhisperInstalledModels: () => ipcRenderer.invoke('whisper:get-installed-models'), + ollamaEnsureReady: () => ipcRenderer.invoke('ollama:ensure-ready'), + validateKey: (data) => ipcRenderer.invoke('model:validate-key', data), + getAutoUpdate: () => ipcRenderer.invoke('settings:get-auto-update'), + setAutoUpdate: (isEnabled) => ipcRenderer.invoke('settings:set-auto-update', isEnabled), + removeApiKey: (provider) => ipcRenderer.invoke('model:remove-api-key', provider), + setSelectedModel: (data) => ipcRenderer.invoke('model:set-selected-model', data), + downloadWhisperModel: (modelId) => ipcRenderer.invoke('whisper:download-model', modelId), + openLoginPage: () => ipcRenderer.invoke('open-login-page'), + toggleContentProtection: () => ipcRenderer.invoke('toggle-content-protection'), + openShortcutEditor: () => ipcRenderer.invoke('open-shortcut-editor'), + quitApplication: () => ipcRenderer.invoke('quit-application'), + firebaseLogout: () => ipcRenderer.invoke('firebase-logout'), + ollamaShutdown: (graceful) => ipcRenderer.invoke('ollama:shutdown', graceful), + startFirebaseAuth: () => ipcRenderer.invoke('start-firebase-auth'), + + // on methods (listeners) + onUserStateChanged: (callback) => ipcRenderer.on('user-state-changed', callback), + removeOnUserStateChanged: (callback) => ipcRenderer.removeListener('user-state-changed', callback), + onSettingsUpdated: (callback) => ipcRenderer.on('settings-updated', callback), + removeOnSettingsUpdated: (callback) => ipcRenderer.removeListener('settings-updated', callback), + onPresetsUpdated: (callback) => ipcRenderer.on('presets-updated', callback), + removeOnPresetsUpdated: (callback) => ipcRenderer.removeListener('presets-updated', callback), + onShortcutsUpdated: (callback) => ipcRenderer.on('shortcuts-updated', callback), + removeOnShortcutsUpdated: (callback) => ipcRenderer.removeListener('shortcuts-updated', callback), + onWhisperDownloadProgress: (callback) => ipcRenderer.on('whisper:download-progress', callback), + removeOnWhisperDownloadProgress: (callback) => ipcRenderer.removeListener('whisper:download-progress', callback), + + // send methods + cancelHideSettingsWindow: () => ipcRenderer.send('cancel-hide-settings-window'), + hideSettingsWindow: () => ipcRenderer.send('hide-settings-window') + } + }, + // 기존 window 유지 + window: { + // 기존 + hide: () => ipcRenderer.send('window:hide'), + onFocusChange: (callback) => ipcRenderer.on('window:focus-change', (e, f) => callback(f)), + + // 추가 + showSettingsWindow: (bounds) => ipcRenderer.send('show-settings-window', bounds), + hideSettingsWindow: () => ipcRenderer.send('hide-settings-window'), + cancelHideSettingsWindow: () => ipcRenderer.send('cancel-hide-settings-window'), + moveWindowStep: (direction) => ipcRenderer.invoke('move-window-step', direction), + openLoginPage: () => ipcRenderer.invoke('open-login-page'), + firebaseLogout: () => ipcRenderer.invoke('firebase-logout'), + ollamaShutdown: (graceful) => ipcRenderer.invoke('ollama:shutdown', graceful), + startFirebaseAuth: () => ipcRenderer.invoke('start-firebase-auth'), + + // on methods (listeners) + onUserStateChanged: (callback) => ipcRenderer.on('user-state-changed', callback), + removeOnUserStateChanged: (callback) => ipcRenderer.removeListener('user-state-changed', callback), + onSettingsUpdated: (callback) => ipcRenderer.on('settings-updated', callback), + removeOnSettingsUpdated: (callback) => ipcRenderer.removeListener('settings-updated', callback), + onPresetsUpdated: (callback) => ipcRenderer.on('presets-updated', callback), + removeOnPresetsUpdated: (callback) => ipcRenderer.removeListener('presets-updated', callback), + onShortcutsUpdated: (callback) => ipcRenderer.on('shortcuts-updated', callback), + removeOnShortcutsUpdated: (callback) => ipcRenderer.removeListener('shortcuts-updated', callback), + onWhisperDownloadProgress: (callback) => ipcRenderer.on('whisper:download-progress', callback), + removeOnWhisperDownloadProgress: (callback) => ipcRenderer.removeListener('whisper:download-progress', callback), + + // send methods + cancelHideSettingsWindow: () => ipcRenderer.send('cancel-hide-settings-window'), + hideSettingsWindow: () => ipcRenderer.send('hide-settings-window') + } +}); \ No newline at end of file