mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-25 19:30:17 +08:00
* feat: add toggle selection assistant functionality and corresponding shortcuts - Implemented toggleEnabled method in SelectionService to manage the selection assistant state. - Registered new shortcut for toggling the selection assistant in ShortcutService. - Updated StoreSyncService to sync the selection assistant state across renderer windows. - Added localization for the toggle selection assistant feature in multiple languages. - Adjusted ShortcutSettings to conditionally display the toggle selection assistant shortcut based on the platform. - Included toggle selection assistant in the initial state of shortcuts in the store. * fix: shortcut key * fix: accelerator name
134 lines
4.0 KiB
TypeScript
134 lines
4.0 KiB
TypeScript
import { IpcChannel } from '@shared/IpcChannel'
|
|
import type { StoreSyncAction } from '@types'
|
|
import { BrowserWindow, ipcMain } from 'electron'
|
|
|
|
/**
|
|
* StoreSyncService class manages Redux store synchronization between multiple windows in the main process
|
|
* It uses singleton pattern to ensure only one sync service instance exists in the application
|
|
*
|
|
* Main features:
|
|
* 1. Manages window subscriptions for store sync
|
|
* 2. Handles IPC communication for store sync between windows
|
|
* 3. Broadcasts Redux actions from one window to all other windows
|
|
* 4. Adds metadata to synced actions to prevent infinite sync loops
|
|
*/
|
|
export class StoreSyncService {
|
|
private static instance: StoreSyncService
|
|
private windowIds: number[] = []
|
|
private isIpcHandlerRegistered = false
|
|
|
|
private constructor() {
|
|
return
|
|
}
|
|
|
|
/**
|
|
* Get the singleton instance of StoreSyncService
|
|
*/
|
|
public static getInstance(): StoreSyncService {
|
|
if (!StoreSyncService.instance) {
|
|
StoreSyncService.instance = new StoreSyncService()
|
|
}
|
|
return StoreSyncService.instance
|
|
}
|
|
|
|
/**
|
|
* Subscribe a window to store sync
|
|
* @param windowId ID of the window to subscribe
|
|
*/
|
|
public subscribe(windowId: number): void {
|
|
if (!this.windowIds.includes(windowId)) {
|
|
this.windowIds.push(windowId)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Unsubscribe a window from store sync
|
|
* @param windowId ID of the window to unsubscribe
|
|
*/
|
|
public unsubscribe(windowId: number): void {
|
|
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
|
|
*/
|
|
public registerIpcHandler(): void {
|
|
if (this.isIpcHandlerRegistered) return
|
|
|
|
ipcMain.handle(IpcChannel.StoreSync_Subscribe, (event) => {
|
|
const windowId = BrowserWindow.fromWebContents(event.sender)?.id
|
|
if (windowId) {
|
|
this.subscribe(windowId)
|
|
}
|
|
})
|
|
|
|
ipcMain.handle(IpcChannel.StoreSync_Unsubscribe, (event) => {
|
|
const windowId = BrowserWindow.fromWebContents(event.sender)?.id
|
|
if (windowId) {
|
|
this.unsubscribe(windowId)
|
|
}
|
|
})
|
|
|
|
ipcMain.handle(IpcChannel.StoreSync_OnUpdate, (event, action: StoreSyncAction) => {
|
|
const sourceWindowId = BrowserWindow.fromWebContents(event.sender)?.id
|
|
|
|
if (!sourceWindowId) return
|
|
|
|
// Broadcast the action to all other windows
|
|
this.broadcastToOtherWindows(sourceWindowId, action)
|
|
})
|
|
|
|
this.isIpcHandlerRegistered = true
|
|
}
|
|
|
|
/**
|
|
* Broadcast a Redux action to all other windows except the source
|
|
* @param sourceWindowId ID of the window that originated the action
|
|
* @param action Redux action to broadcast
|
|
*/
|
|
private broadcastToOtherWindows(sourceWindowId: number, action: StoreSyncAction): void {
|
|
// Add metadata to indicate this action came from sync
|
|
const syncAction = {
|
|
...action,
|
|
meta: {
|
|
...action.meta,
|
|
fromSync: true,
|
|
source: `windowId:${sourceWindowId}`
|
|
}
|
|
}
|
|
|
|
// Send to all windows except the source
|
|
this.windowIds.forEach((windowId) => {
|
|
if (windowId !== sourceWindowId) {
|
|
const targetWindow = BrowserWindow.fromId(windowId)
|
|
if (targetWindow && !targetWindow.isDestroyed()) {
|
|
targetWindow.webContents.send(IpcChannel.StoreSync_BroadcastSync, syncAction)
|
|
} else {
|
|
this.unsubscribe(windowId)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// Export singleton instance
|
|
export default StoreSyncService.getInstance()
|