centralized window layout/movement feature to windowmanager
This commit is contained in:
parent
94ae002d83
commit
4d93df09e2
@ -29,9 +29,12 @@ module.exports = {
|
|||||||
ipcMain.handle('settings:shutdown-ollama', async () => await settingsService.shutdownOllama());
|
ipcMain.handle('settings:shutdown-ollama', async () => await settingsService.shutdownOllama());
|
||||||
|
|
||||||
// Shortcuts
|
// Shortcuts
|
||||||
ipcMain.handle('get-current-shortcuts', async () => await shortcutsService.loadKeybinds());
|
ipcMain.handle('settings:getCurrentShortcuts', async () => await shortcutsService.loadKeybinds());
|
||||||
ipcMain.handle('get-default-shortcuts', async () => await shortcutsService.handleRestoreDefaults());
|
ipcMain.handle('shortcut:getDefaultShortcuts', async () => await shortcutsService.handleRestoreDefaults());
|
||||||
ipcMain.handle('save-shortcuts', async (event, newKeybinds) => await shortcutsService.handleSaveShortcuts(newKeybinds));
|
ipcMain.handle('shortcut:closeShortcutSettingsWindow', async () => await shortcutsService.closeShortcutSettingsWindow());
|
||||||
|
ipcMain.handle('shortcut:openShortcutSettingsWindow', async () => await shortcutsService.openShortcutSettingsWindow());
|
||||||
|
ipcMain.handle('shortcut:saveShortcuts', async (event, newKeybinds) => await shortcutsService.handleSaveShortcuts(newKeybinds));
|
||||||
|
ipcMain.handle('shortcut:toggleAllWindowsVisibility', async () => await shortcutsService.toggleAllWindowsVisibility());
|
||||||
|
|
||||||
// Permissions
|
// Permissions
|
||||||
ipcMain.handle('check-system-permissions', async () => await permissionService.checkSystemPermissions());
|
ipcMain.handle('check-system-permissions', async () => await permissionService.checkSystemPermissions());
|
||||||
|
@ -7,14 +7,13 @@ module.exports = {
|
|||||||
ipcMain.handle('toggle-content-protection', () => windowManager.toggleContentProtection());
|
ipcMain.handle('toggle-content-protection', () => windowManager.toggleContentProtection());
|
||||||
ipcMain.handle('resize-header-window', (event, args) => windowManager.resizeHeaderWindow(args));
|
ipcMain.handle('resize-header-window', (event, args) => windowManager.resizeHeaderWindow(args));
|
||||||
ipcMain.handle('get-content-protection-status', () => windowManager.getContentProtectionStatus());
|
ipcMain.handle('get-content-protection-status', () => windowManager.getContentProtectionStatus());
|
||||||
ipcMain.handle('open-shortcut-editor', () => windowManager.openShortcutEditor());
|
ipcMain.on('show-settings-window', () => windowManager.showSettingsWindow());
|
||||||
ipcMain.on('show-settings-window', (event, bounds) => windowManager.showSettingsWindow(bounds));
|
|
||||||
ipcMain.on('hide-settings-window', () => windowManager.hideSettingsWindow());
|
ipcMain.on('hide-settings-window', () => windowManager.hideSettingsWindow());
|
||||||
ipcMain.on('cancel-hide-settings-window', () => windowManager.cancelHideSettingsWindow());
|
ipcMain.on('cancel-hide-settings-window', () => windowManager.cancelHideSettingsWindow());
|
||||||
|
|
||||||
ipcMain.handle('open-login-page', () => windowManager.openLoginPage());
|
ipcMain.handle('open-login-page', () => windowManager.openLoginPage());
|
||||||
ipcMain.handle('open-personalize-page', () => windowManager.openLoginPage());
|
ipcMain.handle('open-personalize-page', () => windowManager.openLoginPage());
|
||||||
ipcMain.handle('move-window-step', (event, direction) => windowManager.moveWindowStep(direction));
|
ipcMain.handle('move-window-step', (event, direction) => windowManager.moveWindowStep(direction));
|
||||||
ipcMain.on('close-shortcut-editor', () => windowManager.closeWindow('shortcut-settings'));
|
|
||||||
ipcMain.handle('open-external', (event, url) => shell.openExternal(url));
|
ipcMain.handle('open-external', (event, url) => shell.openExternal(url));
|
||||||
|
|
||||||
// Newly moved handlers from windowManager
|
// Newly moved handlers from windowManager
|
||||||
@ -24,9 +23,6 @@ module.exports = {
|
|||||||
ipcMain.handle('move-header', (event, newX, newY) => windowManager.moveHeader(newX, newY));
|
ipcMain.handle('move-header', (event, newX, newY) => windowManager.moveHeader(newX, newY));
|
||||||
ipcMain.handle('move-header-to', (event, newX, newY) => windowManager.moveHeaderTo(newX, newY));
|
ipcMain.handle('move-header-to', (event, newX, newY) => windowManager.moveHeaderTo(newX, newY));
|
||||||
ipcMain.handle('adjust-window-height', (event, targetHeight) => windowManager.adjustWindowHeight(event.sender, targetHeight));
|
ipcMain.handle('adjust-window-height', (event, targetHeight) => windowManager.adjustWindowHeight(event.sender, targetHeight));
|
||||||
ipcMain.handle('toggle-all-windows-visibility', () => windowManager.toggleAllWindowsVisibility());
|
|
||||||
// ipcMain.on('animation-finished', (event) => windowManager.handleAnimationFinished(event.sender));
|
|
||||||
// ipcMain.handle('ask:closeAskWindow', () => windowManager.closeAskWindow());
|
|
||||||
},
|
},
|
||||||
|
|
||||||
notifyFocusChange(win, isFocused) {
|
notifyFocusChange(win, isFocused) {
|
||||||
|
@ -3,7 +3,6 @@ const { createStreamingLLM } = require('../common/ai/factory');
|
|||||||
// Lazy require helper to avoid circular dependency issues
|
// Lazy require helper to avoid circular dependency issues
|
||||||
const getWindowManager = () => require('../../window/windowManager');
|
const getWindowManager = () => require('../../window/windowManager');
|
||||||
const internalBridge = require('../../bridge/internalBridge');
|
const internalBridge = require('../../bridge/internalBridge');
|
||||||
const { EVENTS } = internalBridge;
|
|
||||||
|
|
||||||
const getWindowPool = () => {
|
const getWindowPool = () => {
|
||||||
try {
|
try {
|
||||||
@ -162,11 +161,11 @@ class AskService {
|
|||||||
this._broadcastState();
|
this._broadcastState();
|
||||||
} else {
|
} else {
|
||||||
if (askWindow && askWindow.isVisible()) {
|
if (askWindow && askWindow.isVisible()) {
|
||||||
internalBridge.emit('request-window-visibility', { name: 'ask', visible: false });
|
internalBridge.emit('window:requestVisibility', { name: 'ask', visible: false });
|
||||||
this.state.isVisible = false;
|
this.state.isVisible = false;
|
||||||
} else {
|
} else {
|
||||||
console.log('[AskService] Showing hidden Ask window');
|
console.log('[AskService] Showing hidden Ask window');
|
||||||
internalBridge.emit('request-window-visibility', { name: 'ask', visible: true });
|
internalBridge.emit('window:requestVisibility', { name: 'ask', visible: true });
|
||||||
this.state.isVisible = true;
|
this.state.isVisible = true;
|
||||||
}
|
}
|
||||||
if (this.state.isVisible) {
|
if (this.state.isVisible) {
|
||||||
@ -192,7 +191,7 @@ class AskService {
|
|||||||
};
|
};
|
||||||
this._broadcastState();
|
this._broadcastState();
|
||||||
|
|
||||||
internalBridge.emit('request-window-visibility', { name: 'ask', visible: false });
|
internalBridge.emit('window:requestVisibility', { name: 'ask', visible: false });
|
||||||
|
|
||||||
return { success: true };
|
return { success: true };
|
||||||
}
|
}
|
||||||
@ -217,7 +216,16 @@ class AskService {
|
|||||||
* @returns {Promise<{success: boolean, response?: string, error?: string}>}
|
* @returns {Promise<{success: boolean, response?: string, error?: string}>}
|
||||||
*/
|
*/
|
||||||
async sendMessage(userPrompt, conversationHistoryRaw=[]) {
|
async sendMessage(userPrompt, conversationHistoryRaw=[]) {
|
||||||
// ensureAskWindowVisible();
|
internalBridge.emit('window:requestVisibility', { name: 'ask', visible: true });
|
||||||
|
this.state = {
|
||||||
|
...this.state,
|
||||||
|
isLoading: true,
|
||||||
|
isStreaming: false,
|
||||||
|
currentQuestion: userPrompt,
|
||||||
|
currentResponse: '',
|
||||||
|
showTextInput: false,
|
||||||
|
};
|
||||||
|
this._broadcastState();
|
||||||
|
|
||||||
if (this.abortController) {
|
if (this.abortController) {
|
||||||
this.abortController.abort('New request received.');
|
this.abortController.abort('New request received.');
|
||||||
@ -226,26 +234,10 @@ class AskService {
|
|||||||
const { signal } = this.abortController;
|
const { signal } = this.abortController;
|
||||||
|
|
||||||
|
|
||||||
// if (!userPrompt || userPrompt.trim().length === 0) {
|
|
||||||
// console.warn('[AskService] Cannot process empty message');
|
|
||||||
// return { success: false, error: 'Empty message' };
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
let sessionId;
|
let sessionId;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
console.log(`[AskService] 🤖 Processing message: ${userPrompt.substring(0, 50)}...`);
|
console.log(`[AskService] 🤖 Processing message: ${userPrompt.substring(0, 50)}...`);
|
||||||
|
|
||||||
this.state = {
|
|
||||||
...this.state,
|
|
||||||
isLoading: true,
|
|
||||||
isStreaming: false,
|
|
||||||
currentQuestion: userPrompt,
|
|
||||||
currentResponse: '',
|
|
||||||
showTextInput: false,
|
|
||||||
};
|
|
||||||
this._broadcastState();
|
|
||||||
|
|
||||||
sessionId = await sessionRepository.getOrCreateActive('ask');
|
sessionId = await sessionRepository.getOrCreateActive('ask');
|
||||||
await askRepository.addAiMessage({ sessionId, role: 'user', content: userPrompt.trim() });
|
await askRepository.addAiMessage({ sessionId, role: 'user', content: userPrompt.trim() });
|
||||||
|
@ -5,7 +5,6 @@ const authService = require('../common/services/authService');
|
|||||||
const sessionRepository = require('../common/repositories/session');
|
const sessionRepository = require('../common/repositories/session');
|
||||||
const sttRepository = require('./stt/repositories');
|
const sttRepository = require('./stt/repositories');
|
||||||
const internalBridge = require('../../bridge/internalBridge');
|
const internalBridge = require('../../bridge/internalBridge');
|
||||||
const { EVENTS } = internalBridge;
|
|
||||||
|
|
||||||
class ListenService {
|
class ListenService {
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -109,7 +108,7 @@ class ListenService {
|
|||||||
switch (listenButtonText) {
|
switch (listenButtonText) {
|
||||||
case 'Listen':
|
case 'Listen':
|
||||||
console.log('[ListenService] changeSession to "Listen"');
|
console.log('[ListenService] changeSession to "Listen"');
|
||||||
internalBridge.emit('request-window-visibility', { name: 'listen', visible: true });
|
internalBridge.emit('window:requestVisibility', { name: 'listen', visible: true });
|
||||||
await this.initializeSession();
|
await this.initializeSession();
|
||||||
listenWindow.webContents.send('session-state-changed', { isActive: true });
|
listenWindow.webContents.send('session-state-changed', { isActive: true });
|
||||||
break;
|
break;
|
||||||
@ -122,7 +121,7 @@ class ListenService {
|
|||||||
|
|
||||||
case 'Done':
|
case 'Done':
|
||||||
console.log('[ListenService] changeSession to "Done"');
|
console.log('[ListenService] changeSession to "Done"');
|
||||||
internalBridge.emit('request-window-visibility', { name: 'listen', visible: false });
|
internalBridge.emit('window:requestVisibility', { name: 'listen', visible: false });
|
||||||
listenWindow.webContents.send('session-state-changed', { isActive: false });
|
listenWindow.webContents.send('session-state-changed', { isActive: false });
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ class ShortcutsService {
|
|||||||
this.mouseEventsIgnored = false;
|
this.mouseEventsIgnored = false;
|
||||||
this.movementManager = null;
|
this.movementManager = null;
|
||||||
this.windowPool = null;
|
this.windowPool = null;
|
||||||
|
this.allWindowVisibility = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
initialize(movementManager, windowPool) {
|
initialize(movementManager, windowPool) {
|
||||||
@ -22,6 +23,41 @@ class ShortcutsService {
|
|||||||
console.log('[ShortcutsService] Initialized with dependencies and event listener.');
|
console.log('[ShortcutsService] Initialized with dependencies and event listener.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async openShortcutSettingsWindow () {
|
||||||
|
const keybinds = await this.loadKeybinds();
|
||||||
|
const shortcutWin = this.windowPool.get('shortcut-settings');
|
||||||
|
shortcutWin.webContents.send('shortcut:loadShortcuts', keybinds);
|
||||||
|
|
||||||
|
globalShortcut.unregisterAll();
|
||||||
|
internalBridge.emit('window:requestVisibility', { name: 'shortcut-settings', visible: true });
|
||||||
|
console.log('[ShortcutsService] Shortcut settings window opened.');
|
||||||
|
return { success: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
async closeShortcutSettingsWindow () {
|
||||||
|
await this.registerShortcuts();
|
||||||
|
internalBridge.emit('window:requestVisibility', { name: 'shortcut-settings', visible: false });
|
||||||
|
console.log('[ShortcutsService] Shortcut settings window closed.');
|
||||||
|
return { success: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
async handleSaveShortcuts(newKeybinds) {
|
||||||
|
try {
|
||||||
|
await this.saveKeybinds(newKeybinds);
|
||||||
|
await this.closeShortcutSettingsWindow();
|
||||||
|
return { success: true };
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to save shortcuts:", error);
|
||||||
|
await this.closeShortcutSettingsWindow();
|
||||||
|
return { success: false, error: error.message };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async handleRestoreDefaults() {
|
||||||
|
const defaults = this.getDefaultKeybinds();
|
||||||
|
return defaults;
|
||||||
|
}
|
||||||
|
|
||||||
getDefaultKeybinds() {
|
getDefaultKeybinds() {
|
||||||
const isMac = process.platform === 'darwin';
|
const isMac = process.platform === 'darwin';
|
||||||
return {
|
return {
|
||||||
@ -72,32 +108,6 @@ 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) {
|
||||||
@ -112,38 +122,22 @@ class ShortcutsService {
|
|||||||
console.log(`[Shortcuts] Saved keybinds.`);
|
console.log(`[Shortcuts] Saved keybinds.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleAllWindowsVisibility(windowPool) {
|
async toggleAllWindowsVisibility() {
|
||||||
const header = windowPool.get('header');
|
const targetVisibility = !this.allWindowVisibility;
|
||||||
if (!header) return;
|
internalBridge.emit('window:requestToggleAllWindowsVisibility', {
|
||||||
|
targetVisibility: targetVisibility
|
||||||
if (header.isVisible()) {
|
|
||||||
this.lastVisibleWindows.clear();
|
|
||||||
|
|
||||||
windowPool.forEach((win, name) => {
|
|
||||||
if (win && !win.isDestroyed() && win.isVisible()) {
|
|
||||||
this.lastVisibleWindows.add(name);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.lastVisibleWindows.forEach(name => {
|
|
||||||
if (name === 'header') return;
|
|
||||||
const win = windowPool.get(name);
|
|
||||||
if (win && !win.isDestroyed()) win.hide();
|
|
||||||
});
|
|
||||||
header.hide();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.lastVisibleWindows.forEach(name => {
|
|
||||||
const win = windowPool.get(name);
|
|
||||||
if (win && !win.isDestroyed()) {
|
|
||||||
win.show();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (this.allWindowVisibility) {
|
||||||
|
await this.registerShortcuts(true);
|
||||||
|
} else {
|
||||||
|
await this.registerShortcuts();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.allWindowVisibility = !this.allWindowVisibility;
|
||||||
}
|
}
|
||||||
|
|
||||||
async registerShortcuts() {
|
async registerShortcuts(registerOnlyToggleVisibility = false) {
|
||||||
if (!this.movementManager || !this.windowPool) {
|
if (!this.movementManager || !this.windowPool) {
|
||||||
console.error('[Shortcuts] Service not initialized. Cannot register shortcuts.');
|
console.error('[Shortcuts] Service not initialized. Cannot register shortcuts.');
|
||||||
return;
|
return;
|
||||||
@ -168,6 +162,14 @@ class ShortcutsService {
|
|||||||
|
|
||||||
sendToRenderer('shortcuts-updated', keybinds);
|
sendToRenderer('shortcuts-updated', keybinds);
|
||||||
|
|
||||||
|
if (registerOnlyToggleVisibility) {
|
||||||
|
if (keybinds.toggleVisibility) {
|
||||||
|
globalShortcut.register(keybinds.toggleVisibility, () => this.toggleAllWindowsVisibility());
|
||||||
|
}
|
||||||
|
console.log('[Shortcuts] registerOnlyToggleVisibility, only toggleVisibility shortcut is registered.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// --- Hardcoded shortcuts ---
|
// --- Hardcoded shortcuts ---
|
||||||
const isMac = process.platform === 'darwin';
|
const isMac = process.platform === 'darwin';
|
||||||
const modifier = isMac ? 'Cmd' : 'Ctrl';
|
const modifier = isMac ? 'Cmd' : 'Ctrl';
|
||||||
@ -195,7 +197,7 @@ class ShortcutsService {
|
|||||||
// --- 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(this.windowPool));
|
globalShortcut.register(keybinds.toggleVisibility, () => this.toggleAllWindowsVisibility());
|
||||||
}
|
}
|
||||||
console.log('[Shortcuts] ApiKeyHeader is active, only toggleVisibility shortcut is registered.');
|
console.log('[Shortcuts] ApiKeyHeader is active, only toggleVisibility shortcut is registered.');
|
||||||
return;
|
return;
|
||||||
@ -208,7 +210,7 @@ class ShortcutsService {
|
|||||||
let callback;
|
let callback;
|
||||||
switch(action) {
|
switch(action) {
|
||||||
case 'toggleVisibility':
|
case 'toggleVisibility':
|
||||||
callback = () => this.toggleAllWindowsVisibility(this.windowPool);
|
callback = () => this.toggleAllWindowsVisibility();
|
||||||
break;
|
break;
|
||||||
case 'nextStep':
|
case 'nextStep':
|
||||||
callback = () => askService.toggleAskButton(true);
|
callback = () => askService.toggleAskButton(true);
|
||||||
@ -282,4 +284,7 @@ class ShortcutsService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = new ShortcutsService();
|
|
||||||
|
const shortcutsService = new ShortcutsService();
|
||||||
|
|
||||||
|
module.exports = shortcutsService;
|
@ -95,11 +95,14 @@ contextBridge.exposeInMainWorld('api', {
|
|||||||
|
|
||||||
// Settings Window Management
|
// Settings Window Management
|
||||||
cancelHideSettingsWindow: () => ipcRenderer.send('cancel-hide-settings-window'),
|
cancelHideSettingsWindow: () => ipcRenderer.send('cancel-hide-settings-window'),
|
||||||
showSettingsWindow: (bounds) => ipcRenderer.send('show-settings-window', bounds),
|
showSettingsWindow: () => ipcRenderer.send('show-settings-window'),
|
||||||
hideSettingsWindow: () => ipcRenderer.send('hide-settings-window'),
|
hideSettingsWindow: () => ipcRenderer.send('hide-settings-window'),
|
||||||
|
|
||||||
// Generic invoke (for dynamic channel names)
|
// Generic invoke (for dynamic channel names)
|
||||||
invoke: (channel, ...args) => ipcRenderer.invoke(channel, ...args),
|
// invoke: (channel, ...args) => ipcRenderer.invoke(channel, ...args),
|
||||||
|
sendListenButtonClick: (listenButtonText) => ipcRenderer.invoke('listen:changeSession', listenButtonText),
|
||||||
|
sendAskButtonClick: () => ipcRenderer.invoke('ask:toggleAskButton'),
|
||||||
|
sendToggleAllWindowsVisibility: () => ipcRenderer.invoke('shortcut:toggleAllWindowsVisibility'),
|
||||||
|
|
||||||
// Listeners
|
// Listeners
|
||||||
onListenChangeSessionResult: (callback) => ipcRenderer.on('listen:changeSessionResult', callback),
|
onListenChangeSessionResult: (callback) => ipcRenderer.on('listen:changeSessionResult', callback),
|
||||||
@ -213,8 +216,8 @@ contextBridge.exposeInMainWorld('api', {
|
|||||||
setAutoUpdate: (isEnabled) => ipcRenderer.invoke('settings:set-auto-update', isEnabled),
|
setAutoUpdate: (isEnabled) => ipcRenderer.invoke('settings:set-auto-update', isEnabled),
|
||||||
getContentProtectionStatus: () => ipcRenderer.invoke('get-content-protection-status'),
|
getContentProtectionStatus: () => ipcRenderer.invoke('get-content-protection-status'),
|
||||||
toggleContentProtection: () => ipcRenderer.invoke('toggle-content-protection'),
|
toggleContentProtection: () => ipcRenderer.invoke('toggle-content-protection'),
|
||||||
getCurrentShortcuts: () => ipcRenderer.invoke('get-current-shortcuts'),
|
getCurrentShortcuts: () => ipcRenderer.invoke('settings:getCurrentShortcuts'),
|
||||||
openShortcutEditor: () => ipcRenderer.invoke('open-shortcut-editor'),
|
openShortcutSettingsWindow: () => ipcRenderer.invoke('shortcut:openShortcutSettingsWindow'),
|
||||||
|
|
||||||
// Window Management
|
// Window Management
|
||||||
moveWindowStep: (direction) => ipcRenderer.invoke('move-window-step', direction),
|
moveWindowStep: (direction) => ipcRenderer.invoke('move-window-step', direction),
|
||||||
@ -245,20 +248,17 @@ contextBridge.exposeInMainWorld('api', {
|
|||||||
// src/ui/settings/ShortCutSettingsView.js
|
// src/ui/settings/ShortCutSettingsView.js
|
||||||
shortcutSettingsView: {
|
shortcutSettingsView: {
|
||||||
// Shortcut Management
|
// Shortcut Management
|
||||||
saveShortcuts: (shortcuts) => ipcRenderer.invoke('save-shortcuts', shortcuts),
|
saveShortcuts: (shortcuts) => ipcRenderer.invoke('shortcut:saveShortcuts', shortcuts),
|
||||||
getDefaultShortcuts: () => ipcRenderer.invoke('get-default-shortcuts'),
|
getDefaultShortcuts: () => ipcRenderer.invoke('shortcut:getDefaultShortcuts'),
|
||||||
closeShortcutEditor: () => ipcRenderer.send('close-shortcut-editor'),
|
closeShortcutSettingsWindow: () => ipcRenderer.invoke('shortcut:closeShortcutSettingsWindow'),
|
||||||
|
|
||||||
// Listeners
|
// Listeners
|
||||||
onLoadShortcuts: (callback) => ipcRenderer.on('load-shortcuts', callback),
|
onLoadShortcuts: (callback) => ipcRenderer.on('shortcut:loadShortcuts', callback),
|
||||||
removeOnLoadShortcuts: (callback) => ipcRenderer.removeListener('load-shortcuts', callback)
|
removeOnLoadShortcuts: (callback) => ipcRenderer.removeListener('shortcut:loadShortcuts', callback)
|
||||||
},
|
},
|
||||||
|
|
||||||
// src/ui/app/content.html inline scripts
|
// src/ui/app/content.html inline scripts
|
||||||
content: {
|
content: {
|
||||||
// Animation Management
|
|
||||||
// sendAnimationFinished: () => ipcRenderer.send('animation-finished'),
|
|
||||||
|
|
||||||
// Listeners
|
// Listeners
|
||||||
onSettingsWindowHideAnimation: (callback) => ipcRenderer.on('settings-window-hide-animation', callback),
|
onSettingsWindowHideAnimation: (callback) => ipcRenderer.on('settings-window-hide-animation', callback),
|
||||||
removeOnSettingsWindowHideAnimation: (callback) => ipcRenderer.removeListener('settings-window-hide-animation', callback),
|
removeOnSettingsWindowHideAnimation: (callback) => ipcRenderer.removeListener('settings-window-hide-animation', callback),
|
||||||
|
@ -2,7 +2,6 @@ import { html, css, LitElement } from '../assets/lit-core-2.7.4.min.js';
|
|||||||
|
|
||||||
export class MainHeader extends LitElement {
|
export class MainHeader extends LitElement {
|
||||||
static properties = {
|
static properties = {
|
||||||
// isSessionActive: { type: Boolean, state: true },
|
|
||||||
isTogglingSession: { type: Boolean, state: true },
|
isTogglingSession: { type: Boolean, state: true },
|
||||||
shortcuts: { type: Object, state: true },
|
shortcuts: { type: Object, state: true },
|
||||||
listenSessionStatus: { type: String, state: true },
|
listenSessionStatus: { type: String, state: true },
|
||||||
@ -515,30 +514,12 @@ export class MainHeader extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
invoke(channel, ...args) {
|
|
||||||
if (this.wasJustDragged) return;
|
|
||||||
if (window.api) {
|
|
||||||
window.api.mainHeader.invoke(channel, ...args);
|
|
||||||
}
|
|
||||||
// return Promise.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
showSettingsWindow(element) {
|
showSettingsWindow(element) {
|
||||||
if (this.wasJustDragged) return;
|
if (this.wasJustDragged) return;
|
||||||
if (window.api) {
|
if (window.api) {
|
||||||
console.log(`[MainHeader] showSettingsWindow called at ${Date.now()}`);
|
console.log(`[MainHeader] showSettingsWindow called at ${Date.now()}`);
|
||||||
|
window.api.mainHeader.showSettingsWindow();
|
||||||
window.api.mainHeader.cancelHideSettingsWindow();
|
|
||||||
|
|
||||||
if (element) {
|
|
||||||
const { left, top, width, height } = element.getBoundingClientRect();
|
|
||||||
window.api.mainHeader.showSettingsWindow({
|
|
||||||
x: left,
|
|
||||||
y: top,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -559,9 +540,10 @@ export class MainHeader extends LitElement {
|
|||||||
this.isTogglingSession = true;
|
this.isTogglingSession = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const channel = 'listen:changeSession';
|
|
||||||
const listenButtonText = this._getListenButtonText(this.listenSessionStatus);
|
const listenButtonText = this._getListenButtonText(this.listenSessionStatus);
|
||||||
await this.invoke(channel, listenButtonText);
|
if (window.api) {
|
||||||
|
await window.api.mainHeader.sendListenButtonClick(listenButtonText);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('IPC invoke for session change failed:', error);
|
console.error('IPC invoke for session change failed:', error);
|
||||||
this.isTogglingSession = false;
|
this.isTogglingSession = false;
|
||||||
@ -572,13 +554,26 @@ export class MainHeader extends LitElement {
|
|||||||
if (this.wasJustDragged) return;
|
if (this.wasJustDragged) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const channel = 'ask:toggleAskButton';
|
if (window.api) {
|
||||||
await this.invoke(channel);
|
await window.api.mainHeader.sendAskButtonClick();
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('IPC invoke for ask button failed:', error);
|
console.error('IPC invoke for ask button failed:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async _handleToggleAllWindowsVisibility() {
|
||||||
|
if (this.wasJustDragged) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (window.api) {
|
||||||
|
await window.api.mainHeader.sendToggleAllWindowsVisibility();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('IPC invoke for all windows visibility button failed:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
renderShortcut(accelerator) {
|
renderShortcut(accelerator) {
|
||||||
if (!accelerator) return html``;
|
if (!accelerator) return html``;
|
||||||
@ -656,7 +651,7 @@ export class MainHeader extends LitElement {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="header-actions" @click=${() => this.invoke('toggle-all-windows-visibility')}>
|
<div class="header-actions" @click=${() => this._handleToggleAllWindowsVisibility()}>
|
||||||
<div class="action-text">
|
<div class="action-text">
|
||||||
<div class="action-text-content">Show/Hide</div>
|
<div class="action-text-content">Show/Hide</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -879,7 +879,7 @@ export class SettingsView extends LitElement {
|
|||||||
//////// after_modelStateService ////////
|
//////// after_modelStateService ////////
|
||||||
|
|
||||||
openShortcutEditor() {
|
openShortcutEditor() {
|
||||||
window.api.settingsView.openShortcutEditor();
|
window.api.settingsView.openShortcutSettingsWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
@ -1019,13 +1019,7 @@ export class SettingsView extends LitElement {
|
|||||||
window.api.settingsView.hideSettingsWindow();
|
window.api.settingsView.hideSettingsWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
// getMainShortcuts() {
|
|
||||||
// return [
|
|
||||||
// { name: 'Show / Hide', key: '\\' },
|
|
||||||
// { name: 'Ask Anything', key: '↵' },
|
|
||||||
// { name: 'Scroll AI Response', key: '↕' }
|
|
||||||
// ];
|
|
||||||
// }
|
|
||||||
getMainShortcuts() {
|
getMainShortcuts() {
|
||||||
return [
|
return [
|
||||||
{ name: 'Show / Hide', accelerator: this.shortcuts.toggleVisibility },
|
{ name: 'Show / Hide', accelerator: this.shortcuts.toggleVisibility },
|
||||||
|
@ -179,7 +179,7 @@ export class ShortcutSettingsView extends LitElement {
|
|||||||
|
|
||||||
handleClose() {
|
handleClose() {
|
||||||
if (!window.api) return;
|
if (!window.api) return;
|
||||||
window.api.shortcutSettingsView.closeShortcutEditor();
|
window.api.shortcutSettingsView.closeShortcutSettingsWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleResetToDefault() {
|
async handleResetToDefault() {
|
||||||
|
@ -142,7 +142,14 @@ class WindowLayoutManager {
|
|||||||
const strategy = this.determineLayoutStrategy(headerBounds, screenWidth, screenHeight, relativeX, relativeY, workAreaX, workAreaY);
|
const strategy = this.determineLayoutStrategy(headerBounds, screenWidth, screenHeight, relativeX, relativeY, workAreaX, workAreaY);
|
||||||
|
|
||||||
this.positionFeatureWindows(headerBounds, strategy, screenWidth, screenHeight, workAreaX, workAreaY);
|
this.positionFeatureWindows(headerBounds, strategy, screenWidth, screenHeight, workAreaX, workAreaY);
|
||||||
this.positionSettingsWindow(headerBounds, strategy, screenWidth, screenHeight, workAreaX, workAreaY);
|
const settings = this.windowPool.get('settings');
|
||||||
|
if (settings && !settings.isDestroyed() && settings.isVisible()) {
|
||||||
|
const settingPos = this.calculateSettingsWindowPosition();
|
||||||
|
if (settingPos) {
|
||||||
|
const { width, height } = settings.getBounds();
|
||||||
|
settings.setBounds({ x: settingPos.x, y: settingPos.y, width, height });
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -234,58 +241,54 @@ class WindowLayoutManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
positionSettingsWindow(headerBounds, strategy, screenWidth, screenHeight, workAreaX, workAreaY) {
|
/**
|
||||||
|
* @returns {{x: number, y: number} | null}
|
||||||
|
*/
|
||||||
|
calculateSettingsWindowPosition() {
|
||||||
|
const header = this.windowPool.get('header');
|
||||||
const settings = this.windowPool.get('settings');
|
const settings = this.windowPool.get('settings');
|
||||||
if (!settings?.getBounds || !settings.isVisible()) return;
|
|
||||||
|
|
||||||
if (settings.__lockedByButton) {
|
if (!header || header.isDestroyed() || !settings || settings.isDestroyed()) {
|
||||||
const headerDisplay = getCurrentDisplay(this.windowPool.get('header'));
|
return null;
|
||||||
const settingsDisplay = getCurrentDisplay(settings);
|
|
||||||
if (headerDisplay.id !== settingsDisplay.id) {
|
|
||||||
settings.__lockedByButton = false;
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const headerBounds = header.getBounds();
|
||||||
const settingsBounds = settings.getBounds();
|
const settingsBounds = settings.getBounds();
|
||||||
|
const display = getCurrentDisplay(header);
|
||||||
|
const { x: workAreaX, y: workAreaY, width: screenWidth, height: screenHeight } = display.workArea;
|
||||||
|
|
||||||
const PAD = 5;
|
const PAD = 5;
|
||||||
const buttonPadding = 17;
|
const buttonPadding = 170;
|
||||||
let x = headerBounds.x + headerBounds.width - settingsBounds.width - buttonPadding;
|
|
||||||
let y = headerBounds.y + headerBounds.height + PAD;
|
|
||||||
|
|
||||||
const otherVisibleWindows = [];
|
const x = headerBounds.x + headerBounds.width - settingsBounds.width + buttonPadding;
|
||||||
['listen', 'ask'].forEach(name => {
|
const y = headerBounds.y + headerBounds.height + PAD;
|
||||||
const win = this.windowPool.get(name);
|
|
||||||
if (win && win.isVisible() && !win.isDestroyed()) {
|
|
||||||
otherVisibleWindows.push({ name, bounds: win.getBounds() });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const settingsNewBounds = { x, y, width: settingsBounds.width, height: settingsBounds.height };
|
const clampedX = Math.max(workAreaX + 10, Math.min(workAreaX + screenWidth - settingsBounds.width - 10, x));
|
||||||
let hasOverlap = otherVisibleWindows.some(otherWin => this.boundsOverlap(settingsNewBounds, otherWin.bounds));
|
const clampedY = Math.max(workAreaY + 10, Math.min(workAreaY + screenHeight - settingsBounds.height - 10, y));
|
||||||
|
|
||||||
if (hasOverlap) {
|
return { x: Math.round(clampedX), y: Math.round(clampedY) };
|
||||||
x = headerBounds.x + headerBounds.width + PAD;
|
}
|
||||||
y = headerBounds.y;
|
|
||||||
if (x + settingsBounds.width > screenWidth - 10) {
|
positionShortcutSettingsWindow() {
|
||||||
x = headerBounds.x - settingsBounds.width - PAD;
|
const header = this.windowPool.get('header');
|
||||||
}
|
const shortcutSettings = this.windowPool.get('shortcut-settings');
|
||||||
if (x < 10) {
|
|
||||||
x = headerBounds.x + headerBounds.width - settingsBounds.width - buttonPadding;
|
if (!header || header.isDestroyed() || !shortcutSettings || shortcutSettings.isDestroyed()) {
|
||||||
y = headerBounds.y - settingsBounds.height - PAD;
|
return;
|
||||||
if (y < 10) {
|
|
||||||
x = headerBounds.x + headerBounds.width - settingsBounds.width;
|
|
||||||
y = headerBounds.y + headerBounds.height + PAD;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
x = Math.max(workAreaX + 10, Math.min(workAreaX + screenWidth - settingsBounds.width - 10, x));
|
const headerBounds = header.getBounds();
|
||||||
y = Math.max(workAreaY + 10, Math.min(workAreaY + screenHeight - settingsBounds.height - 10, y));
|
const shortcutBounds = shortcutSettings.getBounds();
|
||||||
|
const display = getCurrentDisplay(header);
|
||||||
|
const { workArea } = display;
|
||||||
|
|
||||||
settings.setBounds({ x: Math.round(x), y: Math.round(y) });
|
let newX = Math.round(headerBounds.x + (headerBounds.width / 2) - (shortcutBounds.width / 2));
|
||||||
settings.moveTop();
|
let newY = Math.round(headerBounds.y);
|
||||||
|
|
||||||
|
newX = Math.max(workArea.x, Math.min(newX, workArea.x + workArea.width - shortcutBounds.width));
|
||||||
|
newY = Math.max(workArea.y, Math.min(newY, workArea.y + workArea.height - shortcutBounds.height));
|
||||||
|
|
||||||
|
shortcutSettings.setBounds({ x: newX, y: newY, width: shortcutBounds.width, height: shortcutBounds.height });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -5,7 +5,6 @@ const path = require('node:path');
|
|||||||
const os = require('os');
|
const os = require('os');
|
||||||
const shortcutsService = require('../features/shortcuts/shortcutsService');
|
const shortcutsService = require('../features/shortcuts/shortcutsService');
|
||||||
const internalBridge = require('../bridge/internalBridge');
|
const internalBridge = require('../bridge/internalBridge');
|
||||||
const { EVENTS } = internalBridge;
|
|
||||||
const permissionRepository = require('../features/common/repositories/permission');
|
const permissionRepository = require('../features/common/repositories/permission');
|
||||||
|
|
||||||
/* ────────────────[ GLASS BYPASS ]─────────────── */
|
/* ────────────────[ GLASS BYPASS ]─────────────── */
|
||||||
@ -30,9 +29,6 @@ if (shouldUseLiquidGlass) {
|
|||||||
/* ────────────────[ GLASS BYPASS ]─────────────── */
|
/* ────────────────[ GLASS BYPASS ]─────────────── */
|
||||||
|
|
||||||
let isContentProtectionOn = true;
|
let isContentProtectionOn = true;
|
||||||
let currentDisplayId = null;
|
|
||||||
|
|
||||||
let mouseEventsIgnored = false;
|
|
||||||
let lastVisibleWindows = new Set(['header']);
|
let lastVisibleWindows = new Set(['header']);
|
||||||
const HEADER_HEIGHT = 47;
|
const HEADER_HEIGHT = 47;
|
||||||
const DEFAULT_WINDOW_WIDTH = 353;
|
const DEFAULT_WINDOW_WIDTH = 353;
|
||||||
@ -42,9 +38,7 @@ const windowPool = new Map();
|
|||||||
|
|
||||||
let settingsHideTimer = null;
|
let settingsHideTimer = null;
|
||||||
|
|
||||||
let selectedCaptureSourceId = null;
|
|
||||||
|
|
||||||
// let shortcutEditorWindow = null;
|
|
||||||
let layoutManager = null;
|
let layoutManager = null;
|
||||||
function updateLayout() {
|
function updateLayout() {
|
||||||
if (layoutManager) {
|
if (layoutManager) {
|
||||||
@ -92,20 +86,69 @@ function fadeWindow(win, from, to, duration = FADE_DURATION, onComplete) {
|
|||||||
}, 1000 / FADE_FPS);
|
}, 1000 / FADE_FPS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const showSettingsWindow = () => {
|
||||||
|
internalBridge.emit('window:requestVisibility', { name: 'settings', visible: true });
|
||||||
|
};
|
||||||
|
|
||||||
function setupAnimationController(windowPool, layoutManager, movementManager) {
|
const hideSettingsWindow = () => {
|
||||||
internalBridge.on('request-window-visibility', ({ name, visible }) => {
|
internalBridge.emit('window:requestVisibility', { name: 'settings', visible: false });
|
||||||
|
};
|
||||||
|
|
||||||
|
const cancelHideSettingsWindow = () => {
|
||||||
|
internalBridge.emit('window:requestVisibility', { name: 'settings', visible: true });
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
function setupWindowController(windowPool, layoutManager, movementManager) {
|
||||||
|
internalBridge.on('window:requestVisibility', ({ name, visible }) => {
|
||||||
handleWindowVisibilityRequest(windowPool, layoutManager, movementManager, name, visible);
|
handleWindowVisibilityRequest(windowPool, layoutManager, movementManager, name, visible);
|
||||||
});
|
});
|
||||||
|
internalBridge.on('window:requestToggleAllWindowsVisibility', ({ targetVisibility }) => {
|
||||||
|
changeAllWindowsVisibility(windowPool, targetVisibility);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function changeAllWindowsVisibility(windowPool, targetVisibility) {
|
||||||
|
const header = windowPool.get('header');
|
||||||
|
if (!header) return;
|
||||||
|
|
||||||
|
if (typeof targetVisibility === 'boolean' &&
|
||||||
|
header.isVisible() === targetVisibility) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (header.isVisible()) {
|
||||||
|
lastVisibleWindows.clear();
|
||||||
|
|
||||||
|
windowPool.forEach((win, name) => {
|
||||||
|
if (win && !win.isDestroyed() && win.isVisible()) {
|
||||||
|
lastVisibleWindows.add(name);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
lastVisibleWindows.forEach(name => {
|
||||||
|
if (name === 'header') return;
|
||||||
|
const win = windowPool.get(name);
|
||||||
|
if (win && !win.isDestroyed()) win.hide();
|
||||||
|
});
|
||||||
|
header.hide();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastVisibleWindows.forEach(name => {
|
||||||
|
const win = windowPool.get(name);
|
||||||
|
if (win && !win.isDestroyed())
|
||||||
|
win.show();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {Map<string, BrowserWindow>} windowPool
|
* @param {Map<string, BrowserWindow>} windowPool
|
||||||
* @param {WindowLayoutManager} layoutManager
|
* @param {WindowLayoutManager} layoutManager
|
||||||
* @param {SmoothMovementManager} movementManager
|
* @param {SmoothMovementManager} movementManager
|
||||||
* @param {'listen' | 'ask'} name
|
* @param {'listen' | 'ask' | 'settings' | 'shortcut-settings'} name
|
||||||
* @param {boolean} shouldBeVisible
|
* @param {boolean} shouldBeVisible
|
||||||
*/
|
*/
|
||||||
async function handleWindowVisibilityRequest(windowPool, layoutManager, movementManager, name, shouldBeVisible) {
|
async function handleWindowVisibilityRequest(windowPool, layoutManager, movementManager, name, shouldBeVisible) {
|
||||||
@ -117,94 +160,171 @@ async function handleWindowVisibilityRequest(windowPool, layoutManager, movement
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isCurrentlyVisible = win.isVisible();
|
if (name !== 'settings') {
|
||||||
if (isCurrentlyVisible === shouldBeVisible) {
|
const isCurrentlyVisible = win.isVisible();
|
||||||
console.log(`[WindowManager] Window '${name}' is already in the desired state.`);
|
if (isCurrentlyVisible === shouldBeVisible) {
|
||||||
|
console.log(`[WindowManager] Window '${name}' is already in the desired state.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const disableClicks = (selectedWindow) => {
|
||||||
|
for (const [name, win] of windowPool) {
|
||||||
|
if (win !== selectedWindow && !win.isDestroyed()) {
|
||||||
|
win.setIgnoreMouseEvents(true, { forward: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const restoreClicks = () => {
|
||||||
|
for (const [, win] of windowPool) {
|
||||||
|
if (!win.isDestroyed()) win.setIgnoreMouseEvents(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (name === 'settings') {
|
||||||
|
if (shouldBeVisible) {
|
||||||
|
// Cancel any pending hide operations
|
||||||
|
if (settingsHideTimer) {
|
||||||
|
clearTimeout(settingsHideTimer);
|
||||||
|
settingsHideTimer = null;
|
||||||
|
}
|
||||||
|
const position = layoutManager.calculateSettingsWindowPosition();
|
||||||
|
if (position) {
|
||||||
|
win.setBounds(position);
|
||||||
|
win.__lockedByButton = true;
|
||||||
|
win.show();
|
||||||
|
win.moveTop();
|
||||||
|
win.setAlwaysOnTop(true);
|
||||||
|
} else {
|
||||||
|
console.warn('[WindowManager] Could not calculate settings window position.');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Hide after a delay
|
||||||
|
if (settingsHideTimer) {
|
||||||
|
clearTimeout(settingsHideTimer);
|
||||||
|
}
|
||||||
|
settingsHideTimer = setTimeout(() => {
|
||||||
|
if (win && !win.isDestroyed()) {
|
||||||
|
win.setAlwaysOnTop(false);
|
||||||
|
win.hide();
|
||||||
|
}
|
||||||
|
settingsHideTimer = null;
|
||||||
|
}, 200);
|
||||||
|
|
||||||
|
win.__lockedByButton = false;
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const otherName = name === 'listen' ? 'ask' : 'listen';
|
|
||||||
const otherWin = windowPool.get(otherName);
|
|
||||||
const isOtherWinVisible = otherWin && !otherWin.isDestroyed() && otherWin.isVisible();
|
|
||||||
|
|
||||||
const ANIM_OFFSET_X = 100;
|
|
||||||
const ANIM_OFFSET_Y = 20;
|
|
||||||
|
|
||||||
if (shouldBeVisible) {
|
|
||||||
win.setOpacity(0);
|
|
||||||
|
|
||||||
if (name === 'listen') {
|
|
||||||
if (!isOtherWinVisible) {
|
|
||||||
const targets = layoutManager.getTargetBoundsForFeatureWindows({ listen: true, ask: false });
|
|
||||||
if (!targets.listen) return;
|
|
||||||
|
|
||||||
const startPos = { x: targets.listen.x - ANIM_OFFSET_X, y: targets.listen.y };
|
|
||||||
win.setBounds(startPos);
|
|
||||||
win.show();
|
|
||||||
fadeWindow(win, 0, 1);
|
|
||||||
movementManager.animateWindow(win, targets.listen.x, targets.listen.y);
|
|
||||||
|
|
||||||
|
if (name === 'shortcut-settings') {
|
||||||
|
if (shouldBeVisible) {
|
||||||
|
layoutManager.positionShortcutSettingsWindow();
|
||||||
|
if (process.platform === 'darwin') {
|
||||||
|
win.setAlwaysOnTop(true, 'screen-saver');
|
||||||
} else {
|
} else {
|
||||||
const targets = layoutManager.getTargetBoundsForFeatureWindows({ listen: true, ask: true });
|
win.setAlwaysOnTop(true);
|
||||||
if (!targets.listen || !targets.ask) return;
|
|
||||||
|
|
||||||
const startListenPos = { x: targets.listen.x - ANIM_OFFSET_X, y: targets.listen.y };
|
|
||||||
win.setBounds(startListenPos);
|
|
||||||
|
|
||||||
win.show();
|
|
||||||
fadeWindow(win, 0, 1);
|
|
||||||
movementManager.animateWindow(otherWin, targets.ask.x, targets.ask.y);
|
|
||||||
movementManager.animateWindow(win, targets.listen.x, targets.listen.y);
|
|
||||||
}
|
}
|
||||||
} else if (name === 'ask') {
|
// globalShortcut.unregisterAll();
|
||||||
if (!isOtherWinVisible) {
|
disableClicks(win);
|
||||||
const targets = layoutManager.getTargetBoundsForFeatureWindows({ listen: false, ask: true });
|
win.show();
|
||||||
if (!targets.ask) return;
|
} else {
|
||||||
|
if (process.platform === 'darwin') {
|
||||||
const startPos = { x: targets.ask.x, y: targets.ask.y - ANIM_OFFSET_Y };
|
win.setAlwaysOnTop(false, 'screen-saver');
|
||||||
win.setBounds(startPos);
|
|
||||||
win.show();
|
|
||||||
fadeWindow(win, 0, 1);
|
|
||||||
movementManager.animateWindow(win, targets.ask.x, targets.ask.y);
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
const targets = layoutManager.getTargetBoundsForFeatureWindows({ listen: true, ask: true });
|
win.setAlwaysOnTop(false);
|
||||||
if (!targets.listen || !targets.ask) return;
|
|
||||||
|
|
||||||
const startAskPos = { x: targets.ask.x, y: targets.ask.y - ANIM_OFFSET_Y };
|
|
||||||
win.setBounds(startAskPos);
|
|
||||||
|
|
||||||
win.show();
|
|
||||||
fadeWindow(win, 0, 1);
|
|
||||||
movementManager.animateWindow(otherWin, targets.listen.x, targets.listen.y);
|
|
||||||
movementManager.animateWindow(win, targets.ask.x, targets.ask.y);
|
|
||||||
}
|
}
|
||||||
|
restoreClicks();
|
||||||
|
win.hide();
|
||||||
}
|
}
|
||||||
} else {
|
return;
|
||||||
const currentBounds = win.getBounds();
|
}
|
||||||
fadeWindow(
|
|
||||||
win, 1, 0, FADE_DURATION,
|
|
||||||
() => win.hide()
|
|
||||||
);
|
|
||||||
if (name === 'listen') {
|
|
||||||
if (!isOtherWinVisible) {
|
|
||||||
const targetX = currentBounds.x - ANIM_OFFSET_X;
|
|
||||||
movementManager.animateWindow(win, targetX, currentBounds.y);
|
|
||||||
} else {
|
|
||||||
const targetX = currentBounds.x - currentBounds.width;
|
|
||||||
movementManager.animateWindow(win, targetX, currentBounds.y);
|
|
||||||
}
|
|
||||||
} else if (name === 'ask') {
|
|
||||||
if (!isOtherWinVisible) {
|
|
||||||
const targetY = currentBounds.y - ANIM_OFFSET_Y;
|
|
||||||
movementManager.animateWindow(win, currentBounds.x, targetY);
|
|
||||||
} else {
|
|
||||||
const targetAskY = currentBounds.y - ANIM_OFFSET_Y;
|
|
||||||
movementManager.animateWindow(win, currentBounds.x, targetAskY);
|
|
||||||
|
|
||||||
const targets = layoutManager.getTargetBoundsForFeatureWindows({ listen: true, ask: false });
|
if (name === 'listen' || name === 'ask') {
|
||||||
if (targets.listen) {
|
const otherName = name === 'listen' ? 'ask' : 'listen';
|
||||||
|
const otherWin = windowPool.get(otherName);
|
||||||
|
const isOtherWinVisible = otherWin && !otherWin.isDestroyed() && otherWin.isVisible();
|
||||||
|
|
||||||
|
const ANIM_OFFSET_X = 100;
|
||||||
|
const ANIM_OFFSET_Y = 20;
|
||||||
|
|
||||||
|
if (shouldBeVisible) {
|
||||||
|
win.setOpacity(0);
|
||||||
|
|
||||||
|
if (name === 'listen') {
|
||||||
|
if (!isOtherWinVisible) {
|
||||||
|
const targets = layoutManager.getTargetBoundsForFeatureWindows({ listen: true, ask: false });
|
||||||
|
if (!targets.listen) return;
|
||||||
|
|
||||||
|
const startPos = { x: targets.listen.x - ANIM_OFFSET_X, y: targets.listen.y };
|
||||||
|
win.setBounds(startPos);
|
||||||
|
win.show();
|
||||||
|
fadeWindow(win, 0, 1);
|
||||||
|
movementManager.animateWindow(win, targets.listen.x, targets.listen.y);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
const targets = layoutManager.getTargetBoundsForFeatureWindows({ listen: true, ask: true });
|
||||||
|
if (!targets.listen || !targets.ask) return;
|
||||||
|
|
||||||
|
const startListenPos = { x: targets.listen.x - ANIM_OFFSET_X, y: targets.listen.y };
|
||||||
|
win.setBounds(startListenPos);
|
||||||
|
|
||||||
|
win.show();
|
||||||
|
fadeWindow(win, 0, 1);
|
||||||
|
movementManager.animateWindow(otherWin, targets.ask.x, targets.ask.y);
|
||||||
|
movementManager.animateWindow(win, targets.listen.x, targets.listen.y);
|
||||||
|
}
|
||||||
|
} else if (name === 'ask') {
|
||||||
|
if (!isOtherWinVisible) {
|
||||||
|
const targets = layoutManager.getTargetBoundsForFeatureWindows({ listen: false, ask: true });
|
||||||
|
if (!targets.ask) return;
|
||||||
|
|
||||||
|
const startPos = { x: targets.ask.x, y: targets.ask.y - ANIM_OFFSET_Y };
|
||||||
|
win.setBounds(startPos);
|
||||||
|
win.show();
|
||||||
|
fadeWindow(win, 0, 1);
|
||||||
|
movementManager.animateWindow(win, targets.ask.x, targets.ask.y);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
const targets = layoutManager.getTargetBoundsForFeatureWindows({ listen: true, ask: true });
|
||||||
|
if (!targets.listen || !targets.ask) return;
|
||||||
|
|
||||||
|
const startAskPos = { x: targets.ask.x, y: targets.ask.y - ANIM_OFFSET_Y };
|
||||||
|
win.setBounds(startAskPos);
|
||||||
|
|
||||||
|
win.show();
|
||||||
|
fadeWindow(win, 0, 1);
|
||||||
movementManager.animateWindow(otherWin, targets.listen.x, targets.listen.y);
|
movementManager.animateWindow(otherWin, targets.listen.x, targets.listen.y);
|
||||||
|
movementManager.animateWindow(win, targets.ask.x, targets.ask.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const currentBounds = win.getBounds();
|
||||||
|
fadeWindow(
|
||||||
|
win, 1, 0, FADE_DURATION,
|
||||||
|
() => win.hide()
|
||||||
|
);
|
||||||
|
if (name === 'listen') {
|
||||||
|
if (!isOtherWinVisible) {
|
||||||
|
const targetX = currentBounds.x - ANIM_OFFSET_X;
|
||||||
|
movementManager.animateWindow(win, targetX, currentBounds.y);
|
||||||
|
} else {
|
||||||
|
const targetX = currentBounds.x - currentBounds.width;
|
||||||
|
movementManager.animateWindow(win, targetX, currentBounds.y);
|
||||||
|
}
|
||||||
|
} else if (name === 'ask') {
|
||||||
|
if (!isOtherWinVisible) {
|
||||||
|
const targetY = currentBounds.y - ANIM_OFFSET_Y;
|
||||||
|
movementManager.animateWindow(win, currentBounds.x, targetY);
|
||||||
|
} else {
|
||||||
|
const targetAskY = currentBounds.y - ANIM_OFFSET_Y;
|
||||||
|
movementManager.animateWindow(win, currentBounds.x, targetAskY);
|
||||||
|
|
||||||
|
const targets = layoutManager.getTargetBoundsForFeatureWindows({ listen: true, ask: false });
|
||||||
|
if (targets.listen) {
|
||||||
|
movementManager.animateWindow(otherWin, targets.listen.x, targets.listen.y);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -276,62 +396,6 @@ const resizeHeaderWindow = ({ width, height }) => {
|
|||||||
return { success: false, error: 'Header window not found' };
|
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 openLoginPage = () => {
|
||||||
const webUrl = process.env.pickleglass_WEB_URL || 'http://localhost:3000';
|
const webUrl = process.env.pickleglass_WEB_URL || 'http://localhost:3000';
|
||||||
@ -474,7 +538,7 @@ function createFeatureWindows(header, namesToCreate) {
|
|||||||
case 'shortcut-settings': {
|
case 'shortcut-settings': {
|
||||||
const shortcutEditor = new BrowserWindow({
|
const shortcutEditor = new BrowserWindow({
|
||||||
...commonChildOptions,
|
...commonChildOptions,
|
||||||
width: 420,
|
width: 353,
|
||||||
height: 720,
|
height: 720,
|
||||||
modal: false,
|
modal: false,
|
||||||
parent: undefined,
|
parent: undefined,
|
||||||
@ -482,36 +546,11 @@ function createFeatureWindows(header, namesToCreate) {
|
|||||||
titleBarOverlay: false,
|
titleBarOverlay: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
shortcutEditor.setContentProtection(isContentProtectionOn);
|
||||||
|
shortcutEditor.setVisibleOnAllWorkspaces(true,{visibleOnFullScreen:true});
|
||||||
if (process.platform === 'darwin') {
|
if (process.platform === 'darwin') {
|
||||||
shortcutEditor.setAlwaysOnTop(true, 'screen-saver');
|
shortcutEditor.setWindowButtonVisibility(false);
|
||||||
} else {
|
|
||||||
shortcutEditor.setAlwaysOnTop(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ──────────[ ① 다른 창 클릭 차단 ]────────── */
|
|
||||||
const disableClicks = () => {
|
|
||||||
for (const [name, win] of windowPool) {
|
|
||||||
if (win !== shortcutEditor && !win.isDestroyed()) {
|
|
||||||
win.setIgnoreMouseEvents(true, { forward: true });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const restoreClicks = () => {
|
|
||||||
for (const [, win] of windowPool) {
|
|
||||||
if (!win.isDestroyed()) win.setIgnoreMouseEvents(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const header = windowPool.get('header');
|
|
||||||
if (header && !header.isDestroyed()) {
|
|
||||||
const { x, y, width } = header.getBounds();
|
|
||||||
shortcutEditor.setBounds({ x, y, width });
|
|
||||||
}
|
|
||||||
|
|
||||||
shortcutEditor.once('ready-to-show', () => {
|
|
||||||
disableClicks();
|
|
||||||
shortcutEditor.show();
|
|
||||||
});
|
|
||||||
|
|
||||||
const loadOptions = { query: { view: 'shortcut-settings' } };
|
const loadOptions = { query: { view: 'shortcut-settings' } };
|
||||||
if (!shouldUseLiquidGlass) {
|
if (!shouldUseLiquidGlass) {
|
||||||
@ -526,23 +565,11 @@ function createFeatureWindows(header, namesToCreate) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
shortcutEditor.on('closed', () => {
|
|
||||||
restoreClicks();
|
|
||||||
windowPool.delete('shortcut-settings');
|
|
||||||
console.log('[Shortcuts] Re-enabled after editing.');
|
|
||||||
shortcutsService.registerShortcuts();
|
|
||||||
});
|
|
||||||
|
|
||||||
shortcutEditor.webContents.once('dom-ready', async () => {
|
|
||||||
const keybinds = await shortcutsService.loadKeybinds();
|
|
||||||
shortcutEditor.webContents.send('load-shortcuts', keybinds);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
windowPool.set('shortcut-settings', shortcutEditor);
|
||||||
if (!app.isPackaged) {
|
if (!app.isPackaged) {
|
||||||
shortcutEditor.webContents.openDevTools({ mode: 'detach' });
|
shortcutEditor.webContents.openDevTools({ mode: 'detach' });
|
||||||
}
|
}
|
||||||
windowPool.set('shortcut-settings', shortcutEditor);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -556,6 +583,7 @@ function createFeatureWindows(header, namesToCreate) {
|
|||||||
createFeatureWindow('listen');
|
createFeatureWindow('listen');
|
||||||
createFeatureWindow('ask');
|
createFeatureWindow('ask');
|
||||||
createFeatureWindow('settings');
|
createFeatureWindow('settings');
|
||||||
|
createFeatureWindow('shortcut-settings');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -593,35 +621,7 @@ function getDisplayById(displayId) {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
function toggleAllWindowsVisibility() {
|
|
||||||
const header = windowPool.get('header');
|
|
||||||
if (!header) return;
|
|
||||||
|
|
||||||
if (header.isVisible()) {
|
|
||||||
lastVisibleWindows.clear();
|
|
||||||
|
|
||||||
windowPool.forEach((win, name) => {
|
|
||||||
if (win && !win.isDestroyed() && win.isVisible()) {
|
|
||||||
lastVisibleWindows.add(name);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
lastVisibleWindows.forEach(name => {
|
|
||||||
if (name === 'header') return;
|
|
||||||
const win = windowPool.get(name);
|
|
||||||
if (win && !win.isDestroyed()) win.hide();
|
|
||||||
});
|
|
||||||
header.hide();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
lastVisibleWindows.forEach(name => {
|
|
||||||
const win = windowPool.get(name);
|
|
||||||
if (win && !win.isDestroyed())
|
|
||||||
win.show();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function createWindows() {
|
function createWindows() {
|
||||||
@ -690,7 +690,7 @@ function createWindows() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
setupIpcHandlers(movementManager);
|
setupIpcHandlers(movementManager);
|
||||||
setupAnimationController(windowPool, layoutManager, movementManager);
|
setupWindowController(windowPool, layoutManager, movementManager);
|
||||||
|
|
||||||
if (currentHeaderState === 'main') {
|
if (currentHeaderState === 'main') {
|
||||||
createFeatureWindows(header, ['listen', 'ask', 'settings', 'shortcut-settings']);
|
createFeatureWindows(header, ['listen', 'ask', 'settings', 'shortcut-settings']);
|
||||||
@ -850,13 +850,6 @@ const adjustWindowHeight = (sender, targetHeight) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const closeWindow = (windowName) => {
|
|
||||||
const win = windowPool.get(windowName);
|
|
||||||
if (win && !win.isDestroyed()) {
|
|
||||||
win.close();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
updateLayout,
|
updateLayout,
|
||||||
createWindows,
|
createWindows,
|
||||||
@ -864,14 +857,11 @@ module.exports = {
|
|||||||
toggleContentProtection,
|
toggleContentProtection,
|
||||||
resizeHeaderWindow,
|
resizeHeaderWindow,
|
||||||
getContentProtectionStatus,
|
getContentProtectionStatus,
|
||||||
openShortcutEditor,
|
|
||||||
showSettingsWindow,
|
showSettingsWindow,
|
||||||
hideSettingsWindow,
|
hideSettingsWindow,
|
||||||
cancelHideSettingsWindow,
|
cancelHideSettingsWindow,
|
||||||
openLoginPage,
|
openLoginPage,
|
||||||
moveWindowStep,
|
moveWindowStep,
|
||||||
closeWindow,
|
|
||||||
toggleAllWindowsVisibility,
|
|
||||||
handleHeaderStateChanged,
|
handleHeaderStateChanged,
|
||||||
handleHeaderAnimationFinished,
|
handleHeaderAnimationFinished,
|
||||||
getHeaderPosition,
|
getHeaderPosition,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user