@lobehub/lobehub 2.0.0-next.164 → 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.
Files changed (101) hide show
  1. package/.cursor/rules/desktop-feature-implementation.mdc +31 -34
  2. package/.cursor/rules/desktop-local-tools-implement.mdc +3 -3
  3. package/.cursor/rules/desktop-window-management.mdc +56 -66
  4. package/CHANGELOG.md +25 -0
  5. package/README.md +6 -6
  6. package/README.zh-CN.md +6 -6
  7. package/apps/desktop/Development.md +42 -46
  8. package/apps/desktop/README.md +37 -1
  9. package/apps/desktop/README.zh-CN.md +26 -1
  10. package/apps/desktop/electron.vite.config.ts +1 -0
  11. package/apps/desktop/src/main/controllers/AuthCtr.ts +4 -3
  12. package/apps/desktop/src/main/controllers/BrowserWindowsCtr.ts +33 -20
  13. package/apps/desktop/src/main/controllers/DevtoolsCtr.ts +4 -2
  14. package/apps/desktop/src/main/controllers/LocalFileCtr.ts +14 -13
  15. package/apps/desktop/src/main/controllers/MenuCtr.ts +5 -4
  16. package/apps/desktop/src/main/controllers/NetworkProxyCtr.ts +18 -19
  17. package/apps/desktop/src/main/controllers/NotificationCtr.ts +4 -3
  18. package/apps/desktop/src/main/controllers/RemoteServerConfigCtr.ts +5 -4
  19. package/apps/desktop/src/main/controllers/RemoteServerSyncCtr.ts +3 -2
  20. package/apps/desktop/src/main/controllers/ShellCommandCtr.ts +5 -4
  21. package/apps/desktop/src/main/controllers/ShortcutCtr.ts +4 -3
  22. package/apps/desktop/src/main/controllers/SystemCtr.ts +7 -37
  23. package/apps/desktop/src/main/controllers/SystemServerCtr.ts +38 -0
  24. package/apps/desktop/src/main/controllers/TrayMenuCtr.ts +5 -4
  25. package/apps/desktop/src/main/controllers/UpdaterCtr.ts +6 -5
  26. package/apps/desktop/src/main/controllers/UploadFileCtr.ts +3 -25
  27. package/apps/desktop/src/main/controllers/UploadFileServerCtr.ts +33 -0
  28. package/apps/desktop/src/main/controllers/__tests__/AuthCtr.test.ts +9 -1
  29. package/apps/desktop/src/main/controllers/__tests__/BrowserWindowsCtr.test.ts +29 -9
  30. package/apps/desktop/src/main/controllers/__tests__/DevtoolsCtr.test.ts +12 -3
  31. package/apps/desktop/src/main/controllers/__tests__/LocalFileCtr.test.ts +7 -0
  32. package/apps/desktop/src/main/controllers/__tests__/MenuCtr.test.ts +10 -0
  33. package/apps/desktop/src/main/controllers/__tests__/NetworkProxyCtr.test.ts +10 -0
  34. package/apps/desktop/src/main/controllers/__tests__/NotificationCtr.test.ts +8 -0
  35. package/apps/desktop/src/main/controllers/__tests__/RemoteServerConfigCtr.test.ts +8 -0
  36. package/apps/desktop/src/main/controllers/__tests__/RemoteServerSyncCtr.test.ts +1 -0
  37. package/apps/desktop/src/main/controllers/__tests__/ShellCommandCtr.test.ts +10 -0
  38. package/apps/desktop/src/main/controllers/__tests__/ShortcutCtr.test.ts +11 -0
  39. package/apps/desktop/src/main/controllers/__tests__/SystemCtr.test.ts +43 -73
  40. package/apps/desktop/src/main/controllers/__tests__/SystemServerCtr.test.ts +75 -0
  41. package/apps/desktop/src/main/controllers/__tests__/TrayMenuCtr.test.ts +24 -13
  42. package/apps/desktop/src/main/controllers/__tests__/UpdaterCtr.test.ts +13 -2
  43. package/apps/desktop/src/main/controllers/__tests__/UploadFileCtr.test.ts +29 -108
  44. package/apps/desktop/src/main/controllers/__tests__/UploadFileServerCtr.test.ts +55 -0
  45. package/apps/desktop/src/main/controllers/_template.ts +2 -2
  46. package/apps/desktop/src/main/controllers/index.ts +5 -29
  47. package/apps/desktop/src/main/controllers/registry.ts +52 -0
  48. package/apps/desktop/src/main/core/App.ts +15 -47
  49. package/apps/desktop/src/main/core/__tests__/App.test.ts +5 -4
  50. package/apps/desktop/src/main/core/infrastructure/IoCContainer.ts +0 -5
  51. package/apps/desktop/src/main/core/infrastructure/__tests__/IoCContainer.test.ts +0 -50
  52. package/apps/desktop/src/main/exports.d.ts +8 -0
  53. package/apps/desktop/src/main/exports.ts +2 -0
  54. package/apps/desktop/src/main/global.d.ts +3 -0
  55. package/apps/desktop/src/main/modules/fileSearch/__tests__/macOS.integration.test.ts +17 -8
  56. package/apps/desktop/src/main/package.json +10 -0
  57. package/apps/desktop/src/main/services/fileSrv.ts +1 -1
  58. package/apps/desktop/src/main/utils/ipc/__tests__/base.test.ts +91 -0
  59. package/apps/desktop/src/main/utils/ipc/base.ts +170 -0
  60. package/apps/desktop/src/main/utils/ipc/index.ts +11 -0
  61. package/apps/desktop/src/main/utils/ipc/utility.ts +20 -0
  62. package/apps/desktop/src/preload/electronApi.ts +4 -1
  63. package/apps/desktop/src/preload/invoke.test.ts +13 -16
  64. package/apps/desktop/src/preload/invoke.ts +2 -5
  65. package/apps/desktop/src/preload/routeInterceptor.test.ts +13 -13
  66. package/apps/desktop/src/preload/routeInterceptor.ts +4 -4
  67. package/apps/desktop/tsconfig.json +15 -5
  68. package/changelog/v1.json +5 -0
  69. package/package.json +4 -3
  70. package/packages/electron-client-ipc/src/index.ts +1 -1
  71. package/packages/electron-client-ipc/src/ipc.test.ts +62 -0
  72. package/packages/electron-client-ipc/src/ipc.ts +63 -0
  73. package/packages/electron-client-ipc/src/streamInvoke.ts +7 -1
  74. package/packages/electron-client-ipc/src/types/dispatch.ts +1 -10
  75. package/packages/electron-client-ipc/vitest.config.mts +10 -0
  76. package/packages/electron-server-ipc/src/ipcClient.ts +1 -2
  77. package/packages/electron-server-ipc/src/ipcServer.ts +1 -2
  78. package/packages/electron-server-ipc/src/types/index.ts +1 -5
  79. package/pnpm-workspace.yaml +1 -1
  80. package/scripts/i18nWorkflow/const.ts +2 -2
  81. package/scripts/i18nWorkflow/i18nConfig.ts +7 -0
  82. package/scripts/i18nWorkflow/utils.ts +1 -1
  83. package/src/app/[variants]/(main)/discover/(detail)/provider/features/Sidebar/ActionButton/ProviderConfig.tsx +2 -2
  84. package/src/locales/default/setting.ts +1 -0
  85. package/src/server/modules/ElectronIPCClient/index.ts +59 -13
  86. package/src/services/electron/__tests__/devtools.test.ts +10 -6
  87. package/src/services/electron/autoUpdate.ts +5 -5
  88. package/src/services/electron/desktopNotification.ts +4 -7
  89. package/src/services/electron/devtools.ts +2 -2
  90. package/src/services/electron/file.ts +3 -2
  91. package/src/services/electron/localFileService.ts +17 -16
  92. package/src/services/electron/remoteServer.ts +7 -6
  93. package/src/services/electron/settings.ts +9 -11
  94. package/src/services/electron/system.ts +8 -6
  95. package/src/store/chat/slices/plugin/actions/pluginTypes.ts +1 -1
  96. package/src/store/global/actions/general.ts +8 -10
  97. package/src/utils/electron/desktopRemoteRPCFetch.ts +3 -2
  98. package/src/utils/electron/ipc.ts +12 -0
  99. package/tsconfig.json +5 -0
  100. package/apps/desktop/src/main/types/ipcClientEvent.ts +0 -3
  101. package/packages/electron-client-ipc/src/dispatch.ts +0 -41
@@ -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
- AppBrowsersIdentifiers,
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, ipcClientEvent, shortcut } from './index';
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
- @ipcClientEvent('openSettingsWindow')
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
- @ipcClientEvent('closeWindow')
57
- closeWindow(data: undefined, sender: IpcClientEventSender) {
58
- this.app.browserManager.closeWindow(sender.identifier);
55
+ @IpcMethod()
56
+ closeWindow() {
57
+ this.withSenderIdentifier((identifier) => {
58
+ this.app.browserManager.closeWindow(identifier);
59
+ });
59
60
  }
60
61
 
61
- @ipcClientEvent('minimizeWindow')
62
- minimizeWindow(data: undefined, sender: IpcClientEventSender) {
63
- this.app.browserManager.minimizeWindow(sender.identifier);
62
+ @IpcMethod()
63
+ minimizeWindow() {
64
+ this.withSenderIdentifier((identifier) => {
65
+ this.app.browserManager.minimizeWindow(identifier);
66
+ });
64
67
  }
65
68
 
66
- @ipcClientEvent('maximizeWindow')
67
- maximizeWindow(data: undefined, sender: IpcClientEventSender) {
68
- this.app.browserManager.maximizeWindow(sender.identifier);
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
- @ipcClientEvent('interceptRoute')
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
- @ipcClientEvent('createMultiInstanceWindow')
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
- @ipcClientEvent('getWindowsByTemplate')
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
- @ipcClientEvent('closeWindowsByTemplate')
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, ipcClientEvent } from './index';
1
+ import { ControllerModule, IpcMethod } from './index';
2
2
 
3
3
  export default class DevtoolsCtr extends ControllerModule {
4
- @ipcClientEvent('openDevtools')
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, ipcClientEvent } from './index';
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
- @ipcClientEvent('openLocalFile')
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
- @ipcClientEvent('openLocalFolder')
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
- @ipcClientEvent('readLocalFiles')
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
- @ipcClientEvent('readLocalFile')
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
- @ipcClientEvent('listLocalFiles')
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
- @ipcClientEvent('moveLocalFiles')
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
- @ipcClientEvent('renameLocalFile')
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
- @ipcClientEvent('writeLocalFile')
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
- @ipcClientEvent('searchLocalFiles')
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
- @ipcClientEvent('grepContent')
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
- @ipcClientEvent('globLocalFiles')
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
- @ipcClientEvent('editLocalFile')
684
+ @IpcMethod()
684
685
  async handleEditFile({
685
686
  file_path: filePath,
686
687
  new_string,
@@ -1,10 +1,11 @@
1
- import { ControllerModule, ipcClientEvent } from './index';
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
- @ipcClientEvent('refreshAppMenu')
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
- @ipcClientEvent('showContextMenu')
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
- @ipcClientEvent('setDevMenuVisibility')
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, ipcClientEvent } from './index';
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
- @ipcClientEvent('getProxySettings')
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
- @ipcClientEvent('setProxySettings')
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
- if (isEqual(currentConfig, config)) {
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
- @ipcClientEvent('testProxyConnection')
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
- @ipcClientEvent('testProxyConfig')
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, ipcClientEvent } from './index';
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
- @ipcClientEvent('showDesktopNotification')
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
- @ipcClientEvent('isMainWindowHidden')
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, ipcClientEvent } from './index';
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
- @ipcClientEvent('getRemoteServerConfig')
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
- @ipcClientEvent('setRemoteServerConfig')
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
- @ipcClientEvent('clearRemoteServerConfig')
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, ipcClientEvent } from './index';
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
- @ipcClientEvent('proxyTRPCRequest')
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, ipcClientEvent } from './index';
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
- @ipcClientEvent('runCommand')
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
- @ipcClientEvent('getCommandOutput')
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
- @ipcClientEvent('killCommand')
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, ipcClientEvent } from '.';
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
- @ipcClientEvent('getShortcutsConfig')
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
- @ipcClientEvent('updateShortcutConfig')
18
+ @IpcMethod()
18
19
  updateShortcutConfig({
19
20
  id,
20
21
  accelerator,
@@ -1,18 +1,16 @@
1
1
  import { ElectronAppState, ThemeMode } from '@lobechat/electron-client-ipc';
2
2
  import { app, nativeTheme, shell, systemPreferences } from 'electron';
3
3
  import { macOS } from 'electron-is';
4
- import { readFileSync, writeFileSync } from 'node:fs';
5
- import { join } from 'node:path';
6
4
  import process from 'node:process';
7
5
 
8
- import { DB_SCHEMA_HASH_FILENAME, LOCAL_DATABASE_DIR, userDataDir } from '@/const/dir';
9
6
  import { createLogger } from '@/utils/logger';
10
7
 
11
- import { ControllerModule, ipcClientEvent, ipcServerEvent } from './index';
8
+ import { ControllerModule, IpcMethod } from './index';
12
9
 
13
10
  const logger = createLogger('controllers:SystemCtr');
14
11
 
15
12
  export default class SystemController extends ControllerModule {
13
+ static override readonly groupName = 'system';
16
14
  private systemThemeListenerInitialized = false;
17
15
 
18
16
  /**
@@ -26,7 +24,7 @@ export default class SystemController extends ControllerModule {
26
24
  * Handles the 'getDesktopAppState' IPC request.
27
25
  * Gathers essential application and system information.
28
26
  */
29
- @ipcClientEvent('getDesktopAppState')
27
+ @IpcMethod()
30
28
  async getAppState(): Promise<ElectronAppState> {
31
29
  const platform = process.platform;
32
30
  const arch = process.arch;
@@ -56,13 +54,13 @@ export default class SystemController extends ControllerModule {
56
54
  /**
57
55
  * 检查可用性
58
56
  */
59
- @ipcClientEvent('checkSystemAccessibility')
57
+ @IpcMethod()
60
58
  checkAccessibilityForMacOS() {
61
59
  if (!macOS()) return;
62
60
  return systemPreferences.isTrustedAccessibilityClient(true);
63
61
  }
64
62
 
65
- @ipcClientEvent('openExternalLink')
63
+ @IpcMethod()
66
64
  openExternalLink(url: string) {
67
65
  return shell.openExternal(url);
68
66
  }
@@ -70,7 +68,7 @@ export default class SystemController extends ControllerModule {
70
68
  /**
71
69
  * 更新应用语言设置
72
70
  */
73
- @ipcClientEvent('updateLocale')
71
+ @IpcMethod()
74
72
  async updateLocale(locale: string) {
75
73
  // 保存语言设置
76
74
  this.app.storeManager.set('locale', locale);
@@ -82,7 +80,7 @@ export default class SystemController extends ControllerModule {
82
80
  return { success: true };
83
81
  }
84
82
 
85
- @ipcClientEvent('updateThemeMode')
83
+ @IpcMethod()
86
84
  async updateThemeModeHandler(themeMode: ThemeMode) {
87
85
  this.app.storeManager.set('themeMode', themeMode);
88
86
  this.app.browserManager.broadcastToAllWindows('themeChanged', { themeMode });
@@ -91,34 +89,6 @@ export default class SystemController extends ControllerModule {
91
89
  this.app.browserManager.handleAppThemeChange();
92
90
  }
93
91
 
94
- @ipcServerEvent('getDatabasePath')
95
- async getDatabasePath() {
96
- return join(this.app.appStoragePath, LOCAL_DATABASE_DIR);
97
- }
98
-
99
- @ipcServerEvent('getDatabaseSchemaHash')
100
- async getDatabaseSchemaHash() {
101
- try {
102
- return readFileSync(this.DB_SCHEMA_HASH_PATH, 'utf8');
103
- } catch {
104
- return undefined;
105
- }
106
- }
107
-
108
- @ipcServerEvent('getUserDataPath')
109
- async getUserDataPath() {
110
- return userDataDir;
111
- }
112
-
113
- @ipcServerEvent('setDatabaseSchemaHash')
114
- async setDatabaseSchemaHash(hash: string) {
115
- writeFileSync(this.DB_SCHEMA_HASH_PATH, hash, 'utf8');
116
- }
117
-
118
- private get DB_SCHEMA_HASH_PATH() {
119
- return join(this.app.appStoragePath, DB_SCHEMA_HASH_FILENAME);
120
- }
121
-
122
92
  /**
123
93
  * Initialize system theme listener to monitor OS theme changes
124
94
  */
@@ -0,0 +1,38 @@
1
+ import { readFileSync, writeFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+
4
+ import { DB_SCHEMA_HASH_FILENAME, LOCAL_DATABASE_DIR, userDataDir } from '@/const/dir';
5
+
6
+ import { ControllerModule, IpcServerMethod } from './index';
7
+
8
+ export default class SystemServerCtr extends ControllerModule {
9
+ static override readonly groupName = 'system';
10
+
11
+ @IpcServerMethod()
12
+ async getDatabasePath() {
13
+ return join(this.app.appStoragePath, LOCAL_DATABASE_DIR);
14
+ }
15
+
16
+ @IpcServerMethod()
17
+ async getDatabaseSchemaHash() {
18
+ try {
19
+ return readFileSync(this.DB_SCHEMA_HASH_PATH, 'utf8');
20
+ } catch {
21
+ return undefined;
22
+ }
23
+ }
24
+
25
+ @IpcServerMethod()
26
+ async getUserDataPath() {
27
+ return userDataDir;
28
+ }
29
+
30
+ @IpcServerMethod()
31
+ async setDatabaseSchemaHash(hash: string) {
32
+ writeFileSync(this.DB_SCHEMA_HASH_PATH, hash, 'utf8');
33
+ }
34
+
35
+ private get DB_SCHEMA_HASH_PATH() {
36
+ return join(this.app.appStoragePath, DB_SCHEMA_HASH_FILENAME);
37
+ }
38
+ }