Merge branch 'refactor/0712_ask' of https://github.com/pickle-com/glass into refactor/0712_ask
This commit is contained in:
commit
c0edcfb0f9
2
aec
2
aec
@ -1 +1 @@
|
|||||||
Subproject commit 9e11f4f95707714464194bdfc9db0222ec5c6163
|
Subproject commit f00bb1fb948053c752b916adfee19f90644a0b2f
|
@ -110,6 +110,13 @@ const LATEST_SCHEMA = {
|
|||||||
{ name: 'selected_stt_model', type: 'TEXT' },
|
{ name: 'selected_stt_model', type: 'TEXT' },
|
||||||
{ name: 'updated_at', type: 'INTEGER' }
|
{ name: 'updated_at', type: 'INTEGER' }
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
shortcuts: {
|
||||||
|
columns: [
|
||||||
|
{ name: 'action', type: 'TEXT PRIMARY KEY' },
|
||||||
|
{ name: 'accelerator', type: 'TEXT NOT NULL' },
|
||||||
|
{ name: 'created_at', type: 'INTEGER' }
|
||||||
|
]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
1
src/features/shortcuts/repositories/index.js
Normal file
1
src/features/shortcuts/repositories/index.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
module.exports = require('./sqlite.repository');
|
48
src/features/shortcuts/repositories/sqlite.repository.js
Normal file
48
src/features/shortcuts/repositories/sqlite.repository.js
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
const sqliteClient = require('../../common/services/sqliteClient');
|
||||||
|
const crypto = require('crypto');
|
||||||
|
|
||||||
|
function getAllKeybinds() {
|
||||||
|
const db = sqliteClient.getDb();
|
||||||
|
const query = 'SELECT * FROM shortcuts';
|
||||||
|
try {
|
||||||
|
return db.prepare(query).all();
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`[DB] Failed to get keybinds:`, error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function upsertKeybinds(keybinds) {
|
||||||
|
if (!keybinds || keybinds.length === 0) return;
|
||||||
|
|
||||||
|
const db = sqliteClient.getDb();
|
||||||
|
const upsert = db.transaction((items) => {
|
||||||
|
const query = `
|
||||||
|
INSERT INTO shortcuts (action, accelerator, created_at)
|
||||||
|
VALUES (@action, @accelerator, @created_at)
|
||||||
|
ON CONFLICT(action) DO UPDATE SET
|
||||||
|
accelerator = excluded.accelerator;
|
||||||
|
`;
|
||||||
|
const insert = db.prepare(query);
|
||||||
|
|
||||||
|
for (const item of items) {
|
||||||
|
insert.run({
|
||||||
|
action: item.action,
|
||||||
|
accelerator: item.accelerator,
|
||||||
|
created_at: Math.floor(Date.now() / 1000)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
upsert(keybinds);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[DB] Failed to upsert keybinds:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
getAllKeybinds,
|
||||||
|
upsertKeybinds
|
||||||
|
};
|
242
src/features/shortcuts/shortcutsService.js
Normal file
242
src/features/shortcuts/shortcutsService.js
Normal file
@ -0,0 +1,242 @@
|
|||||||
|
const { globalShortcut, screen } = require('electron');
|
||||||
|
const shortcutsRepository = require('./repositories');
|
||||||
|
|
||||||
|
|
||||||
|
class ShortcutsService {
|
||||||
|
constructor() {
|
||||||
|
this.lastVisibleWindows = new Set(['header']);
|
||||||
|
this.mouseEventsIgnored = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
getDefaultKeybinds() {
|
||||||
|
const isMac = process.platform === 'darwin';
|
||||||
|
return {
|
||||||
|
moveUp: isMac ? 'Cmd+Up' : 'Ctrl+Up',
|
||||||
|
moveDown: isMac ? 'Cmd+Down' : 'Ctrl+Down',
|
||||||
|
moveLeft: isMac ? 'Cmd+Left' : 'Ctrl+Left',
|
||||||
|
moveRight: isMac ? 'Cmd+Right' : 'Ctrl+Right',
|
||||||
|
toggleVisibility: isMac ? 'Cmd+\\' : 'Ctrl+\\',
|
||||||
|
toggleClickThrough: isMac ? 'Cmd+M' : 'Ctrl+M',
|
||||||
|
nextStep: isMac ? 'Cmd+Enter' : 'Ctrl+Enter',
|
||||||
|
manualScreenshot: isMac ? 'Cmd+Shift+S' : 'Ctrl+Shift+S',
|
||||||
|
previousResponse: isMac ? 'Cmd+[' : 'Ctrl+[',
|
||||||
|
nextResponse: isMac ? 'Cmd+]' : 'Ctrl+]',
|
||||||
|
scrollUp: isMac ? 'Cmd+Shift+Up' : 'Ctrl+Shift+Up',
|
||||||
|
scrollDown: isMac ? 'Cmd+Shift+Down' : 'Ctrl+Shift+Down',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async loadKeybinds() {
|
||||||
|
let keybindsArray = await shortcutsRepository.getAllKeybinds();
|
||||||
|
|
||||||
|
if (!keybindsArray || keybindsArray.length === 0) {
|
||||||
|
console.log(`[Shortcuts] No keybinds found. Loading defaults.`);
|
||||||
|
const defaults = this.getDefaultKeybinds();
|
||||||
|
await this.saveKeybinds(defaults);
|
||||||
|
return defaults;
|
||||||
|
}
|
||||||
|
|
||||||
|
const keybinds = {};
|
||||||
|
keybindsArray.forEach(k => {
|
||||||
|
keybinds[k.action] = k.accelerator;
|
||||||
|
});
|
||||||
|
|
||||||
|
const defaults = this.getDefaultKeybinds();
|
||||||
|
let needsUpdate = false;
|
||||||
|
for (const action in defaults) {
|
||||||
|
if (!keybinds[action]) {
|
||||||
|
keybinds[action] = defaults[action];
|
||||||
|
needsUpdate = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needsUpdate) {
|
||||||
|
console.log('[Shortcuts] Updating missing keybinds with defaults.');
|
||||||
|
await this.saveKeybinds(keybinds);
|
||||||
|
}
|
||||||
|
|
||||||
|
return keybinds;
|
||||||
|
}
|
||||||
|
|
||||||
|
async saveKeybinds(newKeybinds) {
|
||||||
|
const keybindsToSave = [];
|
||||||
|
for (const action in newKeybinds) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(newKeybinds, action)) {
|
||||||
|
keybindsToSave.push({
|
||||||
|
action: action,
|
||||||
|
accelerator: newKeybinds[action],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await shortcutsRepository.upsertKeybinds(keybindsToSave);
|
||||||
|
console.log(`[Shortcuts] Saved keybinds.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleAllWindowsVisibility(windowPool) {
|
||||||
|
const header = windowPool.get('header');
|
||||||
|
if (!header) return;
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async registerShortcuts(movementManager, windowPool) {
|
||||||
|
const keybinds = await this.loadKeybinds();
|
||||||
|
globalShortcut.unregisterAll();
|
||||||
|
|
||||||
|
const header = windowPool.get('header');
|
||||||
|
const mainWindow = header;
|
||||||
|
|
||||||
|
const sendToRenderer = (channel, ...args) => {
|
||||||
|
windowPool.forEach(win => {
|
||||||
|
if (win && !win.isDestroyed()) {
|
||||||
|
try {
|
||||||
|
win.webContents.send(channel, ...args);
|
||||||
|
} catch (e) {
|
||||||
|
// Ignore errors for destroyed windows
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
sendToRenderer('shortcuts-updated', keybinds);
|
||||||
|
|
||||||
|
// --- Hardcoded shortcuts ---
|
||||||
|
const isMac = process.platform === 'darwin';
|
||||||
|
const modifier = isMac ? 'Cmd' : 'Ctrl';
|
||||||
|
|
||||||
|
// Monitor switching
|
||||||
|
const displays = screen.getAllDisplays();
|
||||||
|
if (displays.length > 1) {
|
||||||
|
displays.forEach((display, index) => {
|
||||||
|
const key = `${modifier}+Shift+${index + 1}`;
|
||||||
|
globalShortcut.register(key, () => movementManager.moveToDisplay(display.id));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Edge snapping
|
||||||
|
const edgeDirections = [
|
||||||
|
{ key: `${modifier}+Shift+Left`, direction: 'left' },
|
||||||
|
{ key: `${modifier}+Shift+Right`, direction: 'right' },
|
||||||
|
];
|
||||||
|
edgeDirections.forEach(({ key, direction }) => {
|
||||||
|
globalShortcut.register(key, () => {
|
||||||
|
if (header && header.isVisible()) movementManager.moveToEdge(direction);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// --- User-configurable shortcuts ---
|
||||||
|
if (header?.currentHeaderState === 'apikey') {
|
||||||
|
if (keybinds.toggleVisibility) {
|
||||||
|
globalShortcut.register(keybinds.toggleVisibility, () => this.toggleAllWindowsVisibility(windowPool));
|
||||||
|
}
|
||||||
|
console.log('[Shortcuts] ApiKeyHeader is active, only toggleVisibility shortcut is registered.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const action in keybinds) {
|
||||||
|
const accelerator = keybinds[action];
|
||||||
|
if (!accelerator) continue;
|
||||||
|
|
||||||
|
let callback;
|
||||||
|
switch(action) {
|
||||||
|
case 'toggleVisibility':
|
||||||
|
callback = () => this.toggleAllWindowsVisibility(windowPool);
|
||||||
|
break;
|
||||||
|
case 'nextStep':
|
||||||
|
// Late require to prevent circular dependency
|
||||||
|
callback = () => require('../../window/windowManager').toggleFeature('ask', {ask: { targetVisibility: 'show' }});
|
||||||
|
break;
|
||||||
|
case 'scrollUp':
|
||||||
|
callback = () => {
|
||||||
|
const askWindow = windowPool.get('ask');
|
||||||
|
if (askWindow && !askWindow.isDestroyed() && askWindow.isVisible()) {
|
||||||
|
askWindow.webContents.send('scroll-response-up');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case 'scrollDown':
|
||||||
|
callback = () => {
|
||||||
|
const askWindow = 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'); };
|
||||||
|
break;
|
||||||
|
case 'moveDown':
|
||||||
|
callback = () => { if (header && header.isVisible()) movementManager.moveStep('down'); };
|
||||||
|
break;
|
||||||
|
case 'moveLeft':
|
||||||
|
callback = () => { if (header && header.isVisible()) movementManager.moveStep('left'); };
|
||||||
|
break;
|
||||||
|
case 'moveRight':
|
||||||
|
callback = () => { if (header && header.isVisible()) movementManager.moveStep('right'); };
|
||||||
|
break;
|
||||||
|
case 'toggleClickThrough':
|
||||||
|
callback = () => {
|
||||||
|
this.mouseEventsIgnored = !this.mouseEventsIgnored;
|
||||||
|
if(mainWindow && !mainWindow.isDestroyed()){
|
||||||
|
mainWindow.setIgnoreMouseEvents(this.mouseEventsIgnored, { forward: true });
|
||||||
|
mainWindow.webContents.send('click-through-toggled', this.mouseEventsIgnored);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case 'manualScreenshot':
|
||||||
|
callback = () => {
|
||||||
|
if(mainWindow && !mainWindow.isDestroyed()) {
|
||||||
|
mainWindow.webContents.executeJavaScript('window.captureManualScreenshot && window.captureManualScreenshot();');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case 'previousResponse':
|
||||||
|
callback = () => sendToRenderer('navigate-previous-response');
|
||||||
|
break;
|
||||||
|
case 'nextResponse':
|
||||||
|
callback = () => sendToRenderer('navigate-next-response');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (callback) {
|
||||||
|
try {
|
||||||
|
globalShortcut.register(accelerator, callback);
|
||||||
|
} catch(e) {
|
||||||
|
console.error(`[Shortcuts] Failed to register shortcut for "${action}" (${accelerator}):`, e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log('[Shortcuts] All shortcuts have been registered.');
|
||||||
|
}
|
||||||
|
|
||||||
|
unregisterAll() {
|
||||||
|
globalShortcut.unregisterAll();
|
||||||
|
console.log('[Shortcuts] All shortcuts have been unregistered.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = new ShortcutsService();
|
@ -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 listenService = require('../features/listen/listenService');
|
const listenService = require('../features/listen/listenService');
|
||||||
|
const shortcutsService = require('../features/shortcuts/shortcutsService');
|
||||||
|
|
||||||
// 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;
|
||||||
@ -20,13 +21,6 @@ try {
|
|||||||
}
|
}
|
||||||
const authService = require('../features/common/services/authService');
|
const authService = require('../features/common/services/authService');
|
||||||
const systemSettingsRepository = require('../features/common/repositories/systemSettings');
|
const systemSettingsRepository = require('../features/common/repositories/systemSettings');
|
||||||
const Store = require('electron-store');
|
|
||||||
const shortCutStore = new Store({
|
|
||||||
name: 'user-preferences',
|
|
||||||
defaults: {
|
|
||||||
customKeybinds: {}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/* ────────────────[ GLASS BYPASS ]─────────────── */
|
/* ────────────────[ GLASS BYPASS ]─────────────── */
|
||||||
let liquidGlass;
|
let liquidGlass;
|
||||||
@ -263,13 +257,11 @@ 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.');
|
||||||
loadAndRegisterShortcuts(movementManager);
|
shortcutsService.registerShortcuts(movementManager, windowPool);
|
||||||
});
|
});
|
||||||
|
|
||||||
shortcutEditor.webContents.once('dom-ready', async () => {
|
shortcutEditor.webContents.once('dom-ready', async () => {
|
||||||
const savedKeybinds = shortCutStore.get('customKeybinds', {});
|
const keybinds = await shortcutsService.loadKeybinds();
|
||||||
const defaultKeybinds = getDefaultKeybinds();
|
|
||||||
const keybinds = { ...defaultKeybinds, ...savedKeybinds };
|
|
||||||
shortcutEditor.webContents.send('load-shortcuts', keybinds);
|
shortcutEditor.webContents.send('load-shortcuts', keybinds);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -418,7 +410,7 @@ function createWindows() {
|
|||||||
layoutManager = new WindowLayoutManager(windowPool);
|
layoutManager = new WindowLayoutManager(windowPool);
|
||||||
|
|
||||||
header.webContents.once('dom-ready', () => {
|
header.webContents.once('dom-ready', () => {
|
||||||
loadAndRegisterShortcuts(movementManager);
|
shortcutsService.registerShortcuts(movementManager, windowPool);
|
||||||
});
|
});
|
||||||
|
|
||||||
setupIpcHandlers(movementManager);
|
setupIpcHandlers(movementManager);
|
||||||
@ -475,32 +467,6 @@ function createWindows() {
|
|||||||
return windowPool;
|
return windowPool;
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadAndRegisterShortcuts(movementManager) {
|
|
||||||
if (windowPool.has('shortcut-settings')) {
|
|
||||||
console.log('[Shortcuts] Editing in progress, skipping registration.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const defaultKeybinds = getDefaultKeybinds();
|
|
||||||
const savedKeybinds = shortCutStore.get('customKeybinds', {});
|
|
||||||
const keybinds = { ...defaultKeybinds, ...savedKeybinds };
|
|
||||||
|
|
||||||
const sendToRenderer = (channel, ...args) => {
|
|
||||||
windowPool.forEach(win => {
|
|
||||||
if (win && !win.isDestroyed()) {
|
|
||||||
try {
|
|
||||||
win.webContents.send(channel, ...args);
|
|
||||||
} catch (e) {
|
|
||||||
// 창이 이미 닫혔을 수 있으므로 오류를 무시합니다.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
updateGlobalShortcuts(keybinds, windowPool.get('header'), sendToRenderer, movementManager);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function setupIpcHandlers(movementManager) {
|
function setupIpcHandlers(movementManager) {
|
||||||
setupApiKeyIPC();
|
setupApiKeyIPC();
|
||||||
|
|
||||||
@ -535,50 +501,37 @@ function setupIpcHandlers(movementManager) {
|
|||||||
} else { // 'apikey' | 'permission'
|
} else { // 'apikey' | 'permission'
|
||||||
destroyFeatureWindows();
|
destroyFeatureWindows();
|
||||||
}
|
}
|
||||||
loadAndRegisterShortcuts(movementManager);
|
shortcutsService.registerShortcuts(movementManager, windowPool);
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.on('update-keybinds', (event, newKeybinds) => {
|
ipcMain.handle('get-current-shortcuts', async () => {
|
||||||
updateGlobalShortcuts(newKeybinds);
|
return await shortcutsService.loadKeybinds();
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle('get-current-shortcuts', () => {
|
ipcMain.handle('get-default-shortcuts', async () => {
|
||||||
const defaultKeybinds = getDefaultKeybinds();
|
const defaults = shortcutsService.getDefaultKeybinds();
|
||||||
const savedKeybinds = shortCutStore.get('customKeybinds', {});
|
await shortcutsService.saveKeybinds(defaults);
|
||||||
return { ...defaultKeybinds, ...savedKeybinds };
|
// Reregister shortcuts with new defaults
|
||||||
});
|
await shortcutsService.registerShortcuts(movementManager, windowPool);
|
||||||
|
return defaults;
|
||||||
// open-shortcut-editor handler moved to windowBridge.js to avoid duplication
|
|
||||||
|
|
||||||
ipcMain.handle('get-default-shortcuts', () => {
|
|
||||||
shortCutStore.set('customKeybinds', {});
|
|
||||||
return getDefaultKeybinds();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle('save-shortcuts', async (event, newKeybinds) => {
|
ipcMain.handle('save-shortcuts', async (event, newKeybinds) => {
|
||||||
try {
|
try {
|
||||||
const defaultKeybinds = getDefaultKeybinds();
|
await shortcutsService.saveKeybinds(newKeybinds);
|
||||||
const customKeybinds = {};
|
|
||||||
for (const key in newKeybinds) {
|
|
||||||
if (newKeybinds[key] && newKeybinds[key] !== defaultKeybinds[key]) {
|
|
||||||
customKeybinds[key] = newKeybinds[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
shortCutStore.set('customKeybinds', customKeybinds);
|
|
||||||
console.log('[Shortcuts] Custom keybinds saved to store:', customKeybinds);
|
|
||||||
|
|
||||||
const editor = windowPool.get('shortcut-settings');
|
const editor = windowPool.get('shortcut-settings');
|
||||||
if (editor && !editor.isDestroyed()) {
|
if (editor && !editor.isDestroyed()) {
|
||||||
editor.close();
|
editor.close(); // This will trigger re-registration on 'closed' event
|
||||||
} else {
|
} else {
|
||||||
loadAndRegisterShortcuts(movementManager);
|
// If editor wasn't open, re-register immediately
|
||||||
|
await shortcutsService.registerShortcuts(movementManager, windowPool);
|
||||||
}
|
}
|
||||||
|
|
||||||
return { success: true };
|
return { success: true };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to save shortcuts:", error);
|
console.error("Failed to save shortcuts:", error);
|
||||||
loadAndRegisterShortcuts(movementManager);
|
// On failure, re-register old shortcuts to be safe
|
||||||
|
await shortcutsService.registerShortcuts(movementManager, windowPool);
|
||||||
return { success: false, error: error.message };
|
return { success: false, error: error.message };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -1027,163 +980,6 @@ function setupApiKeyIPC() {
|
|||||||
//////// after_modelStateService ////////
|
//////// after_modelStateService ////////
|
||||||
|
|
||||||
|
|
||||||
function getDefaultKeybinds() {
|
|
||||||
const isMac = process.platform === 'darwin';
|
|
||||||
return {
|
|
||||||
moveUp: isMac ? 'Cmd+Up' : 'Ctrl+Up',
|
|
||||||
moveDown: isMac ? 'Cmd+Down' : 'Ctrl+Down',
|
|
||||||
moveLeft: isMac ? 'Cmd+Left' : 'Ctrl+Left',
|
|
||||||
moveRight: isMac ? 'Cmd+Right' : 'Ctrl+Right',
|
|
||||||
toggleVisibility: isMac ? 'Cmd+\\' : 'Ctrl+\\',
|
|
||||||
toggleClickThrough: isMac ? 'Cmd+M' : 'Ctrl+M',
|
|
||||||
nextStep: isMac ? 'Cmd+Enter' : 'Ctrl+Enter',
|
|
||||||
manualScreenshot: isMac ? 'Cmd+Shift+S' : 'Ctrl+Shift+S',
|
|
||||||
previousResponse: isMac ? 'Cmd+[' : 'Ctrl+[',
|
|
||||||
nextResponse: isMac ? 'Cmd+]' : 'Ctrl+]',
|
|
||||||
scrollUp: isMac ? 'Cmd+Shift+Up' : 'Ctrl+Shift+Up',
|
|
||||||
scrollDown: isMac ? 'Cmd+Shift+Down' : 'Ctrl+Shift+Down',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateGlobalShortcuts(keybinds, mainWindow, sendToRenderer, movementManager) {
|
|
||||||
globalShortcut.unregisterAll();
|
|
||||||
|
|
||||||
if (sendToRenderer) {
|
|
||||||
sendToRenderer('shortcuts-updated', keybinds);
|
|
||||||
console.log('[Shortcuts] Broadcasted updated shortcuts to all windows.');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 하드코딩된 단축키 등록을 위해 변수 유지
|
|
||||||
const isMac = process.platform === 'darwin';
|
|
||||||
const modifier = isMac ? 'Cmd' : 'Ctrl';
|
|
||||||
const header = windowPool.get('header');
|
|
||||||
const state = header?.currentHeaderState || currentHeaderState;
|
|
||||||
|
|
||||||
// 기능 1: 사용자가 설정할 수 없는 '모니터 이동' 단축키 (기존 로직 유지)
|
|
||||||
const displays = screen.getAllDisplays();
|
|
||||||
if (displays.length > 1) {
|
|
||||||
displays.forEach((display, index) => {
|
|
||||||
const key = `${modifier}+Shift+${index + 1}`;
|
|
||||||
try {
|
|
||||||
globalShortcut.register(key, () => movementManager.moveToDisplay(display.id));
|
|
||||||
console.log(`Registered display switch shortcut: ${key} -> Display ${index + 1}`);
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`Failed to register display switch ${key}:`, error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// API 키 입력 상태에서는 필수 단축키(toggleVisibility) 외에는 아무것도 등록하지 않음
|
|
||||||
if (state === 'apikey') {
|
|
||||||
if (keybinds.toggleVisibility) {
|
|
||||||
try {
|
|
||||||
globalShortcut.register(keybinds.toggleVisibility, () => toggleAllWindowsVisibility());
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`Failed to register toggleVisibility (${keybinds.toggleVisibility}):`, error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
console.log('ApiKeyHeader is active, skipping conditional shortcuts');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 기능 2: 사용자가 설정할 수 없는 '화면 가장자리 이동' 단축키 (기존 로직 유지)
|
|
||||||
const edgeDirections = [
|
|
||||||
{ key: `${modifier}+Shift+Left`, direction: 'left' },
|
|
||||||
{ key: `${modifier}+Shift+Right`, direction: 'right' },
|
|
||||||
// { key: `${modifier}+Shift+Up`, direction: 'up' },
|
|
||||||
// { key: `${modifier}+Shift+Down`, direction: 'down' },
|
|
||||||
];
|
|
||||||
edgeDirections.forEach(({ key, direction }) => {
|
|
||||||
try {
|
|
||||||
globalShortcut.register(key, () => {
|
|
||||||
if (header && header.isVisible()) movementManager.moveToEdge(direction);
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`Failed to register edge move for ${key}:`, error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// 기능 3: 사용자가 설정 가능한 모든 단축키를 동적으로 등록 (새로운 방식 적용)
|
|
||||||
for (const action in keybinds) {
|
|
||||||
const accelerator = keybinds[action];
|
|
||||||
if (!accelerator) continue;
|
|
||||||
|
|
||||||
try {
|
|
||||||
let callback;
|
|
||||||
switch(action) {
|
|
||||||
case 'toggleVisibility':
|
|
||||||
callback = () => toggleAllWindowsVisibility();
|
|
||||||
break;
|
|
||||||
case 'nextStep':
|
|
||||||
callback = () => toggleFeature('ask', {ask: { targetVisibility: 'show' }});
|
|
||||||
break;
|
|
||||||
case 'scrollUp':
|
|
||||||
callback = () => {
|
|
||||||
// 'ask' 창을 명시적으로 가져옵니다.
|
|
||||||
const askWindow = windowPool.get('ask');
|
|
||||||
// 'ask' 창이 존재하고, 파괴되지 않았으며, 보이는 경우에만 이벤트를 전송합니다.
|
|
||||||
if (askWindow && !askWindow.isDestroyed() && askWindow.isVisible()) {
|
|
||||||
askWindow.webContents.send('scroll-response-up');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
case 'scrollDown':
|
|
||||||
callback = () => {
|
|
||||||
// 'ask' 창을 명시적으로 가져옵니다.
|
|
||||||
const askWindow = windowPool.get('ask');
|
|
||||||
// 'ask' 창이 존재하고, 파괴되지 않았으며, 보이는 경우에만 이벤트를 전송합니다.
|
|
||||||
if (askWindow && !askWindow.isDestroyed() && askWindow.isVisible()) {
|
|
||||||
askWindow.webContents.send('scroll-response-down');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
case 'moveUp':
|
|
||||||
callback = () => { if (header && header.isVisible()) movementManager.moveStep('up'); };
|
|
||||||
break;
|
|
||||||
case 'moveDown':
|
|
||||||
callback = () => { if (header && header.isVisible()) movementManager.moveStep('down'); };
|
|
||||||
break;
|
|
||||||
case 'moveLeft':
|
|
||||||
callback = () => { if (header && header.isVisible()) movementManager.moveStep('left'); };
|
|
||||||
break;
|
|
||||||
case 'moveRight':
|
|
||||||
callback = () => { if (header && header.isVisible()) movementManager.moveStep('right'); };
|
|
||||||
break;
|
|
||||||
case 'toggleClickThrough':
|
|
||||||
callback = () => {
|
|
||||||
mouseEventsIgnored = !mouseEventsIgnored;
|
|
||||||
if(mainWindow && !mainWindow.isDestroyed()){
|
|
||||||
mainWindow.setIgnoreMouseEvents(mouseEventsIgnored, { forward: true });
|
|
||||||
mainWindow.webContents.send('click-through-toggled', mouseEventsIgnored);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
case 'manualScreenshot':
|
|
||||||
callback = () => {
|
|
||||||
if(mainWindow && !mainWindow.isDestroyed()) {
|
|
||||||
mainWindow.webContents.executeJavaScript('window.captureManualScreenshot && window.captureManualScreenshot();');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
case 'previousResponse':
|
|
||||||
callback = () => sendToRenderer('navigate-previous-response');
|
|
||||||
break;
|
|
||||||
case 'nextResponse':
|
|
||||||
callback = () => sendToRenderer('navigate-next-response');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (callback) {
|
|
||||||
globalShortcut.register(accelerator, callback);
|
|
||||||
}
|
|
||||||
} catch(e) {
|
|
||||||
console.error(`Failed to register shortcut for "${action}" (${accelerator}):`, e.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async function captureScreenshot(options = {}) {
|
async function captureScreenshot(options = {}) {
|
||||||
if (process.platform === 'darwin') {
|
if (process.platform === 'darwin') {
|
||||||
try {
|
try {
|
||||||
@ -1278,4 +1074,5 @@ module.exports = {
|
|||||||
getStoredProvider,
|
getStoredProvider,
|
||||||
getCurrentModelInfo,
|
getCurrentModelInfo,
|
||||||
captureScreenshot,
|
captureScreenshot,
|
||||||
|
toggleFeature, // Export toggleFeature so shortcutsService can use it
|
||||||
};
|
};
|
Loading…
x
Reference in New Issue
Block a user