@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.
Files changed (102) 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 +50 -0
  5. package/Dockerfile +44 -52
  6. package/README.md +6 -6
  7. package/README.zh-CN.md +6 -6
  8. package/apps/desktop/Development.md +42 -46
  9. package/apps/desktop/README.md +37 -1
  10. package/apps/desktop/README.zh-CN.md +26 -1
  11. package/apps/desktop/electron.vite.config.ts +1 -0
  12. package/apps/desktop/src/main/controllers/AuthCtr.ts +4 -3
  13. package/apps/desktop/src/main/controllers/BrowserWindowsCtr.ts +33 -20
  14. package/apps/desktop/src/main/controllers/DevtoolsCtr.ts +4 -2
  15. package/apps/desktop/src/main/controllers/LocalFileCtr.ts +14 -13
  16. package/apps/desktop/src/main/controllers/MenuCtr.ts +5 -4
  17. package/apps/desktop/src/main/controllers/NetworkProxyCtr.ts +18 -19
  18. package/apps/desktop/src/main/controllers/NotificationCtr.ts +4 -3
  19. package/apps/desktop/src/main/controllers/RemoteServerConfigCtr.ts +5 -4
  20. package/apps/desktop/src/main/controllers/RemoteServerSyncCtr.ts +3 -2
  21. package/apps/desktop/src/main/controllers/ShellCommandCtr.ts +5 -4
  22. package/apps/desktop/src/main/controllers/ShortcutCtr.ts +4 -3
  23. package/apps/desktop/src/main/controllers/SystemCtr.ts +7 -37
  24. package/apps/desktop/src/main/controllers/SystemServerCtr.ts +38 -0
  25. package/apps/desktop/src/main/controllers/TrayMenuCtr.ts +5 -4
  26. package/apps/desktop/src/main/controllers/UpdaterCtr.ts +6 -5
  27. package/apps/desktop/src/main/controllers/UploadFileCtr.ts +3 -25
  28. package/apps/desktop/src/main/controllers/UploadFileServerCtr.ts +33 -0
  29. package/apps/desktop/src/main/controllers/__tests__/AuthCtr.test.ts +9 -1
  30. package/apps/desktop/src/main/controllers/__tests__/BrowserWindowsCtr.test.ts +29 -9
  31. package/apps/desktop/src/main/controllers/__tests__/DevtoolsCtr.test.ts +12 -3
  32. package/apps/desktop/src/main/controllers/__tests__/LocalFileCtr.test.ts +7 -0
  33. package/apps/desktop/src/main/controllers/__tests__/MenuCtr.test.ts +10 -0
  34. package/apps/desktop/src/main/controllers/__tests__/NetworkProxyCtr.test.ts +10 -0
  35. package/apps/desktop/src/main/controllers/__tests__/NotificationCtr.test.ts +8 -0
  36. package/apps/desktop/src/main/controllers/__tests__/RemoteServerConfigCtr.test.ts +8 -0
  37. package/apps/desktop/src/main/controllers/__tests__/RemoteServerSyncCtr.test.ts +1 -0
  38. package/apps/desktop/src/main/controllers/__tests__/ShellCommandCtr.test.ts +10 -0
  39. package/apps/desktop/src/main/controllers/__tests__/ShortcutCtr.test.ts +11 -0
  40. package/apps/desktop/src/main/controllers/__tests__/SystemCtr.test.ts +43 -73
  41. package/apps/desktop/src/main/controllers/__tests__/SystemServerCtr.test.ts +75 -0
  42. package/apps/desktop/src/main/controllers/__tests__/TrayMenuCtr.test.ts +24 -13
  43. package/apps/desktop/src/main/controllers/__tests__/UpdaterCtr.test.ts +13 -2
  44. package/apps/desktop/src/main/controllers/__tests__/UploadFileCtr.test.ts +29 -108
  45. package/apps/desktop/src/main/controllers/__tests__/UploadFileServerCtr.test.ts +55 -0
  46. package/apps/desktop/src/main/controllers/_template.ts +2 -2
  47. package/apps/desktop/src/main/controllers/index.ts +5 -29
  48. package/apps/desktop/src/main/controllers/registry.ts +52 -0
  49. package/apps/desktop/src/main/core/App.ts +15 -47
  50. package/apps/desktop/src/main/core/__tests__/App.test.ts +5 -4
  51. package/apps/desktop/src/main/core/infrastructure/IoCContainer.ts +0 -5
  52. package/apps/desktop/src/main/core/infrastructure/__tests__/IoCContainer.test.ts +0 -50
  53. package/apps/desktop/src/main/exports.d.ts +8 -0
  54. package/apps/desktop/src/main/exports.ts +2 -0
  55. package/apps/desktop/src/main/global.d.ts +3 -0
  56. package/apps/desktop/src/main/modules/fileSearch/__tests__/macOS.integration.test.ts +17 -8
  57. package/apps/desktop/src/main/package.json +10 -0
  58. package/apps/desktop/src/main/services/fileSrv.ts +1 -1
  59. package/apps/desktop/src/main/utils/ipc/__tests__/base.test.ts +91 -0
  60. package/apps/desktop/src/main/utils/ipc/base.ts +170 -0
  61. package/apps/desktop/src/main/utils/ipc/index.ts +11 -0
  62. package/apps/desktop/src/main/utils/ipc/utility.ts +20 -0
  63. package/apps/desktop/src/preload/electronApi.ts +4 -1
  64. package/apps/desktop/src/preload/invoke.test.ts +13 -16
  65. package/apps/desktop/src/preload/invoke.ts +2 -5
  66. package/apps/desktop/src/preload/routeInterceptor.test.ts +13 -13
  67. package/apps/desktop/src/preload/routeInterceptor.ts +4 -4
  68. package/apps/desktop/tsconfig.json +15 -5
  69. package/changelog/v1.json +10 -0
  70. package/package.json +4 -3
  71. package/packages/electron-client-ipc/src/index.ts +1 -1
  72. package/packages/electron-client-ipc/src/ipc.test.ts +62 -0
  73. package/packages/electron-client-ipc/src/ipc.ts +63 -0
  74. package/packages/electron-client-ipc/src/streamInvoke.ts +7 -1
  75. package/packages/electron-client-ipc/src/types/dispatch.ts +1 -10
  76. package/packages/electron-client-ipc/vitest.config.mts +10 -0
  77. package/packages/electron-server-ipc/src/ipcClient.ts +1 -2
  78. package/packages/electron-server-ipc/src/ipcServer.ts +1 -2
  79. package/packages/electron-server-ipc/src/types/index.ts +1 -5
  80. package/pnpm-workspace.yaml +1 -1
  81. package/scripts/i18nWorkflow/const.ts +2 -2
  82. package/scripts/i18nWorkflow/i18nConfig.ts +7 -0
  83. package/scripts/i18nWorkflow/utils.ts +1 -1
  84. package/src/app/[variants]/(main)/discover/(detail)/provider/features/Sidebar/ActionButton/ProviderConfig.tsx +2 -2
  85. package/src/locales/default/setting.ts +1 -0
  86. package/src/server/modules/ElectronIPCClient/index.ts +59 -13
  87. package/src/services/electron/__tests__/devtools.test.ts +10 -6
  88. package/src/services/electron/autoUpdate.ts +5 -5
  89. package/src/services/electron/desktopNotification.ts +4 -7
  90. package/src/services/electron/devtools.ts +2 -2
  91. package/src/services/electron/file.ts +3 -2
  92. package/src/services/electron/localFileService.ts +17 -16
  93. package/src/services/electron/remoteServer.ts +7 -6
  94. package/src/services/electron/settings.ts +9 -11
  95. package/src/services/electron/system.ts +8 -6
  96. package/src/store/chat/slices/plugin/actions/pluginTypes.ts +1 -1
  97. package/src/store/global/actions/general.ts +8 -10
  98. package/src/utils/electron/desktopRemoteRPCFetch.ts +3 -2
  99. package/src/utils/electron/ipc.ts +12 -0
  100. package/tsconfig.json +5 -0
  101. package/apps/desktop/src/main/types/ipcClientEvent.ts +0 -3
  102. package/packages/electron-client-ipc/src/dispatch.ts +0 -41
@@ -0,0 +1,63 @@
1
+ /* eslint-disable @typescript-eslint/no-empty-interface */
2
+ import type { DispatchInvoke } from './types/dispatch';
3
+ import type { ProxyTRPCRequestParams } from './types/proxyTRPCRequest';
4
+
5
+ interface StreamerCallbacks {
6
+ onData: (chunk: Uint8Array) => void;
7
+ onEnd: () => void;
8
+ onError: (error: Error) => void;
9
+ onResponse: (response: {
10
+ headers: Record<string, string>;
11
+ status: number;
12
+ statusText: string;
13
+ }) => void;
14
+ }
15
+
16
+ export interface DesktopIpcServicesMap {}
17
+ export type DesktopIpcServices = DesktopIpcServicesMap;
18
+ export type ElectronDesktopIpc = DesktopIpcServices | null;
19
+
20
+ const createInvokeProxy = <IpcServices>(invoke: DispatchInvoke): IpcServices =>
21
+ new Proxy(
22
+ {},
23
+ {
24
+ get(_target, groupKey) {
25
+ if (typeof groupKey !== 'string') return undefined;
26
+
27
+ return new Proxy(
28
+ {},
29
+ {
30
+ get(_methodTarget, methodKey) {
31
+ if (typeof methodKey !== 'string') return undefined;
32
+
33
+ const channel = `${groupKey}.${methodKey}`;
34
+ return (payload?: unknown) =>
35
+ payload === undefined ? invoke(channel) : invoke(channel, payload);
36
+ },
37
+ },
38
+ );
39
+ },
40
+ },
41
+ ) as IpcServices;
42
+
43
+ let cachedProxy: DesktopIpcServices | null = null;
44
+
45
+ declare global {
46
+ interface Window {
47
+ electronAPI?: {
48
+ invoke?: DispatchInvoke;
49
+ onStreamInvoke: (params: ProxyTRPCRequestParams, callbacks: StreamerCallbacks) => () => void;
50
+ };
51
+ }
52
+ }
53
+
54
+ export const getElectronIpc = (): DesktopIpcServices | null => {
55
+ if (typeof window === 'undefined') return null;
56
+ if (cachedProxy) return cachedProxy;
57
+
58
+ const invoke = window.electronAPI?.invoke;
59
+ if (!invoke) return null;
60
+
61
+ cachedProxy = createInvokeProxy<DesktopIpcServices>(invoke);
62
+ return cachedProxy;
63
+ };
@@ -34,7 +34,13 @@ export const streamInvoke = async (input: RequestInfo | URL, init?: RequestInit)
34
34
  },
35
35
  });
36
36
 
37
- const cleanup = window.electronAPI.onStreamInvoke(params, {
37
+ const electronAPI = window.electronAPI;
38
+ if (!electronAPI || !electronAPI.onStreamInvoke) {
39
+ reject(new Error('[streamInvoke] window.electronAPI.onStreamInvoke is not available'));
40
+ return;
41
+ }
42
+
43
+ const cleanup = electronAPI.onStreamInvoke(params, {
38
44
  onData: (chunk) => {
39
45
  if (streamController) streamController.enqueue(chunk);
40
46
  },
@@ -1,10 +1 @@
1
- import type {
2
- ClientDispatchEventKey,
3
- ClientDispatchEvents,
4
- ClientEventReturnType,
5
- } from '../events';
6
-
7
- export type DispatchInvoke = <T extends ClientDispatchEventKey>(
8
- event: T,
9
- ...data: Parameters<ClientDispatchEvents[T]>
10
- ) => Promise<ClientEventReturnType<T>>;
1
+ export type DispatchInvoke = <T = unknown>(event: string, ...data: any[]) => Promise<T>;
@@ -0,0 +1,10 @@
1
+ import { defineConfig } from 'vitest/config';
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ coverage: {
6
+ reporter: ['text', 'json', 'lcov', 'text-summary'],
7
+ },
8
+ environment: 'node',
9
+ },
10
+ });
@@ -5,7 +5,6 @@ import os from 'node:os';
5
5
  import path from 'node:path';
6
6
 
7
7
  import { SOCK_FILE, SOCK_INFO_FILE, WINDOW_PIPE_FILE } from './const';
8
- import { ServerDispatchEventKey } from './events';
9
8
 
10
9
  const log = debug('electron-server-ipc:client');
11
10
 
@@ -178,7 +177,7 @@ export class ElectronIpcClient {
178
177
  }
179
178
 
180
179
  // Send request to Electron IPC server
181
- public async sendRequest<T>(method: ServerDispatchEventKey, params: any = {}): Promise<T> {
180
+ public async sendRequest<T>(method: string, params: any = {}): Promise<T> {
182
181
  if (!this.socketPath) {
183
182
  console.error('Cannot send request: Electron IPC connection not available');
184
183
  throw new Error('Electron IPC connection not available');
@@ -5,7 +5,6 @@ import os from 'node:os';
5
5
  import path from 'node:path';
6
6
 
7
7
  import { SOCK_FILE, SOCK_INFO_FILE, WINDOW_PIPE_FILE } from './const';
8
- import { ServerDispatchEventKey } from './events';
9
8
  import { ElectronIPCEventHandler } from './types';
10
9
 
11
10
  const log = debug('electron-server-ipc:server');
@@ -107,7 +106,7 @@ export class ElectronIPCServer {
107
106
  log('Handling request: %s (ID: %s)', method, id);
108
107
 
109
108
  // Execute corresponding operation based on request method
110
- const eventHandler = this.eventHandler[method as ServerDispatchEventKey];
109
+ const eventHandler = this.eventHandler[method];
111
110
  if (!eventHandler) {
112
111
  console.error('No handler found for method: %s', method);
113
112
  return;
@@ -1,14 +1,10 @@
1
1
  import net from 'node:net';
2
2
 
3
- import { ServerDispatchEventKey } from '../events';
4
-
5
3
  export type IPCEventMethod = (
6
4
  params: any,
7
5
  context: { id: string; method: string; socket: net.Socket },
8
6
  ) => Promise<any>;
9
7
 
10
- export type ElectronIPCEventHandler = {
11
- [key in ServerDispatchEventKey]: IPCEventMethod;
12
- };
8
+ export type ElectronIPCEventHandler = Record<string, IPCEventMethod>;
13
9
 
14
10
  export * from './file';
@@ -2,4 +2,4 @@ packages:
2
2
  - 'packages/**'
3
3
  - '.'
4
4
  - 'e2e'
5
- - '!apps/**'
5
+ - 'apps/desktop/src/main'
@@ -1,7 +1,7 @@
1
1
  import { readdirSync } from 'node:fs';
2
2
  import { resolve } from 'node:path';
3
3
 
4
- import i18nConfig from '../../.i18nrc';
4
+ import i18nConfig from './i18nConfig';
5
5
 
6
6
  export const root = resolve(__dirname, '../..');
7
7
  export const localesDir = resolve(root, i18nConfig.output);
@@ -15,4 +15,4 @@ export const outputLocaleJsonFilepath = (locale: string, file: string) =>
15
15
  resolve(localesDir, locale, file);
16
16
  export const srcDefaultLocales = resolve(root, srcLocalesDir, 'default');
17
17
 
18
- export { default as i18nConfig } from '../../.i18nrc';
18
+ export { default as i18nConfig } from './i18nConfig';
@@ -0,0 +1,7 @@
1
+ import { createRequire } from 'node:module';
2
+
3
+ const require = createRequire(import.meta.url);
4
+
5
+ const config = require('../../.i18nrc');
6
+
7
+ export default config;
@@ -3,7 +3,7 @@ import { colors } from 'consola/utils';
3
3
  import { readFileSync, writeFileSync } from 'node:fs';
4
4
  import { resolve } from 'node:path';
5
5
  import prettier from "@prettier/sync";
6
- import i18nConfig from '../../.i18nrc';
6
+ import i18nConfig from './i18nConfig';
7
7
 
8
8
  let prettierOptions = prettier.resolveConfig(
9
9
  resolve(__dirname, '../../.prettierrc.js')
@@ -30,8 +30,8 @@ const ProviderConfig = memo(() => {
30
30
  const tab = 'provider';
31
31
 
32
32
  if (isDesktop) {
33
- const { dispatch } = await import('@lobechat/electron-client-ipc');
34
- await dispatch('openSettingsWindow', {
33
+ const { ensureElectronIpc } = await import('@/utils/electron/ipc');
34
+ await ensureElectronIpc().windows.openSettingsWindow({
35
35
  searchParams,
36
36
  tab,
37
37
  });
@@ -806,6 +806,7 @@ export default {
806
806
  noEnabled: '暂无启用插件',
807
807
  store: '插件商店',
808
808
  },
809
+
809
810
  tabs: {
810
811
  all: '全部',
811
812
  installed: '已启用',
@@ -1,46 +1,92 @@
1
1
  import { CreateFileParams, ElectronIpcClient, FileMetadata } from '@lobechat/electron-server-ipc';
2
+ import type { DesktopServerIpcServices } from '@lobehub/desktop-ipc-typings';
2
3
 
3
4
  import packageJSON from '@/../apps/desktop/package.json';
4
5
 
6
+ const createServerInvokeProxy = <IpcServices>(
7
+ invoke: (channel: string, payload?: unknown) => Promise<unknown>,
8
+ ): IpcServices =>
9
+ new Proxy(
10
+ {},
11
+ {
12
+ get(_target, groupKey) {
13
+ if (typeof groupKey !== 'string') return undefined;
14
+
15
+ return new Proxy(
16
+ {},
17
+ {
18
+ get(_methodTarget, methodKey) {
19
+ if (typeof methodKey !== 'string') return undefined;
20
+
21
+ const channel = `${groupKey}.${methodKey}`;
22
+ return (payload?: unknown) =>
23
+ payload === undefined ? invoke(channel) : invoke(channel, payload);
24
+ },
25
+ },
26
+ );
27
+ },
28
+ },
29
+ ) as IpcServices;
30
+
5
31
  class LobeHubElectronIpcClient extends ElectronIpcClient {
6
- // 获取数据库路径
32
+ private _services: DesktopServerIpcServices | null = null;
33
+
34
+ private ensureServices(): DesktopServerIpcServices {
35
+ if (this._services) return this._services;
36
+
37
+ this._services = createServerInvokeProxy<DesktopServerIpcServices>((channel, payload) =>
38
+ payload === undefined ? this.sendRequest(channel) : this.sendRequest(channel, payload),
39
+ );
40
+
41
+ return this.services;
42
+ }
43
+
44
+ private get ipc() {
45
+ return this.ensureServices();
46
+ }
47
+
48
+ public get services(): DesktopServerIpcServices {
49
+ return this.ipc;
50
+ }
51
+
7
52
  getDatabasePath = async (): Promise<string> => {
8
- return this.sendRequest<string>('getDatabasePath');
53
+ return this.ipc.system.getDatabasePath();
9
54
  };
10
55
 
11
- // 获取用户数据路径
12
56
  getUserDataPath = async (): Promise<string> => {
13
- return this.sendRequest<string>('getUserDataPath');
57
+ return this.ipc.system.getUserDataPath();
14
58
  };
15
59
 
16
60
  getDatabaseSchemaHash = async () => {
17
- return this.sendRequest<string>('setDatabaseSchemaHash');
61
+ return this.ipc.system.getDatabaseSchemaHash();
18
62
  };
19
63
 
20
64
  setDatabaseSchemaHash = async (hash: string | undefined) => {
21
65
  if (!hash) return;
22
66
 
23
- return this.sendRequest('setDatabaseSchemaHash', hash);
67
+ return this.ipc.system.setDatabaseSchemaHash(hash);
24
68
  };
25
69
 
26
70
  getFilePathById = async (id: string) => {
27
- return this.sendRequest<string>('getStaticFilePath', id);
71
+ return this.ipc.upload.getFileUrlById(id);
28
72
  };
29
73
 
30
74
  getFileHTTPURL = async (path: string) => {
31
- return this.sendRequest<string>('getFileHTTPURL', path);
75
+ return this.ipc.upload.getFileHTTPURL(path);
32
76
  };
33
77
 
34
78
  deleteFiles = async (paths: string[]) => {
35
- return this.sendRequest<{ errors?: { message: string; path: string }[]; success: boolean }>(
36
- 'deleteFiles',
37
- paths,
38
- );
79
+ return this.ipc.upload.deleteFiles(paths);
39
80
  };
40
81
 
41
82
  createFile = async (params: CreateFileParams) => {
42
- return this.sendRequest<{ metadata: FileMetadata; success: boolean }>('createFile', params);
83
+ return this.ipc.upload.createFile(params) as Promise<{
84
+ metadata: FileMetadata;
85
+ success: boolean;
86
+ }>;
43
87
  };
44
88
  }
45
89
 
46
90
  export const electronIpcClient = new LobeHubElectronIpcClient(packageJSON.name);
91
+
92
+ export const ensureElectronServerIpc = (): DesktopServerIpcServices => electronIpcClient.services;
@@ -1,11 +1,14 @@
1
- import { dispatch } from '@lobechat/electron-client-ipc';
2
1
  import { describe, expect, it, vi } from 'vitest';
3
2
 
4
3
  import { electronDevtoolsService } from '../devtools';
5
4
 
6
- vi.mock('@lobechat/electron-client-ipc', () => ({
7
- dispatch: vi.fn(),
5
+ const openDevtoolsMock = vi.fn();
6
+ vi.mock('@/utils/electron/ipc', () => ({
7
+ ensureElectronIpc: vi.fn(() => ({
8
+ devtools: { openDevtools: openDevtoolsMock },
9
+ })),
8
10
  }));
11
+ const { ensureElectronIpc } = await import('@/utils/electron/ipc');
9
12
 
10
13
  describe('DevtoolsService', () => {
11
14
  beforeEach(() => {
@@ -15,18 +18,19 @@ describe('DevtoolsService', () => {
15
18
  describe('openDevtools', () => {
16
19
  it('should call dispatch with openDevtools', async () => {
17
20
  await electronDevtoolsService.openDevtools();
18
- expect(dispatch).toHaveBeenCalledWith('openDevtools');
21
+ expect(ensureElectronIpc).toHaveBeenCalled();
22
+ expect(openDevtoolsMock).toHaveBeenCalled();
19
23
  });
20
24
 
21
25
  it('should return void when dispatch succeeds', async () => {
22
- vi.mocked(dispatch).mockResolvedValueOnce();
26
+ openDevtoolsMock.mockResolvedValueOnce(undefined);
23
27
  const result = await electronDevtoolsService.openDevtools();
24
28
  expect(result).toBeUndefined();
25
29
  });
26
30
 
27
31
  it('should throw error when dispatch fails', async () => {
28
32
  const error = new Error('Failed to open devtools');
29
- vi.mocked(dispatch).mockRejectedValueOnce(error);
33
+ openDevtoolsMock.mockRejectedValueOnce(error);
30
34
 
31
35
  await expect(electronDevtoolsService.openDevtools()).rejects.toThrow(error);
32
36
  });
@@ -1,20 +1,20 @@
1
- import { dispatch } from '@lobechat/electron-client-ipc';
1
+ import { ensureElectronIpc } from '@/utils/electron/ipc';
2
2
 
3
3
  class AutoUpdateService {
4
4
  checkUpdate = async () => {
5
- return dispatch('checkUpdate');
5
+ return ensureElectronIpc().autoUpdate.checkForUpdates();
6
6
  };
7
7
 
8
8
  installNow = async () => {
9
- return dispatch('installNow');
9
+ return ensureElectronIpc().autoUpdate.quitAndInstallUpdate();
10
10
  };
11
11
 
12
12
  installLater = async () => {
13
- return dispatch('installLater');
13
+ return ensureElectronIpc().autoUpdate.installLater();
14
14
  };
15
15
 
16
16
  downloadUpdate() {
17
- return dispatch('downloadUpdate');
17
+ return ensureElectronIpc().autoUpdate.downloadUpdate();
18
18
  }
19
19
  }
20
20
 
@@ -1,8 +1,5 @@
1
- import {
2
- DesktopNotificationResult,
3
- ShowDesktopNotificationParams,
4
- dispatch,
5
- } from '@lobechat/electron-client-ipc';
1
+ import { DesktopNotificationResult, ShowDesktopNotificationParams } from '@lobechat/electron-client-ipc';
2
+ import { ensureElectronIpc } from '@/utils/electron/ipc';
6
3
 
7
4
  /**
8
5
  * Desktop notification service
@@ -16,7 +13,7 @@ export class DesktopNotificationService {
16
13
  async showNotification(
17
14
  params: ShowDesktopNotificationParams,
18
15
  ): Promise<DesktopNotificationResult> {
19
- return dispatch('showDesktopNotification', params);
16
+ return ensureElectronIpc().notification.showDesktopNotification(params);
20
17
  }
21
18
 
22
19
  /**
@@ -24,7 +21,7 @@ export class DesktopNotificationService {
24
21
  * @returns Whether it is hidden
25
22
  */
26
23
  async isMainWindowHidden(): Promise<boolean> {
27
- return dispatch('isMainWindowHidden');
24
+ return ensureElectronIpc().notification.isMainWindowHidden();
28
25
  }
29
26
  }
30
27
 
@@ -1,8 +1,8 @@
1
- import { dispatch } from '@lobechat/electron-client-ipc';
1
+ import { ensureElectronIpc } from '@/utils/electron/ipc';
2
2
 
3
3
  class DevtoolsService {
4
4
  async openDevtools(): Promise<void> {
5
- return dispatch('openDevtools');
5
+ return ensureElectronIpc().devtools.openDevtools();
6
6
  }
7
7
  }
8
8
 
@@ -1,6 +1,7 @@
1
- import { dispatch } from '@lobechat/electron-client-ipc';
2
1
  import { FileMetadata } from '@lobechat/types';
3
2
 
3
+ import { ensureElectronIpc } from '@/utils/electron/ipc';
4
+
4
5
  /**
5
6
  * Desktop application file API client service
6
7
  */
@@ -19,7 +20,7 @@ class DesktopFileAPI {
19
20
  ): Promise<{ metadata: FileMetadata; success: boolean }> {
20
21
  const arrayBuffer = await file.arrayBuffer();
21
22
 
22
- return dispatch('createFile', {
23
+ return ensureElectronIpc().upload.uploadFile({
23
24
  content: arrayBuffer,
24
25
  filename: file.name,
25
26
  hash,
@@ -23,71 +23,72 @@ import {
23
23
  RunCommandParams,
24
24
  RunCommandResult,
25
25
  WriteLocalFileParams,
26
- dispatch,
27
26
  } from '@lobechat/electron-client-ipc';
28
27
 
28
+ import { ensureElectronIpc } from '@/utils/electron/ipc';
29
+
29
30
  class LocalFileService {
30
31
  // File Operations
31
32
  async listLocalFiles(params: ListLocalFileParams): Promise<LocalFileItem[]> {
32
- return dispatch('listLocalFiles', params);
33
+ return ensureElectronIpc().localSystem.listLocalFiles(params);
33
34
  }
34
35
 
35
36
  async readLocalFile(params: LocalReadFileParams): Promise<LocalReadFileResult> {
36
- return dispatch('readLocalFile', params);
37
+ return ensureElectronIpc().localSystem.readFile(params);
37
38
  }
38
39
 
39
40
  async readLocalFiles(params: LocalReadFilesParams): Promise<LocalReadFileResult[]> {
40
- return dispatch('readLocalFiles', params);
41
+ return ensureElectronIpc().localSystem.readFiles(params);
41
42
  }
42
43
 
43
44
  async searchLocalFiles(params: LocalSearchFilesParams): Promise<LocalFileItem[]> {
44
- return dispatch('searchLocalFiles', params);
45
+ return ensureElectronIpc().localSystem.handleLocalFilesSearch(params);
45
46
  }
46
47
 
47
48
  async openLocalFile(params: OpenLocalFileParams) {
48
- return dispatch('openLocalFile', params);
49
+ return ensureElectronIpc().localSystem.handleOpenLocalFile(params);
49
50
  }
50
51
 
51
52
  async openLocalFolder(params: OpenLocalFolderParams) {
52
- return dispatch('openLocalFolder', params);
53
+ return ensureElectronIpc().localSystem.handleOpenLocalFile(params);
53
54
  }
54
55
 
55
56
  async moveLocalFiles(params: MoveLocalFilesParams): Promise<LocalMoveFilesResultItem[]> {
56
- return dispatch('moveLocalFiles', params);
57
+ return ensureElectronIpc().localSystem.handleMoveFiles(params);
57
58
  }
58
59
 
59
60
  async renameLocalFile(params: RenameLocalFileParams) {
60
- return dispatch('renameLocalFile', params);
61
+ return ensureElectronIpc().localSystem.handleRenameFile(params);
61
62
  }
62
63
 
63
64
  async writeFile(params: WriteLocalFileParams) {
64
- return dispatch('writeLocalFile', params);
65
+ return ensureElectronIpc().localSystem.handleWriteFile(params);
65
66
  }
66
67
 
67
68
  async editLocalFile(params: EditLocalFileParams): Promise<EditLocalFileResult> {
68
- return dispatch('editLocalFile', params);
69
+ return ensureElectronIpc().localSystem.handleEditFile(params);
69
70
  }
70
71
 
71
72
  // Shell Commands
72
73
  async runCommand(params: RunCommandParams): Promise<RunCommandResult> {
73
- return dispatch('runCommand', params);
74
+ return ensureElectronIpc().shellCommand.handleRunCommand(params);
74
75
  }
75
76
 
76
77
  async getCommandOutput(params: GetCommandOutputParams): Promise<GetCommandOutputResult> {
77
- return dispatch('getCommandOutput', params);
78
+ return ensureElectronIpc().shellCommand.handleGetCommandOutput(params);
78
79
  }
79
80
 
80
81
  async killCommand(params: KillCommandParams): Promise<KillCommandResult> {
81
- return dispatch('killCommand', params);
82
+ return ensureElectronIpc().shellCommand.handleKillCommand(params);
82
83
  }
83
84
 
84
85
  // Search & Find
85
86
  async grepContent(params: GrepContentParams): Promise<GrepContentResult> {
86
- return dispatch('grepContent', params);
87
+ return ensureElectronIpc().localSystem.handleGrepContent(params);
87
88
  }
88
89
 
89
90
  async globFiles(params: GlobFilesParams): Promise<GlobFilesResult> {
90
- return dispatch('globLocalFiles', params);
91
+ return ensureElectronIpc().localSystem.handleGlobFiles(params);
91
92
  }
92
93
 
93
94
  // Helper methods
@@ -1,39 +1,40 @@
1
- import { DataSyncConfig, MarketAuthorizationParams, dispatch } from '@lobechat/electron-client-ipc';
1
+ import { DataSyncConfig, MarketAuthorizationParams } from '@lobechat/electron-client-ipc';
2
+ import { ensureElectronIpc } from '@/utils/electron/ipc';
2
3
 
3
4
  class RemoteServerService {
4
5
  /**
5
6
  * Get remote server configuration
6
7
  */
7
8
  getRemoteServerConfig = async () => {
8
- return dispatch('getRemoteServerConfig');
9
+ return ensureElectronIpc().remoteServer.getRemoteServerConfig();
9
10
  };
10
11
 
11
12
  /**
12
13
  * Set remote server configuration
13
14
  */
14
15
  setRemoteServerConfig = async (config: DataSyncConfig) => {
15
- return dispatch('setRemoteServerConfig', config);
16
+ return ensureElectronIpc().remoteServer.setRemoteServerConfig(config);
16
17
  };
17
18
 
18
19
  /**
19
20
  * Clear remote server configuration
20
21
  */
21
22
  clearRemoteServerConfig = async () => {
22
- return dispatch('clearRemoteServerConfig');
23
+ return ensureElectronIpc().remoteServer.clearRemoteServerConfig();
23
24
  };
24
25
 
25
26
  /**
26
27
  * Request authorization
27
28
  */
28
29
  requestAuthorization = async (config: DataSyncConfig) => {
29
- return dispatch('requestAuthorization', config);
30
+ return ensureElectronIpc().auth.requestAuthorization(config);
30
31
  };
31
32
 
32
33
  /**
33
34
  * Request Market authorization
34
35
  */
35
36
  requestMarketAuthorization = async (params: MarketAuthorizationParams) => {
36
- return dispatch('requestMarketAuthorization', params);
37
+ return ensureElectronIpc().auth.requestMarketAuthorization(params);
37
38
  };
38
39
  }
39
40
 
@@ -1,50 +1,48 @@
1
- import {
2
- NetworkProxySettings,
3
- ShortcutUpdateResult,
4
- dispatch,
5
- } from '@lobechat/electron-client-ipc';
1
+ import { NetworkProxySettings, ShortcutUpdateResult } from '@lobechat/electron-client-ipc';
2
+
3
+ import { ensureElectronIpc } from '@/utils/electron/ipc';
6
4
 
7
5
  class DesktopSettingsService {
8
6
  /**
9
7
  * Get proxy settings
10
8
  */
11
9
  getProxySettings = async () => {
12
- return dispatch('getProxySettings');
10
+ return ensureElectronIpc().networkProxy.getDesktopSettings();
13
11
  };
14
12
 
15
13
  /**
16
14
  * Set proxy settings
17
15
  */
18
16
  setSettings = async (data: Partial<NetworkProxySettings>) => {
19
- return dispatch('setProxySettings', data);
17
+ return ensureElectronIpc().networkProxy.setProxySettings(data);
20
18
  };
21
19
 
22
20
  /**
23
21
  * Get desktop hotkey configuration
24
22
  */
25
23
  getDesktopHotkeys = async () => {
26
- return dispatch('getShortcutsConfig');
24
+ return ensureElectronIpc().shortcut.getShortcutsConfig();
27
25
  };
28
26
 
29
27
  /**
30
28
  * Update desktop hotkey configuration
31
29
  */
32
30
  updateDesktopHotkey = async (id: string, accelerator: string): Promise<ShortcutUpdateResult> => {
33
- return dispatch('updateShortcutConfig', { accelerator, id });
31
+ return ensureElectronIpc().shortcut.updateShortcutConfig({ accelerator, id });
34
32
  };
35
33
 
36
34
  /**
37
35
  * Test proxy connection
38
36
  */
39
37
  testProxyConnection = async (url: string) => {
40
- return dispatch('testProxyConnection', url);
38
+ return ensureElectronIpc().networkProxy.testProxyConnection(url);
41
39
  };
42
40
 
43
41
  /**
44
42
  * Test specified proxy configuration
45
43
  */
46
44
  testProxyConfig = async (config: NetworkProxySettings, testUrl?: string) => {
47
- return dispatch('testProxyConfig', { config, testUrl });
45
+ return ensureElectronIpc().networkProxy.testProxyConfig({ config, testUrl });
48
46
  };
49
47
  }
50
48