@lobehub/chat
Version:
Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.
198 lines (159 loc) • 5.79 kB
text/typescript
import { MainBroadcastEventKey, MainBroadcastParams } from '@lobechat/electron-client-ipc';
import { WebContents } from 'electron';
import { createLogger } from '@/utils/logger';
import { AppBrowsersIdentifiers, appBrowsers } from '../appBrowsers';
import type { App } from './App';
import type { BrowserWindowOpts } from './Browser';
import Browser from './Browser';
// Create logger
const logger = createLogger('core:BrowserManager');
export default class BrowserManager {
app: App;
browsers: Map<AppBrowsersIdentifiers, Browser> = new Map();
private webContentsMap = new Map<WebContents, AppBrowsersIdentifiers>();
constructor(app: App) {
logger.debug('Initializing BrowserManager');
this.app = app;
}
getMainWindow() {
return this.retrieveByIdentifier('chat');
}
showMainWindow() {
logger.debug('Showing main window');
const window = this.getMainWindow();
window.show();
}
showSettingsWindow() {
logger.debug('Showing settings window');
const window = this.retrieveByIdentifier('settings');
window.show();
return window;
}
broadcastToAllWindows = <T extends MainBroadcastEventKey>(
event: T,
data: MainBroadcastParams<T>,
) => {
logger.debug(`Broadcasting event ${event} to all windows`);
this.browsers.forEach((browser) => {
browser.broadcast(event, data);
});
};
broadcastToWindow = <T extends MainBroadcastEventKey>(
identifier: AppBrowsersIdentifiers,
event: T,
data: MainBroadcastParams<T>,
) => {
logger.debug(`Broadcasting event ${event} to window: ${identifier}`);
this.browsers.get(identifier).broadcast(event, data);
};
/**
* Display the settings window and navigate to a specific tab
* @param tab Settings window sub-path tab
*/
async showSettingsWindowWithTab(tab?: string) {
logger.debug(`Showing settings window with tab: ${tab || 'default'}`);
// common is the main path for settings route
if (tab && tab !== 'common') {
const browser = await this.redirectToPage('settings', tab);
// make provider page more large
if (tab.startsWith('provider/')) {
logger.debug('Resizing window for provider settings');
browser.setWindowSize({ height: 1000, width: 1400 });
browser.moveToCenter();
}
return browser;
} else {
return this.showSettingsWindow();
}
}
/**
* Navigate window to specific sub-path
* @param identifier Window identifier
* @param subPath Sub-path, such as 'agent', 'about', etc.
*/
async redirectToPage(identifier: AppBrowsersIdentifiers, subPath?: string) {
try {
// Ensure window is retrieved or created
const browser = this.retrieveByIdentifier(identifier);
browser.hide();
const baseRoute = appBrowsers[identifier].path;
// Build complete URL path
const fullPath = subPath ? `${baseRoute}/${subPath}` : baseRoute;
logger.debug(`Redirecting to: ${fullPath}`);
// Load URL and show window
await browser.loadUrl(fullPath);
browser.show();
return browser;
} catch (error) {
logger.error(`Failed to redirect (${identifier}/${subPath}):`, error);
throw error;
}
}
/**
* get Browser by identifier
*/
retrieveByIdentifier(identifier: AppBrowsersIdentifiers) {
const browser = this.browsers.get(identifier);
if (browser) return browser;
logger.debug(`Browser ${identifier} not found, initializing new instance`);
return this.retrieveOrInitialize(appBrowsers[identifier]);
}
/**
* Initialize all browsers when app starts up
*/
initializeBrowsers() {
logger.info('Initializing all browsers');
Object.values(appBrowsers).forEach((browser: BrowserWindowOpts) => {
logger.debug(`Initializing browser: ${browser.identifier}`);
if (browser.keepAlive) {
this.retrieveOrInitialize(browser);
}
});
}
// helper
/**
* Retrieve existing browser or initialize a new one
* @param options Browser window options
*/
private retrieveOrInitialize(options: BrowserWindowOpts) {
let browser = this.browsers.get(options.identifier as AppBrowsersIdentifiers);
if (browser) {
logger.debug(`Retrieved existing browser: ${options.identifier}`);
return browser;
}
logger.debug(`Creating new browser: ${options.identifier}`);
browser = new Browser(options, this.app);
const identifier = options.identifier as AppBrowsersIdentifiers;
this.browsers.set(identifier, browser);
// 记录 WebContents 和 identifier 的映射
this.webContentsMap.set(browser.browserWindow.webContents, identifier);
// 当窗口关闭时清理映射
browser.browserWindow.on('close', () => {
if (browser.webContents) this.webContentsMap.delete(browser.webContents);
});
browser.browserWindow.on('show', () => {
if (browser.webContents)
this.webContentsMap.set(browser.webContents, browser.identifier as AppBrowsersIdentifiers);
});
return browser;
}
closeWindow(identifier: string) {
const browser = this.browsers.get(identifier as AppBrowsersIdentifiers);
browser?.close();
}
minimizeWindow(identifier: string) {
const browser = this.browsers.get(identifier as AppBrowsersIdentifiers);
browser?.browserWindow.minimize();
}
maximizeWindow(identifier: string) {
const browser = this.browsers.get(identifier as AppBrowsersIdentifiers);
if (browser.browserWindow.isMaximized()) {
browser?.browserWindow.unmaximize();
} else {
browser?.browserWindow.maximize();
}
}
getIdentifierByWebContents(webContents: WebContents): AppBrowsersIdentifiers | null {
return this.webContentsMap.get(webContents) || null;
}
}