@lobehub/lobehub 2.0.0-next.164 → 2.0.0-next.166
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 +50 -0
- package/Dockerfile +44 -52
- 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 +10 -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]/(main)/discover/(detail)/provider/features/Sidebar/ActionButton/ProviderConfig.tsx +2 -2
- 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
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
|
|
|
@@ -1,22 +1,21 @@
|
|
|
1
1
|
import { InterceptRouteParams, OpenSettingsWindowOptions } from '@lobechat/electron-client-ipc';
|
|
2
2
|
import { findMatchingRoute } from '~common/routes';
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
WindowTemplateIdentifiers,
|
|
7
|
-
} from '@/appBrowsers';
|
|
8
|
-
import { IpcClientEventSender } from '@/types/ipcClientEvent';
|
|
4
|
+
import { AppBrowsersIdentifiers, WindowTemplateIdentifiers } from '@/appBrowsers';
|
|
5
|
+
import { getIpcContext } from '@/utils/ipc';
|
|
9
6
|
|
|
10
|
-
import { ControllerModule,
|
|
7
|
+
import { ControllerModule, IpcMethod, shortcut } from './index';
|
|
11
8
|
|
|
12
9
|
export default class BrowserWindowsCtr extends ControllerModule {
|
|
10
|
+
static override readonly groupName = 'windows';
|
|
11
|
+
|
|
13
12
|
@shortcut('showApp')
|
|
14
13
|
async toggleMainWindow() {
|
|
15
14
|
const mainWindow = this.app.browserManager.getMainWindow();
|
|
16
15
|
mainWindow.toggleVisible();
|
|
17
16
|
}
|
|
18
17
|
|
|
19
|
-
@
|
|
18
|
+
@IpcMethod()
|
|
20
19
|
async openSettingsWindow(options?: string | OpenSettingsWindowOptions) {
|
|
21
20
|
const normalizedOptions: OpenSettingsWindowOptions =
|
|
22
21
|
typeof options === 'string' || options === undefined
|
|
@@ -53,26 +52,32 @@ export default class BrowserWindowsCtr extends ControllerModule {
|
|
|
53
52
|
}
|
|
54
53
|
}
|
|
55
54
|
|
|
56
|
-
@
|
|
57
|
-
closeWindow(
|
|
58
|
-
this.
|
|
55
|
+
@IpcMethod()
|
|
56
|
+
closeWindow() {
|
|
57
|
+
this.withSenderIdentifier((identifier) => {
|
|
58
|
+
this.app.browserManager.closeWindow(identifier);
|
|
59
|
+
});
|
|
59
60
|
}
|
|
60
61
|
|
|
61
|
-
@
|
|
62
|
-
minimizeWindow(
|
|
63
|
-
this.
|
|
62
|
+
@IpcMethod()
|
|
63
|
+
minimizeWindow() {
|
|
64
|
+
this.withSenderIdentifier((identifier) => {
|
|
65
|
+
this.app.browserManager.minimizeWindow(identifier);
|
|
66
|
+
});
|
|
64
67
|
}
|
|
65
68
|
|
|
66
|
-
@
|
|
67
|
-
maximizeWindow(
|
|
68
|
-
this.
|
|
69
|
+
@IpcMethod()
|
|
70
|
+
maximizeWindow() {
|
|
71
|
+
this.withSenderIdentifier((identifier) => {
|
|
72
|
+
this.app.browserManager.maximizeWindow(identifier);
|
|
73
|
+
});
|
|
69
74
|
}
|
|
70
75
|
|
|
71
76
|
/**
|
|
72
77
|
* Handle route interception requests
|
|
73
78
|
* Responsible for handling route interception requests from the renderer process
|
|
74
79
|
*/
|
|
75
|
-
@
|
|
80
|
+
@IpcMethod()
|
|
76
81
|
async interceptRoute(params: InterceptRouteParams) {
|
|
77
82
|
const { path, source } = params;
|
|
78
83
|
console.log(
|
|
@@ -115,7 +120,7 @@ export default class BrowserWindowsCtr extends ControllerModule {
|
|
|
115
120
|
/**
|
|
116
121
|
* Create a new multi-instance window
|
|
117
122
|
*/
|
|
118
|
-
@
|
|
123
|
+
@IpcMethod()
|
|
119
124
|
async createMultiInstanceWindow(params: {
|
|
120
125
|
path: string;
|
|
121
126
|
templateId: WindowTemplateIdentifiers;
|
|
@@ -149,7 +154,7 @@ export default class BrowserWindowsCtr extends ControllerModule {
|
|
|
149
154
|
/**
|
|
150
155
|
* Get all windows by template
|
|
151
156
|
*/
|
|
152
|
-
@
|
|
157
|
+
@IpcMethod()
|
|
153
158
|
async getWindowsByTemplate(templateId: string) {
|
|
154
159
|
try {
|
|
155
160
|
const windowIds = this.app.browserManager.getWindowsByTemplate(templateId);
|
|
@@ -169,7 +174,7 @@ export default class BrowserWindowsCtr extends ControllerModule {
|
|
|
169
174
|
/**
|
|
170
175
|
* Close all windows by template
|
|
171
176
|
*/
|
|
172
|
-
@
|
|
177
|
+
@IpcMethod()
|
|
173
178
|
async closeWindowsByTemplate(templateId: string) {
|
|
174
179
|
try {
|
|
175
180
|
this.app.browserManager.closeWindowsByTemplate(templateId);
|
|
@@ -191,4 +196,12 @@ export default class BrowserWindowsCtr extends ControllerModule {
|
|
|
191
196
|
const browser = this.app.browserManager.retrieveByIdentifier(targetWindow);
|
|
192
197
|
browser.show();
|
|
193
198
|
}
|
|
199
|
+
|
|
200
|
+
private withSenderIdentifier(fn: (identifier: string) => void) {
|
|
201
|
+
const context = getIpcContext();
|
|
202
|
+
if (!context) return;
|
|
203
|
+
const identifier = this.app.browserManager.getIdentifierByWebContents(context.sender);
|
|
204
|
+
if (!identifier) return;
|
|
205
|
+
fn(identifier);
|
|
206
|
+
}
|
|
194
207
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { ControllerModule,
|
|
1
|
+
import { ControllerModule, IpcMethod } from './index';
|
|
2
2
|
|
|
3
3
|
export default class DevtoolsCtr extends ControllerModule {
|
|
4
|
-
|
|
4
|
+
static override readonly groupName = 'devtools';
|
|
5
|
+
|
|
6
|
+
@IpcMethod()
|
|
5
7
|
async openDevtools() {
|
|
6
8
|
const devtoolsBrowser = this.app.browserManager.retrieveByIdentifier('devtools');
|
|
7
9
|
devtoolsBrowser.show();
|
|
@@ -30,19 +30,20 @@ import { FileResult, SearchOptions } from '@/types/fileSearch';
|
|
|
30
30
|
import { makeSureDirExist } from '@/utils/file-system';
|
|
31
31
|
import { createLogger } from '@/utils/logger';
|
|
32
32
|
|
|
33
|
-
import { ControllerModule,
|
|
33
|
+
import { ControllerModule, IpcMethod } from './index';
|
|
34
34
|
|
|
35
35
|
// Create logger
|
|
36
36
|
const logger = createLogger('controllers:LocalFileCtr');
|
|
37
37
|
|
|
38
38
|
export default class LocalFileCtr extends ControllerModule {
|
|
39
|
+
static override readonly groupName = 'localSystem';
|
|
39
40
|
private get searchService() {
|
|
40
41
|
return this.app.getService(FileSearchService);
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
// ==================== File Operation ====================
|
|
44
45
|
|
|
45
|
-
@
|
|
46
|
+
@IpcMethod()
|
|
46
47
|
async handleOpenLocalFile({ path: filePath }: OpenLocalFileParams): Promise<{
|
|
47
48
|
error?: string;
|
|
48
49
|
success: boolean;
|
|
@@ -59,7 +60,7 @@ export default class LocalFileCtr extends ControllerModule {
|
|
|
59
60
|
}
|
|
60
61
|
}
|
|
61
62
|
|
|
62
|
-
@
|
|
63
|
+
@IpcMethod()
|
|
63
64
|
async handleOpenLocalFolder({ path: targetPath, isDirectory }: OpenLocalFolderParams): Promise<{
|
|
64
65
|
error?: string;
|
|
65
66
|
success: boolean;
|
|
@@ -77,7 +78,7 @@ export default class LocalFileCtr extends ControllerModule {
|
|
|
77
78
|
}
|
|
78
79
|
}
|
|
79
80
|
|
|
80
|
-
@
|
|
81
|
+
@IpcMethod()
|
|
81
82
|
async readFiles({ paths }: LocalReadFilesParams): Promise<LocalReadFileResult[]> {
|
|
82
83
|
logger.debug('Starting batch file reading:', { count: paths.length });
|
|
83
84
|
|
|
@@ -94,7 +95,7 @@ export default class LocalFileCtr extends ControllerModule {
|
|
|
94
95
|
return results;
|
|
95
96
|
}
|
|
96
97
|
|
|
97
|
-
@
|
|
98
|
+
@IpcMethod()
|
|
98
99
|
async readFile({
|
|
99
100
|
path: filePath,
|
|
100
101
|
loc,
|
|
@@ -192,7 +193,7 @@ export default class LocalFileCtr extends ControllerModule {
|
|
|
192
193
|
}
|
|
193
194
|
}
|
|
194
195
|
|
|
195
|
-
@
|
|
196
|
+
@IpcMethod()
|
|
196
197
|
async listLocalFiles({ path: dirPath }: ListLocalFileParams): Promise<FileResult[]> {
|
|
197
198
|
logger.debug('Listing directory contents:', { dirPath });
|
|
198
199
|
|
|
@@ -250,7 +251,7 @@ export default class LocalFileCtr extends ControllerModule {
|
|
|
250
251
|
}
|
|
251
252
|
}
|
|
252
253
|
|
|
253
|
-
@
|
|
254
|
+
@IpcMethod()
|
|
254
255
|
async handleMoveFiles({ items }: MoveLocalFilesParams): Promise<LocalMoveFilesResultItem[]> {
|
|
255
256
|
logger.debug('Starting batch file move:', { itemsCount: items?.length });
|
|
256
257
|
|
|
@@ -355,7 +356,7 @@ export default class LocalFileCtr extends ControllerModule {
|
|
|
355
356
|
return results;
|
|
356
357
|
}
|
|
357
358
|
|
|
358
|
-
@
|
|
359
|
+
@IpcMethod()
|
|
359
360
|
async handleRenameFile({
|
|
360
361
|
path: currentPath,
|
|
361
362
|
newName,
|
|
@@ -440,7 +441,7 @@ export default class LocalFileCtr extends ControllerModule {
|
|
|
440
441
|
}
|
|
441
442
|
}
|
|
442
443
|
|
|
443
|
-
@
|
|
444
|
+
@IpcMethod()
|
|
444
445
|
async handleWriteFile({ path: filePath, content }: WriteLocalFileParams) {
|
|
445
446
|
const logPrefix = `[Writing file ${filePath}]`;
|
|
446
447
|
logger.debug(`${logPrefix} Starting to write file`, { contentLength: content?.length });
|
|
@@ -485,7 +486,7 @@ export default class LocalFileCtr extends ControllerModule {
|
|
|
485
486
|
/**
|
|
486
487
|
* Handle IPC event for local file search
|
|
487
488
|
*/
|
|
488
|
-
@
|
|
489
|
+
@IpcMethod()
|
|
489
490
|
async handleLocalFilesSearch(params: LocalSearchFilesParams): Promise<FileResult[]> {
|
|
490
491
|
logger.debug('Received file search request:', {
|
|
491
492
|
directory: params.directory,
|
|
@@ -523,7 +524,7 @@ export default class LocalFileCtr extends ControllerModule {
|
|
|
523
524
|
}
|
|
524
525
|
}
|
|
525
526
|
|
|
526
|
-
@
|
|
527
|
+
@IpcMethod()
|
|
527
528
|
async handleGrepContent(params: GrepContentParams): Promise<GrepContentResult> {
|
|
528
529
|
const {
|
|
529
530
|
pattern,
|
|
@@ -639,7 +640,7 @@ export default class LocalFileCtr extends ControllerModule {
|
|
|
639
640
|
}
|
|
640
641
|
}
|
|
641
642
|
|
|
642
|
-
@
|
|
643
|
+
@IpcMethod()
|
|
643
644
|
async handleGlobFiles({
|
|
644
645
|
path: searchPath = process.cwd(),
|
|
645
646
|
pattern,
|
|
@@ -680,7 +681,7 @@ export default class LocalFileCtr extends ControllerModule {
|
|
|
680
681
|
|
|
681
682
|
// ==================== File Editing ====================
|
|
682
683
|
|
|
683
|
-
@
|
|
684
|
+
@IpcMethod()
|
|
684
685
|
async handleEditFile({
|
|
685
686
|
file_path: filePath,
|
|
686
687
|
new_string,
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { ControllerModule,
|
|
1
|
+
import { ControllerModule, IpcMethod } from './index';
|
|
2
2
|
|
|
3
3
|
export default class MenuController extends ControllerModule {
|
|
4
|
+
static override readonly groupName = 'menu';
|
|
4
5
|
/**
|
|
5
6
|
* Refresh menu
|
|
6
7
|
*/
|
|
7
|
-
@
|
|
8
|
+
@IpcMethod()
|
|
8
9
|
refreshAppMenu() {
|
|
9
10
|
// Note: May need to decide whether to allow renderer process to refresh all menus based on specific circumstances
|
|
10
11
|
return this.app.menuManager.refreshMenus();
|
|
@@ -13,7 +14,7 @@ export default class MenuController extends ControllerModule {
|
|
|
13
14
|
/**
|
|
14
15
|
* Show context menu
|
|
15
16
|
*/
|
|
16
|
-
@
|
|
17
|
+
@IpcMethod()
|
|
17
18
|
showContextMenu(params: { data?: any; type: string }) {
|
|
18
19
|
return this.app.menuManager.showContextMenu(params.type, params.data);
|
|
19
20
|
}
|
|
@@ -21,7 +22,7 @@ export default class MenuController extends ControllerModule {
|
|
|
21
22
|
/**
|
|
22
23
|
* Set development menu visibility
|
|
23
24
|
*/
|
|
24
|
-
@
|
|
25
|
+
@IpcMethod()
|
|
25
26
|
setDevMenuVisibility(visible: boolean) {
|
|
26
27
|
// Call MenuManager method to rebuild application menu
|
|
27
28
|
return this.app.menuManager.rebuildAppMenu({ showDevItems: visible });
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
ProxyDispatcherManager,
|
|
12
12
|
ProxyTestResult,
|
|
13
13
|
} from '../modules/networkProxy';
|
|
14
|
-
import { ControllerModule,
|
|
14
|
+
import { ControllerModule, IpcMethod } from './index';
|
|
15
15
|
|
|
16
16
|
// Create logger
|
|
17
17
|
const logger = createLogger('controllers:NetworkProxyCtr');
|
|
@@ -21,10 +21,11 @@ const logger = createLogger('controllers:NetworkProxyCtr');
|
|
|
21
21
|
* 处理桌面应用的网络代理相关功能
|
|
22
22
|
*/
|
|
23
23
|
export default class NetworkProxyCtr extends ControllerModule {
|
|
24
|
+
static override readonly groupName = 'networkProxy';
|
|
24
25
|
/**
|
|
25
26
|
* 获取代理设置
|
|
26
27
|
*/
|
|
27
|
-
@
|
|
28
|
+
@IpcMethod()
|
|
28
29
|
async getDesktopSettings(): Promise<NetworkProxySettings> {
|
|
29
30
|
try {
|
|
30
31
|
const settings = this.app.storeManager.get(
|
|
@@ -45,32 +46,30 @@ export default class NetworkProxyCtr extends ControllerModule {
|
|
|
45
46
|
/**
|
|
46
47
|
* 设置代理配置
|
|
47
48
|
*/
|
|
48
|
-
@
|
|
49
|
-
async setProxySettings(config: NetworkProxySettings): Promise<void> {
|
|
49
|
+
@IpcMethod()
|
|
50
|
+
async setProxySettings(config: Partial<NetworkProxySettings>): Promise<void> {
|
|
50
51
|
try {
|
|
51
|
-
// 验证配置
|
|
52
|
-
const validation = ProxyConfigValidator.validate(config);
|
|
53
|
-
if (!validation.isValid) {
|
|
54
|
-
const errorMessage = `Invalid proxy configuration: ${validation.errors.join(', ')}`;
|
|
55
|
-
logger.error(errorMessage);
|
|
56
|
-
throw new Error(errorMessage);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
52
|
// 获取当前配置
|
|
60
53
|
const currentConfig = this.app.storeManager.get(
|
|
61
54
|
'networkProxy',
|
|
62
55
|
defaultProxySettings,
|
|
63
56
|
) as NetworkProxySettings;
|
|
64
57
|
|
|
65
|
-
//
|
|
66
|
-
|
|
58
|
+
// 合并配置并验证
|
|
59
|
+
const newConfig = merge({}, currentConfig, config);
|
|
60
|
+
|
|
61
|
+
const validation = ProxyConfigValidator.validate(newConfig);
|
|
62
|
+
if (!validation.isValid) {
|
|
63
|
+
const errorMessage = `Invalid proxy configuration: ${validation.errors.join(', ')}`;
|
|
64
|
+
logger.error(errorMessage);
|
|
65
|
+
throw new Error(errorMessage);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (isEqual(currentConfig, newConfig)) {
|
|
67
69
|
logger.debug('Proxy settings unchanged, skipping update');
|
|
68
70
|
return;
|
|
69
71
|
}
|
|
70
72
|
|
|
71
|
-
// 合并配置
|
|
72
|
-
const newConfig = merge({}, currentConfig, config);
|
|
73
|
-
|
|
74
73
|
// 应用代理设置
|
|
75
74
|
await ProxyDispatcherManager.applyProxySettings(newConfig);
|
|
76
75
|
|
|
@@ -92,7 +91,7 @@ export default class NetworkProxyCtr extends ControllerModule {
|
|
|
92
91
|
/**
|
|
93
92
|
* 测试代理连接
|
|
94
93
|
*/
|
|
95
|
-
@
|
|
94
|
+
@IpcMethod()
|
|
96
95
|
async testProxyConnection(url: string): Promise<{ message?: string; success: boolean }> {
|
|
97
96
|
try {
|
|
98
97
|
const result = await ProxyConnectionTester.testConnection(url);
|
|
@@ -112,7 +111,7 @@ export default class NetworkProxyCtr extends ControllerModule {
|
|
|
112
111
|
/**
|
|
113
112
|
* 测试指定代理配置
|
|
114
113
|
*/
|
|
115
|
-
@
|
|
114
|
+
@IpcMethod()
|
|
116
115
|
async testProxyConfig({
|
|
117
116
|
config,
|
|
118
117
|
testUrl,
|
|
@@ -7,11 +7,12 @@ import { macOS, windows } from 'electron-is';
|
|
|
7
7
|
|
|
8
8
|
import { createLogger } from '@/utils/logger';
|
|
9
9
|
|
|
10
|
-
import { ControllerModule,
|
|
10
|
+
import { ControllerModule, IpcMethod } from './index';
|
|
11
11
|
|
|
12
12
|
const logger = createLogger('controllers:NotificationCtr');
|
|
13
13
|
|
|
14
14
|
export default class NotificationCtr extends ControllerModule {
|
|
15
|
+
static override readonly groupName = 'notification';
|
|
15
16
|
/**
|
|
16
17
|
* Set up desktop notifications after the application is ready
|
|
17
18
|
*/
|
|
@@ -51,7 +52,7 @@ export default class NotificationCtr extends ControllerModule {
|
|
|
51
52
|
/**
|
|
52
53
|
* Show system desktop notification (only when window is hidden)
|
|
53
54
|
*/
|
|
54
|
-
@
|
|
55
|
+
@IpcMethod()
|
|
55
56
|
async showDesktopNotification(
|
|
56
57
|
params: ShowDesktopNotificationParams,
|
|
57
58
|
): Promise<DesktopNotificationResult> {
|
|
@@ -126,7 +127,7 @@ export default class NotificationCtr extends ControllerModule {
|
|
|
126
127
|
/**
|
|
127
128
|
* Check if the main window is hidden
|
|
128
129
|
*/
|
|
129
|
-
@
|
|
130
|
+
@IpcMethod()
|
|
130
131
|
isMainWindowHidden(): boolean {
|
|
131
132
|
try {
|
|
132
133
|
const mainWindow = this.app.browserManager.getMainWindow();
|
|
@@ -7,7 +7,7 @@ import { URL } from 'node:url';
|
|
|
7
7
|
import { OFFICIAL_CLOUD_SERVER } from '@/const/env';
|
|
8
8
|
import { createLogger } from '@/utils/logger';
|
|
9
9
|
|
|
10
|
-
import { ControllerModule,
|
|
10
|
+
import { ControllerModule, IpcMethod } from './index';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Non-retryable OIDC error codes
|
|
@@ -39,6 +39,7 @@ const logger = createLogger('controllers:RemoteServerConfigCtr');
|
|
|
39
39
|
* Used to manage custom remote LobeChat server configuration
|
|
40
40
|
*/
|
|
41
41
|
export default class RemoteServerConfigCtr extends ControllerModule {
|
|
42
|
+
static override readonly groupName = 'remoteServer';
|
|
42
43
|
/**
|
|
43
44
|
* Key used to store encrypted tokens in electron-store.
|
|
44
45
|
*/
|
|
@@ -47,7 +48,7 @@ export default class RemoteServerConfigCtr extends ControllerModule {
|
|
|
47
48
|
/**
|
|
48
49
|
* Get remote server configuration
|
|
49
50
|
*/
|
|
50
|
-
@
|
|
51
|
+
@IpcMethod()
|
|
51
52
|
async getRemoteServerConfig() {
|
|
52
53
|
logger.debug('Getting remote server configuration');
|
|
53
54
|
const { storeManager } = this.app;
|
|
@@ -64,7 +65,7 @@ export default class RemoteServerConfigCtr extends ControllerModule {
|
|
|
64
65
|
/**
|
|
65
66
|
* Set remote server configuration
|
|
66
67
|
*/
|
|
67
|
-
@
|
|
68
|
+
@IpcMethod()
|
|
68
69
|
async setRemoteServerConfig(config: Partial<DataSyncConfig>) {
|
|
69
70
|
logger.info(
|
|
70
71
|
`Setting remote server storageMode: active=${config.active}, storageMode=${config.storageMode}, url=${config.remoteServerUrl}`,
|
|
@@ -81,7 +82,7 @@ export default class RemoteServerConfigCtr extends ControllerModule {
|
|
|
81
82
|
/**
|
|
82
83
|
* Clear remote server configuration
|
|
83
84
|
*/
|
|
84
|
-
@
|
|
85
|
+
@IpcMethod()
|
|
85
86
|
async clearRemoteServerConfig() {
|
|
86
87
|
logger.info('Clearing remote server configuration');
|
|
87
88
|
const { storeManager } = this.app;
|
|
@@ -15,7 +15,7 @@ import { defaultProxySettings } from '@/const/store';
|
|
|
15
15
|
import { createLogger } from '@/utils/logger';
|
|
16
16
|
|
|
17
17
|
import RemoteServerConfigCtr from './RemoteServerConfigCtr';
|
|
18
|
-
import { ControllerModule,
|
|
18
|
+
import { ControllerModule, IpcMethod } from './index';
|
|
19
19
|
|
|
20
20
|
// Create logger
|
|
21
21
|
const logger = createLogger('controllers:RemoteServerSyncCtr');
|
|
@@ -25,6 +25,7 @@ const logger = createLogger('controllers:RemoteServerSyncCtr');
|
|
|
25
25
|
* For handling data synchronization with remote servers via IPC.
|
|
26
26
|
*/
|
|
27
27
|
export default class RemoteServerSyncCtr extends ControllerModule {
|
|
28
|
+
static override readonly groupName = 'remoteServerSync';
|
|
28
29
|
/**
|
|
29
30
|
* Cached instance of RemoteServerConfigCtr
|
|
30
31
|
*/
|
|
@@ -345,7 +346,7 @@ export default class RemoteServerSyncCtr extends ControllerModule {
|
|
|
345
346
|
* Handles the 'proxy-trpc-request' IPC call from the renderer process.
|
|
346
347
|
* This method should be invoked by the ipcMain.handle setup in your main process entry point.
|
|
347
348
|
*/
|
|
348
|
-
@
|
|
349
|
+
@IpcMethod()
|
|
349
350
|
public async proxyTRPCRequest(args: ProxyTRPCRequestParams): Promise<ProxyTRPCRequestResult> {
|
|
350
351
|
logger.debug('Received proxyTRPCRequest IPC call:', {
|
|
351
352
|
headers: args.headers,
|
|
@@ -11,7 +11,7 @@ import { randomUUID } from 'node:crypto';
|
|
|
11
11
|
|
|
12
12
|
import { createLogger } from '@/utils/logger';
|
|
13
13
|
|
|
14
|
-
import { ControllerModule,
|
|
14
|
+
import { ControllerModule, IpcMethod } from './index';
|
|
15
15
|
|
|
16
16
|
const logger = createLogger('controllers:ShellCommandCtr');
|
|
17
17
|
|
|
@@ -24,10 +24,11 @@ interface ShellProcess {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
export default class ShellCommandCtr extends ControllerModule {
|
|
27
|
+
static override readonly groupName = 'shellCommand';
|
|
27
28
|
// Shell process management
|
|
28
29
|
private shellProcesses = new Map<string, ShellProcess>();
|
|
29
30
|
|
|
30
|
-
@
|
|
31
|
+
@IpcMethod()
|
|
31
32
|
async handleRunCommand({
|
|
32
33
|
command,
|
|
33
34
|
description,
|
|
@@ -153,7 +154,7 @@ export default class ShellCommandCtr extends ControllerModule {
|
|
|
153
154
|
}
|
|
154
155
|
}
|
|
155
156
|
|
|
156
|
-
@
|
|
157
|
+
@IpcMethod()
|
|
157
158
|
async handleGetCommandOutput({
|
|
158
159
|
filter,
|
|
159
160
|
shell_id,
|
|
@@ -212,7 +213,7 @@ export default class ShellCommandCtr extends ControllerModule {
|
|
|
212
213
|
};
|
|
213
214
|
}
|
|
214
215
|
|
|
215
|
-
@
|
|
216
|
+
@IpcMethod()
|
|
216
217
|
async handleKillCommand({ shell_id }: KillCommandParams): Promise<KillCommandResult> {
|
|
217
218
|
const logPrefix = `[killCommand: ${shell_id}]`;
|
|
218
219
|
logger.debug(`${logPrefix} Attempting to kill shell`);
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { ShortcutUpdateResult } from '@/core/ui/ShortcutManager';
|
|
2
2
|
|
|
3
|
-
import { ControllerModule,
|
|
3
|
+
import { ControllerModule, IpcMethod } from '.';
|
|
4
4
|
|
|
5
5
|
export default class ShortcutController extends ControllerModule {
|
|
6
|
+
static override readonly groupName = 'shortcut';
|
|
6
7
|
/**
|
|
7
8
|
* Get all shortcut configurations
|
|
8
9
|
*/
|
|
9
|
-
@
|
|
10
|
+
@IpcMethod()
|
|
10
11
|
getShortcutsConfig() {
|
|
11
12
|
return this.app.shortcutManager.getShortcutsConfig();
|
|
12
13
|
}
|
|
@@ -14,7 +15,7 @@ export default class ShortcutController extends ControllerModule {
|
|
|
14
15
|
/**
|
|
15
16
|
* Update a single shortcut configuration
|
|
16
17
|
*/
|
|
17
|
-
@
|
|
18
|
+
@IpcMethod()
|
|
18
19
|
updateShortcutConfig({
|
|
19
20
|
id,
|
|
20
21
|
accelerator,
|