shortcut moved

This commit is contained in:
samtiz 2025-07-13 10:49:19 +09:00
parent 6d708d6dcd
commit 73a6e1345e
8 changed files with 259 additions and 226 deletions

2
aec

@ -1 +1 @@
Subproject commit 9e11f4f95707714464194bdfc9db0222ec5c6163 Subproject commit f00bb1fb948053c752b916adfee19f90644a0b2f

19
docs/refactor-plan.md Normal file
View File

@ -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.

View File

@ -5,6 +5,7 @@ const authService = require('../features/common/services/authService');
const whisperService = require('../features/common/services/whisperService'); const whisperService = require('../features/common/services/whisperService');
const ollamaService = require('../features/common/services/ollamaService'); const ollamaService = require('../features/common/services/ollamaService');
const modelStateService = require('../features/common/services/modelStateService'); const modelStateService = require('../features/common/services/modelStateService');
const shortcutsService = require('../features/shortcuts/shortcutsService');
const askService = require('../features/ask/askService'); const askService = require('../features/ask/askService');
const listenService = require('../features/listen/listenService'); 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:ensure-ollama-ready', async () => await settingsService.ensureOllamaReady());
ipcMain.handle('settings:shutdown-ollama', async () => await settingsService.shutdownOllama()); 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 // User/Auth
ipcMain.handle('get-current-user', () => authService.getCurrentUser()); ipcMain.handle('get-current-user', () => authService.getCurrentUser());
ipcMain.handle('start-firebase-auth', async () => await authService.startFirebaseAuthFlow()); ipcMain.handle('start-firebase-auth', async () => await authService.startFirebaseAuthFlow());

View File

@ -2,9 +2,10 @@
const { EventEmitter } = require('events'); const { EventEmitter } = require('events');
// FeatureCore와 WindowCore를 잇는 내부 이벤트 버스 // FeatureCore와 WindowCore를 잇는 내부 이벤트 버스
module.exports = new EventEmitter(); const internalBridge = new EventEmitter();
module.exports = internalBridge;
// 예시 이벤트 // 예시 이벤트
internalBridge.on('content-protection-changed', (enabled) => { // internalBridge.on('content-protection-changed', (enabled) => {
// windowManager에서 처리 // // windowManager에서 처리
}); // });

View File

@ -1,159 +1,23 @@
// src/bridge/windowBridge.js // src/bridge/windowBridge.js
const { ipcMain, BrowserWindow, globalShortcut } = require('electron'); const { ipcMain } = require('electron');
const windowManager = require('../window/windowManager');
module.exports = { module.exports = {
// windowManager에서 필요한 변수들을 매개변수로 받도록 수정 initialize() {
initialize(windowPool, app, shell, getCurrentDisplay, createFeatureWindows, movementManager, getContentProtectionStatus, setContentProtection, updateLayout) { ipcMain.handle('toggle-content-protection', () => windowManager.toggleContentProtection());
let settingsHideTimer = null; 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) { notifyFocusChange(win, isFocused) {
win.webContents.send('window:focus-change', isFocused); win.webContents.send('window:focus-change', isFocused);
}, }
}; };

View File

@ -1,11 +1,24 @@
const { globalShortcut, screen } = require('electron'); const { globalShortcut, screen } = require('electron');
const shortcutsRepository = require('./repositories'); const shortcutsRepository = require('./repositories');
const internalBridge = require('../../bridge/internalBridge');
class ShortcutsService { class ShortcutsService {
constructor() { constructor() {
this.lastVisibleWindows = new Set(['header']); this.lastVisibleWindows = new Set(['header']);
this.mouseEventsIgnored = false; 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() { getDefaultKeybinds() {
@ -58,6 +71,32 @@ class ShortcutsService {
return keybinds; 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) { async saveKeybinds(newKeybinds) {
const keybindsToSave = []; const keybindsToSave = [];
for (const action in newKeybinds) { 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(); const keybinds = await this.loadKeybinds();
globalShortcut.unregisterAll(); globalShortcut.unregisterAll();
const header = windowPool.get('header'); const header = this.windowPool.get('header');
const mainWindow = header; const mainWindow = header;
const sendToRenderer = (channel, ...args) => { const sendToRenderer = (channel, ...args) => {
windowPool.forEach(win => { this.windowPool.forEach(win => {
if (win && !win.isDestroyed()) { if (win && !win.isDestroyed()) {
try { try {
win.webContents.send(channel, ...args); win.webContents.send(channel, ...args);
@ -133,7 +176,7 @@ class ShortcutsService {
if (displays.length > 1) { if (displays.length > 1) {
displays.forEach((display, index) => { displays.forEach((display, index) => {
const key = `${modifier}+Shift+${index + 1}`; 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 }) => { edgeDirections.forEach(({ key, direction }) => {
globalShortcut.register(key, () => { globalShortcut.register(key, () => {
if (header && header.isVisible()) movementManager.moveToEdge(direction); if (header && header.isVisible()) this.movementManager.moveToEdge(direction);
}); });
}); });
// --- User-configurable shortcuts --- // --- User-configurable shortcuts ---
if (header?.currentHeaderState === 'apikey') { if (header?.currentHeaderState === 'apikey') {
if (keybinds.toggleVisibility) { 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.'); console.log('[Shortcuts] ApiKeyHeader is active, only toggleVisibility shortcut is registered.');
return; return;
@ -164,7 +207,7 @@ class ShortcutsService {
let callback; let callback;
switch(action) { switch(action) {
case 'toggleVisibility': case 'toggleVisibility':
callback = () => this.toggleAllWindowsVisibility(windowPool); callback = () => this.toggleAllWindowsVisibility(this.windowPool);
break; break;
case 'nextStep': case 'nextStep':
// Late require to prevent circular dependency // Late require to prevent circular dependency
@ -172,7 +215,7 @@ class ShortcutsService {
break; break;
case 'scrollUp': case 'scrollUp':
callback = () => { callback = () => {
const askWindow = windowPool.get('ask'); const askWindow = this.windowPool.get('ask');
if (askWindow && !askWindow.isDestroyed() && askWindow.isVisible()) { if (askWindow && !askWindow.isDestroyed() && askWindow.isVisible()) {
askWindow.webContents.send('scroll-response-up'); askWindow.webContents.send('scroll-response-up');
} }
@ -180,23 +223,23 @@ class ShortcutsService {
break; break;
case 'scrollDown': case 'scrollDown':
callback = () => { callback = () => {
const askWindow = windowPool.get('ask'); const askWindow = this.windowPool.get('ask');
if (askWindow && !askWindow.isDestroyed() && askWindow.isVisible()) { if (askWindow && !askWindow.isDestroyed() && askWindow.isVisible()) {
askWindow.webContents.send('scroll-response-down'); askWindow.webContents.send('scroll-response-down');
} }
}; };
break; break;
case 'moveUp': case 'moveUp':
callback = () => { if (header && header.isVisible()) movementManager.moveStep('up'); }; callback = () => { if (header && header.isVisible()) this.movementManager.moveStep('up'); };
break; break;
case 'moveDown': case 'moveDown':
callback = () => { if (header && header.isVisible()) movementManager.moveStep('down'); }; callback = () => { if (header && header.isVisible()) this.movementManager.moveStep('down'); };
break; break;
case 'moveLeft': case 'moveLeft':
callback = () => { if (header && header.isVisible()) movementManager.moveStep('left'); }; callback = () => { if (header && header.isVisible()) this.movementManager.moveStep('left'); };
break; break;
case 'moveRight': case 'moveRight':
callback = () => { if (header && header.isVisible()) movementManager.moveStep('right'); }; callback = () => { if (header && header.isVisible()) this.movementManager.moveStep('right'); };
break; break;
case 'toggleClickThrough': case 'toggleClickThrough':
callback = () => { callback = () => {

View File

@ -27,6 +27,7 @@ const settingsService = require('./features/settings/settingsService');
const sessionRepository = require('./features/common/repositories/session'); const sessionRepository = require('./features/common/repositories/session');
const modelStateService = require('./features/common/services/modelStateService'); const modelStateService = require('./features/common/services/modelStateService');
const featureBridge = require('./bridge/featureBridge'); const featureBridge = require('./bridge/featureBridge');
const windowBridge = require('./bridge/windowBridge');
// Global variables // Global variables
const eventBridge = new EventEmitter(); const eventBridge = new EventEmitter();
@ -198,6 +199,7 @@ app.whenReady().then(async () => {
//////// after_modelStateService //////// //////// after_modelStateService ////////
featureBridge.initialize(); // 추가: featureBridge 초기화 featureBridge.initialize(); // 추가: featureBridge 초기화
windowBridge.initialize();
setupWebDataHandlers(); setupWebDataHandlers();
// Initialize Ollama models in database // Initialize Ollama models in database

View File

@ -7,6 +7,7 @@ const os = require('os');
const util = require('util'); const util = require('util');
const execFile = util.promisify(require('child_process').execFile); const execFile = util.promisify(require('child_process').execFile);
const shortcutsService = require('../features/shortcuts/shortcutsService'); const shortcutsService = require('../features/shortcuts/shortcutsService');
const internalBridge = require('../bridge/internalBridge');
// Try to load sharp, but don't fail if it's not available // Try to load sharp, but don't fail if it's not available
let sharp; let sharp;
@ -67,8 +68,140 @@ function updateLayout() {
} }
let movementManager = null; 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) { function createFeatureWindows(header, namesToCreate) {
@ -255,7 +388,7 @@ function createFeatureWindows(header, namesToCreate) {
restoreClicks(); restoreClicks();
windowPool.delete('shortcut-settings'); windowPool.delete('shortcut-settings');
console.log('[Shortcuts] Re-enabled after editing.'); console.log('[Shortcuts] Re-enabled after editing.');
shortcutsService.registerShortcuts(movementManager, windowPool); shortcutsService.registerShortcuts();
}); });
shortcutEditor.webContents.once('dom-ready', async () => { shortcutEditor.webContents.once('dom-ready', async () => {
@ -408,25 +541,11 @@ function createWindows() {
layoutManager = new WindowLayoutManager(windowPool); layoutManager = new WindowLayoutManager(windowPool);
header.webContents.once('dom-ready', () => { header.webContents.once('dom-ready', () => {
shortcutsService.registerShortcuts(movementManager, windowPool); shortcutsService.initialize(movementManager, windowPool);
shortcutsService.registerShortcuts();
}); });
setupIpcHandlers(movementManager); 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') { if (currentHeaderState === 'main') {
createFeatureWindows(header, ['listen', 'ask', 'settings', 'shortcut-settings']); createFeatureWindows(header, ['listen', 'ask', 'settings', 'shortcut-settings']);
@ -499,46 +618,7 @@ function setupIpcHandlers(movementManager) {
} else { // 'apikey' | 'permission' } else { // 'apikey' | 'permission'
destroyFeatureWindows(); destroyFeatureWindows();
} }
shortcutsService.registerShortcuts(movementManager, windowPool); internalBridge.emit('reregister-shortcuts');
});
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();
}
}); });
// resize-header-window handler moved to windowBridge.js to avoid duplication // 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 = { module.exports = {
updateLayout, updateLayout,
createWindows, createWindows,
@ -1073,4 +1160,14 @@ module.exports = {
getCurrentModelInfo, getCurrentModelInfo,
captureScreenshot, captureScreenshot,
toggleFeature, // Export toggleFeature so shortcutsService can use it toggleFeature, // Export toggleFeature so shortcutsService can use it
toggleContentProtection,
resizeHeaderWindow,
getContentProtectionStatus,
openShortcutEditor,
showSettingsWindow,
hideSettingsWindow,
cancelHideSettingsWindow,
openLoginPage,
moveWindowStep,
closeWindow,
}; };