From 73a6e1345ee76acf20601087e6295050606c2d37 Mon Sep 17 00:00:00 2001 From: samtiz Date: Sun, 13 Jul 2025 10:49:19 +0900 Subject: [PATCH] shortcut moved --- aec | 2 +- docs/refactor-plan.md | 19 ++ src/bridge/featureBridge.js | 7 + src/bridge/internalBridge.js | 9 +- src/bridge/windowBridge.js | 164 ++-------------- src/features/shortcuts/shortcutsService.js | 69 +++++-- src/index.js | 2 + src/window/windowManager.js | 213 +++++++++++++++------ 8 files changed, 259 insertions(+), 226 deletions(-) create mode 100644 docs/refactor-plan.md diff --git a/aec b/aec index 9e11f4f..f00bb1f 160000 --- a/aec +++ b/aec @@ -1 +1 @@ -Subproject commit 9e11f4f95707714464194bdfc9db0222ec5c6163 +Subproject commit f00bb1fb948053c752b916adfee19f90644a0b2f diff --git a/docs/refactor-plan.md b/docs/refactor-plan.md new file mode 100644 index 0000000..6fa95a7 --- /dev/null +++ b/docs/refactor-plan.md @@ -0,0 +1,19 @@ +# Refactor Plan: Non-Window Logic Migration from windowManager.js + +## Goal +`windowManager.js`를 순수 창 관리 모듈로 만들기 위해 비즈니스 로직을 해당 서비스와 `featureBridge.js`로 이전. + +## Steps (based on initial plan) +1. **Shortcuts**: Completed. Logic moved to `shortcutsService.js` and IPC to `featureBridge.js`. Used `internalBridge` for coordination. + +2. **Screenshot**: Next. Move `captureScreenshot` function and related IPC handlers from `windowManager.js` to `askService.js` (since it's primarily used there). Update `askService.js` to use its own screenshot method. Add IPC handlers to `featureBridge.js` if needed. + +3. **System Permissions**: Create new `permissionService.js` in `src/features/common/services/`. Move all permission-related logic (check, request, open preferences, mark completed, etc.) and IPC handlers from `windowManager.js` to the new service and `featureBridge.js`. + +4. **API Key / Model State**: Completely remove from `windowManager.js` (e.g., `setupApiKeyIPC` and helpers). Ensure all usages (e.g., in `askService.js`) directly require and use `modelStateService.js` instead. + +## Notes +- Maintain original logic without changes. +- Break circular dependencies if found. +- Use `internalBridge` for inter-module communication where appropriate. +- After each step, verify no errors and test functionality. \ No newline at end of file diff --git a/src/bridge/featureBridge.js b/src/bridge/featureBridge.js index 0ddd514..5f3da4f 100644 --- a/src/bridge/featureBridge.js +++ b/src/bridge/featureBridge.js @@ -5,6 +5,7 @@ const authService = require('../features/common/services/authService'); const whisperService = require('../features/common/services/whisperService'); const ollamaService = require('../features/common/services/ollamaService'); const modelStateService = require('../features/common/services/modelStateService'); +const shortcutsService = require('../features/shortcuts/shortcutsService'); const askService = require('../features/ask/askService'); const listenService = require('../features/listen/listenService'); @@ -26,6 +27,12 @@ module.exports = { ipcMain.handle('settings:ensure-ollama-ready', async () => await settingsService.ensureOllamaReady()); ipcMain.handle('settings:shutdown-ollama', async () => await settingsService.shutdownOllama()); + // Shortcuts + ipcMain.handle('get-current-shortcuts', async () => await shortcutsService.loadKeybinds()); + ipcMain.handle('get-default-shortcuts', async () => await shortcutsService.handleRestoreDefaults()); + ipcMain.handle('save-shortcuts', async (event, newKeybinds) => await shortcutsService.handleSaveShortcuts(newKeybinds)); + + // User/Auth ipcMain.handle('get-current-user', () => authService.getCurrentUser()); ipcMain.handle('start-firebase-auth', async () => await authService.startFirebaseAuthFlow()); diff --git a/src/bridge/internalBridge.js b/src/bridge/internalBridge.js index 28c41cf..3fd8f73 100644 --- a/src/bridge/internalBridge.js +++ b/src/bridge/internalBridge.js @@ -2,9 +2,10 @@ const { EventEmitter } = require('events'); // FeatureCore와 WindowCore를 잇는 내부 이벤트 버스 -module.exports = new EventEmitter(); +const internalBridge = new EventEmitter(); +module.exports = internalBridge; // 예시 이벤트 -internalBridge.on('content-protection-changed', (enabled) => { - // windowManager에서 처리 -}); \ No newline at end of file +// 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 index edcb29a..b6b7479 100644 --- a/src/bridge/windowBridge.js +++ b/src/bridge/windowBridge.js @@ -1,159 +1,23 @@ // src/bridge/windowBridge.js -const { ipcMain, BrowserWindow, globalShortcut } = require('electron'); +const { ipcMain } = require('electron'); +const windowManager = require('../window/windowManager'); module.exports = { - // windowManager에서 필요한 변수들을 매개변수로 받도록 수정 - initialize(windowPool, app, shell, getCurrentDisplay, createFeatureWindows, movementManager, getContentProtectionStatus, setContentProtection, updateLayout) { - let settingsHideTimer = null; + initialize() { + ipcMain.handle('toggle-content-protection', () => windowManager.toggleContentProtection()); + ipcMain.handle('resize-header-window', (event, args) => windowManager.resizeHeaderWindow(args)); + ipcMain.handle('get-content-protection-status', () => windowManager.getContentProtectionStatus()); + ipcMain.handle('open-shortcut-editor', () => windowManager.openShortcutEditor()); + ipcMain.on('show-settings-window', (event, bounds) => windowManager.showSettingsWindow(bounds)); + ipcMain.on('hide-settings-window', () => windowManager.hideSettingsWindow()); + ipcMain.on('cancel-hide-settings-window', () => windowManager.cancelHideSettingsWindow()); + ipcMain.handle('open-login-page', () => windowManager.openLoginPage()); + ipcMain.handle('move-window-step', (event, direction) => windowManager.moveWindowStep(direction)); + ipcMain.on('close-shortcut-editor', () => windowManager.closeWindow('shortcut-settings')); - // 기존 - ipcMain.on('window:hide', (e) => BrowserWindow.fromWebContents(e.sender)?.hide()); - - // windowManager 관련 추가 - ipcMain.handle('toggle-content-protection', () => { - // windowManager의 toggle-content-protection 로직 - const newStatus = !getContentProtectionStatus(); - setContentProtection(newStatus); - return newStatus; - }); - - - ipcMain.handle('resize-header-window', (event, { width, height }) => { - const header = windowPool.get('header'); - if (header) { - console.log(`[WindowBridge] Resize request: ${width}x${height}`); - - // Prevent resizing during animations or if already at target size - if (movementManager && movementManager.isAnimating) { - console.log('[WindowBridge] Skipping resize during animation'); - return { success: false, error: 'Cannot resize during animation' }; - } - - const currentBounds = header.getBounds(); - console.log(`[WindowBridge] Current bounds: ${currentBounds.width}x${currentBounds.height} at (${currentBounds.x}, ${currentBounds.y})`); - - // Skip if already at target size to prevent unnecessary operations - if (currentBounds.width === width && currentBounds.height === height) { - console.log('[WindowBridge] Already at target size, skipping resize'); - return { success: true }; - } - - const wasResizable = header.isResizable(); - if (!wasResizable) { - header.setResizable(true); - } - - // Calculate the center point of the current window - const centerX = currentBounds.x + currentBounds.width / 2; - // Calculate new X position to keep the window centered - const newX = Math.round(centerX - width / 2); - - // Get the current display to ensure we stay within bounds - const display = getCurrentDisplay(header); - const { x: workAreaX, width: workAreaWidth } = display.workArea; - - // Clamp the new position to stay within display bounds - const clampedX = Math.max(workAreaX, Math.min(workAreaX + workAreaWidth - width, newX)); - - header.setBounds({ x: clampedX, y: currentBounds.y, width, height }); - - if (!wasResizable) { - header.setResizable(false); - } - - // Update layout after resize - if (updateLayout) { - updateLayout(); - } - - return { success: true }; - } - return { success: false, error: 'Header window not found' }; - }); - - ipcMain.handle('get-content-protection-status', () => { - return getContentProtectionStatus(); - }); - - ipcMain.handle('open-shortcut-editor', () => { - // open-shortcut-editor 로직 - const header = windowPool.get('header'); - if (!header) return; - globalShortcut.unregisterAll(); - createFeatureWindows(header, 'shortcut-settings'); - }); - - - // 추가: 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); - settingsHideTimer = null; - } - // 위치 조정 로직 - 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); - } - }); - - ipcMain.on('hide-settings-window', (event) => { - const window = windowPool.get("settings"); - if (window && !window.isDestroyed()) { - if (settingsHideTimer) { - clearTimeout(settingsHideTimer); - } - settingsHideTimer = setTimeout(() => { - if (window && !window.isDestroyed()) { - window.setAlwaysOnTop(false); - window.hide(); - } - settingsHideTimer = null; - }, 200); - - window.__lockedByButton = false; - } - }); - - ipcMain.on('cancel-hide-settings-window', (event) => { - if (settingsHideTimer) { - clearTimeout(settingsHideTimer); - settingsHideTimer = null; - } - }); - - // 로그인 페이지 열기 - ipcMain.handle('open-personalize-page', () => { - const webUrl = process.env.pickleglass_WEB_URL || 'http://localhost:3000'; - const personalizeUrl = `${webUrl}/personalize?desktop=true`; - shell.openExternal(personalizeUrl); - console.log('Opening personalization page:', personalizeUrl); - }); - - // 윈도우 이동 - ipcMain.handle('move-window-step', (event, direction) => { - if (movementManager) { - movementManager.moveStep(direction); - } - }); }, - // Renderer로 상태를 전송 notifyFocusChange(win, isFocused) { win.webContents.send('window:focus-change', isFocused); - }, + } }; \ No newline at end of file diff --git a/src/features/shortcuts/shortcutsService.js b/src/features/shortcuts/shortcutsService.js index 83bbcd5..17bff2e 100644 --- a/src/features/shortcuts/shortcutsService.js +++ b/src/features/shortcuts/shortcutsService.js @@ -1,11 +1,24 @@ const { globalShortcut, screen } = require('electron'); const shortcutsRepository = require('./repositories'); +const internalBridge = require('../../bridge/internalBridge'); class ShortcutsService { constructor() { this.lastVisibleWindows = new Set(['header']); this.mouseEventsIgnored = false; + this.movementManager = null; + this.windowPool = null; + } + + initialize(movementManager, windowPool) { + this.movementManager = movementManager; + this.windowPool = windowPool; + internalBridge.on('reregister-shortcuts', () => { + console.log('[ShortcutsService] Reregistering shortcuts due to header state change.'); + this.registerShortcuts(); + }); + console.log('[ShortcutsService] Initialized with dependencies and event listener.'); } getDefaultKeybinds() { @@ -58,6 +71,32 @@ class ShortcutsService { return keybinds; } + async handleSaveShortcuts(newKeybinds) { + try { + await this.saveKeybinds(newKeybinds); + const shortcutEditor = this.windowPool.get('shortcut-settings'); + if (shortcutEditor && !shortcutEditor.isDestroyed()) { + shortcutEditor.close(); // This will trigger re-registration on 'closed' event in windowManager + } else { + // If editor wasn't open, re-register immediately + await this.registerShortcuts(); + } + return { success: true }; + } catch (error) { + console.error("Failed to save shortcuts:", error); + // On failure, re-register old shortcuts to be safe + await this.registerShortcuts(); + return { success: false, error: error.message }; + } + } + + async handleRestoreDefaults() { + const defaults = this.getDefaultKeybinds(); + await this.saveKeybinds(defaults); + await this.registerShortcuts(); + return defaults; + } + async saveKeybinds(newKeybinds) { const keybindsToSave = []; for (const action in newKeybinds) { @@ -103,15 +142,19 @@ class ShortcutsService { }); } - async registerShortcuts(movementManager, windowPool) { + async registerShortcuts() { + if (!this.movementManager || !this.windowPool) { + console.error('[Shortcuts] Service not initialized. Cannot register shortcuts.'); + return; + } const keybinds = await this.loadKeybinds(); globalShortcut.unregisterAll(); - const header = windowPool.get('header'); + const header = this.windowPool.get('header'); const mainWindow = header; const sendToRenderer = (channel, ...args) => { - windowPool.forEach(win => { + this.windowPool.forEach(win => { if (win && !win.isDestroyed()) { try { win.webContents.send(channel, ...args); @@ -133,7 +176,7 @@ class ShortcutsService { if (displays.length > 1) { displays.forEach((display, index) => { const key = `${modifier}+Shift+${index + 1}`; - globalShortcut.register(key, () => movementManager.moveToDisplay(display.id)); + globalShortcut.register(key, () => this.movementManager.moveToDisplay(display.id)); }); } @@ -144,14 +187,14 @@ class ShortcutsService { ]; edgeDirections.forEach(({ key, direction }) => { globalShortcut.register(key, () => { - if (header && header.isVisible()) movementManager.moveToEdge(direction); + if (header && header.isVisible()) this.movementManager.moveToEdge(direction); }); }); // --- User-configurable shortcuts --- if (header?.currentHeaderState === 'apikey') { if (keybinds.toggleVisibility) { - globalShortcut.register(keybinds.toggleVisibility, () => this.toggleAllWindowsVisibility(windowPool)); + globalShortcut.register(keybinds.toggleVisibility, () => this.toggleAllWindowsVisibility(this.windowPool)); } console.log('[Shortcuts] ApiKeyHeader is active, only toggleVisibility shortcut is registered.'); return; @@ -164,7 +207,7 @@ class ShortcutsService { let callback; switch(action) { case 'toggleVisibility': - callback = () => this.toggleAllWindowsVisibility(windowPool); + callback = () => this.toggleAllWindowsVisibility(this.windowPool); break; case 'nextStep': // Late require to prevent circular dependency @@ -172,7 +215,7 @@ class ShortcutsService { break; case 'scrollUp': callback = () => { - const askWindow = windowPool.get('ask'); + const askWindow = this.windowPool.get('ask'); if (askWindow && !askWindow.isDestroyed() && askWindow.isVisible()) { askWindow.webContents.send('scroll-response-up'); } @@ -180,23 +223,23 @@ class ShortcutsService { break; case 'scrollDown': callback = () => { - const askWindow = windowPool.get('ask'); + const askWindow = this.windowPool.get('ask'); if (askWindow && !askWindow.isDestroyed() && askWindow.isVisible()) { askWindow.webContents.send('scroll-response-down'); } }; break; case 'moveUp': - callback = () => { if (header && header.isVisible()) movementManager.moveStep('up'); }; + callback = () => { if (header && header.isVisible()) this.movementManager.moveStep('up'); }; break; case 'moveDown': - callback = () => { if (header && header.isVisible()) movementManager.moveStep('down'); }; + callback = () => { if (header && header.isVisible()) this.movementManager.moveStep('down'); }; break; case 'moveLeft': - callback = () => { if (header && header.isVisible()) movementManager.moveStep('left'); }; + callback = () => { if (header && header.isVisible()) this.movementManager.moveStep('left'); }; break; case 'moveRight': - callback = () => { if (header && header.isVisible()) movementManager.moveStep('right'); }; + callback = () => { if (header && header.isVisible()) this.movementManager.moveStep('right'); }; break; case 'toggleClickThrough': callback = () => { diff --git a/src/index.js b/src/index.js index f160e67..627c560 100644 --- a/src/index.js +++ b/src/index.js @@ -27,6 +27,7 @@ const settingsService = require('./features/settings/settingsService'); const sessionRepository = require('./features/common/repositories/session'); const modelStateService = require('./features/common/services/modelStateService'); const featureBridge = require('./bridge/featureBridge'); +const windowBridge = require('./bridge/windowBridge'); // Global variables const eventBridge = new EventEmitter(); @@ -198,6 +199,7 @@ app.whenReady().then(async () => { //////// after_modelStateService //////// featureBridge.initialize(); // 추가: featureBridge 초기화 + windowBridge.initialize(); setupWebDataHandlers(); // Initialize Ollama models in database diff --git a/src/window/windowManager.js b/src/window/windowManager.js index 7f85be7..21eff86 100644 --- a/src/window/windowManager.js +++ b/src/window/windowManager.js @@ -7,6 +7,7 @@ const os = require('os'); const util = require('util'); const execFile = util.promisify(require('child_process').execFile); const shortcutsService = require('../features/shortcuts/shortcutsService'); +const internalBridge = require('../bridge/internalBridge'); // Try to load sharp, but don't fail if it's not available let sharp; @@ -67,8 +68,140 @@ function updateLayout() { } let movementManager = null; -const windowBridge = require('../bridge/windowBridge'); +const setContentProtection = (status) => { + isContentProtectionOn = status; + console.log(`[Protection] Content protection toggled to: ${isContentProtectionOn}`); + windowPool.forEach(win => { + if (win && !win.isDestroyed()) { + win.setContentProtection(isContentProtectionOn); + } + }); +}; + +const getContentProtectionStatus = () => isContentProtectionOn; + +const toggleContentProtection = () => { + const newStatus = !getContentProtectionStatus(); + setContentProtection(newStatus); + return newStatus; +}; + +const resizeHeaderWindow = ({ width, height }) => { + const header = windowPool.get('header'); + if (header) { + console.log(`[WindowManager] Resize request: ${width}x${height}`); + + if (movementManager && movementManager.isAnimating) { + console.log('[WindowManager] Skipping resize during animation'); + return { success: false, error: 'Cannot resize during animation' }; + } + + const currentBounds = header.getBounds(); + console.log(`[WindowManager] Current bounds: ${currentBounds.width}x${currentBounds.height} at (${currentBounds.x}, ${currentBounds.y})`); + + if (currentBounds.width === width && currentBounds.height === height) { + console.log('[WindowManager] Already at target size, skipping resize'); + return { success: true }; + } + + const wasResizable = header.isResizable(); + if (!wasResizable) { + header.setResizable(true); + } + + const centerX = currentBounds.x + currentBounds.width / 2; + const newX = Math.round(centerX - width / 2); + + const display = getCurrentDisplay(header); + const { x: workAreaX, width: workAreaWidth } = display.workArea; + + const clampedX = Math.max(workAreaX, Math.min(workAreaX + workAreaWidth - width, newX)); + + header.setBounds({ x: clampedX, y: currentBounds.y, width, height }); + + if (!wasResizable) { + header.setResizable(false); + } + + if (updateLayout) { + updateLayout(); + } + + return { success: true }; + } + return { success: false, error: 'Header window not found' }; +}; + +const openShortcutEditor = () => { + const header = windowPool.get('header'); + if (!header) return; + globalShortcut.unregisterAll(); + createFeatureWindows(header, 'shortcut-settings'); +}; + +const showSettingsWindow = (bounds) => { + if (!bounds) return; + const win = windowPool.get('settings'); + if (win && !win.isDestroyed()) { + if (settingsHideTimer) { + clearTimeout(settingsHideTimer); + settingsHideTimer = null; + } + 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); + } +}; + +const hideSettingsWindow = () => { + const window = windowPool.get("settings"); + if (window && !window.isDestroyed()) { + if (settingsHideTimer) { + clearTimeout(settingsHideTimer); + } + settingsHideTimer = setTimeout(() => { + if (window && !window.isDestroyed()) { + window.setAlwaysOnTop(false); + window.hide(); + } + settingsHideTimer = null; + }, 200); + + window.__lockedByButton = false; + } +}; + +const cancelHideSettingsWindow = () => { + if (settingsHideTimer) { + clearTimeout(settingsHideTimer); + settingsHideTimer = null; + } +}; + +const openLoginPage = () => { + const webUrl = process.env.pickleglass_WEB_URL || 'http://localhost:3000'; + const personalizeUrl = `${webUrl}/personalize?desktop=true`; + shell.openExternal(personalizeUrl); + console.log('Opening personalization page:', personalizeUrl); +}; + +const moveWindowStep = (direction) => { + if (movementManager) { + movementManager.moveStep(direction); + } +}; function createFeatureWindows(header, namesToCreate) { @@ -255,7 +388,7 @@ function createFeatureWindows(header, namesToCreate) { restoreClicks(); windowPool.delete('shortcut-settings'); console.log('[Shortcuts] Re-enabled after editing.'); - shortcutsService.registerShortcuts(movementManager, windowPool); + shortcutsService.registerShortcuts(); }); shortcutEditor.webContents.once('dom-ready', async () => { @@ -408,25 +541,11 @@ function createWindows() { layoutManager = new WindowLayoutManager(windowPool); header.webContents.once('dom-ready', () => { - shortcutsService.registerShortcuts(movementManager, windowPool); + shortcutsService.initialize(movementManager, windowPool); + shortcutsService.registerShortcuts(); }); setupIpcHandlers(movementManager); - - // Content protection helper functions - const getContentProtectionStatus = () => isContentProtectionOn; - const setContentProtection = (status) => { - isContentProtectionOn = status; - console.log(`[Protection] Content protection toggled to: ${isContentProtectionOn}`); - windowPool.forEach(win => { - if (win && !win.isDestroyed()) { - win.setContentProtection(isContentProtectionOn); - } - }); - }; - - // Initialize windowBridge with required dependencies - windowBridge.initialize(windowPool, require('electron').app, require('electron').shell, getCurrentDisplay, createFeatureWindows, movementManager, getContentProtectionStatus, setContentProtection, updateLayout); if (currentHeaderState === 'main') { createFeatureWindows(header, ['listen', 'ask', 'settings', 'shortcut-settings']); @@ -499,46 +618,7 @@ function setupIpcHandlers(movementManager) { } else { // 'apikey' | 'permission' destroyFeatureWindows(); } - shortcutsService.registerShortcuts(movementManager, windowPool); - }); - - ipcMain.handle('get-current-shortcuts', async () => { - return await shortcutsService.loadKeybinds(); - }); - - ipcMain.handle('get-default-shortcuts', async () => { - const defaults = shortcutsService.getDefaultKeybinds(); - await shortcutsService.saveKeybinds(defaults); - // Reregister shortcuts with new defaults - await shortcutsService.registerShortcuts(movementManager, windowPool); - return defaults; - }); - - ipcMain.handle('save-shortcuts', async (event, newKeybinds) => { - try { - await shortcutsService.saveKeybinds(newKeybinds); - - const editor = windowPool.get('shortcut-settings'); - if (editor && !editor.isDestroyed()) { - editor.close(); // This will trigger re-registration on 'closed' event - } else { - // If editor wasn't open, re-register immediately - await shortcutsService.registerShortcuts(movementManager, windowPool); - } - return { success: true }; - } catch (error) { - console.error("Failed to save shortcuts:", error); - // On failure, re-register old shortcuts to be safe - await shortcutsService.registerShortcuts(movementManager, windowPool); - return { success: false, error: error.message }; - } - }); - - ipcMain.on('close-shortcut-editor', () => { - const editor = windowPool.get('shortcut-settings'); - if (editor && !editor.isDestroyed()) { - editor.close(); - } + internalBridge.emit('reregister-shortcuts'); }); // resize-header-window handler moved to windowBridge.js to avoid duplication @@ -1063,6 +1143,13 @@ async function captureScreenshot(options = {}) { } } +const closeWindow = (windowName) => { + const win = windowPool.get(windowName); + if (win && !win.isDestroyed()) { + win.close(); + } +}; + module.exports = { updateLayout, createWindows, @@ -1073,4 +1160,14 @@ module.exports = { getCurrentModelInfo, captureScreenshot, toggleFeature, // Export toggleFeature so shortcutsService can use it + toggleContentProtection, + resizeHeaderWindow, + getContentProtectionStatus, + openShortcutEditor, + showSettingsWindow, + hideSettingsWindow, + cancelHideSettingsWindow, + openLoginPage, + moveWindowStep, + closeWindow, }; \ No newline at end of file