@lobehub/lobehub 2.0.0-next.163 → 2.0.0-next.165
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursor/rules/desktop-feature-implementation.mdc +31 -34
- package/.cursor/rules/desktop-local-tools-implement.mdc +3 -3
- package/.cursor/rules/desktop-window-management.mdc +56 -66
- package/CHANGELOG.md +52 -0
- package/README.md +6 -6
- package/README.zh-CN.md +6 -6
- package/apps/desktop/Development.md +42 -46
- package/apps/desktop/README.md +37 -1
- package/apps/desktop/README.zh-CN.md +26 -1
- package/apps/desktop/electron.vite.config.ts +1 -0
- package/apps/desktop/src/main/controllers/AuthCtr.ts +4 -3
- package/apps/desktop/src/main/controllers/BrowserWindowsCtr.ts +33 -20
- package/apps/desktop/src/main/controllers/DevtoolsCtr.ts +4 -2
- package/apps/desktop/src/main/controllers/LocalFileCtr.ts +14 -13
- package/apps/desktop/src/main/controllers/MenuCtr.ts +5 -4
- package/apps/desktop/src/main/controllers/NetworkProxyCtr.ts +18 -19
- package/apps/desktop/src/main/controllers/NotificationCtr.ts +4 -3
- package/apps/desktop/src/main/controllers/RemoteServerConfigCtr.ts +5 -4
- package/apps/desktop/src/main/controllers/RemoteServerSyncCtr.ts +3 -2
- package/apps/desktop/src/main/controllers/ShellCommandCtr.ts +5 -4
- package/apps/desktop/src/main/controllers/ShortcutCtr.ts +4 -3
- package/apps/desktop/src/main/controllers/SystemCtr.ts +7 -37
- package/apps/desktop/src/main/controllers/SystemServerCtr.ts +38 -0
- package/apps/desktop/src/main/controllers/TrayMenuCtr.ts +5 -4
- package/apps/desktop/src/main/controllers/UpdaterCtr.ts +6 -5
- package/apps/desktop/src/main/controllers/UploadFileCtr.ts +3 -25
- package/apps/desktop/src/main/controllers/UploadFileServerCtr.ts +33 -0
- package/apps/desktop/src/main/controllers/__tests__/AuthCtr.test.ts +9 -1
- package/apps/desktop/src/main/controllers/__tests__/BrowserWindowsCtr.test.ts +29 -9
- package/apps/desktop/src/main/controllers/__tests__/DevtoolsCtr.test.ts +12 -3
- package/apps/desktop/src/main/controllers/__tests__/LocalFileCtr.test.ts +7 -0
- package/apps/desktop/src/main/controllers/__tests__/MenuCtr.test.ts +10 -0
- package/apps/desktop/src/main/controllers/__tests__/NetworkProxyCtr.test.ts +10 -0
- package/apps/desktop/src/main/controllers/__tests__/NotificationCtr.test.ts +8 -0
- package/apps/desktop/src/main/controllers/__tests__/RemoteServerConfigCtr.test.ts +8 -0
- package/apps/desktop/src/main/controllers/__tests__/RemoteServerSyncCtr.test.ts +1 -0
- package/apps/desktop/src/main/controllers/__tests__/ShellCommandCtr.test.ts +10 -0
- package/apps/desktop/src/main/controllers/__tests__/ShortcutCtr.test.ts +11 -0
- package/apps/desktop/src/main/controllers/__tests__/SystemCtr.test.ts +43 -73
- package/apps/desktop/src/main/controllers/__tests__/SystemServerCtr.test.ts +75 -0
- package/apps/desktop/src/main/controllers/__tests__/TrayMenuCtr.test.ts +24 -13
- package/apps/desktop/src/main/controllers/__tests__/UpdaterCtr.test.ts +13 -2
- package/apps/desktop/src/main/controllers/__tests__/UploadFileCtr.test.ts +29 -108
- package/apps/desktop/src/main/controllers/__tests__/UploadFileServerCtr.test.ts +55 -0
- package/apps/desktop/src/main/controllers/_template.ts +2 -2
- package/apps/desktop/src/main/controllers/index.ts +5 -29
- package/apps/desktop/src/main/controllers/registry.ts +52 -0
- package/apps/desktop/src/main/core/App.ts +15 -47
- package/apps/desktop/src/main/core/__tests__/App.test.ts +5 -4
- package/apps/desktop/src/main/core/infrastructure/IoCContainer.ts +0 -5
- package/apps/desktop/src/main/core/infrastructure/__tests__/IoCContainer.test.ts +0 -50
- package/apps/desktop/src/main/exports.d.ts +8 -0
- package/apps/desktop/src/main/exports.ts +2 -0
- package/apps/desktop/src/main/global.d.ts +3 -0
- package/apps/desktop/src/main/modules/fileSearch/__tests__/macOS.integration.test.ts +17 -8
- package/apps/desktop/src/main/package.json +10 -0
- package/apps/desktop/src/main/services/fileSrv.ts +1 -1
- package/apps/desktop/src/main/utils/ipc/__tests__/base.test.ts +91 -0
- package/apps/desktop/src/main/utils/ipc/base.ts +170 -0
- package/apps/desktop/src/main/utils/ipc/index.ts +11 -0
- package/apps/desktop/src/main/utils/ipc/utility.ts +20 -0
- package/apps/desktop/src/preload/electronApi.ts +4 -1
- package/apps/desktop/src/preload/invoke.test.ts +13 -16
- package/apps/desktop/src/preload/invoke.ts +2 -5
- package/apps/desktop/src/preload/routeInterceptor.test.ts +13 -13
- package/apps/desktop/src/preload/routeInterceptor.ts +4 -4
- package/apps/desktop/tsconfig.json +15 -5
- package/changelog/v1.json +14 -0
- package/locales/ar/auth.json +3 -0
- package/locales/bg-BG/auth.json +3 -0
- package/locales/de-DE/auth.json +3 -0
- package/locales/en-US/auth.json +4 -1
- package/locales/es-ES/auth.json +3 -0
- package/locales/fa-IR/auth.json +3 -0
- package/locales/fr-FR/auth.json +3 -0
- package/locales/it-IT/auth.json +3 -0
- package/locales/ja-JP/auth.json +3 -0
- package/locales/ko-KR/auth.json +3 -0
- package/locales/nl-NL/auth.json +3 -0
- package/locales/pl-PL/auth.json +3 -0
- package/locales/pt-BR/auth.json +3 -0
- package/locales/ru-RU/auth.json +3 -0
- package/locales/tr-TR/auth.json +3 -0
- package/locales/vi-VN/auth.json +3 -0
- package/locales/zh-CN/auth.json +3 -0
- package/locales/zh-TW/auth.json +3 -0
- package/package.json +4 -3
- package/packages/electron-client-ipc/src/index.ts +1 -1
- package/packages/electron-client-ipc/src/ipc.test.ts +62 -0
- package/packages/electron-client-ipc/src/ipc.ts +63 -0
- package/packages/electron-client-ipc/src/streamInvoke.ts +7 -1
- package/packages/electron-client-ipc/src/types/dispatch.ts +1 -10
- package/packages/electron-client-ipc/vitest.config.mts +10 -0
- package/packages/electron-server-ipc/src/ipcClient.ts +1 -2
- package/packages/electron-server-ipc/src/ipcServer.ts +1 -2
- package/packages/electron-server-ipc/src/types/index.ts +1 -5
- package/pnpm-workspace.yaml +1 -1
- package/scripts/i18nWorkflow/const.ts +2 -2
- package/scripts/i18nWorkflow/i18nConfig.ts +7 -0
- package/scripts/i18nWorkflow/utils.ts +1 -1
- package/src/app/[variants]/(auth)/signup/[[...signup]]/BetterAuthSignUpForm.tsx +23 -0
- package/src/app/[variants]/(main)/(mobile)/me/(home)/features/UserBanner.tsx +3 -9
- package/src/app/[variants]/(main)/discover/(detail)/provider/features/Sidebar/ActionButton/ProviderConfig.tsx +2 -2
- package/src/app/[variants]/(main)/profile/(home)/Client.tsx +206 -138
- package/src/features/User/PlanTag.tsx +4 -4
- package/src/locales/default/auth.ts +3 -0
- package/src/locales/default/setting.ts +1 -0
- package/src/server/modules/ElectronIPCClient/index.ts +59 -13
- package/src/services/electron/__tests__/devtools.test.ts +10 -6
- package/src/services/electron/autoUpdate.ts +5 -5
- package/src/services/electron/desktopNotification.ts +4 -7
- package/src/services/electron/devtools.ts +2 -2
- package/src/services/electron/file.ts +3 -2
- package/src/services/electron/localFileService.ts +17 -16
- package/src/services/electron/remoteServer.ts +7 -6
- package/src/services/electron/settings.ts +9 -11
- package/src/services/electron/system.ts +8 -6
- package/src/store/chat/slices/plugin/actions/pluginTypes.ts +1 -1
- package/src/store/global/actions/general.ts +8 -10
- package/src/utils/electron/desktopRemoteRPCFetch.ts +3 -2
- package/src/utils/electron/ipc.ts +12 -0
- package/tsconfig.json +5 -0
- package/apps/desktop/src/main/types/ipcClientEvent.ts +0 -3
- package/packages/electron-client-ipc/src/dispatch.ts +0 -41
|
@@ -36,13 +36,13 @@ LobeChat 桌面端基于 Electron 框架构建,采用主进程-渲染进程架
|
|
|
36
36
|
1. **创建控制器 (Controller)**
|
|
37
37
|
- 位置:`apps/desktop/src/main/controllers/`
|
|
38
38
|
- 示例:创建 `NewFeatureCtr.ts`
|
|
39
|
-
-
|
|
40
|
-
-
|
|
39
|
+
- 需继承 `ControllerModule`,并设置 `static readonly groupName`(例如 `static override readonly groupName = 'newFeature';`)
|
|
40
|
+
- 按 `_template.ts` 模板格式实现,并在 `apps/desktop/src/main/controllers/registry.ts` 的 `controllerIpcConstructors`(或 `controllerServerIpcConstructors`)中注册,保证类型推导与自动装配
|
|
41
41
|
|
|
42
42
|
2. **定义 IPC 事件处理器**
|
|
43
|
-
- 使用 `@
|
|
44
|
-
-
|
|
45
|
-
-
|
|
43
|
+
- 使用 `@IpcMethod()` 装饰器暴露渲染进程可访问的通道,或使用 `@IpcServerMethod()` 声明仅供 Next.js 服务器调用的 IPC
|
|
44
|
+
- 通道名称基于 `groupName.methodName` 自动生成,不再手动拼接字符串
|
|
45
|
+
- 处理函数可通过 `getIpcContext()` 获取 `sender`、`event` 等上下文信息,并按照需要返回结构化结果
|
|
46
46
|
|
|
47
47
|
3. **实现业务逻辑**
|
|
48
48
|
- 可能需要调用 Electron API 或 Node.js 原生模块
|
|
@@ -60,15 +60,17 @@ LobeChat 桌面端基于 Electron 框架构建,采用主进程-渲染进程架
|
|
|
60
60
|
1. **创建服务层**
|
|
61
61
|
- 位置:`src/services/electron/`
|
|
62
62
|
- 添加服务方法调用 IPC
|
|
63
|
-
- 使用 `
|
|
63
|
+
- 使用 `ensureElectronIpc()` 生成的类型安全代理,避免手动拼通道名称
|
|
64
64
|
|
|
65
65
|
```typescript
|
|
66
66
|
// src/services/electron/newFeatureService.ts
|
|
67
|
-
import {
|
|
68
|
-
import { NewFeatureParams } from '
|
|
67
|
+
import { ensureElectronIpc } from '@/utils/electron/ipc';
|
|
68
|
+
import type { NewFeatureParams } from '@lobechat/electron-client-ipc';
|
|
69
|
+
|
|
70
|
+
const ipc = ensureElectronIpc();
|
|
69
71
|
|
|
70
72
|
export const newFeatureService = async (params: NewFeatureParams) => {
|
|
71
|
-
return
|
|
73
|
+
return ipc.newFeature.doSomething(params);
|
|
72
74
|
};
|
|
73
75
|
```
|
|
74
76
|
|
|
@@ -118,36 +120,31 @@ LobeChat 桌面端基于 Electron 框架构建,采用主进程-渲染进程架
|
|
|
118
120
|
|
|
119
121
|
```typescript
|
|
120
122
|
// apps/desktop/src/main/controllers/NotificationCtr.ts
|
|
121
|
-
import {
|
|
122
|
-
import {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
123
|
+
import { Notification } from 'electron';
|
|
124
|
+
import { ControllerModule, IpcMethod } from '@/controllers';
|
|
125
|
+
import type {
|
|
126
|
+
DesktopNotificationResult,
|
|
127
|
+
ShowDesktopNotificationParams,
|
|
128
|
+
} from '@lobechat/electron-client-ipc';
|
|
129
|
+
|
|
130
|
+
export default class NotificationCtr extends ControllerModule {
|
|
131
|
+
static override readonly groupName = 'notification';
|
|
132
|
+
|
|
133
|
+
@IpcMethod()
|
|
134
|
+
async showDesktopNotification(
|
|
135
|
+
params: ShowDesktopNotificationParams,
|
|
136
|
+
): Promise<DesktopNotificationResult> {
|
|
137
|
+
if (!Notification.isSupported()) {
|
|
138
|
+
return { error: 'Notifications not supported', success: false };
|
|
139
|
+
}
|
|
128
140
|
|
|
129
|
-
export class NotificationCtr {
|
|
130
|
-
@ipcClientEvent('showNotification')
|
|
131
|
-
async handleShowNotification({ title, body }: ShowNotificationParams) {
|
|
132
141
|
try {
|
|
133
|
-
|
|
134
|
-
return { success: false, error: 'Notifications not supported' };
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
const notification = new Notification({
|
|
138
|
-
title,
|
|
139
|
-
body,
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
+
const notification = new Notification({ body: params.body, title: params.title });
|
|
142
143
|
notification.show();
|
|
143
|
-
|
|
144
144
|
return { success: true };
|
|
145
145
|
} catch (error) {
|
|
146
|
-
console.error('Failed to show notification:', error);
|
|
147
|
-
return {
|
|
148
|
-
success: false,
|
|
149
|
-
error: error instanceof Error ? error.message : 'Unknown error'
|
|
150
|
-
};
|
|
146
|
+
console.error('[NotificationCtr] Failed to show notification:', error);
|
|
147
|
+
return { error: error instanceof Error ? error.message : 'Unknown error', success: false };
|
|
151
148
|
}
|
|
152
149
|
}
|
|
153
150
|
}
|
|
@@ -51,15 +51,15 @@ alwaysApply: false
|
|
|
51
51
|
* 导入在步骤 2 中定义的 IPC 参数类型。
|
|
52
52
|
* 添加一个新的 `async` 方法,方法名通常与 Action 名称对应 (例如: `renameLocalFile`)。
|
|
53
53
|
* 方法接收 `params` (符合 IPC 参数类型)。
|
|
54
|
-
*
|
|
54
|
+
* 通过 `ensureElectronIpc()` 获取 IPC 代理 (`const ipc = ensureElectronIpc();`),调用与 Manifest 中 `name` 字段匹配的链式方法,并将 `params` 传递过去。
|
|
55
55
|
* 定义方法的返回类型,通常是 `Promise<{ success: boolean; error?: string }>`,与后端 Controller 返回的结构一致。
|
|
56
56
|
|
|
57
57
|
5. **实现后端逻辑 (Controller / IPC Handler):**
|
|
58
58
|
* **文件:** `apps/desktop/src/main/controllers/[ToolName]Ctr.ts` (例如: `apps/desktop/src/main/controllers/LocalFileCtr.ts`)
|
|
59
59
|
* **操作:**
|
|
60
|
-
* 导入 Node.js 相关模块 (`fs`, `path` 等) 和 IPC 相关依赖 (`
|
|
60
|
+
* 导入 Node.js 相关模块 (`fs`, `path` 等) 和 IPC 相关依赖 (`ControllerModule`, `IpcMethod`/`IpcServerMethod`、参数类型等)。
|
|
61
61
|
* 添加一个新的 `async` 方法,方法名通常以 `handle` 开头 (例如: `handleRenameFile`)。
|
|
62
|
-
* 使用 `@
|
|
62
|
+
* 使用 `@IpcMethod()` 或 `@IpcServerMethod()` 装饰器将此方法注册为对应 IPC 事件的处理器,确保方法名与 Manifest 中的 `name` 以及 Service 层的链式调用一致。
|
|
63
63
|
* 方法的参数应解构自 Service 层传递过来的对象,类型与步骤 2 中定义的 IPC 参数类型匹配。
|
|
64
64
|
* 实现核心业务逻辑:
|
|
65
65
|
* 进行必要的输入验证。
|
|
@@ -149,50 +149,52 @@ export const createMainWindow = () => {
|
|
|
149
149
|
|
|
150
150
|
1. **在主进程中注册 IPC 处理器**
|
|
151
151
|
```typescript
|
|
152
|
-
// BrowserWindowsCtr.ts
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
152
|
+
// apps/desktop/src/main/controllers/BrowserWindowsCtr.ts
|
|
153
|
+
import { BrowserWindow } from 'electron';
|
|
154
|
+
import { ControllerModule, IpcMethod } from '@/controllers';
|
|
155
|
+
|
|
156
|
+
export default class BrowserWindowsCtr extends ControllerModule {
|
|
157
|
+
static override readonly groupName = 'windows';
|
|
158
|
+
|
|
159
|
+
@IpcMethod()
|
|
160
|
+
minimizeWindow() {
|
|
161
|
+
const focusedWindow = BrowserWindow.getFocusedWindow();
|
|
162
|
+
focusedWindow?.minimize();
|
|
163
|
+
return { success: true };
|
|
158
164
|
}
|
|
159
|
-
return { success: true };
|
|
160
|
-
}
|
|
161
165
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
} else {
|
|
169
|
-
focusedWindow.maximize();
|
|
170
|
-
}
|
|
166
|
+
@IpcMethod()
|
|
167
|
+
maximizeWindow() {
|
|
168
|
+
const focusedWindow = BrowserWindow.getFocusedWindow();
|
|
169
|
+
if (focusedWindow?.isMaximized()) focusedWindow.restore();
|
|
170
|
+
else focusedWindow?.maximize();
|
|
171
|
+
return { success: true };
|
|
171
172
|
}
|
|
172
|
-
return { success: true };
|
|
173
|
-
}
|
|
174
173
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
focusedWindow.close();
|
|
174
|
+
@IpcMethod()
|
|
175
|
+
closeWindow() {
|
|
176
|
+
BrowserWindow.getFocusedWindow()?.close();
|
|
177
|
+
return { success: true };
|
|
180
178
|
}
|
|
181
|
-
return { success: true };
|
|
182
179
|
}
|
|
183
180
|
```
|
|
181
|
+
- `@IpcMethod()` 根据控制器的 `groupName` 自动将方法映射为 `windows.minimizeWindow` 形式的通道名称。
|
|
182
|
+
- 控制器需继承 `ControllerModule`,并在 `controllers/registry.ts` 中通过 `controllerIpcConstructors` 注册,便于类型生成。
|
|
184
183
|
|
|
185
184
|
2. **在渲染进程中调用**
|
|
186
185
|
```typescript
|
|
187
186
|
// src/services/electron/windowService.ts
|
|
188
|
-
import {
|
|
187
|
+
import { ensureElectronIpc } from '@/utils/electron/ipc';
|
|
188
|
+
|
|
189
|
+
const ipc = ensureElectronIpc();
|
|
189
190
|
|
|
190
191
|
export const windowService = {
|
|
191
|
-
minimize: () =>
|
|
192
|
-
maximize: () =>
|
|
193
|
-
close: () =>
|
|
192
|
+
minimize: () => ipc.windows.minimizeWindow(),
|
|
193
|
+
maximize: () => ipc.windows.maximizeWindow(),
|
|
194
|
+
close: () => ipc.windows.closeWindow(),
|
|
194
195
|
};
|
|
195
196
|
```
|
|
197
|
+
- `ensureElectronIpc()` 会基于 `DesktopIpcServices` 运行时生成 Proxy,并通过 `window.electronAPI.invoke` 与主进程通信;不再直接使用 `dispatch`。
|
|
196
198
|
|
|
197
199
|
### 5. 自定义窗口控制 (无边框窗口)
|
|
198
200
|
|
|
@@ -252,45 +254,33 @@ export const createMainWindow = () => {
|
|
|
252
254
|
|
|
253
255
|
```typescript
|
|
254
256
|
// apps/desktop/src/main/controllers/BrowserWindowsCtr.ts
|
|
257
|
+
import type { OpenSettingsWindowOptions } from '@lobechat/electron-client-ipc';
|
|
258
|
+
import { ControllerModule, IpcMethod } from '@/controllers';
|
|
259
|
+
|
|
260
|
+
export default class BrowserWindowsCtr extends ControllerModule {
|
|
261
|
+
static override readonly groupName = 'windows';
|
|
262
|
+
|
|
263
|
+
@IpcMethod()
|
|
264
|
+
async openSettingsWindow(options?: string | OpenSettingsWindowOptions) {
|
|
265
|
+
const normalizedOptions =
|
|
266
|
+
typeof options === 'string' || options === undefined
|
|
267
|
+
? { tab: typeof options === 'string' ? options : undefined }
|
|
268
|
+
: options;
|
|
269
|
+
|
|
270
|
+
const mainWindow = this.app.browserManager.getMainWindow();
|
|
271
|
+
const query = new URLSearchParams();
|
|
272
|
+
if (normalizedOptions.tab) query.set('active', normalizedOptions.tab);
|
|
273
|
+
if (normalizedOptions.searchParams) {
|
|
274
|
+
for (const [key, value] of Object.entries(normalizedOptions.searchParams)) {
|
|
275
|
+
if (value) query.set(key, value);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const fullPath = `/settings${query.size ? `?${query.toString()}` : ''}`;
|
|
280
|
+
await mainWindow.loadUrl(fullPath);
|
|
281
|
+
mainWindow.show();
|
|
255
282
|
|
|
256
|
-
@ipcClientEvent('openSettings')
|
|
257
|
-
handleOpenSettings() {
|
|
258
|
-
// 检查设置窗口是否已经存在
|
|
259
|
-
if (this.settingsWindow && !this.settingsWindow.isDestroyed()) {
|
|
260
|
-
// 如果窗口已存在,将其置于前台
|
|
261
|
-
this.settingsWindow.focus();
|
|
262
283
|
return { success: true };
|
|
263
284
|
}
|
|
264
|
-
|
|
265
|
-
// 创建新窗口
|
|
266
|
-
this.settingsWindow = new BrowserWindow({
|
|
267
|
-
width: 800,
|
|
268
|
-
height: 600,
|
|
269
|
-
title: 'Settings',
|
|
270
|
-
parent: this.mainWindow, // 设置父窗口,使其成为模态窗口
|
|
271
|
-
modal: true,
|
|
272
|
-
webPreferences: {
|
|
273
|
-
preload: path.join(__dirname, '../preload/index.js'),
|
|
274
|
-
contextIsolation: true,
|
|
275
|
-
nodeIntegration: false,
|
|
276
|
-
},
|
|
277
|
-
});
|
|
278
|
-
|
|
279
|
-
// 加载设置页面
|
|
280
|
-
if (isDev) {
|
|
281
|
-
this.settingsWindow.loadURL('http://localhost:3000/settings');
|
|
282
|
-
} else {
|
|
283
|
-
this.settingsWindow.loadFile(
|
|
284
|
-
path.join(__dirname, '../../renderer/index.html'),
|
|
285
|
-
{ hash: 'settings' }
|
|
286
|
-
);
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
// 监听窗口关闭事件
|
|
290
|
-
this.settingsWindow.on('closed', () => {
|
|
291
|
-
this.settingsWindow = null;
|
|
292
|
-
});
|
|
293
|
-
|
|
294
|
-
return { success: true };
|
|
295
285
|
}
|
|
296
286
|
```
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,58 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
## [Version 2.0.0-next.165](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.164...v2.0.0-next.165)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2025-12-09**</sup>
|
|
8
|
+
|
|
9
|
+
#### ♻ Code Refactoring
|
|
10
|
+
|
|
11
|
+
- **electron-main**: Client ipc decorate.
|
|
12
|
+
|
|
13
|
+
<br/>
|
|
14
|
+
|
|
15
|
+
<details>
|
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
17
|
+
|
|
18
|
+
#### Code refactoring
|
|
19
|
+
|
|
20
|
+
- **electron-main**: Client ipc decorate, closes [#10679](https://github.com/lobehub/lobe-chat/issues/10679) ([f74befa](https://github.com/lobehub/lobe-chat/commit/f74befa))
|
|
21
|
+
|
|
22
|
+
</details>
|
|
23
|
+
|
|
24
|
+
<div align="right">
|
|
25
|
+
|
|
26
|
+
[](#readme-top)
|
|
27
|
+
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
## [Version 2.0.0-next.164](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.163...v2.0.0-next.164)
|
|
31
|
+
|
|
32
|
+
<sup>Released on **2025-12-08**</sup>
|
|
33
|
+
|
|
34
|
+
#### 💄 Styles
|
|
35
|
+
|
|
36
|
+
- **profile**: Add mobile responsive layout and signup improvements.
|
|
37
|
+
- **misc**: Update link handling in PlanTag component to use react-router-dom.
|
|
38
|
+
|
|
39
|
+
<br/>
|
|
40
|
+
|
|
41
|
+
<details>
|
|
42
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
43
|
+
|
|
44
|
+
#### Styles
|
|
45
|
+
|
|
46
|
+
- **profile**: Add mobile responsive layout and signup improvements, closes [#10669](https://github.com/lobehub/lobe-chat/issues/10669) ([1afd471](https://github.com/lobehub/lobe-chat/commit/1afd471))
|
|
47
|
+
- **misc**: Update link handling in PlanTag component to use react-router-dom, closes [#10673](https://github.com/lobehub/lobe-chat/issues/10673) ([3aceeb6](https://github.com/lobehub/lobe-chat/commit/3aceeb6))
|
|
48
|
+
|
|
49
|
+
</details>
|
|
50
|
+
|
|
51
|
+
<div align="right">
|
|
52
|
+
|
|
53
|
+
[](#readme-top)
|
|
54
|
+
|
|
55
|
+
</div>
|
|
56
|
+
|
|
5
57
|
## [Version 2.0.0-next.163](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.162...v2.0.0-next.163)
|
|
6
58
|
|
|
7
59
|
<sup>Released on **2025-12-06**</sup>
|
package/README.md
CHANGED
|
@@ -345,12 +345,12 @@ In addition, these plugins are not limited to news aggregation, but can also ext
|
|
|
345
345
|
|
|
346
346
|
<!-- PLUGIN LIST -->
|
|
347
347
|
|
|
348
|
-
| Recent Submits
|
|
349
|
-
|
|
|
350
|
-
| [
|
|
351
|
-
| [
|
|
352
|
-
| [
|
|
353
|
-
| [
|
|
348
|
+
| Recent Submits | Description |
|
|
349
|
+
| --------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
|
|
350
|
+
| [AladinBooks](https://lobechat.com/discover/plugin/AladinSearchBooks)<br/><sup>By **azurewebsites** on **2025-12-08**</sup> | Search for books on Aladin.<br/>`book` `search` |
|
|
351
|
+
| [PortfolioMeta](https://lobechat.com/discover/plugin/StockData)<br/><sup>By **portfoliometa** on **2025-11-28**</sup> | Analyze stocks and get comprehensive real-time investment data and analytics.<br/>`stock` |
|
|
352
|
+
| [SEO](https://lobechat.com/discover/plugin/SEO)<br/><sup>By **orrenprunckun** on **2025-11-14**</sup> | Enter any URL and keyword and get an On-Page SEO analysis & insights!<br/>`seo` |
|
|
353
|
+
| [Shopping tools](https://lobechat.com/discover/plugin/ShoppingTools)<br/><sup>By **shoppingtools** on **2025-10-27**</sup> | Search for products on eBay & AliExpress, find eBay events & coupons. Get prompt examples.<br/>`shopping` `e-bay` `ali-express` `coupons` |
|
|
354
354
|
|
|
355
355
|
> 📊 Total plugins: [<kbd>**41**</kbd>](https://lobechat.com/discover/plugins)
|
|
356
356
|
|
package/README.zh-CN.md
CHANGED
|
@@ -338,12 +338,12 @@ LobeChat 的插件生态系统是其核心功能的重要扩展,它极大地
|
|
|
338
338
|
|
|
339
339
|
<!-- PLUGIN LIST -->
|
|
340
340
|
|
|
341
|
-
| 最近新增
|
|
342
|
-
|
|
|
343
|
-
| [
|
|
344
|
-
| [
|
|
345
|
-
| [
|
|
346
|
-
| [
|
|
341
|
+
| 最近新增 | 描述 |
|
|
342
|
+
| --------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
|
|
343
|
+
| [AladinBooks](https://lobechat.com/discover/plugin/AladinSearchBooks)<br/><sup>By **azurewebsites** on **2025-12-08**</sup> | 在阿拉丁上搜索书籍。<br/>`书籍` `搜索` |
|
|
344
|
+
| [PortfolioMeta](https://lobechat.com/discover/plugin/StockData)<br/><sup>By **portfoliometa** on **2025-11-28**</sup> | 分析股票并获取全面的实时投资数据和分析。<br/>`股票` |
|
|
345
|
+
| [SEO](https://lobechat.com/discover/plugin/SEO)<br/><sup>By **orrenprunckun** on **2025-11-14**</sup> | 输入任何 URL 和关键词,获取页面 SEO 分析和见解!<br/>`seo` |
|
|
346
|
+
| [购物工具](https://lobechat.com/discover/plugin/ShoppingTools)<br/><sup>By **shoppingtools** on **2025-10-27**</sup> | 在 eBay 和 AliExpress 上搜索产品,查找 eBay 活动和优惠券。获取快速示例。<br/>`购物` `e-bay` `ali-express` `优惠券` |
|
|
347
347
|
|
|
348
348
|
> 📊 Total plugins: [<kbd>**41**</kbd>](https://lobechat.com/discover/plugins)
|
|
349
349
|
|
|
@@ -156,24 +156,26 @@ apps/desktop/src/main/
|
|
|
156
156
|
- 事件广播:向渲染进程通知授权状态变化
|
|
157
157
|
|
|
158
158
|
```typescript
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
159
|
+
import { ControllerModule, IpcMethod } from '@/controllers';
|
|
160
|
+
|
|
161
|
+
export default class AuthCtr extends ControllerModule {
|
|
162
|
+
static override groupName = 'auth';
|
|
163
|
+
|
|
164
|
+
@IpcMethod()
|
|
165
|
+
async requestAuthorization(config: DataSyncConfig) {
|
|
166
|
+
this.authRequestState = crypto.randomBytes(16).toString('hex');
|
|
167
|
+
|
|
168
|
+
const authUrl = new URL('/oidc/auth', remoteUrl);
|
|
169
|
+
authUrl.search = querystring.stringify({
|
|
170
|
+
client_id: 'lobe-chat',
|
|
171
|
+
redirect_uri: `${protocolPrefix}://auth/callback`,
|
|
172
|
+
response_type: 'code',
|
|
173
|
+
scope: 'openid profile',
|
|
174
|
+
state: this.authRequestState,
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
await shell.openExternal(authUrl.toString());
|
|
178
|
+
}
|
|
177
179
|
}
|
|
178
180
|
```
|
|
179
181
|
|
|
@@ -267,20 +269,27 @@ export class ShortcutManager {
|
|
|
267
269
|
- 注入 App 实例
|
|
268
270
|
|
|
269
271
|
```typescript
|
|
270
|
-
|
|
272
|
+
import { ControllerModule, IpcMethod, IpcServerMethod } from '@/controllers'
|
|
273
|
+
|
|
271
274
|
export class ControllerModule implements IControllerModule {
|
|
272
275
|
constructor(public app: App) {
|
|
273
|
-
this.app = app
|
|
276
|
+
this.app = app
|
|
274
277
|
}
|
|
275
278
|
}
|
|
276
279
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
+
export class BrowserWindowsCtr extends ControllerModule {
|
|
281
|
+
static override readonly groupName = 'windows' // must be readonly
|
|
282
|
+
|
|
283
|
+
@IpcMethod()
|
|
284
|
+
openSettingsWindow(params?: OpenSettingsWindowOptions) {
|
|
285
|
+
// ...
|
|
286
|
+
}
|
|
280
287
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
288
|
+
@IpcServerMethod()
|
|
289
|
+
handleServerCommand(payload: any) {
|
|
290
|
+
// ...
|
|
291
|
+
}
|
|
292
|
+
}
|
|
284
293
|
```
|
|
285
294
|
|
|
286
295
|
2. **IoC 容器**:
|
|
@@ -346,26 +355,13 @@ makeSureDirExist(storagePath);
|
|
|
346
355
|
- 自动映射控制器方法到 IPC 事件
|
|
347
356
|
|
|
348
357
|
```typescript
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
});
|
|
357
|
-
|
|
358
|
-
// 注册服务器事件处理程序
|
|
359
|
-
const ipcServerEvents = {} as ElectronIPCEventHandler;
|
|
360
|
-
this.ipcServerEventMap.forEach((eventInfo, key) => {
|
|
361
|
-
ipcServerEvents[key] = async (payload) => {
|
|
362
|
-
return await eventInfo.controller[eventInfo.methodName](payload);
|
|
363
|
-
};
|
|
364
|
-
});
|
|
365
|
-
|
|
366
|
-
// 创建 IPC 服务器
|
|
367
|
-
this.ipcServer = new ElectronIPCServer(name, ipcServerEvents);
|
|
368
|
-
}
|
|
358
|
+
import { ensureElectronIpc } from '@/utils/electron/ipc';
|
|
359
|
+
|
|
360
|
+
// 渲染进程中使用 type-safe proxy 调用主进程方法
|
|
361
|
+
const ipc = ensureElectronIpc();
|
|
362
|
+
|
|
363
|
+
await ipc.localSystem.readLocalFile({ path });
|
|
364
|
+
await ipc.system.updateLocale('en-US');
|
|
369
365
|
```
|
|
370
366
|
|
|
371
367
|
2. **事件广播**:
|
package/apps/desktop/README.md
CHANGED
|
@@ -183,10 +183,18 @@ The `App.ts` class orchestrates the entire application lifecycle through key pha
|
|
|
183
183
|
#### 🔌 Dependency Injection & Event System
|
|
184
184
|
|
|
185
185
|
- **IoC Container** - WeakMap-based container for decorated controller methods
|
|
186
|
-
- **
|
|
186
|
+
- **Typed IPC Decorators** - `@IpcMethod` and `@IpcServerMethod` wire controller methods into type-safe channels
|
|
187
187
|
- **Automatic Event Mapping** - Events registered during controller loading
|
|
188
188
|
- **Service Locator** - Type-safe service and controller retrieval
|
|
189
189
|
|
|
190
|
+
##### 🧠 Type-Safe IPC Flow
|
|
191
|
+
|
|
192
|
+
- **Async Context Propagation** - `src/main/utils/ipc/base.ts` captures the `IpcContext` with `AsyncLocalStorage`, so controller logic can call `getIpcContext()` anywhere inside an IPC handler without explicitly threading arguments.
|
|
193
|
+
- **Service Constructors Registry** - `src/main/controllers/registry.ts` exports `controllerIpcConstructors`, `DesktopIpcServices`, and `DesktopServerIpcServices`, enabling automatic typing of both renderer and server IPC proxies.
|
|
194
|
+
- **Renderer Proxy Helper** - `src/utils/electron/ipc.ts` exposes `ensureElectronIpc()` which lazily builds a proxy on top of `window.electronAPI.invoke`, giving React/Next.js code a type-safe API surface without exposing raw proxies in preload.
|
|
195
|
+
- **Server Proxy Helper** - `src/server/modules/ElectronIPCClient/index.ts` mirrors the same typing strategy for the Next.js server runtime, providing a dedicated proxy for `@IpcServerMethod` handlers.
|
|
196
|
+
- **Shared Typings Package** - `apps/desktop/src/main/exports.d.ts` augments `@lobechat/electron-client-ipc` so every package can consume `DesktopIpcServices` without importing desktop business code directly.
|
|
197
|
+
|
|
190
198
|
#### 🪟 Window Management
|
|
191
199
|
|
|
192
200
|
- **Theme-Aware Windows** - Automatic adaptation to system dark/light mode
|
|
@@ -235,6 +243,7 @@ The `App.ts` class orchestrates the entire application lifecycle through key pha
|
|
|
235
243
|
|
|
236
244
|
#### 🎮 Controller Pattern
|
|
237
245
|
|
|
246
|
+
- **Typed IPC Decorators** - Controllers extend `ControllerModule` and expose renderer methods via `@IpcMethod`
|
|
238
247
|
- **IPC Event Handling** - Processes events from renderer with decorator-based registration
|
|
239
248
|
- **Lifecycle Hooks** - `beforeAppReady` and `afterAppReady` for initialization phases
|
|
240
249
|
- **Type-Safe Communication** - Strong typing for all IPC events and responses
|
|
@@ -256,6 +265,33 @@ The `App.ts` class orchestrates the entire application lifecycle through key pha
|
|
|
256
265
|
- **Context Awareness** - Events include sender context for window-specific operations
|
|
257
266
|
- **Error Propagation** - Centralized error handling with proper status codes
|
|
258
267
|
|
|
268
|
+
##### 🧩 Renderer IPC Helper
|
|
269
|
+
|
|
270
|
+
Renderer code uses a lightweight proxy generated at runtime to keep IPC calls type-safe without exposing raw Electron objects through `contextBridge`. Use the helper exported from `src/utils/electron/ipc.ts` to access the main-process services:
|
|
271
|
+
|
|
272
|
+
```ts
|
|
273
|
+
import { ensureElectronIpc } from '@/utils/electron/ipc';
|
|
274
|
+
|
|
275
|
+
const ipc = ensureElectronIpc();
|
|
276
|
+
await ipc.windows.openSettingsWindow({ tab: 'provider' });
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
The helper internally builds a proxy on top of `window.electronAPI.invoke`, so no proxy objects need to be cloned across the preload boundary.
|
|
280
|
+
|
|
281
|
+
##### 🖥️ Server IPC Helper
|
|
282
|
+
|
|
283
|
+
Next.js (Node) modules use the same proxy pattern via `ensureElectronServerIpc` from `src/server/modules/ElectronIPCClient`. It lazily wraps the socket-based `ElectronIpcClient` so server code can call controllers with full type safety:
|
|
284
|
+
|
|
285
|
+
```ts
|
|
286
|
+
import { ensureElectronServerIpc } from '@/server/modules/ElectronIPCClient';
|
|
287
|
+
|
|
288
|
+
const ipc = ensureElectronServerIpc();
|
|
289
|
+
const dbPath = await ipc.system.getDatabasePath();
|
|
290
|
+
await ipc.upload.deleteFiles(['foo.txt']);
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
All server methods are declared via `@IpcServerMethod` and live in dedicated controller classes, keeping renderer typings clean.
|
|
294
|
+
|
|
259
295
|
#### 🛡️ Security Features
|
|
260
296
|
|
|
261
297
|
- **OAuth 2.0 + PKCE** - Secure authentication with state parameter validation
|
|
@@ -183,7 +183,7 @@ src/main/core/
|
|
|
183
183
|
#### 🔌 依赖注入和事件系统
|
|
184
184
|
|
|
185
185
|
- **IoC 容器** - 基于 WeakMap 的装饰控制器方法容器
|
|
186
|
-
- **装饰器注册** - `@
|
|
186
|
+
- **装饰器注册** - `@IpcMethod` 和 `@IpcServerMethod` 装饰器
|
|
187
187
|
- **自动事件映射** - 控制器加载期间注册的事件
|
|
188
188
|
- **服务定位器** - 类型安全的服务和控制器检索
|
|
189
189
|
|
|
@@ -256,6 +256,31 @@ src/main/core/
|
|
|
256
256
|
- **上下文感知** - 事件包含用于窗口特定操作的发送者上下文
|
|
257
257
|
- **错误传播** - 具有适当状态码的集中错误处理
|
|
258
258
|
|
|
259
|
+
##### 🧩 渲染器 IPC 助手
|
|
260
|
+
|
|
261
|
+
渲染端通过 `src/utils/electron/ipc.ts` 提供的 `ensureElectronIpc` 获得一个运行时代理,无需在 preload 中暴露 Proxy 对象即可获得类型安全的调用体验:
|
|
262
|
+
|
|
263
|
+
```ts
|
|
264
|
+
import { ensureElectronIpc } from '@/utils/electron/ipc';
|
|
265
|
+
|
|
266
|
+
const ipc = ensureElectronIpc();
|
|
267
|
+
await ipc.windows.openSettingsWindow({ tab: 'provider' });
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
##### 🖥️ Server IPC 助手
|
|
271
|
+
|
|
272
|
+
Next.js 服务端模块可通过 `ensureElectronServerIpc`(位于 `src/server/modules/ElectronIPCClient`)获得同样的类型安全代理,并复用 socket IPC 通道:
|
|
273
|
+
|
|
274
|
+
```ts
|
|
275
|
+
import { ensureElectronServerIpc } from '@/server/modules/ElectronIPCClient';
|
|
276
|
+
|
|
277
|
+
const ipc = ensureElectronServerIpc();
|
|
278
|
+
const path = await ipc.system.getDatabasePath();
|
|
279
|
+
await ipc.upload.deleteFiles(['foo.txt']);
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
所有 `@IpcServerMethod` 方法都放在独立的控制器中,这样渲染端的类型推导不会包含这些仅供服务器调用的通道。
|
|
283
|
+
|
|
259
284
|
#### 🛡️ 安全功能
|
|
260
285
|
|
|
261
286
|
- **OAuth 2.0 + PKCE** - 具有状态参数验证的安全认证
|
|
@@ -7,7 +7,7 @@ import { URL } from 'node:url';
|
|
|
7
7
|
import { createLogger } from '@/utils/logger';
|
|
8
8
|
|
|
9
9
|
import RemoteServerConfigCtr from './RemoteServerConfigCtr';
|
|
10
|
-
import { ControllerModule,
|
|
10
|
+
import { ControllerModule, IpcMethod } from './index';
|
|
11
11
|
|
|
12
12
|
// Create logger
|
|
13
13
|
const logger = createLogger('controllers:AuthCtr');
|
|
@@ -17,6 +17,7 @@ const logger = createLogger('controllers:AuthCtr');
|
|
|
17
17
|
* Implements OAuth authorization flow using intermediate page + polling mechanism
|
|
18
18
|
*/
|
|
19
19
|
export default class AuthCtr extends ControllerModule {
|
|
20
|
+
static override readonly groupName = 'auth';
|
|
20
21
|
/**
|
|
21
22
|
* Remote server configuration controller
|
|
22
23
|
*/
|
|
@@ -56,7 +57,7 @@ export default class AuthCtr extends ControllerModule {
|
|
|
56
57
|
/**
|
|
57
58
|
* Request OAuth authorization
|
|
58
59
|
*/
|
|
59
|
-
@
|
|
60
|
+
@IpcMethod()
|
|
60
61
|
async requestAuthorization(config: DataSyncConfig) {
|
|
61
62
|
// Clear any old authorization state
|
|
62
63
|
this.clearAuthorizationState();
|
|
@@ -119,7 +120,7 @@ export default class AuthCtr extends ControllerModule {
|
|
|
119
120
|
/**
|
|
120
121
|
* Request Market OAuth authorization (desktop)
|
|
121
122
|
*/
|
|
122
|
-
@
|
|
123
|
+
@IpcMethod()
|
|
123
124
|
async requestMarketAuthorization(params: MarketAuthorizationParams) {
|
|
124
125
|
const { authUrl } = params;
|
|
125
126
|
|