@lobehub/chat 1.103.1 → 1.103.2

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 (79) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/apps/desktop/build/icon-beta.ico +0 -0
  3. package/apps/desktop/build/icon-dev.ico +0 -0
  4. package/apps/desktop/build/icon-nightly.ico +0 -0
  5. package/apps/desktop/build/icon.ico +0 -0
  6. package/apps/desktop/electron.vite.config.ts +4 -2
  7. package/apps/desktop/package.json +1 -0
  8. package/apps/desktop/src/main/appBrowsers.ts +2 -2
  9. package/apps/desktop/src/main/const/env.ts +5 -4
  10. package/apps/desktop/src/main/const/store.ts +1 -0
  11. package/apps/desktop/src/main/const/theme.ts +11 -0
  12. package/apps/desktop/src/main/controllers/BrowserWindowsCtr.ts +1 -1
  13. package/apps/desktop/src/main/controllers/NotificationCtr.ts +2 -4
  14. package/apps/desktop/src/main/controllers/SystemCtr.ts +4 -0
  15. package/apps/desktop/src/main/controllers/TrayMenuCtr.ts +5 -5
  16. package/apps/desktop/src/main/controllers/index.ts +1 -1
  17. package/apps/desktop/src/main/core/App.ts +9 -10
  18. package/apps/desktop/src/main/core/{Browser.ts → browser/Browser.ts} +129 -88
  19. package/apps/desktop/src/main/core/{BrowserManager.ts → browser/BrowserManager.ts} +13 -3
  20. package/apps/desktop/src/main/core/{StaticFileServerManager.ts → infrastructure/StaticFileServerManager.ts} +13 -7
  21. package/apps/desktop/src/main/core/{StoreManager.ts → infrastructure/StoreManager.ts} +1 -1
  22. package/apps/desktop/src/main/core/{UpdaterManager.ts → infrastructure/UpdaterManager.ts} +1 -1
  23. package/apps/desktop/src/main/core/{MenuManager.ts → ui/MenuManager.ts} +2 -2
  24. package/apps/desktop/src/main/core/{ShortcutManager.ts → ui/ShortcutManager.ts} +7 -1
  25. package/apps/desktop/src/main/core/{Tray.ts → ui/Tray.ts} +61 -59
  26. package/apps/desktop/src/main/core/{TrayManager.ts → ui/TrayManager.ts} +5 -5
  27. package/apps/desktop/src/main/shortcuts/config.ts +2 -2
  28. package/apps/desktop/src/main/types/store.ts +1 -0
  29. package/changelog/v1.json +12 -0
  30. package/docs/development/basic/add-new-image-model.mdx +162 -0
  31. package/docs/development/basic/add-new-image-model.zh-CN.mdx +162 -0
  32. package/docs/usage/providers/fal.mdx +1 -1
  33. package/docs/usage/providers/fal.zh-CN.mdx +1 -1
  34. package/package.json +66 -66
  35. package/src/app/[variants]/(main)/chat/(workspace)/_layout/Desktop/Portal.tsx +3 -1
  36. package/src/app/[variants]/(main)/chat/(workspace)/features/AgentSettings/index.tsx +4 -2
  37. package/src/app/[variants]/(main)/image/@menu/components/SeedNumberInput/index.tsx +1 -1
  38. package/src/app/[variants]/(main)/image/features/GenerationFeed/BatchItem.tsx +39 -3
  39. package/src/app/[variants]/(main)/image/features/GenerationFeed/ReferenceImages.tsx +122 -0
  40. package/src/config/aiModels/fal.ts +31 -7
  41. package/src/config/aiModels/openai.ts +10 -1
  42. package/src/features/ElectronTitlebar/WinControl/index.tsx +85 -90
  43. package/src/features/ElectronTitlebar/hooks/useWatchThemeUpdate.ts +10 -5
  44. package/src/features/ImageTopicPanel/index.tsx +0 -1
  45. package/src/features/PluginDevModal/index.tsx +3 -1
  46. package/src/features/User/__tests__/UserAvatar.test.tsx +5 -4
  47. package/src/libs/model-runtime/fal/index.ts +1 -1
  48. package/src/libs/model-runtime/types/image.ts +1 -1
  49. package/src/libs/model-runtime/utils/openaiCompatibleFactory/index.ts +1 -1
  50. package/src/libs/model-runtime/utils/response.ts +2 -0
  51. package/src/libs/model-runtime/utils/streams/google-ai.test.ts +46 -0
  52. package/src/libs/model-runtime/utils/streams/google-ai.ts +4 -4
  53. package/src/libs/model-runtime/utils/streams/vertex-ai.ts +6 -8
  54. package/src/libs/standard-parameters/{meta-schema.test.ts → index.test.ts} +1 -1
  55. package/src/libs/standard-parameters/index.ts +152 -1
  56. package/src/server/ld.test.ts +4 -3
  57. package/src/server/routers/async/image.ts +1 -1
  58. package/src/services/__tests__/chat.test.ts +3 -4
  59. package/src/store/chat/slices/message/selectors.test.ts +2 -3
  60. package/src/store/chat/slices/plugin/action.test.ts +2 -1
  61. package/src/store/image/slices/generationConfig/action.test.ts +2 -2
  62. package/src/store/image/slices/generationConfig/action.ts +1 -1
  63. package/src/store/image/slices/generationConfig/hooks.test.ts +2 -2
  64. package/src/store/image/slices/generationConfig/hooks.ts +1 -4
  65. package/src/store/image/slices/generationConfig/initialState.ts +2 -2
  66. package/src/store/image/slices/generationConfig/selectors.test.ts +2 -2
  67. package/src/store/image/slices/generationConfig/selectors.ts +1 -1
  68. package/src/store/user/slices/auth/selectors.test.ts +3 -2
  69. package/src/types/generation/index.ts +1 -0
  70. package/docs/development/basic/add-new-ai-image-model.mdx +0 -36
  71. package/docs/development/basic/add-new-ai-image-model.zh-CN.mdx +0 -0
  72. package/src/config/paramsSchemas/fal/flux-kontext-dev.ts +0 -8
  73. package/src/config/paramsSchemas/fal/flux-pro-kontext.ts +0 -11
  74. package/src/config/paramsSchemas/fal/flux-schnell.ts +0 -9
  75. package/src/config/paramsSchemas/fal/imagen4.ts +0 -10
  76. package/src/config/paramsSchemas/openai/gpt-image-1.ts +0 -10
  77. package/src/libs/standard-parameters/meta-schema.ts +0 -147
  78. /package/apps/desktop/src/main/core/{I18nManager.ts → infrastructure/I18nManager.ts} +0 -0
  79. /package/apps/desktop/src/main/core/{IoCContainer.ts → infrastructure/IoCContainer.ts} +0 -0
package/CHANGELOG.md CHANGED
@@ -2,6 +2,41 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ### [Version 1.103.2](https://github.com/lobehub/lobe-chat/compare/v1.103.1...v1.103.2)
6
+
7
+ <sup>Released on **2025-07-24**</sup>
8
+
9
+ #### 🐛 Bug Fixes
10
+
11
+ - **misc**: Fix chat stream in desktop and update shortcut.
12
+
13
+ #### 💄 Styles
14
+
15
+ - **misc**: Add cached token count to usage of GoogleAI and VertexAI, fix desktop titlebar style in window, fix sub topic width in md responsive.
16
+
17
+ <br/>
18
+
19
+ <details>
20
+ <summary><kbd>Improvements and Fixes</kbd></summary>
21
+
22
+ #### What's fixed
23
+
24
+ - **misc**: Fix chat stream in desktop and update shortcut, closes [#8520](https://github.com/lobehub/lobe-chat/issues/8520) ([0192140](https://github.com/lobehub/lobe-chat/commit/0192140))
25
+
26
+ #### Styles
27
+
28
+ - **misc**: Add cached token count to usage of GoogleAI and VertexAI, closes [#8545](https://github.com/lobehub/lobe-chat/issues/8545) ([66dbb24](https://github.com/lobehub/lobe-chat/commit/66dbb24))
29
+ - **misc**: Fix desktop titlebar style in window, closes [#8439](https://github.com/lobehub/lobe-chat/issues/8439) ([fd7662c](https://github.com/lobehub/lobe-chat/commit/fd7662c))
30
+ - **misc**: Fix sub topic width in md responsive, closes [#8443](https://github.com/lobehub/lobe-chat/issues/8443) ([9bae13b](https://github.com/lobehub/lobe-chat/commit/9bae13b))
31
+
32
+ </details>
33
+
34
+ <div align="right">
35
+
36
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
37
+
38
+ </div>
39
+
5
40
  ### [Version 1.103.1](https://github.com/lobehub/lobe-chat/compare/v1.103.0...v1.103.1)
6
41
 
7
42
  <sup>Released on **2025-07-23**</sup>
Binary file
Binary file
Binary file
Binary file
@@ -11,8 +11,9 @@ console.log(`[electron-vite.config.ts] Detected UPDATE_CHANNEL: ${updateChannel}
11
11
  export default defineConfig({
12
12
  main: {
13
13
  build: {
14
+ minify: !isDev,
14
15
  outDir: 'dist/main',
15
- sourcemap: isDev,
16
+ sourcemap: isDev ? 'inline' : false,
16
17
  },
17
18
  // 这里是关键:在构建时进行文本替换
18
19
  define: {
@@ -30,8 +31,9 @@ export default defineConfig({
30
31
  },
31
32
  preload: {
32
33
  build: {
34
+ minify: !isDev,
33
35
  outDir: 'dist/preload',
34
- sourcemap: isDev,
36
+ sourcemap: isDev ? 'inline' : false,
35
37
  },
36
38
  plugins: [externalizeDepsPlugin({})],
37
39
  resolve: {
@@ -31,6 +31,7 @@
31
31
  },
32
32
  "dependencies": {
33
33
  "electron-updater": "^6.6.2",
34
+ "electron-window-state": "^5.0.3",
34
35
  "get-port-please": "^3.1.2",
35
36
  "pdfjs-dist": "4.10.38"
36
37
  },
@@ -1,4 +1,4 @@
1
- import type { BrowserWindowOpts } from './core/Browser';
1
+ import type { BrowserWindowOpts } from './core/browser/Browser';
2
2
 
3
3
  export const BrowsersIdentifiers = {
4
4
  chat: 'chat',
@@ -36,7 +36,7 @@ export const appBrowsers = {
36
36
  autoHideMenuBar: true,
37
37
  height: 800,
38
38
  identifier: 'settings',
39
- // keepAlive: true,
39
+ keepAlive: true,
40
40
  minWidth: 600,
41
41
  parentIdentifier: 'chat',
42
42
  path: '/settings',
@@ -1,12 +1,13 @@
1
+ import { dev, linux, macOS, windows } from 'electron-is';
1
2
  import os from 'node:os';
2
3
 
3
- export const isDev = process.env.NODE_ENV === 'development';
4
+ export const isDev = dev();
4
5
 
5
6
  export const OFFICIAL_CLOUD_SERVER = process.env.OFFICIAL_CLOUD_SERVER || 'https://lobechat.com';
6
7
 
7
- export const isMac = process.platform === 'darwin';
8
- export const isWindows = process.platform === 'win32';
9
- export const isLinux = process.platform === 'linux';
8
+ export const isMac = macOS();
9
+ export const isWindows = windows();
10
+ export const isLinux = linux();
10
11
 
11
12
  function getIsWindows11() {
12
13
  if (!isWindows) return false;
@@ -31,4 +31,5 @@ export const STORE_DEFAULTS: ElectronMainStore = {
31
31
  networkProxy: defaultProxySettings,
32
32
  shortcuts: DEFAULT_SHORTCUTS_CONFIG,
33
33
  storagePath: appStorageDir,
34
+ themeMode: 'auto',
34
35
  };
@@ -0,0 +1,11 @@
1
+ // Theme colors
2
+ export const BACKGROUND_DARK = '#000';
3
+ export const BACKGROUND_LIGHT = '#f8f8f8';
4
+ export const SYMBOL_COLOR_DARK = '#ffffff80';
5
+ export const SYMBOL_COLOR_LIGHT = '#00000080';
6
+
7
+ // Window dimensions and constraints
8
+ export const TITLE_BAR_HEIGHT = 29;
9
+
10
+ // Default window configuration
11
+ export const THEME_CHANGE_DELAY = 100;
@@ -7,7 +7,7 @@ import { IpcClientEventSender } from '@/types/ipcClientEvent';
7
7
  import { ControllerModule, ipcClientEvent, shortcut } from './index';
8
8
 
9
9
  export default class BrowserWindowsCtr extends ControllerModule {
10
- @shortcut('toggleMainWindow')
10
+ @shortcut('showMainWindow')
11
11
  async toggleMainWindow() {
12
12
  const mainWindow = this.app.browserManager.getMainWindow();
13
13
  mainWindow.toggleVisible();
@@ -77,10 +77,8 @@ export default class NotificationCtr extends ControllerModule {
77
77
  const notification = new Notification({
78
78
  body: params.body,
79
79
  // 添加更多配置以确保通知能正常显示
80
- hasReply: false,
81
-
82
- silent: params.silent || false,
83
-
80
+ hasReply: false,
81
+ silent: params.silent || false,
84
82
  timeoutType: 'default',
85
83
  title: params.title,
86
84
  urgency: 'normal',
@@ -83,7 +83,11 @@ export default class SystemController extends ControllerModule {
83
83
 
84
84
  @ipcClientEvent('updateThemeMode')
85
85
  async updateThemeModeHandler(themeMode: ThemeMode) {
86
+ this.app.storeManager.set('themeMode', themeMode);
86
87
  this.app.browserManager.broadcastToAllWindows('themeChanged', { themeMode });
88
+
89
+ // Apply visual effects to all browser windows when theme mode changes
90
+ this.app.browserManager.handleAppThemeChange();
87
91
  }
88
92
 
89
93
  @ipcServerEvent('getDatabasePath')
@@ -15,7 +15,7 @@ export default class TrayMenuCtr extends ControllerModule {
15
15
  /**
16
16
  * 使用快捷键切换窗口可见性
17
17
  */
18
- @shortcut('toggleMainWindow')
18
+ @shortcut('showMainWindow')
19
19
  async toggleMainWindow() {
20
20
  logger.debug('通过快捷键切换主窗口可见性');
21
21
  const mainWindow = this.app.browserManager.getMainWindow();
@@ -47,7 +47,7 @@ export default class TrayMenuCtr extends ControllerModule {
47
47
 
48
48
  return {
49
49
  error: '托盘通知仅在 Windows 平台支持',
50
- success: false
50
+ success: false,
51
51
  };
52
52
  }
53
53
 
@@ -71,7 +71,7 @@ export default class TrayMenuCtr extends ControllerModule {
71
71
  logger.error('更新托盘图标失败:', error);
72
72
  return {
73
73
  error: String(error),
74
- success: false
74
+ success: false,
75
75
  };
76
76
  }
77
77
  }
@@ -79,7 +79,7 @@ export default class TrayMenuCtr extends ControllerModule {
79
79
 
80
80
  return {
81
81
  error: '托盘功能仅在 Windows 平台支持',
82
- success: false
82
+ success: false,
83
83
  };
84
84
  }
85
85
 
@@ -103,7 +103,7 @@ export default class TrayMenuCtr extends ControllerModule {
103
103
 
104
104
  return {
105
105
  error: '托盘功能仅在 Windows 平台支持',
106
- success: false
106
+ success: false,
107
107
  };
108
108
  }
109
109
  }
@@ -2,7 +2,7 @@ import type { ClientDispatchEvents } from '@lobechat/electron-client-ipc';
2
2
  import type { ServerDispatchEvents } from '@lobechat/electron-server-ipc';
3
3
 
4
4
  import type { App } from '@/core/App';
5
- import { IoCContainer } from '@/core/IoCContainer';
5
+ import { IoCContainer } from '@/core/infrastructure/IoCContainer';
6
6
  import { ShortcutActionType } from '@/shortcuts';
7
7
 
8
8
  const ipcDecorator =
@@ -9,20 +9,19 @@ import { buildDir, nextStandaloneDir } from '@/const/dir';
9
9
  import { isDev } from '@/const/env';
10
10
  import { IControlModule } from '@/controllers';
11
11
  import { IServiceModule } from '@/services';
12
- import FileService from '@/services/fileSrv';
13
12
  import { IpcClientEventSender } from '@/types/ipcClientEvent';
14
13
  import { createLogger } from '@/utils/logger';
15
14
  import { CustomRequestHandler, createHandler } from '@/utils/next-electron-rsc';
16
15
 
17
- import BrowserManager from './BrowserManager';
18
- import { I18nManager } from './I18nManager';
19
- import { IoCContainer } from './IoCContainer';
20
- import MenuManager from './MenuManager';
21
- import { ShortcutManager } from './ShortcutManager';
22
- import { StaticFileServerManager } from './StaticFileServerManager';
23
- import { StoreManager } from './StoreManager';
24
- import TrayManager from './TrayManager';
25
- import { UpdaterManager } from './UpdaterManager';
16
+ import { BrowserManager } from './browser/BrowserManager';
17
+ import { I18nManager } from './infrastructure/I18nManager';
18
+ import { IoCContainer } from './infrastructure/IoCContainer';
19
+ import { StaticFileServerManager } from './infrastructure/StaticFileServerManager';
20
+ import { StoreManager } from './infrastructure/StoreManager';
21
+ import { UpdaterManager } from './infrastructure/UpdaterManager';
22
+ import { MenuManager } from './ui/MenuManager';
23
+ import { ShortcutManager } from './ui/ShortcutManager';
24
+ import { TrayManager } from './ui/TrayManager';
26
25
 
27
26
  const logger = createLogger('core:App');
28
27
 
@@ -6,13 +6,21 @@ import {
6
6
  nativeTheme,
7
7
  screen,
8
8
  } from 'electron';
9
- import os from 'node:os';
10
9
  import { join } from 'node:path';
11
10
 
11
+ import { buildDir, preloadDir, resourcesDir } from '@/const/dir';
12
+ import { isDev, isWindows } from '@/const/env';
13
+ import {
14
+ BACKGROUND_DARK,
15
+ BACKGROUND_LIGHT,
16
+ SYMBOL_COLOR_DARK,
17
+ SYMBOL_COLOR_LIGHT,
18
+ THEME_CHANGE_DELAY,
19
+ TITLE_BAR_HEIGHT,
20
+ } from '@/const/theme';
12
21
  import { createLogger } from '@/utils/logger';
13
22
 
14
- import { preloadDir, resourcesDir } from '../const/dir';
15
- import type { App } from './App';
23
+ import type { App } from '../App';
16
24
 
17
25
  // Create logger
18
26
  const logger = createLogger('core:Browser');
@@ -20,9 +28,6 @@ const logger = createLogger('core:Browser');
20
28
  export interface BrowserWindowOpts extends BrowserWindowConstructorOptions {
21
29
  devTools?: boolean;
22
30
  height?: number;
23
- /**
24
- * URL
25
- */
26
31
  identifier: string;
27
32
  keepAlive?: boolean;
28
33
  parentIdentifier?: string;
@@ -34,38 +39,18 @@ export interface BrowserWindowOpts extends BrowserWindowConstructorOptions {
34
39
 
35
40
  export default class Browser {
36
41
  private app: App;
37
-
38
- /**
39
- * Internal electron window
40
- */
41
42
  private _browserWindow?: BrowserWindow;
42
-
43
+ private themeListenerSetup = false;
43
44
  private stopInterceptHandler;
44
- /**
45
- * Identifier
46
- */
47
45
  identifier: string;
48
-
49
- /**
50
- * Options at creation
51
- */
52
46
  options: BrowserWindowOpts;
53
-
54
- /**
55
- * Key for storing window state in storeManager
56
- */
57
47
  private readonly windowStateKey: string;
58
48
 
59
- /**
60
- * Method to expose window externally
61
- */
62
49
  get browserWindow() {
63
50
  return this.retrieveOrInitialize();
64
51
  }
65
-
66
52
  get webContents() {
67
53
  if (this._browserWindow.isDestroyed()) return null;
68
-
69
54
  return this._browserWindow.webContents;
70
55
  }
71
56
 
@@ -86,6 +71,101 @@ export default class Browser {
86
71
  this.retrieveOrInitialize();
87
72
  }
88
73
 
74
+ /**
75
+ * Get platform-specific theme configuration for window creation
76
+ */
77
+ private getPlatformThemeConfig(isDarkMode?: boolean): Record<string, any> {
78
+ const darkMode = isDarkMode ?? nativeTheme.shouldUseDarkColors;
79
+
80
+ if (isWindows) {
81
+ return this.getWindowsThemeConfig(darkMode);
82
+ }
83
+
84
+ return {};
85
+ }
86
+
87
+ /**
88
+ * Get Windows-specific theme configuration
89
+ */
90
+ private getWindowsThemeConfig(isDarkMode: boolean) {
91
+ return {
92
+ backgroundColor: isDarkMode ? BACKGROUND_DARK : BACKGROUND_LIGHT,
93
+ icon: isDev ? join(buildDir, 'icon-dev.ico') : undefined,
94
+ titleBarOverlay: {
95
+ color: isDarkMode ? BACKGROUND_DARK : BACKGROUND_LIGHT,
96
+ height: TITLE_BAR_HEIGHT,
97
+ symbolColor: isDarkMode ? SYMBOL_COLOR_DARK : SYMBOL_COLOR_LIGHT,
98
+ },
99
+ titleBarStyle: 'hidden' as const,
100
+ };
101
+ }
102
+
103
+ private setupThemeListener(): void {
104
+ if (this.themeListenerSetup) return;
105
+
106
+ nativeTheme.on('updated', this.handleThemeChange);
107
+ this.themeListenerSetup = true;
108
+ }
109
+
110
+ private handleThemeChange = (): void => {
111
+ logger.debug(`[${this.identifier}] System theme changed, reapplying visual effects.`);
112
+ setTimeout(() => {
113
+ this.applyVisualEffects();
114
+ }, THEME_CHANGE_DELAY);
115
+ };
116
+
117
+ /**
118
+ * Handle application theme mode change (called from BrowserManager)
119
+ */
120
+ handleAppThemeChange = (): void => {
121
+ logger.debug(`[${this.identifier}] App theme mode changed, reapplying visual effects.`);
122
+ setTimeout(() => {
123
+ this.applyVisualEffects();
124
+ }, THEME_CHANGE_DELAY);
125
+ };
126
+
127
+ private applyVisualEffects(): void {
128
+ if (!this._browserWindow || this._browserWindow.isDestroyed()) return;
129
+
130
+ logger.debug(`[${this.identifier}] Applying visual effects for platform`);
131
+ const isDarkMode = this.isDarkMode;
132
+
133
+ try {
134
+ if (isWindows) {
135
+ this.applyWindowsVisualEffects(isDarkMode);
136
+ }
137
+
138
+ logger.debug(
139
+ `[${this.identifier}] Visual effects applied successfully (dark mode: ${isDarkMode})`,
140
+ );
141
+ } catch (error) {
142
+ logger.error(`[${this.identifier}] Failed to apply visual effects:`, error);
143
+ }
144
+ }
145
+
146
+ private applyWindowsVisualEffects(isDarkMode: boolean): void {
147
+ const config = this.getWindowsThemeConfig(isDarkMode);
148
+
149
+ this._browserWindow.setBackgroundColor(config.backgroundColor);
150
+ this._browserWindow.setTitleBarOverlay(config.titleBarOverlay);
151
+ }
152
+
153
+ private cleanupThemeListener(): void {
154
+ if (this.themeListenerSetup) {
155
+ // Note: nativeTheme listeners are global, consider using a centralized theme manager
156
+ nativeTheme.off('updated', this.handleThemeChange);
157
+ // for multiple windows to avoid duplicate listeners
158
+ this.themeListenerSetup = false;
159
+ }
160
+ }
161
+
162
+ private get isDarkMode() {
163
+ const themeMode = this.app.storeManager.get('themeMode');
164
+ if (themeMode === 'auto') return nativeTheme.shouldUseDarkColors;
165
+
166
+ return themeMode === 'dark';
167
+ }
168
+
89
169
  loadUrl = async (path: string) => {
90
170
  const initUrl = this.app.nextServerUrl + path;
91
171
 
@@ -203,6 +283,7 @@ export default class Browser {
203
283
  destroy() {
204
284
  logger.debug(`Destroying window instance: ${this.identifier}`);
205
285
  this.stopInterceptHandler?.();
286
+ this.cleanupThemeListener();
206
287
  this._browserWindow = undefined;
207
288
  }
208
289
 
@@ -228,45 +309,37 @@ export default class Browser {
228
309
  `[${this.identifier}] Saved window state (only size used): ${JSON.stringify(savedState)}`,
229
310
  );
230
311
 
231
- const { isWindows11, isWindows } = this.getWindowsVersion();
232
312
  const isDarkMode = nativeTheme.shouldUseDarkColors;
233
313
 
234
314
  const browserWindow = new BrowserWindow({
235
315
  ...res,
236
- ...(isWindows
237
- ? {
238
- titleBarStyle: 'hidden',
239
- }
240
- : {}),
241
- ...(isWindows11
242
- ? {
243
- backgroundMaterial: isDarkMode ? 'mica' : 'acrylic',
244
- vibrancy: 'under-window',
245
- visualEffectState: 'active',
246
- }
247
- : {}),
248
316
  autoHideMenuBar: true,
249
317
  backgroundColor: '#00000000',
318
+ darkTheme: isDarkMode,
250
319
  frame: false,
251
-
252
320
  height: savedState?.height || height,
253
- // Always create hidden first
254
321
  show: false,
255
322
  title,
256
-
323
+ vibrancy: 'sidebar',
324
+ visualEffectState: 'active',
257
325
  webPreferences: {
258
- // Context isolation environment
259
- // https://www.electronjs.org/docs/tutorial/context-isolation
326
+ backgroundThrottling: false,
260
327
  contextIsolation: true,
261
328
  preload: join(preloadDir, 'index.js'),
262
329
  },
263
330
  width: savedState?.width || width,
331
+ ...this.getPlatformThemeConfig(isDarkMode),
264
332
  });
265
333
 
266
334
  this._browserWindow = browserWindow;
267
335
  logger.debug(`[${this.identifier}] BrowserWindow instance created.`);
268
336
 
269
- if (isWindows11) this.applyVisualEffects();
337
+ // Initialize theme listener for this window to handle theme changes
338
+ this.setupThemeListener();
339
+ logger.debug(`[${this.identifier}] Theme listener setup and applying initial visual effects.`);
340
+
341
+ // Apply initial visual effects
342
+ this.applyVisualEffects();
270
343
 
271
344
  logger.debug(`[${this.identifier}] Setting up nextInterceptor.`);
272
345
  this.stopInterceptHandler = this.app.nextInterceptor({
@@ -320,8 +393,9 @@ export default class Browser {
320
393
  } catch (error) {
321
394
  logger.error(`[${this.identifier}] Failed to save window state on quit:`, error);
322
395
  }
323
- // Need to clean up intercept handler
396
+ // Need to clean up intercept handler and theme manager
324
397
  this.stopInterceptHandler?.();
398
+ this.cleanupThemeListener();
325
399
  return;
326
400
  }
327
401
 
@@ -355,8 +429,9 @@ export default class Browser {
355
429
  } catch (error) {
356
430
  logger.error(`[${this.identifier}] Failed to save window state on close:`, error);
357
431
  }
358
- // Need to clean up intercept handler
432
+ // Need to clean up intercept handler and theme manager
359
433
  this.stopInterceptHandler?.();
434
+ this.cleanupThemeListener();
360
435
  }
361
436
  });
362
437
 
@@ -387,16 +462,6 @@ export default class Browser {
387
462
  this._browserWindow.webContents.send(channel, data);
388
463
  };
389
464
 
390
- applyVisualEffects() {
391
- // Windows 11 can use this new API
392
- if (this._browserWindow) {
393
- logger.debug(`[${this.identifier}] Setting window background material for Windows 11`);
394
- const isDarkMode = nativeTheme.shouldUseDarkColors;
395
- this._browserWindow?.setBackgroundMaterial(isDarkMode ? 'mica' : 'acrylic');
396
- this._browserWindow?.setVibrancy('under-window');
397
- }
398
- }
399
-
400
465
  toggleVisible() {
401
466
  logger.debug(`Toggling visibility for window: ${this.identifier}`);
402
467
  if (this._browserWindow.isVisible() && this._browserWindow.isFocused()) {
@@ -407,35 +472,11 @@ export default class Browser {
407
472
  }
408
473
  }
409
474
 
410
- getWindowsVersion() {
411
- if (process.platform !== 'win32') {
412
- return {
413
- isWindows: false,
414
- isWindows10: false,
415
- isWindows11: false,
416
- version: null,
417
- };
418
- }
419
-
420
- // 获取操作系统版本(如 "10.0.22621")
421
- const release = os.release();
422
- const parts = release.split('.');
423
-
424
- // 主版本和次版本
425
- const majorVersion = parseInt(parts[0], 10);
426
- const minorVersion = parseInt(parts[1], 10);
427
-
428
- // 构建号是第三部分
429
- const buildNumber = parseInt(parts[2], 10);
430
-
431
- // Windows 11 的构建号从 22000 开始
432
- const isWindows11 = majorVersion === 10 && minorVersion === 0 && buildNumber >= 22_000;
433
-
434
- return {
435
- buildNumber,
436
- isWindows: true,
437
- isWindows11,
438
- version: release,
439
- };
475
+ /**
476
+ * Manually reapply visual effects (useful for fixing lost effects after window state changes)
477
+ */
478
+ reapplyVisualEffects(): void {
479
+ logger.debug(`[${this.identifier}] Manually reapplying visual effects via Browser.`);
480
+ this.applyVisualEffects();
440
481
  }
441
482
  }
@@ -3,15 +3,15 @@ import { WebContents } from 'electron';
3
3
 
4
4
  import { createLogger } from '@/utils/logger';
5
5
 
6
- import { AppBrowsersIdentifiers, appBrowsers } from '../appBrowsers';
7
- import type { App } from './App';
6
+ import { AppBrowsersIdentifiers, appBrowsers } from '../../appBrowsers';
7
+ import type { App } from '../App';
8
8
  import type { BrowserWindowOpts } from './Browser';
9
9
  import Browser from './Browser';
10
10
 
11
11
  // Create logger
12
12
  const logger = createLogger('core:BrowserManager');
13
13
 
14
- export default class BrowserManager {
14
+ export class BrowserManager {
15
15
  app: App;
16
16
 
17
17
  browsers: Map<AppBrowsersIdentifiers, Browser> = new Map();
@@ -194,4 +194,14 @@ export default class BrowserManager {
194
194
  getIdentifierByWebContents(webContents: WebContents): AppBrowsersIdentifiers | null {
195
195
  return this.webContentsMap.get(webContents) || null;
196
196
  }
197
+
198
+ /**
199
+ * Handle application theme mode changes and reapply visual effects to all windows
200
+ */
201
+ handleAppThemeChange(): void {
202
+ logger.debug('Handling app theme change for all browser windows');
203
+ this.browsers.forEach((browser) => {
204
+ browser.handleAppThemeChange();
205
+ });
206
+ }
197
207
  }
@@ -5,7 +5,7 @@ import { LOCAL_STORAGE_URL_PREFIX } from '@/const/dir';
5
5
  import FileService from '@/services/fileSrv';
6
6
  import { createLogger } from '@/utils/logger';
7
7
 
8
- import type { App } from './App';
8
+ import type { App } from '../App';
9
9
 
10
10
  const logger = createLogger('core:StaticFileServerManager');
11
11
 
@@ -54,9 +54,12 @@ export class StaticFileServerManager {
54
54
  try {
55
55
  // 使用 get-port-please 获取可用端口
56
56
  this.serverPort = await getPort({
57
- port: 33250, // 首选端口
58
- ports: [33251, 33252, 33253, 33254, 33255], // 备用端口
57
+ // 备用端口
59
58
  host: '127.0.0.1',
59
+
60
+ port: 33_250,
61
+ // 首选端口
62
+ ports: [33_251, 33_252, 33_253, 33_254, 33_255],
60
63
  });
61
64
 
62
65
  logger.debug(`Found available port: ${this.serverPort}`);
@@ -64,7 +67,7 @@ export class StaticFileServerManager {
64
67
  return new Promise((resolve, reject) => {
65
68
  const server = createServer(async (req, res) => {
66
69
  // 设置请求超时
67
- req.setTimeout(30000, () => {
70
+ req.setTimeout(30_000, () => {
68
71
  logger.warn('Request timeout, closing connection');
69
72
  if (!res.destroyed && !res.headersSent) {
70
73
  res.writeHead(408, { 'Content-Type': 'text/plain' });
@@ -155,10 +158,13 @@ export class StaticFileServerManager {
155
158
 
156
159
  // 设置响应头
157
160
  res.writeHead(200, {
158
- 'Content-Type': fileResult.mimeType,
159
- 'Cache-Control': 'public, max-age=31536000', // 缓存一年
160
- 'Access-Control-Allow-Origin': 'http://localhost:*', // 允许 localhost 的任意端口
161
+ // 缓存一年
162
+ 'Access-Control-Allow-Origin': 'http://localhost:*',
163
+
164
+ 'Cache-Control': 'public, max-age=31536000',
165
+ // 允许 localhost 的任意端口
161
166
  'Content-Length': Buffer.byteLength(fileResult.content),
167
+ 'Content-Type': fileResult.mimeType,
162
168
  });
163
169
 
164
170
  // 发送文件内容
@@ -5,7 +5,7 @@ import { ElectronMainStore, StoreKey } from '@/types/store';
5
5
  import { makeSureDirExist } from '@/utils/file-system';
6
6
  import { createLogger } from '@/utils/logger';
7
7
 
8
- import { App } from './App';
8
+ import { App } from '../App';
9
9
 
10
10
  // Create logger
11
11
  const logger = createLogger('core:StoreManager');