163 lines
6.4 KiB
TypeScript
163 lines
6.4 KiB
TypeScript
import type { BrowserWindow } from 'electron'
|
|
import { ipcMain } from 'electron';
|
|
import { WINDOW_NAMES, MAIN_WIN_SIZE, IPC_EVENTS, MENU_IDS, CONVERSATION_ITEM_MENU_IDS, CONVERSATION_LIST_MENU_IDS, MESSAGE_ITEM_MENU_IDS, CONFIG_KEYS } from '@common/constants'
|
|
import { createProvider } from '../providers'
|
|
import { windowManager } from '@main/service/window-service'
|
|
import { menuManager } from '@main/service/menu-service'
|
|
import { logManager } from '@main/service/logger'
|
|
import { configManager } from '@main/service/config-service'
|
|
import { trayManager } from '@main/service/tray-service'
|
|
import { TabManager } from '@service/tab-manager'
|
|
|
|
const handleTray = (minimizeToTray: boolean) => {
|
|
if (minimizeToTray) {
|
|
trayManager.create();
|
|
return;
|
|
}
|
|
|
|
trayManager.destroy();
|
|
}
|
|
|
|
const registerMenus = (window: BrowserWindow) => {
|
|
|
|
const conversationItemMenuItemClick = (id: string) => {
|
|
logManager.logUserOperation(`${IPC_EVENTS.SHOW_CONTEXT_MENU}:${MENU_IDS.CONVERSATION_ITEM}-${id}`)
|
|
window.webContents.send(`${IPC_EVENTS.SHOW_CONTEXT_MENU}:${MENU_IDS.CONVERSATION_ITEM}`, id);
|
|
}
|
|
|
|
menuManager.register(MENU_IDS.CONVERSATION_ITEM, [
|
|
{
|
|
id: CONVERSATION_ITEM_MENU_IDS.PIN,
|
|
label: 'menu.conversation.pinConversation',
|
|
click: () => conversationItemMenuItemClick(CONVERSATION_ITEM_MENU_IDS.PIN)
|
|
},
|
|
{
|
|
id: CONVERSATION_ITEM_MENU_IDS.RENAME,
|
|
label: 'menu.conversation.renameConversation',
|
|
click: () => conversationItemMenuItemClick(CONVERSATION_ITEM_MENU_IDS.RENAME)
|
|
},
|
|
{
|
|
id: CONVERSATION_ITEM_MENU_IDS.DEL,
|
|
label: 'menu.conversation.delConversation',
|
|
click: () => conversationItemMenuItemClick(CONVERSATION_ITEM_MENU_IDS.DEL)
|
|
},
|
|
])
|
|
|
|
const conversationListMenuItemClick = (id: string) => {
|
|
logManager.logUserOperation(`${IPC_EVENTS.SHOW_CONTEXT_MENU}:${MENU_IDS.CONVERSATION_LIST}-${id}`)
|
|
window.webContents.send(`${IPC_EVENTS.SHOW_CONTEXT_MENU}:${MENU_IDS.CONVERSATION_LIST}`, id);
|
|
}
|
|
|
|
menuManager.register(MENU_IDS.CONVERSATION_LIST, [
|
|
{
|
|
id: CONVERSATION_LIST_MENU_IDS.NEW_CONVERSATION,
|
|
label: 'menu.conversation.newConversation',
|
|
click: () => conversationListMenuItemClick(CONVERSATION_LIST_MENU_IDS.NEW_CONVERSATION)
|
|
},
|
|
{ type: 'separator' },
|
|
{
|
|
id: CONVERSATION_LIST_MENU_IDS.SORT_BY, label: 'menu.conversation.sortBy', submenu: [
|
|
{ id: CONVERSATION_LIST_MENU_IDS.SORT_BY_CREATE_TIME, label: 'menu.conversation.sortByCreateTime', type: 'radio', checked: false, click: () => conversationListMenuItemClick(CONVERSATION_LIST_MENU_IDS.SORT_BY_CREATE_TIME) },
|
|
{ id: CONVERSATION_LIST_MENU_IDS.SORT_BY_UPDATE_TIME, label: 'menu.conversation.sortByUpdateTime', type: 'radio', checked: false, click: () => conversationListMenuItemClick(CONVERSATION_LIST_MENU_IDS.SORT_BY_UPDATE_TIME) },
|
|
{ id: CONVERSATION_LIST_MENU_IDS.SORT_BY_NAME, label: 'menu.conversation.sortByName', type: 'radio', checked: false, click: () => conversationListMenuItemClick(CONVERSATION_LIST_MENU_IDS.SORT_BY_NAME) },
|
|
{ id: CONVERSATION_LIST_MENU_IDS.SORT_BY_MODEL, label: 'menu.conversation.sortByModel', type: 'radio', checked: false, click: () => conversationListMenuItemClick(CONVERSATION_LIST_MENU_IDS.SORT_BY_MODEL) },
|
|
{ type: 'separator' },
|
|
{ id: CONVERSATION_LIST_MENU_IDS.SORT_ASCENDING, label: 'menu.conversation.sortAscending', type: 'radio', checked: false, click: () => conversationListMenuItemClick(CONVERSATION_LIST_MENU_IDS.SORT_ASCENDING) },
|
|
{ id: CONVERSATION_LIST_MENU_IDS.SORT_DESCENDING, label: 'menu.conversation.sortDescending', type: 'radio', checked: false, click: () => conversationListMenuItemClick(CONVERSATION_LIST_MENU_IDS.SORT_DESCENDING) },
|
|
]
|
|
},
|
|
{
|
|
id: CONVERSATION_LIST_MENU_IDS.BATCH_OPERATIONS,
|
|
label: 'menu.conversation.batchOperations',
|
|
click: () => conversationListMenuItemClick(CONVERSATION_LIST_MENU_IDS.BATCH_OPERATIONS)
|
|
}
|
|
])
|
|
|
|
const messageItemMenuItemClick = (id: string) => {
|
|
logManager.logUserOperation(`${IPC_EVENTS.SHOW_CONTEXT_MENU}:${MENU_IDS.MESSAGE_ITEM}-${id}`)
|
|
window.webContents.send(`${IPC_EVENTS.SHOW_CONTEXT_MENU}:${MENU_IDS.MESSAGE_ITEM}`, id);
|
|
}
|
|
|
|
menuManager.register(MENU_IDS.MESSAGE_ITEM, [
|
|
{
|
|
id: MESSAGE_ITEM_MENU_IDS.COPY,
|
|
label: 'menu.message.copyMessage',
|
|
click: () => messageItemMenuItemClick(MESSAGE_ITEM_MENU_IDS.COPY)
|
|
},
|
|
{
|
|
id: MESSAGE_ITEM_MENU_IDS.SELECT,
|
|
label: 'menu.message.selectMessage',
|
|
click: () => messageItemMenuItemClick(MESSAGE_ITEM_MENU_IDS.SELECT)
|
|
},
|
|
{ type: 'separator' },
|
|
{
|
|
id: MESSAGE_ITEM_MENU_IDS.DELETE,
|
|
label: 'menu.message.deleteMessage',
|
|
click: () => messageItemMenuItemClick(MESSAGE_ITEM_MENU_IDS.DELETE)
|
|
},
|
|
])
|
|
}
|
|
|
|
export function setupMainWindow() {
|
|
windowManager.onWindowCreate(WINDOW_NAMES.MAIN, (mainWindow) => {
|
|
let minimizeToTray = configManager.get(CONFIG_KEYS.MINIMIZE_TO_TRAY);
|
|
|
|
configManager.onConfigChange((config) => {
|
|
if (minimizeToTray === config[CONFIG_KEYS.MINIMIZE_TO_TRAY]) return;
|
|
minimizeToTray = config[CONFIG_KEYS.MINIMIZE_TO_TRAY];
|
|
handleTray(minimizeToTray);
|
|
});
|
|
|
|
handleTray(minimizeToTray);
|
|
registerMenus(mainWindow);
|
|
|
|
const tabManager = new TabManager(mainWindow)
|
|
tabManager.enable()
|
|
|
|
mainWindow.on('closed', () => {
|
|
tabManager.destroy()
|
|
})
|
|
});
|
|
|
|
windowManager.create(WINDOW_NAMES.MAIN, MAIN_WIN_SIZE);
|
|
|
|
ipcMain.on(IPC_EVENTS.START_A_DIALOGUE, async (_event, props: CreateDialogueProps) => {
|
|
const { providerName, messages, messageId, selectedModel } = props;
|
|
const mainWindow = windowManager.get(WINDOW_NAMES.MAIN);
|
|
|
|
if (!mainWindow) {
|
|
throw new Error('mainWindow not found');
|
|
}
|
|
|
|
try {
|
|
|
|
const provider = createProvider(providerName);
|
|
const chunks = await provider?.chat(messages, selectedModel);
|
|
|
|
if (!chunks) {
|
|
throw new Error('chunks or stream not found');
|
|
}
|
|
|
|
for await (const chunk of chunks) {
|
|
const chunkContent = {
|
|
messageId,
|
|
data: chunk
|
|
}
|
|
mainWindow.webContents.send(IPC_EVENTS.START_A_DIALOGUE + 'back' + messageId, chunkContent);
|
|
}
|
|
|
|
} catch (error) {
|
|
const errorContent = {
|
|
messageId,
|
|
data: {
|
|
isEnd: true,
|
|
isError: true,
|
|
result: error instanceof Error ? error.message : String(error),
|
|
}
|
|
}
|
|
|
|
mainWindow.webContents.send(IPC_EVENTS.START_A_DIALOGUE + 'back' + messageId, errorContent);
|
|
}
|
|
})
|
|
}
|