mirror of
https://github.com/lobehub/lobe-chat.git
synced 2026-06-14 03:30:19 +00:00
302 lines
7.5 KiB
Plaintext
302 lines
7.5 KiB
Plaintext
---
|
|
description:
|
|
globs:
|
|
alwaysApply: false
|
|
---
|
|
|
|
# 桌面端窗口管理指南
|
|
|
|
## 窗口管理概述
|
|
|
|
LobeChat 桌面应用使用 Electron 的 `BrowserWindow` 管理应用窗口。主要的窗口管理功能包括:
|
|
|
|
1. **窗口创建和配置**
|
|
2. **窗口状态管理**(大小、位置、最大化等)
|
|
3. **多窗口协调**
|
|
4. **窗口事件处理**
|
|
|
|
## 相关文件结构
|
|
|
|
```plaintext
|
|
apps/desktop/src/main/
|
|
├── appBrowsers.ts # 窗口管理的核心文件
|
|
├── controllers/
|
|
│ └── BrowserWindowsCtr.ts # 窗口控制器
|
|
└── modules/
|
|
└── browserWindowManager.ts # 窗口管理模块
|
|
```
|
|
|
|
## 窗口管理流程
|
|
|
|
### 1. 窗口创建
|
|
|
|
在 `appBrowsers.ts` 或 `BrowserWindowsCtr.ts` 中定义窗口创建逻辑:
|
|
|
|
```typescript
|
|
export const createMainWindow = () => {
|
|
const mainWindow = new BrowserWindow({
|
|
width: 1200,
|
|
height: 800,
|
|
minWidth: 600,
|
|
minHeight: 400,
|
|
webPreferences: {
|
|
preload: path.join(__dirname, '../preload/index.js'),
|
|
contextIsolation: true,
|
|
nodeIntegration: false,
|
|
},
|
|
// 其他窗口配置项...
|
|
});
|
|
|
|
// 加载应用内容
|
|
if (isDev) {
|
|
mainWindow.loadURL('http://localhost:3000');
|
|
mainWindow.webContents.openDevTools();
|
|
} else {
|
|
mainWindow.loadFile(path.join(__dirname, '../../renderer/index.html'));
|
|
}
|
|
|
|
return mainWindow;
|
|
};
|
|
```
|
|
|
|
### 2. 窗口状态管理
|
|
|
|
实现窗口状态持久化保存和恢复:
|
|
|
|
1. **保存窗口状态**
|
|
|
|
```typescript
|
|
const saveWindowState = (window: BrowserWindow) => {
|
|
if (!window.isMinimized() && !window.isMaximized()) {
|
|
const position = window.getPosition();
|
|
const size = window.getSize();
|
|
|
|
settings.set('windowState', {
|
|
x: position[0],
|
|
y: position[1],
|
|
width: size[0],
|
|
height: size[1],
|
|
});
|
|
}
|
|
};
|
|
```
|
|
|
|
2. **恢复窗口状态**
|
|
|
|
```typescript
|
|
const restoreWindowState = (window: BrowserWindow) => {
|
|
const savedState = settings.get('windowState');
|
|
|
|
if (savedState) {
|
|
window.setBounds({
|
|
x: savedState.x,
|
|
y: savedState.y,
|
|
width: savedState.width,
|
|
height: savedState.height,
|
|
});
|
|
}
|
|
};
|
|
```
|
|
|
|
3. **监听窗口事件**
|
|
|
|
```typescript
|
|
window.on('close', () => saveWindowState(window));
|
|
window.on('moved', () => saveWindowState(window));
|
|
window.on('resized', () => saveWindowState(window));
|
|
```
|
|
|
|
### 3. 实现多窗口管理
|
|
|
|
对于需要多窗口支持的功能:
|
|
|
|
1. **跟踪窗口**
|
|
|
|
```typescript
|
|
export class WindowManager {
|
|
private windows: Map<string, BrowserWindow> = new Map();
|
|
|
|
createWindow(id: string, options: BrowserWindowConstructorOptions) {
|
|
const window = new BrowserWindow(options);
|
|
this.windows.set(id, window);
|
|
|
|
window.on('closed', () => {
|
|
this.windows.delete(id);
|
|
});
|
|
|
|
return window;
|
|
}
|
|
|
|
getWindow(id: string) {
|
|
return this.windows.get(id);
|
|
}
|
|
|
|
getAllWindows() {
|
|
return Array.from(this.windows.values());
|
|
}
|
|
}
|
|
```
|
|
|
|
2. **窗口间通信**
|
|
|
|
```typescript
|
|
// 从一个窗口向另一个窗口发送消息
|
|
sendMessageToWindow(targetWindowId, channel, data) {
|
|
const targetWindow = this.getWindow(targetWindowId);
|
|
if (targetWindow) {
|
|
targetWindow.webContents.send(channel, data);
|
|
}
|
|
}
|
|
```
|
|
|
|
### 4. 窗口与渲染进程通信
|
|
|
|
通过 IPC 实现窗口操作:
|
|
|
|
1. **在主进程中注册 IPC 处理器**
|
|
|
|
```typescript
|
|
// apps/desktop/src/main/controllers/BrowserWindowsCtr.ts
|
|
import { BrowserWindow } from 'electron';
|
|
|
|
import { ControllerModule, IpcMethod } from '@/controllers';
|
|
|
|
export default class BrowserWindowsCtr extends ControllerModule {
|
|
static override readonly groupName = 'windows';
|
|
|
|
@IpcMethod()
|
|
minimizeWindow() {
|
|
const focusedWindow = BrowserWindow.getFocusedWindow();
|
|
focusedWindow?.minimize();
|
|
return { success: true };
|
|
}
|
|
|
|
@IpcMethod()
|
|
maximizeWindow() {
|
|
const focusedWindow = BrowserWindow.getFocusedWindow();
|
|
if (focusedWindow?.isMaximized()) focusedWindow.restore();
|
|
else focusedWindow?.maximize();
|
|
return { success: true };
|
|
}
|
|
|
|
@IpcMethod()
|
|
closeWindow() {
|
|
BrowserWindow.getFocusedWindow()?.close();
|
|
return { success: true };
|
|
}
|
|
}
|
|
```
|
|
|
|
- `@IpcMethod()` 根据控制器的 `groupName` 自动将方法映射为 `windows.minimizeWindow` 形式的通道名称。
|
|
- 控制器需继承 `ControllerModule`,并在 `controllers/registry.ts` 中通过 `controllerIpcConstructors` 注册,便于类型生成。
|
|
|
|
2. **在渲染进程中调用**
|
|
|
|
```typescript
|
|
// src/services/electron/windowService.ts
|
|
import { ensureElectronIpc } from '@/utils/electron/ipc';
|
|
|
|
const ipc = ensureElectronIpc();
|
|
|
|
export const windowService = {
|
|
minimize: () => ipc.windows.minimizeWindow(),
|
|
maximize: () => ipc.windows.maximizeWindow(),
|
|
close: () => ipc.windows.closeWindow(),
|
|
};
|
|
```
|
|
|
|
- `ensureElectronIpc()` 会基于 `DesktopIpcServices` 运行时生成 Proxy,并通过 `window.electronAPI.invoke` 与主进程通信;不再直接使用 `dispatch`。
|
|
|
|
### 5. 自定义窗口控制 (无边框窗口)
|
|
|
|
对于自定义窗口标题栏:
|
|
|
|
1. **创建无边框窗口**
|
|
|
|
```typescript
|
|
const window = new BrowserWindow({
|
|
frame: false,
|
|
titleBarStyle: 'hidden',
|
|
// 其他选项...
|
|
});
|
|
```
|
|
|
|
2. **在渲染进程中实现拖拽区域**
|
|
|
|
```css
|
|
/* CSS */
|
|
.titlebar {
|
|
-webkit-app-region: drag;
|
|
}
|
|
|
|
.titlebar-button {
|
|
-webkit-app-region: no-drag;
|
|
}
|
|
```
|
|
|
|
## 最佳实践
|
|
|
|
1. **性能考虑**
|
|
- 避免创建过多窗口
|
|
- 使用 `show: false` 创建窗口,在内容加载完成后再显示,避免白屏
|
|
|
|
2. **安全性**
|
|
- 始终设置适当的 `webPreferences` 确保安全
|
|
|
|
```typescript
|
|
webPreferences: {
|
|
preload: path.join(__dirname, '../preload/index.js'),
|
|
contextIsolation: true,
|
|
nodeIntegration: false,
|
|
sandbox: true,
|
|
}
|
|
```
|
|
|
|
3. **跨平台兼容性**
|
|
- 考虑不同操作系统的窗口行为差异
|
|
- 使用 `process.platform` 为不同平台提供特定实现
|
|
|
|
4. **崩溃恢复**
|
|
- 监听 `webContents.on('crashed')` 事件处理崩溃
|
|
- 提供崩溃恢复选项
|
|
|
|
5. **内存管理**
|
|
- 确保窗口关闭时清理所有相关资源
|
|
- 使用 `window.on('closed')` 而不是 `window.on('close')` 进行最终清理
|
|
|
|
## 示例:创建设置窗口
|
|
|
|
```typescript
|
|
// apps/desktop/src/main/controllers/BrowserWindowsCtr.ts
|
|
import type { OpenSettingsWindowOptions } from '@lobechat/electron-client-ipc';
|
|
|
|
import { ControllerModule, IpcMethod } from '@/controllers';
|
|
|
|
export default class BrowserWindowsCtr extends ControllerModule {
|
|
static override readonly groupName = 'windows';
|
|
|
|
@IpcMethod()
|
|
async openSettingsWindow(options?: string | OpenSettingsWindowOptions) {
|
|
const normalizedOptions =
|
|
typeof options === 'string' || options === undefined
|
|
? { tab: typeof options === 'string' ? options : undefined }
|
|
: options;
|
|
|
|
const mainWindow = this.app.browserManager.getMainWindow();
|
|
const query = new URLSearchParams();
|
|
if (normalizedOptions.tab) query.set('active', normalizedOptions.tab);
|
|
if (normalizedOptions.searchParams) {
|
|
for (const [key, value] of Object.entries(normalizedOptions.searchParams)) {
|
|
if (value) query.set(key, value);
|
|
}
|
|
}
|
|
|
|
const fullPath = `/settings${query.size ? `?${query.toString()}` : ''}`;
|
|
await mainWindow.loadUrl(fullPath);
|
|
mainWindow.show();
|
|
|
|
return { success: true };
|
|
}
|
|
}
|
|
```
|