diff --git a/src/main/services/SelectionService.ts b/src/main/services/SelectionService.ts index 16aaac6890..49ed189055 100644 --- a/src/main/services/SelectionService.ts +++ b/src/main/services/SelectionService.ts @@ -14,6 +14,7 @@ import type { import type { ActionItem } from '../../renderer/src/types/selectionTypes' import { ConfigKeys, configManager } from './ConfigManager' +import storeSyncService from './StoreSyncService' let SelectionHook: SelectionHookConstructor | null = null try { @@ -334,6 +335,20 @@ export class SelectionService { this.logInfo('SelectionService Quitted') } + /** + * Toggle the enabled state of the selection service + * Will sync the new enabled store to all renderer windows + */ + public toggleEnabled(enabled: boolean | undefined = undefined) { + if (!this.selectionHook) return + + const newEnabled = enabled === undefined ? !configManager.getSelectionAssistantEnabled() : enabled + + configManager.setSelectionAssistantEnabled(newEnabled) + + //sync the new enabled state to all renderer windows + storeSyncService.syncToRenderer('selectionStore/setSelectionEnabled', newEnabled) + } /** * Create and configure the toolbar window * Sets up window properties, event handlers, and loads the toolbar UI diff --git a/src/main/services/ShortcutService.ts b/src/main/services/ShortcutService.ts index d69c80b325..0bf696d4bf 100644 --- a/src/main/services/ShortcutService.ts +++ b/src/main/services/ShortcutService.ts @@ -4,10 +4,15 @@ import { BrowserWindow, globalShortcut } from 'electron' import Logger from 'electron-log' import { configManager } from './ConfigManager' +import selectionService from './SelectionService' import { windowService } from './WindowService' let showAppAccelerator: string | null = null let showMiniWindowAccelerator: string | null = null +let selectionAssistantToggleAccelerator: string | null = null + +//indicate if the shortcuts are registered on app boot time +let isRegisterOnBoot = true // store the focus and blur handlers for each window to unregister them later const windowOnHandlers = new Map void; onBlurHandler: () => void }>() @@ -28,6 +33,12 @@ function getShortcutHandler(shortcut: Shortcut) { return () => { windowService.toggleMiniWindow() } + case 'selection_assistant_toggle': + return () => { + if (selectionService) { + selectionService.toggleEnabled() + } + } default: return null } @@ -93,11 +104,14 @@ const convertShortcutRecordedByKeyboardEventKeyValueToElectronGlobalShortcutForm } export function registerShortcuts(window: BrowserWindow) { - window.once('ready-to-show', () => { - if (configManager.getLaunchToTray()) { - registerOnlyUniversalShortcuts() - } - }) + if (isRegisterOnBoot) { + window.once('ready-to-show', () => { + if (configManager.getLaunchToTray()) { + registerOnlyUniversalShortcuts() + } + }) + isRegisterOnBoot = false + } //only for clearer code const registerOnlyUniversalShortcuts = () => { @@ -124,7 +138,10 @@ export function registerShortcuts(window: BrowserWindow) { } // only register universal shortcuts when needed - if (onlyUniversalShortcuts && !['show_app', 'mini_window'].includes(shortcut.key)) { + if ( + onlyUniversalShortcuts && + !['show_app', 'mini_window', 'selection_assistant_toggle'].includes(shortcut.key) + ) { return } @@ -146,6 +163,10 @@ export function registerShortcuts(window: BrowserWindow) { showMiniWindowAccelerator = formatShortcutKey(shortcut.shortcut) break + case 'selection_assistant_toggle': + selectionAssistantToggleAccelerator = formatShortcutKey(shortcut.shortcut) + break + //the following ZOOMs will register shortcuts seperately, so will return case 'zoom_in': globalShortcut.register('CommandOrControl+=', () => handler(window)) @@ -192,6 +213,14 @@ export function registerShortcuts(window: BrowserWindow) { convertShortcutRecordedByKeyboardEventKeyValueToElectronGlobalShortcutFormat(showMiniWindowAccelerator) handler && globalShortcut.register(accelerator, () => handler(window)) } + + if (selectionAssistantToggleAccelerator) { + const handler = getShortcutHandler({ key: 'selection_assistant_toggle' } as Shortcut) + const accelerator = convertShortcutRecordedByKeyboardEventKeyValueToElectronGlobalShortcutFormat( + selectionAssistantToggleAccelerator + ) + handler && globalShortcut.register(accelerator, () => handler(window)) + } } catch (error) { Logger.error('[ShortcutService] Failed to unregister shortcuts') } @@ -217,6 +246,7 @@ export function unregisterAllShortcuts() { try { showAppAccelerator = null showMiniWindowAccelerator = null + selectionAssistantToggleAccelerator = null windowOnHandlers.forEach((handlers, window) => { window.off('focus', handlers.onFocusHandler) window.off('blur', handlers.onBlurHandler) diff --git a/src/main/services/StoreSyncService.ts b/src/main/services/StoreSyncService.ts index 84d84d1ad4..57f07195b6 100644 --- a/src/main/services/StoreSyncService.ts +++ b/src/main/services/StoreSyncService.ts @@ -49,6 +49,23 @@ export class StoreSyncService { this.windowIds = this.windowIds.filter((id) => id !== windowId) } + /** + * Sync an action to all renderer windows + * @param type Action type, like 'settings/setTray' + * @param payload Action payload + * + * NOTICE: DO NOT use directly in ConfigManager, may cause infinite sync loop + */ + public syncToRenderer(type: string, payload: any): void { + const action: StoreSyncAction = { + type, + payload + } + + //-1 means the action is from the main process, will be broadcast to all windows + this.broadcastToOtherWindows(-1, action) + } + /** * Register IPC handlers for store sync communication * Handles window subscription, unsubscription and action broadcasting diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json index fc20b5ab7d..9a8d70a3ea 100644 --- a/src/renderer/src/i18n/locales/en-us.json +++ b/src/renderer/src/i18n/locales/en-us.json @@ -1701,6 +1701,7 @@ "exit_fullscreen": "Exit Fullscreen", "key": "Key", "mini_window": "Quick Assistant", + "selection_assistant_toggle": "Toggle Selection Assistant", "new_topic": "New Topic", "press_shortcut": "Press Shortcut", "reset_defaults": "Reset Defaults", diff --git a/src/renderer/src/i18n/locales/ja-jp.json b/src/renderer/src/i18n/locales/ja-jp.json index 576205e028..108bc5b49f 100644 --- a/src/renderer/src/i18n/locales/ja-jp.json +++ b/src/renderer/src/i18n/locales/ja-jp.json @@ -1689,6 +1689,7 @@ "exit_fullscreen": "フルスクリーンを終了", "key": "キー", "mini_window": "クイックアシスタント", + "selection_assistant_toggle": "選択アシスタントを切り替え", "new_topic": "新しいトピック", "press_shortcut": "ショートカットを押す", "reset_defaults": "デフォルトのショートカットをリセット", diff --git a/src/renderer/src/i18n/locales/ru-ru.json b/src/renderer/src/i18n/locales/ru-ru.json index 024718b008..e469c89592 100644 --- a/src/renderer/src/i18n/locales/ru-ru.json +++ b/src/renderer/src/i18n/locales/ru-ru.json @@ -1689,6 +1689,7 @@ "exit_fullscreen": "Выйти из полноэкранного режима", "key": "Клавиша", "mini_window": "Быстрый помощник", + "selection_assistant_toggle": "Переключить помощник выделения", "new_topic": "Новый топик", "press_shortcut": "Нажмите сочетание клавиш", "reset_defaults": "Сбросить настройки по умолчанию", diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json index 4edad4311c..548089e76e 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -1701,6 +1701,7 @@ "exit_fullscreen": "退出全屏", "key": "按键", "mini_window": "快捷助手", + "selection_assistant_toggle": "开关划词助手", "new_topic": "新建话题", "press_shortcut": "按下快捷键", "reset_defaults": "重置默认快捷键", diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index ac7334089b..080cef0fad 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -1691,6 +1691,7 @@ "copy_last_message": "複製上一則訊息", "key": "按鍵", "mini_window": "快捷助手", + "selection_assistant_toggle": "開關劃詞助手", "new_topic": "新增話題", "press_shortcut": "按下快捷鍵", "reset_defaults": "重設預設快捷鍵", diff --git a/src/renderer/src/pages/settings/ShortcutSettings.tsx b/src/renderer/src/pages/settings/ShortcutSettings.tsx index d392fee3eb..4d4cb3321f 100644 --- a/src/renderer/src/pages/settings/ShortcutSettings.tsx +++ b/src/renderer/src/pages/settings/ShortcutSettings.tsx @@ -18,10 +18,17 @@ const ShortcutSettings: FC = () => { const { t } = useTranslation() const { theme } = useTheme() const dispatch = useAppDispatch() - const { shortcuts } = useShortcuts() + const { shortcuts: originalShortcuts } = useShortcuts() const inputRefs = useRef>({}) const [editingKey, setEditingKey] = useState(null) + //TODO: if shortcut is not available on all the platforms, block the shortcut here + let shortcuts = originalShortcuts + if (!isWindows) { + //Selection Assistant only available on Windows now + shortcuts = shortcuts.filter((s) => s.key !== 'selection_assistant_toggle') + } + const handleClear = (record: Shortcut) => { dispatch( updateShortcut({ diff --git a/src/renderer/src/store/migrate.ts b/src/renderer/src/store/migrate.ts index f792383afd..9086a03b98 100644 --- a/src/renderer/src/store/migrate.ts +++ b/src/renderer/src/store/migrate.ts @@ -1508,6 +1508,24 @@ const migrateConfig = { state.llm.translateModel = SYSTEM_MODELS.defaultModel[2] } + // Add selection_assistant_toggle shortcut after mini_window if it doesn't exist + if (state.shortcuts) { + const miniWindowIndex = state.shortcuts.shortcuts.findIndex((shortcut) => shortcut.key === 'mini_window') + const hasSelectionAssistant = state.shortcuts.shortcuts.some( + (shortcut) => shortcut.key === 'selection_assistant_toggle' + ) + + if (miniWindowIndex !== -1 && !hasSelectionAssistant) { + state.shortcuts.shortcuts.splice(miniWindowIndex + 1, 0, { + key: 'selection_assistant_toggle', + shortcut: [], + editable: true, + enabled: false, + system: true + }) + } + } + return state } catch (error) { return state diff --git a/src/renderer/src/store/shortcuts.ts b/src/renderer/src/store/shortcuts.ts index 93080f4f3d..c0c110f5a4 100644 --- a/src/renderer/src/store/shortcuts.ts +++ b/src/renderer/src/store/shortcuts.ts @@ -31,6 +31,14 @@ const initialState: ShortcutsState = { enabled: false, system: true }, + { + //enable/disable selection assistant + key: 'selection_assistant_toggle', + shortcut: [], + editable: true, + enabled: false, + system: true + }, { key: 'new_topic', shortcut: [isMac ? 'Command' : 'Ctrl', 'N'], @@ -45,6 +53,7 @@ const initialState: ShortcutsState = { enabled: true, system: false }, + { key: 'toggle_show_topics', shortcut: [isMac ? 'Command' : 'Ctrl', ']'],