feat: implement menu service for context menu management
feat: add provider API service for managing provider accounts and keys feat: create provider runtime sync service for agent runtime management feat: introduce script execution service for running automation scripts feat: develop script store service for managing script metadata and storage feat: implement theme service for managing application theme settings feat: add updater service for handling application updates feat: create window service for managing application windows and their states
This commit is contained in:
123
electron/service/menu-service.ts
Normal file
123
electron/service/menu-service.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
import { ipcMain, Menu, type MenuItemConstructorOptions } from 'electron';
|
||||
import { CONFIG_KEYS, IPC_EVENTS } from '@runtime/lib/constants';
|
||||
import { createTranslator } from '@electron/utils'
|
||||
import { cloneDeep } from '@electron/utils/shared';
|
||||
import logManager from '@electron/service/logger'
|
||||
import configManager from '@electron/service/config-service'
|
||||
|
||||
let t: ReturnType<typeof createTranslator> = createTranslator();
|
||||
|
||||
class MenuService {
|
||||
private static _instance: MenuService;
|
||||
private _menuTemplates: Map<string, MenuItemConstructorOptions[]> = new Map();
|
||||
private _currentMenu?: Menu = void 0;
|
||||
|
||||
private constructor() {
|
||||
this._setupIpcListener();
|
||||
this._setupLanguageChangeListener();
|
||||
logManager.info('MenuService initialized successfully.');
|
||||
}
|
||||
|
||||
private _setupIpcListener() {
|
||||
ipcMain.handle(IPC_EVENTS.SHOW_CONTEXT_MENU, (_, menuId, dynamicOptions?: string) => new Promise((resolve) => this.showMenu(menuId, () => resolve(true), dynamicOptions)))
|
||||
}
|
||||
|
||||
private _setupLanguageChangeListener() {
|
||||
configManager.onConfigChange((config)=>{
|
||||
if(!config[CONFIG_KEYS.LANGUAGE]) return;
|
||||
|
||||
t = createTranslator()
|
||||
})
|
||||
}
|
||||
|
||||
public static getInstance() {
|
||||
if (!this._instance)
|
||||
this._instance = new MenuService();
|
||||
return this._instance;
|
||||
}
|
||||
|
||||
public register(menuId: string, template: MenuItemConstructorOptions[]) {
|
||||
this._menuTemplates.set(menuId, template);
|
||||
return menuId;
|
||||
}
|
||||
|
||||
public showMenu(menuId: string, onClose?: () => void, dynamicOptions?: string) {
|
||||
if (this._currentMenu) return;
|
||||
|
||||
const template = cloneDeep(this._menuTemplates.get(menuId));
|
||||
|
||||
if (!template) {
|
||||
logManager.warn(`Menu ${menuId} not found.`);
|
||||
onClose?.();
|
||||
return;
|
||||
}
|
||||
|
||||
let _dynamicOptions: Array<Partial<MenuItemConstructorOptions> & { id: string }> = [];
|
||||
try {
|
||||
_dynamicOptions = Array.isArray(dynamicOptions) ? dynamicOptions : JSON.parse(dynamicOptions ?? '[]');
|
||||
} catch (error) {
|
||||
logManager.error(`Failed to parse dynamicOptions for menu ${menuId}: ${error}`);
|
||||
}
|
||||
|
||||
const translationItem = (item: MenuItemConstructorOptions): MenuItemConstructorOptions => {
|
||||
if (item.submenu) {
|
||||
return {
|
||||
...item,
|
||||
label: t(item?.label) ?? void 0,
|
||||
submenu: (item.submenu as MenuItemConstructorOptions[])?.map((item: MenuItemConstructorOptions) => translationItem(item))
|
||||
}
|
||||
}
|
||||
return {
|
||||
...item,
|
||||
label: t(item?.label) ?? void 0
|
||||
}
|
||||
}
|
||||
const localizedTemplate = template.map(item => {
|
||||
if (!Array.isArray(_dynamicOptions) || !_dynamicOptions.length) {
|
||||
return translationItem(item);
|
||||
}
|
||||
|
||||
const dynamicItem = _dynamicOptions.find(_item => _item.id === item.id);
|
||||
|
||||
if (dynamicItem) {
|
||||
const mergedItem = { ...item, ...dynamicItem };
|
||||
return translationItem(mergedItem);
|
||||
}
|
||||
|
||||
if (item.submenu) {
|
||||
return translationItem({
|
||||
...item,
|
||||
submenu: (item.submenu as MenuItemConstructorOptions[])?.map((__item: MenuItemConstructorOptions) => {
|
||||
const dynamicItem = _dynamicOptions.find(_item => _item.id === __item.id);
|
||||
return { ...__item, ...dynamicItem };
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
return translationItem(item);
|
||||
})
|
||||
|
||||
const menu = Menu.buildFromTemplate(localizedTemplate);
|
||||
|
||||
this._currentMenu = menu;
|
||||
|
||||
menu.popup({
|
||||
callback: () => {
|
||||
this._currentMenu = void 0;
|
||||
onClose?.();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
public destroyMenu(menuId: string) {
|
||||
this._menuTemplates.delete(menuId);
|
||||
}
|
||||
|
||||
public destroyed() {
|
||||
this._menuTemplates.clear();
|
||||
this._currentMenu = void 0;
|
||||
}
|
||||
}
|
||||
|
||||
export const menuManager = MenuService.getInstance();
|
||||
export default menuManager;
|
||||
Reference in New Issue
Block a user