@lobehub/lobehub 2.0.0-next.303 → 2.0.0-next.305

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 (87) hide show
  1. package/.github/workflows/manual-build-desktop.yml +11 -1
  2. package/CHANGELOG.md +50 -0
  3. package/apps/desktop/.i18nrc.js +3 -3
  4. package/apps/desktop/electron.vite.config.ts +0 -2
  5. package/apps/desktop/resources/locales/ar/dialog.json +5 -1
  6. package/apps/desktop/resources/locales/ar/menu.json +16 -0
  7. package/apps/desktop/resources/locales/bg-BG/dialog.json +5 -1
  8. package/apps/desktop/resources/locales/bg-BG/menu.json +16 -0
  9. package/apps/desktop/resources/locales/de-DE/dialog.json +5 -1
  10. package/apps/desktop/resources/locales/de-DE/menu.json +16 -0
  11. package/apps/desktop/resources/locales/en/common.json +26 -0
  12. package/apps/desktop/resources/locales/en/dialog.json +27 -0
  13. package/apps/desktop/resources/locales/en/menu.json +73 -0
  14. package/apps/desktop/resources/locales/es-ES/dialog.json +5 -1
  15. package/apps/desktop/resources/locales/es-ES/menu.json +16 -0
  16. package/apps/desktop/resources/locales/fa-IR/dialog.json +5 -1
  17. package/apps/desktop/resources/locales/fa-IR/menu.json +16 -0
  18. package/apps/desktop/resources/locales/fr-FR/dialog.json +5 -1
  19. package/apps/desktop/resources/locales/fr-FR/menu.json +16 -0
  20. package/apps/desktop/resources/locales/it-IT/dialog.json +5 -1
  21. package/apps/desktop/resources/locales/it-IT/menu.json +16 -0
  22. package/apps/desktop/resources/locales/ja-JP/dialog.json +5 -1
  23. package/apps/desktop/resources/locales/ja-JP/menu.json +16 -0
  24. package/apps/desktop/resources/locales/ko-KR/dialog.json +5 -1
  25. package/apps/desktop/resources/locales/ko-KR/menu.json +16 -0
  26. package/apps/desktop/resources/locales/nl-NL/dialog.json +5 -1
  27. package/apps/desktop/resources/locales/nl-NL/menu.json +16 -0
  28. package/apps/desktop/resources/locales/pl-PL/dialog.json +5 -1
  29. package/apps/desktop/resources/locales/pl-PL/menu.json +16 -0
  30. package/apps/desktop/resources/locales/pt-BR/dialog.json +5 -1
  31. package/apps/desktop/resources/locales/pt-BR/menu.json +16 -0
  32. package/apps/desktop/resources/locales/ru-RU/dialog.json +5 -1
  33. package/apps/desktop/resources/locales/ru-RU/menu.json +16 -0
  34. package/apps/desktop/resources/locales/tr-TR/dialog.json +5 -1
  35. package/apps/desktop/resources/locales/tr-TR/menu.json +16 -0
  36. package/apps/desktop/resources/locales/vi-VN/dialog.json +5 -1
  37. package/apps/desktop/resources/locales/vi-VN/menu.json +16 -0
  38. package/apps/desktop/resources/locales/zh-TW/dialog.json +5 -1
  39. package/apps/desktop/resources/locales/zh-TW/menu.json +16 -0
  40. package/apps/desktop/scripts/update-test/README.md +15 -0
  41. package/apps/desktop/src/common/routes.ts +8 -8
  42. package/apps/desktop/src/main/const/dir.ts +2 -2
  43. package/apps/desktop/src/main/const/env.ts +4 -4
  44. package/apps/desktop/src/main/const/store.ts +3 -3
  45. package/apps/desktop/src/main/controllers/AuthCtr.ts +1 -1
  46. package/apps/desktop/src/main/controllers/McpInstallCtr.ts +8 -8
  47. package/apps/desktop/src/main/controllers/NetworkProxyCtr.ts +9 -9
  48. package/apps/desktop/src/main/controllers/RemoteServerSyncCtr.ts +8 -8
  49. package/apps/desktop/src/main/core/App.ts +9 -9
  50. package/apps/desktop/src/main/core/infrastructure/BackendProxyProtocolManager.ts +7 -6
  51. package/apps/desktop/src/main/core/infrastructure/StaticFileServerManager.ts +2 -2
  52. package/apps/desktop/src/main/core/infrastructure/UpdaterManager.ts +38 -5
  53. package/apps/desktop/src/main/core/ui/ShortcutManager.ts +10 -10
  54. package/apps/desktop/src/main/core/ui/TrayManager.ts +12 -12
  55. package/apps/desktop/src/main/locales/resources.ts +4 -4
  56. package/apps/desktop/src/main/menus/impls/macOS.ts +1 -1
  57. package/apps/desktop/src/main/menus/types.ts +5 -5
  58. package/apps/desktop/src/main/modules/updater/configs.ts +10 -10
  59. package/apps/desktop/src/main/modules/updater/utils.ts +9 -9
  60. package/apps/desktop/src/main/services/fileSrv.ts +62 -62
  61. package/apps/desktop/src/main/shortcuts/config.ts +3 -3
  62. package/apps/desktop/src/main/types/protocol.ts +12 -12
  63. package/apps/desktop/src/main/utils/file-system.ts +2 -2
  64. package/apps/desktop/src/main/utils/logger.ts +4 -4
  65. package/apps/desktop/src/main/utils/protocol.ts +32 -32
  66. package/changelog/v1.json +14 -0
  67. package/locales/en-US/auth.json +5 -0
  68. package/locales/en-US/plugin.json +1 -0
  69. package/locales/zh-CN/auth.json +5 -0
  70. package/locales/zh-CN/discover.json +4 -4
  71. package/locales/zh-CN/plugin.json +1 -0
  72. package/package.json +6 -5
  73. package/packages/builtin-tool-agent-builder/src/ExecutionRuntime/index.ts +362 -30
  74. package/packages/builtin-tool-agent-builder/src/client/Intervention/InstallPlugin.tsx +28 -4
  75. package/packages/builtin-tool-group-management/src/client/Inspector/ExecuteAgentTask/index.tsx +78 -0
  76. package/packages/builtin-tool-group-management/src/client/Inspector/{ExecuteTasks → ExecuteAgentTasks}/index.tsx +1 -5
  77. package/packages/builtin-tool-group-management/src/client/Inspector/index.ts +4 -2
  78. package/packages/database/src/schemas/relations.ts +4 -4
  79. package/scripts/electronWorkflow/buildDesktopChannel.ts +135 -0
  80. package/src/app/[variants]/(main)/_layout/index.tsx +2 -0
  81. package/src/features/Conversation/ChatList/components/AutoScroll.tsx +3 -9
  82. package/src/features/Conversation/ChatList/components/VirtualizedList.tsx +2 -6
  83. package/src/features/DesktopNavigationBridge/index.tsx +0 -9
  84. package/src/features/Electron/AuthRequiredModal/index.tsx +151 -0
  85. package/src/locales/default/auth.ts +6 -0
  86. package/src/locales/default/plugin.ts +1 -0
  87. package/src/utils/errorResponse.ts +21 -1
@@ -8,23 +8,23 @@ const logger = createLogger('controllers:McpInstallCtr');
8
8
  const protocolHandler = createProtocolHandler('plugin');
9
9
 
10
10
  /**
11
- * 验证 MCP Schema 对象结构
11
+ * Validate MCP Schema object structure
12
12
  */
13
13
  function validateMcpSchema(schema: any): schema is McpSchema {
14
14
  if (!schema || typeof schema !== 'object') return false;
15
15
 
16
- // 必填字段验证
16
+ // Required field validation
17
17
  if (typeof schema.identifier !== 'string' || !schema.identifier) return false;
18
18
  if (typeof schema.name !== 'string' || !schema.name) return false;
19
19
  if (typeof schema.author !== 'string' || !schema.author) return false;
20
20
  if (typeof schema.description !== 'string' || !schema.description) return false;
21
21
  if (typeof schema.version !== 'string' || !schema.version) return false;
22
22
 
23
- // 可选字段验证
23
+ // Optional field validation
24
24
  if (schema.homepage !== undefined && typeof schema.homepage !== 'string') return false;
25
25
  if (schema.icon !== undefined && typeof schema.icon !== 'string') return false;
26
26
 
27
- // config 字段验证
27
+ // config field validation
28
28
  if (!schema.config || typeof schema.config !== 'object') return false;
29
29
  const config = schema.config;
30
30
 
@@ -35,13 +35,13 @@ function validateMcpSchema(schema: any): schema is McpSchema {
35
35
  } else if (config.type === 'http') {
36
36
  if (typeof config.url !== 'string' || !config.url) return false;
37
37
  try {
38
- new URL(config.url); // 验证URL格式
38
+ new URL(config.url); // Validate URL format
39
39
  } catch {
40
40
  return false;
41
41
  }
42
42
  if (config.headers !== undefined && typeof config.headers !== 'object') return false;
43
43
  } else {
44
- return false; // 未知的 config type
44
+ return false; // Unknown config type
45
45
  }
46
46
 
47
47
  return true;
@@ -54,8 +54,8 @@ interface McpInstallParams {
54
54
  }
55
55
 
56
56
  /**
57
- * MCP 插件安装控制器
58
- * 负责处理 MCP 插件安装流程
57
+ * MCP plugin installation controller
58
+ * Responsible for handling MCP plugin installation process
59
59
  */
60
60
  export default class McpInstallController extends ControllerModule {
61
61
  /**
@@ -16,13 +16,13 @@ import { ControllerModule, IpcMethod } from './index';
16
16
  const logger = createLogger('controllers:NetworkProxyCtr');
17
17
 
18
18
  /**
19
- * 网络代理控制器
20
- * 处理桌面应用的网络代理相关功能
19
+ * Network proxy controller
20
+ * Handle desktop application network proxy related functions
21
21
  */
22
22
  export default class NetworkProxyCtr extends ControllerModule {
23
23
  static override readonly groupName = 'networkProxy';
24
24
  /**
25
- * 获取代理设置
25
+ * Get proxy settings
26
26
  */
27
27
  @IpcMethod()
28
28
  async getDesktopSettings(): Promise<NetworkProxySettings> {
@@ -43,18 +43,18 @@ export default class NetworkProxyCtr extends ControllerModule {
43
43
  }
44
44
 
45
45
  /**
46
- * 设置代理配置
46
+ * Set proxy configuration
47
47
  */
48
48
  @IpcMethod()
49
49
  async setProxySettings(config: Partial<NetworkProxySettings>): Promise<void> {
50
50
  try {
51
- // 获取当前配置
51
+ // Get current configuration
52
52
  const currentConfig = this.app.storeManager.get(
53
53
  'networkProxy',
54
54
  defaultProxySettings,
55
55
  ) as NetworkProxySettings;
56
56
 
57
- // 合并配置并验证
57
+ // Merge configuration and validate
58
58
  const newConfig = merge({}, currentConfig, config);
59
59
 
60
60
  const validation = ProxyConfigValidator.validate(newConfig);
@@ -69,10 +69,10 @@ export default class NetworkProxyCtr extends ControllerModule {
69
69
  return;
70
70
  }
71
71
 
72
- // 应用代理设置
72
+ // Apply proxy settings
73
73
  await ProxyDispatcherManager.applyProxySettings(newConfig);
74
74
 
75
- // 保存到存储
75
+ // Save to storage
76
76
  this.app.storeManager.set('networkProxy', newConfig);
77
77
 
78
78
  logger.info('Proxy settings updated successfully', {
@@ -149,7 +149,7 @@ export default class NetworkProxyCtr extends ControllerModule {
149
149
  return;
150
150
  }
151
151
 
152
- // 应用代理设置
152
+ // Apply proxy settings
153
153
  await ProxyDispatcherManager.applyProxySettings(networkProxy);
154
154
 
155
155
  logger.info('Initial proxy settings applied successfully', {
@@ -47,7 +47,7 @@ export default class RemoteServerSyncCtr extends ControllerModule {
47
47
  }
48
48
 
49
49
  /**
50
- * 处理流式请求的 IPC 调用
50
+ * Handle IPC calls for streaming requests
51
51
  */
52
52
  private handleStreamRequest = async (event: IpcMainEvent, args: ProxyTRPCStreamRequestParams) => {
53
53
  const { requestId } = args;
@@ -79,7 +79,7 @@ export default class RemoteServerSyncCtr extends ControllerModule {
79
79
  return;
80
80
  }
81
81
 
82
- // 调用新的流式转发方法
82
+ // Call new streaming forwarding method
83
83
  await this.forwardStreamRequest(event.sender, {
84
84
  ...args,
85
85
  accessToken: token,
@@ -95,7 +95,7 @@ export default class RemoteServerSyncCtr extends ControllerModule {
95
95
  };
96
96
 
97
97
  /**
98
- * 执行实际的流式请求转发
98
+ * Execute actual streaming request forwarding
99
99
  */
100
100
  private async forwardStreamRequest(
101
101
  sender: WebContents,
@@ -123,14 +123,14 @@ export default class RemoteServerSyncCtr extends ControllerModule {
123
123
  const clientReq = requester.request(requestOptions, (clientRes: IncomingMessage) => {
124
124
  logger.debug(`${logPrefix} Received response with status ${clientRes.statusCode}`);
125
125
 
126
- // 添加调试信息
126
+ // Add debug information
127
127
  logger.debug(`${logPrefix} Response details:`, {
128
128
  headers: clientRes.headers,
129
129
  statusCode: clientRes.statusCode,
130
130
  statusMessage: clientRes.statusMessage,
131
131
  });
132
132
 
133
- // 1. 立刻发送响应头和状态码
133
+ // 1. Immediately send response headers and status code
134
134
  const responseData = {
135
135
  headers: clientRes.headers || {},
136
136
  status: clientRes.statusCode || 500,
@@ -140,21 +140,21 @@ export default class RemoteServerSyncCtr extends ControllerModule {
140
140
  logger.debug(`${logPrefix} Sending response data:`, responseData);
141
141
  sender.send(`stream:response:${requestId}`, responseData);
142
142
 
143
- // 2. 监听数据块并转发
143
+ // 2. Listen for data chunks and forward
144
144
  clientRes.on('data', (chunk: Buffer) => {
145
145
  if (sender.isDestroyed()) return;
146
146
  logger.debug(`${logPrefix} Received data chunk, size: ${chunk.length}. Forwarding...`);
147
147
  sender.send(`stream:data:${requestId}`, chunk);
148
148
  });
149
149
 
150
- // 3. 监听结束信号并转发
150
+ // 3. Listen for end signal and forward
151
151
  clientRes.on('end', () => {
152
152
  logger.debug(`${logPrefix} Stream ended. Forwarding end signal...`);
153
153
  if (sender.isDestroyed()) return;
154
154
  sender.send(`stream:end:${requestId}`);
155
155
  });
156
156
 
157
- // 4. 监听响应流错误并转发
157
+ // 4. Listen for response stream errors and forward
158
158
  clientRes.on('error', (error) => {
159
159
  logger.error(`${logPrefix} Error reading response stream:`, error);
160
160
  if (sender.isDestroyed()) return;
@@ -127,7 +127,7 @@ export class App {
127
127
  // initialize protocol handlers
128
128
  this.protocolManager.initialize();
129
129
 
130
- // 统一处理 before-quit 事件
130
+ // Unified handling of before-quit event
131
131
  app.on('before-quit', this.handleBeforeQuit);
132
132
 
133
133
  // Initialize theme mode from store
@@ -224,10 +224,10 @@ export class App {
224
224
 
225
225
  /**
226
226
  * Handle protocol request by dispatching to registered handlers
227
- * @param urlType 协议URL类型 (如: 'plugin')
228
- * @param action 操作类型 (如: 'install')
229
- * @param data 解析后的协议数据
230
- * @returns 是否成功处理
227
+ * @param urlType Protocol URL type (e.g., 'plugin')
228
+ * @param action Action type (e.g., 'install')
229
+ * @param data Parsed protocol data
230
+ * @returns Whether successfully handled
231
231
  */
232
232
  async handleProtocolRequest(urlType: string, action: string, data: any): Promise<boolean> {
233
233
  const key = `${urlType}:${action}`;
@@ -241,7 +241,7 @@ export class App {
241
241
  try {
242
242
  logger.debug(`Dispatching protocol request ${key} to controller`);
243
243
  const result = await handler.controller[handler.methodName](data);
244
- return result !== false; // 假设控制器返回 false 表示处理失败
244
+ return result !== false; // Assume controller returning false indicates handling failure
245
245
  } catch (error) {
246
246
  logger.error(`Error handling protocol request ${key}:`, error);
247
247
  return false;
@@ -385,17 +385,17 @@ export class App {
385
385
  this.ipcServer = new ElectronIPCServer(name, ipcServerEvents);
386
386
  }
387
387
 
388
- // 新增 before-quit 处理函数
388
+ // Add before-quit handler function
389
389
  private handleBeforeQuit = () => {
390
390
  logger.info('Application is preparing to quit');
391
391
  this.isQuiting = true;
392
392
 
393
- // 销毁托盘
393
+ // Destroy tray
394
394
  if (process.platform === 'win32') {
395
395
  this.trayManager.destroyAll();
396
396
  }
397
397
 
398
- // 执行清理操作
398
+ // Execute cleanup operations
399
399
  this.staticFileServerManager.destroy();
400
400
  };
401
401
  }
@@ -163,13 +163,14 @@ export class BackendProxyProtocolManager {
163
163
  responseHeaders.set('Access-Control-Allow-Headers', '*');
164
164
  responseHeaders.set('X-Src-Url', rewrittenUrl);
165
165
 
166
- // Handle 401 Unauthorized: notify authorization required regardless of token presence
167
- // This covers cases where:
168
- // 1. No token exists
169
- // 2. Token has expired
170
- // 3. Token has been revoked server-side
166
+ // Handle 401 Unauthorized: only notify authorization required for real auth failures
167
+ // The server sets X-Auth-Required header for real authentication failures (e.g., token expired)
168
+ // Other 401 errors (e.g., invalid API keys) should not trigger re-authentication
171
169
  if (upstreamResponse.status === 401) {
172
- this.notifyAuthorizationRequired();
170
+ const authRequired = upstreamResponse.headers.get('X-Auth-Required') === 'true';
171
+ if (authRequired) {
172
+ this.notifyAuthorizationRequired();
173
+ }
173
174
  }
174
175
 
175
176
  return new Response(upstreamResponse.body, {
@@ -160,7 +160,7 @@ export class StaticFileServerManager {
160
160
  logger.debug(`Request method: ${req.method}`);
161
161
  logger.debug(`Request headers: ${JSON.stringify(req.headers)}`);
162
162
 
163
- // 提取文件路径:从 /desktop-file/path/to/file.png 中提取相对路径
163
+ // 提取File path:从 /desktop-file/path/to/file.png 中提取相对路径
164
164
  let filePath = decodeURIComponent(url.pathname.slice(1)); // 移除开头的 /
165
165
  logger.debug(`Initial file path after decode: ${filePath}`);
166
166
 
@@ -243,7 +243,7 @@ export class StaticFileServerManager {
243
243
  }
244
244
 
245
245
  /**
246
- * 获取文件服务器域名
246
+ * Get file server domain
247
247
  */
248
248
  getFileServerDomain(): string {
249
249
  if (!this.isInitialized || !this.serverPort) {
@@ -113,6 +113,26 @@ export class UpdaterManager {
113
113
 
114
114
  logger.info(`${manual ? 'Manually checking' : 'Auto checking'} for updates...`);
115
115
 
116
+ // Log detailed updater configuration for debugging
117
+ const inferredChannel =
118
+ autoUpdater.channel ||
119
+ (autoUpdater.currentVersion?.prerelease?.[0]
120
+ ? String(autoUpdater.currentVersion.prerelease[0])
121
+ : null);
122
+
123
+ logger.info('[Updater Config] Channel:', autoUpdater.channel);
124
+ logger.info('[Updater Config] inferredChannel:', inferredChannel);
125
+ logger.info('[Updater Config] allowPrerelease:', autoUpdater.allowPrerelease);
126
+ logger.info('[Updater Config] currentVersion:', autoUpdater.currentVersion?.version);
127
+ logger.info('[Updater Config] allowDowngrade:', autoUpdater.allowDowngrade);
128
+ logger.info('[Updater Config] autoDownload:', autoUpdater.autoDownload);
129
+ logger.info('[Updater Config] forceDevUpdateConfig:', autoUpdater.forceDevUpdateConfig);
130
+ logger.info('[Updater Config] Build channel from config:', channel);
131
+ logger.info('[Updater Config] isStableChannel:', isStableChannel);
132
+ logger.info('[Updater Config] UPDATE_SERVER_URL:', UPDATE_SERVER_URL || '(not set)');
133
+ logger.info('[Updater Config] usingFallbackProvider:', this.usingFallbackProvider);
134
+ logger.info('[Updater Config] GitHub config:', JSON.stringify(githubConfig));
135
+
116
136
  // If manual check, notify renderer process about check start
117
137
  if (manual) {
118
138
  this.mainWindow.broadcast('manualUpdateCheckStart');
@@ -322,7 +342,7 @@ export class UpdaterManager {
322
342
  /**
323
343
  * Configure update provider based on channel
324
344
  * - Stable channel + UPDATE_SERVER_URL: Use generic HTTP provider (S3) as primary, channel=stable
325
- * - Other channels (beta/nightly) or no S3: Use GitHub provider, channel=latest
345
+ * - Other channels (beta/nightly) or no S3: Use GitHub provider, channel unset (defaults to latest)
326
346
  *
327
347
  * Important: S3 has stable-mac.yml, GitHub has latest-mac.yml
328
348
  */
@@ -340,12 +360,15 @@ export class UpdaterManager {
340
360
  url: UPDATE_SERVER_URL,
341
361
  });
342
362
  } else {
343
- // Beta/nightly channels use GitHub, or fallback to GitHub if UPDATE_SERVER_URL not configured
344
- // GitHub releases have latest-mac.yml, so we use default channel (latest)
345
- autoUpdater.channel = 'latest';
363
+ // GitHub provider:
364
+ // - stable: use default latest-mac.yml (GitHub uploads latest* only)
365
+ // - beta/nightly: leave channel unset so prerelease matching uses tag (e.g. next)
346
366
  const reason = this.usingFallbackProvider ? '(fallback from S3)' : '';
347
367
  logger.info(`Configuring GitHub provider for ${channel} channel ${reason}`);
348
- logger.info(`Channel set to: latest (will look for latest-mac.yml)`);
368
+ if (autoUpdater.channel !== null) {
369
+ autoUpdater.channel = null;
370
+ }
371
+ logger.info('Channel left unset (defaults to latest-mac.yml for GitHub)');
349
372
 
350
373
  // For beta/nightly channels, we need prerelease versions
351
374
  const needPrerelease = channel !== 'stable';
@@ -406,6 +429,8 @@ export class UpdaterManager {
406
429
 
407
430
  autoUpdater.on('checking-for-update', () => {
408
431
  logger.info('[Updater] Checking for update...');
432
+ logger.info('[Updater] Current channel:', autoUpdater.channel);
433
+ logger.info('[Updater] Current allowPrerelease:', autoUpdater.allowPrerelease);
409
434
  });
410
435
 
411
436
  autoUpdater.on('update-available', (info) => {
@@ -437,6 +462,14 @@ export class UpdaterManager {
437
462
 
438
463
  autoUpdater.on('error', async (err) => {
439
464
  logger.error('Error in auto-updater:', err);
465
+ // Log configuration state when error occurs for debugging
466
+ logger.error('[Updater Error Context] Channel:', autoUpdater.channel);
467
+ logger.error('[Updater Error Context] allowPrerelease:', autoUpdater.allowPrerelease);
468
+ logger.error('[Updater Error Context] Build channel from config:', channel);
469
+ logger.error('[Updater Error Context] isStableChannel:', isStableChannel);
470
+ logger.error('[Updater Error Context] UPDATE_SERVER_URL:', UPDATE_SERVER_URL || '(not set)');
471
+ logger.error('[Updater Error Context] usingFallbackProvider:', this.usingFallbackProvider);
472
+ logger.error('[Updater Error Context] GitHub config:', JSON.stringify(githubConfig));
440
473
 
441
474
  // Try fallback to GitHub if S3 failed
442
475
  if (!this.usingFallbackProvider && isStableChannel && UPDATE_SERVER_URL) {
@@ -77,25 +77,25 @@ export class ShortcutManager {
77
77
  try {
78
78
  logger.debug(`Updating shortcut ${id} to ${accelerator}`);
79
79
 
80
- // 1. 检查 ID 是否有效
80
+ // 1. Check if ID is valid
81
81
  if (!DEFAULT_SHORTCUTS_CONFIG[id]) {
82
82
  logger.error(`Invalid shortcut ID: ${id}`);
83
83
  return { errorType: 'INVALID_ID', success: false };
84
84
  }
85
85
 
86
- // 2. 基本格式校验
86
+ // 2. Basic format validation
87
87
  if (!accelerator || typeof accelerator !== 'string' || accelerator.trim() === '') {
88
88
  logger.error(`Invalid accelerator format: ${accelerator}`);
89
89
  return { errorType: 'INVALID_FORMAT', success: false };
90
90
  }
91
91
 
92
- // 转换前端格式到 Electron 格式
92
+ // Convert frontend format to Electron format
93
93
  const convertedAccelerator = this.convertAcceleratorFormat(accelerator.trim());
94
94
  const cleanAccelerator = convertedAccelerator.toLowerCase();
95
95
 
96
96
  logger.debug(`Converted accelerator from ${accelerator} to ${convertedAccelerator}`);
97
97
 
98
- // 3. 检查是否包含 + 号(修饰键格式)
98
+ // 3. Check if contains + sign (modifier key format)
99
99
  if (!cleanAccelerator.includes('+')) {
100
100
  logger.error(
101
101
  `Invalid accelerator format: ${cleanAccelerator}. Must contain modifier keys like 'CommandOrControl+E'`,
@@ -103,7 +103,7 @@ export class ShortcutManager {
103
103
  return { errorType: 'INVALID_FORMAT', success: false };
104
104
  }
105
105
 
106
- // 4. 检查是否有基本的修饰键
106
+ // 4. Check if has basic modifier keys
107
107
  const hasModifier = ['CommandOrControl', 'Command', 'Ctrl', 'Alt', 'Shift'].some((modifier) =>
108
108
  cleanAccelerator.includes(modifier.toLowerCase()),
109
109
  );
@@ -113,7 +113,7 @@ export class ShortcutManager {
113
113
  return { errorType: 'NO_MODIFIER', success: false };
114
114
  }
115
115
 
116
- // 5. 检查冲突
116
+ // 5. Check for conflicts
117
117
  for (const [existingId, existingAccelerator] of Object.entries(this.shortcutsConfig)) {
118
118
  if (
119
119
  existingId !== id &&
@@ -125,7 +125,7 @@ export class ShortcutManager {
125
125
  }
126
126
  }
127
127
 
128
- // 6. 尝试注册测试(检查是否被系统占用)
128
+ // 6. Try test registration (check if occupied by system)
129
129
  const testSuccess = globalShortcut.register(convertedAccelerator, () => {});
130
130
  if (!testSuccess) {
131
131
  logger.error(
@@ -133,11 +133,11 @@ export class ShortcutManager {
133
133
  );
134
134
  return { errorType: 'SYSTEM_OCCUPIED', success: false };
135
135
  } else {
136
- // 测试成功,立即取消注册
136
+ // Test successful, immediately unregister
137
137
  globalShortcut.unregister(convertedAccelerator);
138
138
  }
139
139
 
140
- // 7. 更新配置
140
+ // 7. Update configuration
141
141
  this.shortcutsConfig[id] = convertedAccelerator;
142
142
 
143
143
  this.saveShortcutsConfig();
@@ -285,7 +285,7 @@ export class ShortcutManager {
285
285
  Object.entries(this.shortcutsConfig).forEach(([id, accelerator]) => {
286
286
  logger.debug(`Registering shortcut '${id}' with ${accelerator}`);
287
287
 
288
- // 只注册在 DEFAULT_SHORTCUTS_CONFIG 中存在的快捷键
288
+ // Only register shortcuts that exist in DEFAULT_SHORTCUTS_CONFIG
289
289
  if (!DEFAULT_SHORTCUTS_CONFIG[id]) {
290
290
  logger.debug(`Skipping shortcut '${id}' - not found in DEFAULT_SHORTCUTS_CONFIG`);
291
291
  return;
@@ -8,11 +8,11 @@ import { createLogger } from '@/utils/logger';
8
8
  import type { App } from '../App';
9
9
  import { Tray, TrayOptions } from './Tray';
10
10
 
11
- // 创建日志记录器
11
+ // Create logger
12
12
  const logger = createLogger('core:TrayManager');
13
13
 
14
14
  /**
15
- * 托盘标识符类型
15
+ * Tray identifier type
16
16
  */
17
17
  export type TrayIdentifiers = 'main';
18
18
 
@@ -20,41 +20,41 @@ export class TrayManager {
20
20
  app: App;
21
21
 
22
22
  /**
23
- * 存储所有托盘实例
23
+ * Store all tray instances
24
24
  */
25
25
  trays: Map<TrayIdentifiers, Tray> = new Map();
26
26
 
27
27
  /**
28
- * 构造方法
29
- * @param app 应用实例
28
+ * Constructor
29
+ * @param app Application instance
30
30
  */
31
31
  constructor(app: App) {
32
- logger.debug('初始化 TrayManager');
32
+ logger.debug('Initialize TrayManager');
33
33
  this.app = app;
34
34
  }
35
35
 
36
36
  /**
37
- * 初始化所有托盘
37
+ * Initialize all trays
38
38
  */
39
39
  initializeTrays() {
40
- logger.debug('初始化应用托盘');
40
+ logger.debug('Initialize application tray');
41
41
 
42
- // 初始化主托盘
42
+ // Initialize main tray
43
43
  this.initializeMainTray();
44
44
  }
45
45
 
46
46
  /**
47
- * 获取主托盘
47
+ * Get main tray
48
48
  */
49
49
  getMainTray() {
50
50
  return this.retrieveByIdentifier('main');
51
51
  }
52
52
 
53
53
  /**
54
- * 初始化主托盘
54
+ * Initialize main tray
55
55
  */
56
56
  initializeMainTray() {
57
- logger.debug('初始化主托盘');
57
+ logger.debug('Initialize main tray');
58
58
  return this.retrieveOrInitialize({
59
59
  iconPath: isMac
60
60
  ? nativeTheme.shouldUseDarkColorsForSystemIntegratedUI
@@ -1,12 +1,12 @@
1
1
  /**
2
- * 规范化语言代码
2
+ * Normalize language code
3
3
  */
4
4
  export const normalizeLocale = (locale: string) => {
5
5
  return locale.toLowerCase().replace('_', '-');
6
6
  };
7
7
 
8
8
  /**
9
- * 按需加载翻译资源
9
+ * Load translation resources on demand
10
10
  */
11
11
  export const loadResources = async (lng: string, ns: string) => {
12
12
  // All en-* locales fallback to 'en' and use default TypeScript files
@@ -16,7 +16,7 @@ export const loadResources = async (lng: string, ns: string) => {
16
16
 
17
17
  return content;
18
18
  } catch (error) {
19
- console.error(`[I18n] 无法加载翻译文件: ${ns}`, error);
19
+ console.error(`[I18n] Unable to load translation file: ${ns}`, error);
20
20
  return {};
21
21
  }
22
22
  }
@@ -26,7 +26,7 @@ export const loadResources = async (lng: string, ns: string) => {
26
26
 
27
27
  return content;
28
28
  } catch (error) {
29
- console.error(`无法加载翻译文件: ${lng} - ${ns}`, error);
29
+ console.error(`Unable to load translation file: ${lng} - ${ns}`, error);
30
30
  return {};
31
31
  }
32
32
  };
@@ -47,7 +47,7 @@ export class MacOSMenu extends BaseMenuPlatform implements IMenuPlatform {
47
47
  }
48
48
 
49
49
  refresh(options?: MenuOptions): void {
50
- // 重建应用菜单
50
+ // 重建Application menu
51
51
  this.buildAndSetAppMenu(options);
52
52
  // 如果托盘菜单存在,也重建它(如果需要动态更新)
53
53
  // this.trayMenu = this.buildTrayMenu();
@@ -2,27 +2,27 @@ import { Menu } from 'electron';
2
2
 
3
3
  export interface MenuOptions {
4
4
  showDevItems?: boolean;
5
- // 其他可能的配置项
5
+ // Other possible configuration items
6
6
  }
7
7
 
8
8
  export interface IMenuPlatform {
9
9
  /**
10
- * 构建并设置应用菜单
10
+ * Build and set application menu
11
11
  */
12
12
  buildAndSetAppMenu(options?: MenuOptions): Menu;
13
13
 
14
14
  /**
15
- * 构建上下文菜单
15
+ * Build context menu
16
16
  */
17
17
  buildContextMenu(type: string, data?: any): Menu;
18
18
 
19
19
  /**
20
- * 构建托盘菜单
20
+ * Build tray menu
21
21
  */
22
22
  buildTrayMenu(): Menu;
23
23
 
24
24
  /**
25
- * 刷新菜单
25
+ * Refresh menu
26
26
  */
27
27
  refresh(options?: MenuOptions): void;
28
28
  }
@@ -1,34 +1,34 @@
1
1
  import { isDev } from '@/const/env';
2
2
  import { getDesktopEnv } from '@/env';
3
3
 
4
- // 更新频道(stable, beta, alpha 等)
4
+ // Update channel (stable, beta, alpha, etc.)
5
5
  export const UPDATE_CHANNEL = getDesktopEnv().UPDATE_CHANNEL || 'stable';
6
6
 
7
- // 判断是否为 stable 频道
7
+ // Determine if stable channel
8
8
  export const isStableChannel = UPDATE_CHANNEL === 'stable' || !UPDATE_CHANNEL;
9
9
 
10
- // 自定义更新服务器 URL (用于 stable 频道)
10
+ // Custom update server URL (for stable channel)
11
11
  // e.g., https://releases.lobehub.com/stable
12
12
  export const UPDATE_SERVER_URL = getDesktopEnv().UPDATE_SERVER_URL;
13
13
 
14
- // GitHub 配置 (用于 beta/nightly 频道,或作为 fallback)
14
+ // GitHub configuration (for beta/nightly channels, or as fallback)
15
15
  export const githubConfig = {
16
16
  owner: 'lobehub',
17
17
  repo: 'lobe-chat',
18
18
  };
19
19
 
20
20
  export const updaterConfig = {
21
- // 应用更新配置
21
+ // 应用Update configuration
22
22
  app: {
23
- // 是否自动检查更新
23
+ // Whether to auto-check for updates
24
24
  autoCheckUpdate: true,
25
- // 是否自动下载更新
25
+ // Whether to auto-download updates
26
26
  autoDownloadUpdate: true,
27
- // 检查更新的时间间隔(毫秒)
28
- checkUpdateInterval: 60 * 60 * 1000, // 1小时
27
+ // Update check interval (milliseconds)
28
+ checkUpdateInterval: 60 * 60 * 1000, // 1 hour
29
29
  },
30
30
 
31
- // 是否启用应用更新
31
+ // Whether to enable application updates
32
32
  enableAppUpdate: !isDev,
33
33
 
34
34
  // 是否启用渲染层热更新