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
124 lines
3.7 KiB
TypeScript
124 lines
3.7 KiB
TypeScript
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;
|