@lobehub/chat 1.82.10 → 1.83.1

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 (195) hide show
  1. package/.env.desktop +1 -2
  2. package/.github/workflows/{release-desktop.yml → desktop-pr-build.yml} +59 -137
  3. package/.github/workflows/release-desktop-beta.yml +194 -0
  4. package/CHANGELOG.md +42 -0
  5. package/apps/desktop/.i18nrc.js +31 -0
  6. package/apps/desktop/Development.md +47 -0
  7. package/apps/desktop/README.md +6 -0
  8. package/apps/desktop/build/Icon-beta.icns +0 -0
  9. package/apps/desktop/build/Icon-nightly.icns +0 -0
  10. package/apps/desktop/build/Icon.icns +0 -0
  11. package/apps/desktop/build/entitlements.mac.plist +12 -0
  12. package/apps/desktop/build/favicon.ico +0 -0
  13. package/apps/desktop/build/icon-beta.png +0 -0
  14. package/apps/desktop/build/icon-dev.png +0 -0
  15. package/apps/desktop/build/icon-nightly.ico +0 -0
  16. package/apps/desktop/build/icon-nightly.png +0 -0
  17. package/apps/desktop/build/icon.ico +0 -0
  18. package/apps/desktop/build/icon.png +0 -0
  19. package/apps/desktop/dev-app-update.yml +6 -0
  20. package/apps/desktop/electron-builder.js +92 -0
  21. package/apps/desktop/electron.vite.config.ts +40 -0
  22. package/apps/desktop/package.json +72 -0
  23. package/apps/desktop/pnpm-workspace.yaml +5 -0
  24. package/apps/desktop/resources/error.html +136 -0
  25. package/apps/desktop/resources/locales/ar/common.json +32 -0
  26. package/apps/desktop/resources/locales/ar/dialog.json +31 -0
  27. package/apps/desktop/resources/locales/ar/menu.json +70 -0
  28. package/apps/desktop/resources/locales/bg-BG/common.json +32 -0
  29. package/apps/desktop/resources/locales/bg-BG/dialog.json +31 -0
  30. package/apps/desktop/resources/locales/bg-BG/menu.json +70 -0
  31. package/apps/desktop/resources/locales/de-DE/common.json +32 -0
  32. package/apps/desktop/resources/locales/de-DE/dialog.json +31 -0
  33. package/apps/desktop/resources/locales/de-DE/menu.json +70 -0
  34. package/apps/desktop/resources/locales/en-US/common.json +32 -0
  35. package/apps/desktop/resources/locales/en-US/dialog.json +31 -0
  36. package/apps/desktop/resources/locales/en-US/menu.json +70 -0
  37. package/apps/desktop/resources/locales/es-ES/common.json +32 -0
  38. package/apps/desktop/resources/locales/es-ES/dialog.json +31 -0
  39. package/apps/desktop/resources/locales/es-ES/menu.json +70 -0
  40. package/apps/desktop/resources/locales/fa-IR/common.json +32 -0
  41. package/apps/desktop/resources/locales/fa-IR/dialog.json +31 -0
  42. package/apps/desktop/resources/locales/fa-IR/menu.json +70 -0
  43. package/apps/desktop/resources/locales/fr-FR/common.json +32 -0
  44. package/apps/desktop/resources/locales/fr-FR/dialog.json +31 -0
  45. package/apps/desktop/resources/locales/fr-FR/menu.json +70 -0
  46. package/apps/desktop/resources/locales/it-IT/common.json +32 -0
  47. package/apps/desktop/resources/locales/it-IT/dialog.json +31 -0
  48. package/apps/desktop/resources/locales/it-IT/menu.json +70 -0
  49. package/apps/desktop/resources/locales/ja-JP/common.json +32 -0
  50. package/apps/desktop/resources/locales/ja-JP/dialog.json +31 -0
  51. package/apps/desktop/resources/locales/ja-JP/menu.json +70 -0
  52. package/apps/desktop/resources/locales/ko-KR/common.json +32 -0
  53. package/apps/desktop/resources/locales/ko-KR/dialog.json +31 -0
  54. package/apps/desktop/resources/locales/ko-KR/menu.json +70 -0
  55. package/apps/desktop/resources/locales/nl-NL/common.json +32 -0
  56. package/apps/desktop/resources/locales/nl-NL/dialog.json +31 -0
  57. package/apps/desktop/resources/locales/nl-NL/menu.json +70 -0
  58. package/apps/desktop/resources/locales/pl-PL/common.json +32 -0
  59. package/apps/desktop/resources/locales/pl-PL/dialog.json +31 -0
  60. package/apps/desktop/resources/locales/pl-PL/menu.json +70 -0
  61. package/apps/desktop/resources/locales/pt-BR/common.json +32 -0
  62. package/apps/desktop/resources/locales/pt-BR/dialog.json +31 -0
  63. package/apps/desktop/resources/locales/pt-BR/menu.json +70 -0
  64. package/apps/desktop/resources/locales/ru-RU/common.json +32 -0
  65. package/apps/desktop/resources/locales/ru-RU/dialog.json +31 -0
  66. package/apps/desktop/resources/locales/ru-RU/menu.json +70 -0
  67. package/apps/desktop/resources/locales/tr-TR/common.json +32 -0
  68. package/apps/desktop/resources/locales/tr-TR/dialog.json +31 -0
  69. package/apps/desktop/resources/locales/tr-TR/menu.json +70 -0
  70. package/apps/desktop/resources/locales/vi-VN/common.json +32 -0
  71. package/apps/desktop/resources/locales/vi-VN/dialog.json +31 -0
  72. package/apps/desktop/resources/locales/vi-VN/menu.json +70 -0
  73. package/apps/desktop/resources/locales/zh-CN/common.json +32 -0
  74. package/apps/desktop/resources/locales/zh-CN/dialog.json +31 -0
  75. package/apps/desktop/resources/locales/zh-CN/menu.json +70 -0
  76. package/apps/desktop/resources/locales/zh-TW/common.json +32 -0
  77. package/apps/desktop/resources/locales/zh-TW/dialog.json +31 -0
  78. package/apps/desktop/resources/locales/zh-TW/menu.json +70 -0
  79. package/apps/desktop/resources/splash.html +88 -0
  80. package/apps/desktop/scripts/i18nWorkflow/const.ts +18 -0
  81. package/apps/desktop/scripts/i18nWorkflow/genDefaultLocale.ts +35 -0
  82. package/apps/desktop/scripts/i18nWorkflow/genDiff.ts +57 -0
  83. package/apps/desktop/scripts/i18nWorkflow/index.ts +35 -0
  84. package/apps/desktop/scripts/i18nWorkflow/utils.ts +54 -0
  85. package/apps/desktop/scripts/pglite-server.ts +14 -0
  86. package/apps/desktop/src/common/routes.ts +78 -0
  87. package/apps/desktop/src/main/appBrowsers.ts +47 -0
  88. package/apps/desktop/src/main/const/dir.ts +29 -0
  89. package/apps/desktop/src/main/const/env.ts +3 -0
  90. package/apps/desktop/src/main/const/store.ts +22 -0
  91. package/apps/desktop/src/main/controllers/AuthCtr.ts +390 -0
  92. package/apps/desktop/src/main/controllers/BrowserWindowsCtr.ts +95 -0
  93. package/apps/desktop/src/main/controllers/DevtoolsCtr.ts +9 -0
  94. package/apps/desktop/src/main/controllers/LocalFileCtr.ts +380 -0
  95. package/apps/desktop/src/main/controllers/MenuCtr.ts +29 -0
  96. package/apps/desktop/src/main/controllers/RemoteServerConfigCtr.ts +335 -0
  97. package/apps/desktop/src/main/controllers/RemoteServerSyncCtr.ts +321 -0
  98. package/apps/desktop/src/main/controllers/ShortcutCtr.ts +19 -0
  99. package/apps/desktop/src/main/controllers/SystemCtr.ts +93 -0
  100. package/apps/desktop/src/main/controllers/UpdaterCtr.ts +43 -0
  101. package/apps/desktop/src/main/controllers/UploadFileCtr.ts +34 -0
  102. package/apps/desktop/src/main/controllers/_template.ts +9 -0
  103. package/apps/desktop/src/main/controllers/index.ts +58 -0
  104. package/apps/desktop/src/main/core/App.ts +370 -0
  105. package/apps/desktop/src/main/core/Browser.ts +345 -0
  106. package/apps/desktop/src/main/core/BrowserManager.ts +154 -0
  107. package/apps/desktop/src/main/core/I18nManager.ts +185 -0
  108. package/apps/desktop/src/main/core/IoCContainer.ts +12 -0
  109. package/apps/desktop/src/main/core/MenuManager.ts +64 -0
  110. package/apps/desktop/src/main/core/ShortcutManager.ts +173 -0
  111. package/apps/desktop/src/main/core/StoreManager.ts +89 -0
  112. package/apps/desktop/src/main/core/UpdaterManager.ts +321 -0
  113. package/apps/desktop/src/main/index.ts +5 -0
  114. package/apps/desktop/src/main/locales/default/common.ts +34 -0
  115. package/apps/desktop/src/main/locales/default/dialog.ts +33 -0
  116. package/apps/desktop/src/main/locales/default/index.ts +11 -0
  117. package/apps/desktop/src/main/locales/default/menu.ts +72 -0
  118. package/apps/desktop/src/main/locales/resources.ts +35 -0
  119. package/apps/desktop/src/main/menus/impls/BaseMenuPlatform.ts +10 -0
  120. package/apps/desktop/src/main/menus/impls/linux.ts +243 -0
  121. package/apps/desktop/src/main/menus/impls/macOS.ts +360 -0
  122. package/apps/desktop/src/main/menus/impls/windows.ts +226 -0
  123. package/apps/desktop/src/main/menus/index.ts +34 -0
  124. package/apps/desktop/src/main/menus/types.ts +28 -0
  125. package/apps/desktop/src/main/modules/fileSearch/impl/macOS.ts +577 -0
  126. package/apps/desktop/src/main/modules/fileSearch/index.ts +23 -0
  127. package/apps/desktop/src/main/modules/fileSearch/type.ts +27 -0
  128. package/apps/desktop/src/main/modules/updater/configs.ts +22 -0
  129. package/apps/desktop/src/main/modules/updater/utils.ts +33 -0
  130. package/apps/desktop/src/main/services/fileSearchSrv.ts +35 -0
  131. package/apps/desktop/src/main/services/fileSrv.ts +255 -0
  132. package/apps/desktop/src/main/services/index.ts +9 -0
  133. package/apps/desktop/src/main/shortcuts/config.ts +18 -0
  134. package/apps/desktop/src/main/shortcuts/index.ts +1 -0
  135. package/apps/desktop/src/main/types/fileSearch.ts +51 -0
  136. package/apps/desktop/src/main/types/store.ts +14 -0
  137. package/apps/desktop/src/main/utils/file-system.ts +15 -0
  138. package/apps/desktop/src/main/utils/logger.ts +44 -0
  139. package/apps/desktop/src/main/utils/next-electron-rsc.ts +383 -0
  140. package/apps/desktop/src/preload/electronApi.ts +18 -0
  141. package/apps/desktop/src/preload/index.ts +14 -0
  142. package/apps/desktop/src/preload/invoke.ts +10 -0
  143. package/apps/desktop/src/preload/routeInterceptor.ts +162 -0
  144. package/apps/desktop/tsconfig.json +21 -0
  145. package/changelog/v1.json +14 -0
  146. package/package.json +1 -1
  147. package/packages/electron-client-ipc/src/events/remoteServer.ts +11 -4
  148. package/packages/electron-client-ipc/src/types/dataSync.ts +15 -0
  149. package/packages/electron-client-ipc/src/types/index.ts +2 -1
  150. package/packages/electron-client-ipc/src/types/proxyTRPCRequest.ts +21 -0
  151. package/packages/electron-server-ipc/src/const.ts +3 -3
  152. package/packages/electron-server-ipc/src/ipcClient.test.ts +7 -6
  153. package/packages/electron-server-ipc/src/ipcClient.ts +17 -8
  154. package/packages/electron-server-ipc/src/ipcServer.ts +7 -3
  155. package/scripts/electronWorkflow/setDesktopVersion.ts +60 -43
  156. package/src/app/[variants]/(main)/_layout/Desktop/index.tsx +1 -1
  157. package/src/components/Analytics/Desktop.tsx +19 -0
  158. package/src/components/Analytics/index.tsx +3 -0
  159. package/src/database/core/db-adaptor.ts +4 -1
  160. package/src/database/core/electron.ts +317 -0
  161. package/src/{app/[variants]/(main)/_layout/Desktop/ElectronTitlebar/Connection/Mode.tsx → features/ElectronTitlebar/Connection/ConnectionMode.tsx} +24 -21
  162. package/src/{app/[variants]/(main)/_layout/Desktop → features}/ElectronTitlebar/Connection/Option.tsx +3 -5
  163. package/src/{app/[variants]/(main)/_layout/Desktop/ElectronTitlebar/Connection/Sync.tsx → features/ElectronTitlebar/Connection/RemoteStatus.tsx} +10 -7
  164. package/src/{app/[variants]/(main)/_layout/Desktop → features}/ElectronTitlebar/Connection/index.tsx +4 -4
  165. package/src/{app/[variants]/(main)/_layout/Desktop → features}/ElectronTitlebar/UpdateModal.tsx +2 -1
  166. package/src/libs/trpc/client/async.ts +6 -0
  167. package/src/libs/trpc/client/edge.ts +6 -0
  168. package/src/libs/trpc/client/helpers/desktopRemoteRPCFetch.ts +72 -0
  169. package/src/libs/trpc/client/index.ts +1 -0
  170. package/src/libs/trpc/client/lambda.ts +10 -1
  171. package/src/libs/trpc/client/tools.ts +6 -0
  172. package/src/server/globalConfig/index.ts +0 -3
  173. package/src/server/modules/ElectronIPCClient/index.ts +3 -1
  174. package/src/server/routers/desktop/index.ts +2 -0
  175. package/src/server/routers/desktop/mcp.ts +47 -0
  176. package/src/server/routers/lambda/user.ts +38 -23
  177. package/src/server/routers/tools/mcp.ts +0 -6
  178. package/src/services/electron/remoteServer.ts +4 -4
  179. package/src/services/mcp.ts +17 -7
  180. package/src/services/upload.ts +9 -0
  181. package/src/store/chat/slices/aiChat/actions/generateAIChat.ts +11 -2
  182. package/src/store/chat/slices/builtinTool/actions/localFile.ts +110 -53
  183. package/src/store/electron/actions/sync.ts +20 -19
  184. package/src/store/electron/initialState.ts +3 -3
  185. package/src/store/electron/selectors/sync.ts +6 -3
  186. package/src/store/electron/store.ts +2 -0
  187. package/src/store/file/slices/upload/action.ts +11 -3
  188. package/src/store/tool/selectors/tool.ts +10 -1
  189. package/src/utils/fetch/headers.ts +27 -0
  190. package/src/utils/fetch/index.ts +2 -0
  191. package/src/utils/fetch/request.ts +28 -0
  192. package/packages/electron-client-ipc/src/types/remoteServer.ts +0 -8
  193. /package/src/{app/[variants]/(main)/_layout/Desktop → features}/ElectronTitlebar/Connection/Waiting.tsx +0 -0
  194. /package/src/{app/[variants]/(main)/_layout/Desktop → features}/ElectronTitlebar/UpdateNotification.tsx +0 -0
  195. /package/src/{app/[variants]/(main)/_layout/Desktop → features}/ElectronTitlebar/index.tsx +0 -0
@@ -0,0 +1,64 @@
1
+ import { Menu } from 'electron';
2
+
3
+ import { IMenuPlatform, MenuOptions, createMenuImpl } from '@/menus';
4
+ import { createLogger } from '@/utils/logger';
5
+
6
+ import type { App } from './App';
7
+
8
+ // Create logger
9
+ const logger = createLogger('core:MenuManager');
10
+
11
+ export default class MenuManager {
12
+ app: App;
13
+ private platformImpl: IMenuPlatform;
14
+
15
+ constructor(app: App) {
16
+ logger.debug('Initializing MenuManager');
17
+ this.app = app;
18
+ this.platformImpl = createMenuImpl(app);
19
+ }
20
+
21
+ /**
22
+ * Initialize menus (mainly application menu)
23
+ */
24
+ initialize(options?: MenuOptions) {
25
+ logger.info('Initializing application menu');
26
+ this.platformImpl.buildAndSetAppMenu(options);
27
+ }
28
+
29
+ /**
30
+ * Build and show context menu
31
+ */
32
+ showContextMenu(type: string, data?: any) {
33
+ logger.debug(`Showing context menu of type: ${type}`);
34
+ const menu = this.platformImpl.buildContextMenu(type, data);
35
+ menu.popup(); // popup must be called in main process
36
+ return { success: true };
37
+ }
38
+
39
+ /**
40
+ * Build tray menu (usually called by tray manager)
41
+ */
42
+ buildTrayMenu(): Menu {
43
+ logger.debug('Building tray menu');
44
+ return this.platformImpl.buildTrayMenu();
45
+ }
46
+
47
+ /**
48
+ * Refresh menus
49
+ */
50
+ refreshMenus(options?: MenuOptions) {
51
+ logger.debug('Refreshing all menus');
52
+ this.platformImpl.refresh(options);
53
+ return { success: true };
54
+ }
55
+
56
+ /**
57
+ * Rebuild and set application menu (e.g., when toggling dev menu visibility)
58
+ */
59
+ rebuildAppMenu(options?: MenuOptions) {
60
+ logger.debug('Rebuilding application menu');
61
+ this.platformImpl.buildAndSetAppMenu(options);
62
+ return { success: true };
63
+ }
64
+ }
@@ -0,0 +1,173 @@
1
+ import { globalShortcut } from 'electron';
2
+
3
+ import { DEFAULT_SHORTCUTS_CONFIG } from '@/shortcuts';
4
+ import { createLogger } from '@/utils/logger';
5
+
6
+ import type { App } from './App';
7
+
8
+ // Create logger
9
+ const logger = createLogger('core:ShortcutManager');
10
+
11
+ export class ShortcutManager {
12
+ private app: App;
13
+ private shortcuts: Map<string, () => void> = new Map();
14
+ private shortcutsConfig: Record<string, string> = {};
15
+
16
+ constructor(app: App) {
17
+ logger.debug('Initializing ShortcutManager');
18
+ this.app = app;
19
+
20
+ app.shortcutMethodMap.forEach((method, key) => {
21
+ this.shortcuts.set(key, method);
22
+ });
23
+ }
24
+
25
+ initialize() {
26
+ logger.info('Initializing global shortcuts');
27
+ // Load shortcuts configuration from storage
28
+ this.loadShortcutsConfig();
29
+ // Register configured shortcuts
30
+ this.registerConfiguredShortcuts();
31
+ }
32
+
33
+ /**
34
+ * Get shortcuts configuration
35
+ */
36
+ getShortcutsConfig(): Record<string, string> {
37
+ return this.shortcutsConfig;
38
+ }
39
+
40
+ /**
41
+ * Update a single shortcut configuration
42
+ */
43
+ updateShortcutConfig(id: string, accelerator: string): boolean {
44
+ try {
45
+ logger.debug(`Updating shortcut ${id} to ${accelerator}`);
46
+ // Update configuration
47
+ this.shortcutsConfig[id] = accelerator;
48
+
49
+ this.saveShortcutsConfig();
50
+ this.registerConfiguredShortcuts();
51
+ return true;
52
+ } catch (error) {
53
+ logger.error(`Error updating shortcut ${id}:`, error);
54
+ return false;
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Register global shortcut
60
+ * @param accelerator Shortcut key combination
61
+ * @param callback Callback function
62
+ * @returns Whether registration was successful
63
+ */
64
+ registerShortcut(accelerator: string, callback: () => void): boolean {
65
+ try {
66
+ // If already registered, unregister first
67
+ if (this.shortcuts.has(accelerator)) {
68
+ this.unregisterShortcut(accelerator);
69
+ }
70
+
71
+ // Register new shortcut
72
+ const success = globalShortcut.register(accelerator, callback);
73
+
74
+ if (success) {
75
+ this.shortcuts.set(accelerator, callback);
76
+ logger.debug(`Registered shortcut: ${accelerator}`);
77
+ } else {
78
+ logger.error(`Failed to register shortcut: ${accelerator}`);
79
+ }
80
+
81
+ return success;
82
+ } catch (error) {
83
+ logger.error(`Error registering shortcut: ${accelerator}`, error);
84
+ return false;
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Unregister global shortcut
90
+ * @param accelerator Shortcut key combination
91
+ */
92
+ unregisterShortcut(accelerator: string): void {
93
+ try {
94
+ globalShortcut.unregister(accelerator);
95
+ this.shortcuts.delete(accelerator);
96
+ logger.debug(`Unregistered shortcut: ${accelerator}`);
97
+ } catch (error) {
98
+ logger.error(`Error unregistering shortcut: ${accelerator}`, error);
99
+ }
100
+ }
101
+
102
+ /**
103
+ * Check if a shortcut is already registered
104
+ * @param accelerator Shortcut key combination
105
+ * @returns Whether it is registered
106
+ */
107
+ isRegistered(accelerator: string): boolean {
108
+ return globalShortcut.isRegistered(accelerator);
109
+ }
110
+
111
+ /**
112
+ * Unregister all shortcuts
113
+ */
114
+ unregisterAll(): void {
115
+ globalShortcut.unregisterAll();
116
+ logger.info('Unregistered all shortcuts');
117
+ }
118
+
119
+ /**
120
+ * Load shortcuts configuration from storage
121
+ */
122
+ private loadShortcutsConfig() {
123
+ try {
124
+ // Try to get configuration from storage
125
+ const config = this.app.storeManager.get('shortcuts');
126
+
127
+ // If no configuration, use default configuration
128
+ if (!config || Object.keys(config).length === 0) {
129
+ logger.debug('No shortcuts config found, using defaults');
130
+ this.shortcutsConfig = DEFAULT_SHORTCUTS_CONFIG;
131
+ this.saveShortcutsConfig();
132
+ } else {
133
+ this.shortcutsConfig = config;
134
+ }
135
+
136
+ logger.debug('Loaded shortcuts config:', this.shortcutsConfig);
137
+ } catch (error) {
138
+ logger.error('Error loading shortcuts config:', error);
139
+ this.shortcutsConfig = DEFAULT_SHORTCUTS_CONFIG;
140
+ this.saveShortcutsConfig();
141
+ }
142
+ }
143
+
144
+ /**
145
+ * Save shortcuts configuration to storage
146
+ */
147
+ private saveShortcutsConfig() {
148
+ try {
149
+ this.app.storeManager.set('shortcuts', this.shortcutsConfig);
150
+ logger.debug('Saved shortcuts config');
151
+ } catch (error) {
152
+ logger.error('Error saving shortcuts config:', error);
153
+ }
154
+ }
155
+
156
+ /**
157
+ * Register configured shortcuts
158
+ */
159
+ private registerConfiguredShortcuts() {
160
+ // Unregister all shortcuts first
161
+ this.unregisterAll();
162
+
163
+ // Register each enabled shortcut
164
+ Object.entries(this.shortcutsConfig).forEach(([id, accelerator]) => {
165
+ logger.debug(`Registering shortcut '${id}' with ${accelerator}`);
166
+
167
+ const method = this.shortcuts.get(id);
168
+ if (accelerator && method) {
169
+ this.registerShortcut(accelerator, method);
170
+ }
171
+ });
172
+ }
173
+ }
@@ -0,0 +1,89 @@
1
+ import Store from 'electron-store';
2
+
3
+ import { STORE_DEFAULTS, STORE_NAME } from '@/const/store';
4
+ import { ElectronMainStore, StoreKey } from '@/types/store';
5
+ import { makeSureDirExist } from '@/utils/file-system';
6
+ import { createLogger } from '@/utils/logger';
7
+
8
+ import { App } from './App';
9
+
10
+ // Create logger
11
+ const logger = createLogger('core:StoreManager');
12
+
13
+ /**
14
+ * Application configuration storage manager
15
+ */
16
+ export class StoreManager {
17
+ /**
18
+ * Global configuration store instance
19
+ */
20
+ private store: Store<ElectronMainStore>;
21
+ private app: App;
22
+
23
+ constructor(app: App) {
24
+ logger.debug('Initializing StoreManager');
25
+ this.app = app;
26
+ this.store = new Store<ElectronMainStore>({
27
+ defaults: STORE_DEFAULTS,
28
+ name: STORE_NAME,
29
+ });
30
+ logger.info('StoreManager initialized with store name:', STORE_NAME);
31
+
32
+ const storagePath = this.store.get('storagePath');
33
+ logger.info('app storage path:', storagePath);
34
+
35
+ makeSureDirExist(storagePath);
36
+ }
37
+
38
+ /**
39
+ * Get configuration item
40
+ * @param key Configuration key
41
+ * @param defaultValue Default value
42
+ */
43
+ get<K extends StoreKey>(key: K, defaultValue?: ElectronMainStore[K]): ElectronMainStore[K] {
44
+ logger.debug('Getting configuration value for key:', key);
45
+ return this.store.get(key, defaultValue as any);
46
+ }
47
+
48
+ /**
49
+ * Set configuration item
50
+ * @param key Configuration key
51
+ * @param value Configuration value
52
+ */
53
+ set<K extends StoreKey>(key: K, value: ElectronMainStore[K]): void {
54
+ logger.debug('Setting configuration value for key:', key);
55
+ this.store.set(key, value);
56
+ }
57
+
58
+ /**
59
+ * Delete configuration item
60
+ * @param key Configuration key
61
+ */
62
+ delete(key: StoreKey): void {
63
+ logger.debug('Deleting configuration key:', key);
64
+ this.store.delete(key);
65
+ }
66
+
67
+ /**
68
+ * Clear all storage
69
+ */
70
+ clear(): void {
71
+ logger.warn('Clearing all store data');
72
+ this.store.clear();
73
+ }
74
+
75
+ /**
76
+ * Check if a configuration item exists
77
+ * @param key Configuration key
78
+ */
79
+ has(key: StoreKey): boolean {
80
+ const exists = this.store.has(key);
81
+ logger.debug('Checking if key exists:', key, exists);
82
+ return exists;
83
+ }
84
+
85
+ async openInEditor() {
86
+ logger.info('Opening store in editor');
87
+ await this.store.openInEditor();
88
+ }
89
+ }
@@ -0,0 +1,321 @@
1
+ import log from 'electron-log';
2
+ import { autoUpdater } from 'electron-updater';
3
+
4
+ import { isDev } from '@/const/env';
5
+ import { UPDATE_CHANNEL as channel, updaterConfig } from '@/modules/updater/configs';
6
+ import { createLogger } from '@/utils/logger';
7
+
8
+ import type { App as AppCore } from './App';
9
+
10
+ // Create logger
11
+ const logger = createLogger('core:UpdaterManager');
12
+
13
+ export class UpdaterManager {
14
+ private app: AppCore;
15
+ private checking: boolean = false;
16
+ private downloading: boolean = false;
17
+ private updateAvailable: boolean = false;
18
+ private isManualCheck: boolean = false;
19
+
20
+ constructor(app: AppCore) {
21
+ this.app = app;
22
+
23
+ // 设置日志
24
+ log.transports.file.level = 'info';
25
+ autoUpdater.logger = log;
26
+
27
+ logger.debug(`[Updater] Log file should be at: ${log.transports.file.getFile().path}`); // 打印路径
28
+ }
29
+
30
+ get mainWindow() {
31
+ return this.app.browserManager.getMainWindow();
32
+ }
33
+
34
+ public initialize = async () => {
35
+ logger.debug('Initializing UpdaterManager');
36
+ // If updates are disabled and in production environment, don't initialize updates
37
+ if (!updaterConfig.enableAppUpdate && !isDev) {
38
+ logger.info('App updates are disabled, skipping updater initialization');
39
+ return;
40
+ }
41
+
42
+ // Configure autoUpdater
43
+ autoUpdater.autoDownload = false; // Set to false, we'll control downloads manually
44
+ autoUpdater.autoInstallOnAppQuit = false;
45
+
46
+ autoUpdater.channel = channel;
47
+ autoUpdater.allowPrerelease = channel !== 'stable';
48
+ autoUpdater.allowDowngrade = false;
49
+
50
+ // Enable test mode in development environment
51
+ if (isDev) {
52
+ logger.info(`Running in dev mode, forcing update check, channel: ${autoUpdater.channel}`);
53
+ // Allow testing updates in development environment
54
+ autoUpdater.forceDevUpdateConfig = true;
55
+ }
56
+
57
+ // Register events
58
+ this.registerEvents();
59
+
60
+ // If auto-check for updates is configured, set up periodic checks
61
+ if (updaterConfig.app.autoCheckUpdate) {
62
+ // Delay update check by 1 minute after startup to avoid network instability
63
+ setTimeout(() => this.checkForUpdates(), 60 * 1000);
64
+
65
+ // Set up periodic checks
66
+ setInterval(() => this.checkForUpdates(), updaterConfig.app.checkUpdateInterval);
67
+ }
68
+
69
+ // Log the channel and allowPrerelease values
70
+ logger.debug(
71
+ `Initialized with channel: ${autoUpdater.channel}, allowPrerelease: ${autoUpdater.allowPrerelease}`,
72
+ );
73
+
74
+ logger.info('UpdaterManager initialization completed');
75
+ };
76
+
77
+ /**
78
+ * Check for updates
79
+ * @param manual whether this is a manual check for updates
80
+ */
81
+ public checkForUpdates = async ({ manual = false }: { manual?: boolean } = {}) => {
82
+ if (this.checking || this.downloading) return;
83
+
84
+ this.checking = true;
85
+ this.isManualCheck = manual;
86
+ logger.info(`${manual ? 'Manually checking' : 'Auto checking'} for updates...`);
87
+
88
+ // If manual check, notify renderer process about check start
89
+ if (manual) {
90
+ this.mainWindow.broadcast('manualUpdateCheckStart');
91
+ }
92
+
93
+ try {
94
+ await autoUpdater.checkForUpdates();
95
+ } catch (error) {
96
+ logger.error('Error checking for updates:', error.message);
97
+
98
+ // If manual check, notify renderer process about check error
99
+ if (manual) {
100
+ this.mainWindow.broadcast('updateError', (error as Error).message);
101
+ }
102
+ } finally {
103
+ this.checking = false;
104
+ }
105
+ };
106
+
107
+ /**
108
+ * Download update
109
+ * @param manual whether this is a manual download
110
+ */
111
+ public downloadUpdate = async (manual: boolean = false) => {
112
+ if (this.downloading || !this.updateAvailable) return;
113
+
114
+ this.downloading = true;
115
+ logger.info(`${manual ? 'Manually downloading' : 'Auto downloading'} update...`);
116
+
117
+ // If manual download or manual check, notify renderer process about download start
118
+ if (manual || this.isManualCheck) {
119
+ this.mainWindow.broadcast('updateDownloadStart');
120
+ }
121
+
122
+ try {
123
+ await autoUpdater.downloadUpdate();
124
+ } catch (error) {
125
+ this.downloading = false;
126
+ logger.error('Error downloading update:', error);
127
+
128
+ // If manual download or manual check, notify renderer process about download error
129
+ if (manual || this.isManualCheck) {
130
+ this.mainWindow.broadcast('updateError', (error as Error).message);
131
+ }
132
+ }
133
+ };
134
+
135
+ /**
136
+ * Install update immediately
137
+ */
138
+ public installNow = () => {
139
+ logger.info('Installing update now...');
140
+
141
+ // Mark application for exit
142
+ this.app.isQuiting = true;
143
+
144
+ // Delay installation by 1 second to ensure window is closed
145
+ autoUpdater.quitAndInstall();
146
+ };
147
+
148
+ /**
149
+ * Install update on next launch
150
+ */
151
+ public installLater = () => {
152
+ logger.info('Update will be installed on next restart');
153
+
154
+ // Mark for installation on next launch, but don't exit application
155
+ autoUpdater.autoInstallOnAppQuit = true;
156
+
157
+ // Notify renderer process that update will be installed on next launch
158
+ this.mainWindow.broadcast('updateWillInstallLater');
159
+ };
160
+
161
+ /**
162
+ * Test mode: Simulate update available
163
+ * Only for use in development environment
164
+ */
165
+ public simulateUpdateAvailable = () => {
166
+ if (!isDev) return;
167
+
168
+ logger.info('Simulating update available...');
169
+
170
+ const mainWindow = this.mainWindow;
171
+ // Simulate a new version update
172
+ const mockUpdateInfo = {
173
+ releaseDate: new Date().toISOString(),
174
+ releaseNotes: ` #### Version 1.0.0 Release Notes
175
+ - Added some great new features
176
+ - Fixed bugs affecting usability
177
+ - Optimized overall application performance
178
+ - Updated dependency libraries
179
+ `,
180
+ version: '1.0.0',
181
+ };
182
+
183
+ // Set update available state
184
+ this.updateAvailable = true;
185
+
186
+ // Notify renderer process
187
+ if (this.isManualCheck) {
188
+ mainWindow.broadcast('manualUpdateAvailable', mockUpdateInfo);
189
+ } else {
190
+ // In auto-check mode, directly simulate download
191
+ this.simulateDownloadProgress();
192
+ }
193
+ };
194
+
195
+ /**
196
+ * Test mode: Simulate update downloaded
197
+ * Only for use in development environment
198
+ */
199
+ public simulateUpdateDownloaded = () => {
200
+ if (!isDev) return;
201
+
202
+ logger.info('Simulating update downloaded...');
203
+
204
+ const mainWindow = this.app.browserManager.getMainWindow();
205
+ if (mainWindow) {
206
+ // Simulate a new version update
207
+ const mockUpdateInfo = {
208
+ releaseDate: new Date().toISOString(),
209
+ releaseNotes: ` #### Version 1.0.0 Release Notes
210
+ - Added some great new features
211
+ - Fixed bugs affecting usability
212
+ - Optimized overall application performance
213
+ - Updated dependency libraries
214
+ `,
215
+ version: '1.0.0',
216
+ };
217
+
218
+ // Set download state
219
+ this.downloading = false;
220
+
221
+ // Notify renderer process
222
+ mainWindow.broadcast('updateDownloaded', mockUpdateInfo);
223
+ }
224
+ };
225
+
226
+ /**
227
+ * Test mode: Simulate update download progress
228
+ * Only for use in development environment
229
+ */
230
+ public simulateDownloadProgress = () => {
231
+ if (!isDev) return;
232
+
233
+ logger.info('Simulating download progress...');
234
+
235
+ const mainWindow = this.app.browserManager.getMainWindow();
236
+
237
+ // Set download state
238
+ this.downloading = true;
239
+
240
+ // Only broadcast download start event if manual check
241
+ if (this.isManualCheck) {
242
+ mainWindow.broadcast('updateDownloadStart');
243
+ }
244
+
245
+ // Simulate progress updates
246
+ let progress = 0;
247
+ const interval = setInterval(() => {
248
+ progress += 10;
249
+
250
+ if (
251
+ progress <= 100 && // Only broadcast download progress if manual check
252
+ this.isManualCheck
253
+ ) {
254
+ mainWindow.broadcast('updateDownloadProgress', {
255
+ bytesPerSecond: 1024 * 1024,
256
+ percent: progress, // 1MB/s
257
+ total: 1024 * 1024 * 100, // 100MB
258
+ transferred: 1024 * 1024 * progress, // Progress * 1MB
259
+ });
260
+ }
261
+
262
+ if (progress >= 100) {
263
+ clearInterval(interval);
264
+ this.simulateUpdateDownloaded();
265
+ }
266
+ }, 300);
267
+ };
268
+
269
+ private registerEvents() {
270
+ logger.debug('Registering updater events');
271
+
272
+ autoUpdater.on('checking-for-update', () => {
273
+ logger.info('[Updater] Checking for update...');
274
+ });
275
+
276
+ autoUpdater.on('update-available', (info) => {
277
+ logger.info(`Update available: ${info.version}`);
278
+ this.updateAvailable = true;
279
+
280
+ if (this.isManualCheck) {
281
+ this.mainWindow.broadcast('manualUpdateAvailable', info);
282
+ } else {
283
+ // If it's an automatic check, start downloading automatically
284
+ logger.info('Auto check found update, starting download automatically...');
285
+ this.downloadUpdate();
286
+ }
287
+ });
288
+
289
+ autoUpdater.on('update-not-available', (info) => {
290
+ logger.info(`Update not available. Current: ${info.version}`);
291
+ if (this.isManualCheck) {
292
+ this.mainWindow.broadcast('manualUpdateNotAvailable', info);
293
+ }
294
+ });
295
+
296
+ autoUpdater.on('error', (err) => {
297
+ logger.error('Error in auto-updater:', err);
298
+ if (this.isManualCheck) {
299
+ this.mainWindow.broadcast('updateError', err.message);
300
+ }
301
+ });
302
+
303
+ autoUpdater.on('download-progress', (progressObj) => {
304
+ logger.debug(
305
+ `Download speed: ${progressObj.bytesPerSecond} - Downloaded ${progressObj.percent}% (${progressObj.transferred}/${progressObj.total})`,
306
+ );
307
+ if (this.isManualCheck) {
308
+ this.mainWindow.broadcast('updateDownloadProgress', progressObj);
309
+ }
310
+ });
311
+
312
+ autoUpdater.on('update-downloaded', (info) => {
313
+ logger.info(`Update downloaded: ${info.version}`);
314
+ this.downloading = false;
315
+ // Always notify about downloaded update
316
+ this.mainWindow.broadcast('updateDownloaded', info);
317
+ });
318
+
319
+ logger.debug('Updater events registered');
320
+ }
321
+ }
@@ -0,0 +1,5 @@
1
+ import { App } from './core/App';
2
+
3
+ const app = new App();
4
+
5
+ app.bootstrap();
@@ -0,0 +1,34 @@
1
+ const common = {
2
+ actions: {
3
+ add: '添加',
4
+ back: '返回',
5
+ cancel: '取消',
6
+ close: '关闭',
7
+ confirm: '确认',
8
+ delete: '删除',
9
+ edit: '编辑',
10
+ more: '更多',
11
+ next: '下一步',
12
+ ok: '确定',
13
+ previous: '上一步',
14
+ refresh: '刷新',
15
+ remove: '移除',
16
+ retry: '重试',
17
+ save: '保存',
18
+ search: '搜索',
19
+ submit: '提交',
20
+ },
21
+ app: {
22
+ description: '你的 AI 助手协作平台',
23
+ name: 'LobeHub',
24
+ },
25
+ status: {
26
+ error: '错误',
27
+ info: '信息',
28
+ loading: '加载中',
29
+ success: '成功',
30
+ warning: '警告',
31
+ },
32
+ };
33
+
34
+ export default common;