@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.
- package/CHANGELOG.md +35 -0
- package/apps/desktop/build/icon-beta.ico +0 -0
- package/apps/desktop/build/icon-dev.ico +0 -0
- package/apps/desktop/build/icon-nightly.ico +0 -0
- package/apps/desktop/build/icon.ico +0 -0
- package/apps/desktop/electron.vite.config.ts +4 -2
- package/apps/desktop/package.json +1 -0
- package/apps/desktop/src/main/appBrowsers.ts +2 -2
- package/apps/desktop/src/main/const/env.ts +5 -4
- package/apps/desktop/src/main/const/store.ts +1 -0
- package/apps/desktop/src/main/const/theme.ts +11 -0
- package/apps/desktop/src/main/controllers/BrowserWindowsCtr.ts +1 -1
- package/apps/desktop/src/main/controllers/NotificationCtr.ts +2 -4
- package/apps/desktop/src/main/controllers/SystemCtr.ts +4 -0
- package/apps/desktop/src/main/controllers/TrayMenuCtr.ts +5 -5
- package/apps/desktop/src/main/controllers/index.ts +1 -1
- package/apps/desktop/src/main/core/App.ts +9 -10
- package/apps/desktop/src/main/core/{Browser.ts → browser/Browser.ts} +129 -88
- package/apps/desktop/src/main/core/{BrowserManager.ts → browser/BrowserManager.ts} +13 -3
- package/apps/desktop/src/main/core/{StaticFileServerManager.ts → infrastructure/StaticFileServerManager.ts} +13 -7
- package/apps/desktop/src/main/core/{StoreManager.ts → infrastructure/StoreManager.ts} +1 -1
- package/apps/desktop/src/main/core/{UpdaterManager.ts → infrastructure/UpdaterManager.ts} +1 -1
- package/apps/desktop/src/main/core/{MenuManager.ts → ui/MenuManager.ts} +2 -2
- package/apps/desktop/src/main/core/{ShortcutManager.ts → ui/ShortcutManager.ts} +7 -1
- package/apps/desktop/src/main/core/{Tray.ts → ui/Tray.ts} +61 -59
- package/apps/desktop/src/main/core/{TrayManager.ts → ui/TrayManager.ts} +5 -5
- package/apps/desktop/src/main/shortcuts/config.ts +2 -2
- package/apps/desktop/src/main/types/store.ts +1 -0
- package/changelog/v1.json +12 -0
- package/docs/development/basic/add-new-image-model.mdx +162 -0
- package/docs/development/basic/add-new-image-model.zh-CN.mdx +162 -0
- package/docs/usage/providers/fal.mdx +1 -1
- package/docs/usage/providers/fal.zh-CN.mdx +1 -1
- package/package.json +66 -66
- package/src/app/[variants]/(main)/chat/(workspace)/_layout/Desktop/Portal.tsx +3 -1
- package/src/app/[variants]/(main)/chat/(workspace)/features/AgentSettings/index.tsx +4 -2
- package/src/app/[variants]/(main)/image/@menu/components/SeedNumberInput/index.tsx +1 -1
- package/src/app/[variants]/(main)/image/features/GenerationFeed/BatchItem.tsx +39 -3
- package/src/app/[variants]/(main)/image/features/GenerationFeed/ReferenceImages.tsx +122 -0
- package/src/config/aiModels/fal.ts +31 -7
- package/src/config/aiModels/openai.ts +10 -1
- package/src/features/ElectronTitlebar/WinControl/index.tsx +85 -90
- package/src/features/ElectronTitlebar/hooks/useWatchThemeUpdate.ts +10 -5
- package/src/features/ImageTopicPanel/index.tsx +0 -1
- package/src/features/PluginDevModal/index.tsx +3 -1
- package/src/features/User/__tests__/UserAvatar.test.tsx +5 -4
- package/src/libs/model-runtime/fal/index.ts +1 -1
- package/src/libs/model-runtime/types/image.ts +1 -1
- package/src/libs/model-runtime/utils/openaiCompatibleFactory/index.ts +1 -1
- package/src/libs/model-runtime/utils/response.ts +2 -0
- package/src/libs/model-runtime/utils/streams/google-ai.test.ts +46 -0
- package/src/libs/model-runtime/utils/streams/google-ai.ts +4 -4
- package/src/libs/model-runtime/utils/streams/vertex-ai.ts +6 -8
- package/src/libs/standard-parameters/{meta-schema.test.ts → index.test.ts} +1 -1
- package/src/libs/standard-parameters/index.ts +152 -1
- package/src/server/ld.test.ts +4 -3
- package/src/server/routers/async/image.ts +1 -1
- package/src/services/__tests__/chat.test.ts +3 -4
- package/src/store/chat/slices/message/selectors.test.ts +2 -3
- package/src/store/chat/slices/plugin/action.test.ts +2 -1
- package/src/store/image/slices/generationConfig/action.test.ts +2 -2
- package/src/store/image/slices/generationConfig/action.ts +1 -1
- package/src/store/image/slices/generationConfig/hooks.test.ts +2 -2
- package/src/store/image/slices/generationConfig/hooks.ts +1 -4
- package/src/store/image/slices/generationConfig/initialState.ts +2 -2
- package/src/store/image/slices/generationConfig/selectors.test.ts +2 -2
- package/src/store/image/slices/generationConfig/selectors.ts +1 -1
- package/src/store/user/slices/auth/selectors.test.ts +3 -2
- package/src/types/generation/index.ts +1 -0
- package/docs/development/basic/add-new-ai-image-model.mdx +0 -36
- package/docs/development/basic/add-new-ai-image-model.zh-CN.mdx +0 -0
- package/src/config/paramsSchemas/fal/flux-kontext-dev.ts +0 -8
- package/src/config/paramsSchemas/fal/flux-pro-kontext.ts +0 -11
- package/src/config/paramsSchemas/fal/flux-schnell.ts +0 -9
- package/src/config/paramsSchemas/fal/imagen4.ts +0 -10
- package/src/config/paramsSchemas/openai/gpt-image-1.ts +0 -10
- package/src/libs/standard-parameters/meta-schema.ts +0 -147
- /package/apps/desktop/src/main/core/{I18nManager.ts → infrastructure/I18nManager.ts} +0 -0
- /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
|
+
[](#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: {
|
@@ -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
|
-
|
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 =
|
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 =
|
8
|
-
export const isWindows =
|
9
|
-
export const isLinux =
|
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;
|
@@ -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('
|
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('
|
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
|
21
|
-
import {
|
22
|
-
import {
|
23
|
-
import {
|
24
|
-
import
|
25
|
-
import {
|
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 {
|
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
|
-
|
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
|
-
|
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
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
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 '
|
7
|
-
import type { App } from '
|
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
|
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 '
|
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
|
-
|
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(
|
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
|
-
|
159
|
-
'
|
160
|
-
|
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 '
|
8
|
+
import { App } from '../App';
|
9
9
|
|
10
10
|
// Create logger
|
11
11
|
const logger = createLogger('core:StoreManager');
|