@lobehub/lobehub 2.0.0-next.268 → 2.0.0-next.269
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 +25 -0
- package/apps/desktop/src/main/core/browser/Browser.ts +6 -0
- package/changelog/v1.json +5 -0
- package/package.json +1 -1
- package/packages/utils/src/index.ts +1 -0
- package/packages/utils/src/platform.ts +35 -3
- package/src/app/[variants]/(desktop)/desktop-onboarding/_layout/index.tsx +30 -22
- package/src/app/[variants]/(desktop)/desktop-onboarding/_layout/style.ts +8 -5
- package/src/app/[variants]/(main)/_layout/DesktopLayoutContainer.tsx +2 -3
- package/src/features/ElectronTitlebar/SimpleTitleBar.tsx +31 -0
- package/src/features/ElectronTitlebar/index.tsx +1 -0
- package/src/utils/platform.ts +2 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
## [Version 2.0.0-next.269](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.268...v2.0.0-next.269)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2026-01-12**</sup>
|
|
8
|
+
|
|
9
|
+
#### ✨ Features
|
|
10
|
+
|
|
11
|
+
- **electron**: Add custom titlebar for Electron windows.
|
|
12
|
+
|
|
13
|
+
<br/>
|
|
14
|
+
|
|
15
|
+
<details>
|
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
17
|
+
|
|
18
|
+
#### What's improved
|
|
19
|
+
|
|
20
|
+
- **electron**: Add custom titlebar for Electron windows, closes [#11438](https://github.com/lobehub/lobe-chat/issues/11438) ([08f6ee3](https://github.com/lobehub/lobe-chat/commit/08f6ee3))
|
|
21
|
+
|
|
22
|
+
</details>
|
|
23
|
+
|
|
24
|
+
<div align="right">
|
|
25
|
+
|
|
26
|
+
[](#readme-top)
|
|
27
|
+
|
|
28
|
+
</div>
|
|
29
|
+
|
|
5
30
|
## [Version 2.0.0-next.268](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.267...v2.0.0-next.268)
|
|
6
31
|
|
|
7
32
|
<sup>Released on **2026-01-12**</sup>
|
|
@@ -12,6 +12,7 @@ import { join } from 'node:path';
|
|
|
12
12
|
import { preloadDir, resourcesDir } from '@/const/dir';
|
|
13
13
|
import { isMac } from '@/const/env';
|
|
14
14
|
import { ELECTRON_BE_PROTOCOL_SCHEME } from '@/const/protocol';
|
|
15
|
+
import { TITLE_BAR_HEIGHT } from '@/const/theme';
|
|
15
16
|
import RemoteServerConfigCtr from '@/controllers/RemoteServerConfigCtr';
|
|
16
17
|
import { backendProxyProtocolManager } from '@/core/infrastructure/BackendProxyProtocolManager';
|
|
17
18
|
import { setResponseHeader } from '@/utils/http-headers';
|
|
@@ -119,6 +120,10 @@ export default class Browser {
|
|
|
119
120
|
logger.info(`Creating new BrowserWindow instance: ${this.identifier}`);
|
|
120
121
|
logger.debug(`[${this.identifier}] Resolved window state: ${JSON.stringify(resolvedState)}`);
|
|
121
122
|
|
|
123
|
+
// Calculate traffic light position to center vertically in title bar
|
|
124
|
+
// Traffic light buttons are approximately 12px tall
|
|
125
|
+
const trafficLightY = Math.round((TITLE_BAR_HEIGHT - 12) / 2);
|
|
126
|
+
|
|
122
127
|
return new BrowserWindow({
|
|
123
128
|
...rest,
|
|
124
129
|
autoHideMenuBar: true,
|
|
@@ -128,6 +133,7 @@ export default class Browser {
|
|
|
128
133
|
height: resolvedState.height,
|
|
129
134
|
show: false,
|
|
130
135
|
title,
|
|
136
|
+
trafficLightPosition: isMac ? { x: 12, y: trafficLightY } : undefined,
|
|
131
137
|
vibrancy: 'sidebar',
|
|
132
138
|
visualEffectState: 'active',
|
|
133
139
|
webPreferences: {
|
package/changelog/v1.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/lobehub",
|
|
3
|
-
"version": "2.0.0-next.
|
|
3
|
+
"version": "2.0.0-next.269",
|
|
4
4
|
"description": "LobeHub - an open-source,comprehensive AI Agent framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"framework",
|
|
@@ -25,18 +25,50 @@ export const browserInfo = {
|
|
|
25
25
|
|
|
26
26
|
export const isMacOS = () => getPlatform() === 'Mac OS';
|
|
27
27
|
|
|
28
|
+
/**
|
|
29
|
+
* Get macOS Darwin major version number
|
|
30
|
+
* @returns Darwin major version (e.g., 25, 26) or 0 if not available
|
|
31
|
+
*/
|
|
32
|
+
export const getDarwinMajorVersion = (): number => {
|
|
33
|
+
if (isOnServerSide || typeof window === 'undefined') return 0;
|
|
34
|
+
|
|
35
|
+
// In Electron environment, use window.lobeEnv.darwinMajorVersion if available
|
|
36
|
+
if (typeof (window as any)?.lobeEnv?.darwinMajorVersion === 'number') {
|
|
37
|
+
return (window as any).lobeEnv.darwinMajorVersion;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// In web environment, try to parse from userAgent
|
|
41
|
+
if (typeof navigator !== 'undefined') {
|
|
42
|
+
const match = navigator.userAgent.match(/Mac OS X (\d+)[._](\d+)/);
|
|
43
|
+
if (match) {
|
|
44
|
+
return parseInt(match[1], 10);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return 0;
|
|
49
|
+
};
|
|
50
|
+
|
|
28
51
|
/**
|
|
29
52
|
*
|
|
30
53
|
* We can't use it to detect the macOS real version, and we also don't know if it's macOS 26, only an estimated value.
|
|
31
|
-
* @returns true if the current browser is macOS and the version is 10.15 or later
|
|
54
|
+
* @returns true if the current browser is macOS and the version is 10.15 or later (web) or darwinMajorVersion >= 25 (Electron)
|
|
32
55
|
*/
|
|
33
56
|
export const isMacOSWithLargeWindowBorders = () => {
|
|
34
57
|
if (isOnServerSide || typeof navigator === 'undefined') return false;
|
|
35
58
|
|
|
36
|
-
//
|
|
59
|
+
// Check if we're in Electron environment
|
|
37
60
|
const isElectron =
|
|
38
61
|
/Electron\//.test(navigator.userAgent) || Boolean((window as any)?.process?.type);
|
|
39
|
-
|
|
62
|
+
|
|
63
|
+
// In Electron environment, check darwinMajorVersion from window.lobeEnv
|
|
64
|
+
if (isElectron) {
|
|
65
|
+
const darwinMajorVersion = getDarwinMajorVersion();
|
|
66
|
+
// macOS 25+ has large window borders
|
|
67
|
+
return darwinMajorVersion >= 25;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// keep consistent with the original logic: only for macOS on web (exclude Electron)
|
|
71
|
+
if (!isMacOS()) return false;
|
|
40
72
|
|
|
41
73
|
const match = navigator.userAgent.match(/Mac OS X (\d+)[._](\d+)/);
|
|
42
74
|
if (!match) return false;
|
|
@@ -5,6 +5,7 @@ import { Divider } from 'antd';
|
|
|
5
5
|
import { cx } from 'antd-style';
|
|
6
6
|
import type { FC, PropsWithChildren } from 'react';
|
|
7
7
|
|
|
8
|
+
import { SimpleTitleBar, TITLE_BAR_HEIGHT } from '@/features/ElectronTitlebar';
|
|
8
9
|
import LangButton from '@/features/User/UserPanel/LangButton';
|
|
9
10
|
import ThemeButton from '@/features/User/UserPanel/ThemeButton';
|
|
10
11
|
import { useIsDark } from '@/hooks/useIsDark';
|
|
@@ -14,36 +15,43 @@ import { styles } from './style';
|
|
|
14
15
|
const OnboardingContainer: FC<PropsWithChildren> = ({ children }) => {
|
|
15
16
|
const isDarkMode = useIsDark();
|
|
16
17
|
return (
|
|
17
|
-
<Flexbox
|
|
18
|
+
<Flexbox height={'100%'} width={'100%'}>
|
|
19
|
+
<SimpleTitleBar />
|
|
18
20
|
<Flexbox
|
|
19
|
-
className={
|
|
20
|
-
height={
|
|
21
|
+
className={styles.outerContainer}
|
|
22
|
+
height={`calc(100% - ${TITLE_BAR_HEIGHT}px)`}
|
|
23
|
+
style={{ paddingBottom: 8, paddingInline: 8 }}
|
|
21
24
|
width={'100%'}
|
|
22
25
|
>
|
|
23
26
|
<Flexbox
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
gap={8}
|
|
27
|
-
horizontal
|
|
28
|
-
justify={'space-between'}
|
|
29
|
-
padding={16}
|
|
27
|
+
className={cx(isDarkMode ? styles.innerContainerDark : styles.innerContainerLight)}
|
|
28
|
+
height={'100%'}
|
|
30
29
|
width={'100%'}
|
|
31
30
|
>
|
|
32
|
-
<
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
31
|
+
<Flexbox
|
|
32
|
+
align={'center'}
|
|
33
|
+
gap={8}
|
|
34
|
+
horizontal
|
|
35
|
+
justify={'space-between'}
|
|
36
|
+
padding={16}
|
|
37
|
+
width={'100%'}
|
|
38
|
+
>
|
|
39
|
+
<div />
|
|
40
|
+
<Flexbox align={'center'} horizontal>
|
|
41
|
+
<LangButton placement={'bottomRight'} size={18} />
|
|
42
|
+
<Divider className={styles.divider} orientation={'vertical'} />
|
|
43
|
+
<ThemeButton placement={'bottomRight'} size={18} />
|
|
44
|
+
</Flexbox>
|
|
37
45
|
</Flexbox>
|
|
46
|
+
<Center height={'100%'} padding={16} width={'100%'}>
|
|
47
|
+
{children}
|
|
48
|
+
</Center>
|
|
49
|
+
<Center padding={24}>
|
|
50
|
+
<Text align={'center'} type={'secondary'}>
|
|
51
|
+
© 2025 LobeHub. All rights reserved.
|
|
52
|
+
</Text>
|
|
53
|
+
</Center>
|
|
38
54
|
</Flexbox>
|
|
39
|
-
<Center height={'100%'} padding={16} width={'100%'}>
|
|
40
|
-
{children}
|
|
41
|
-
</Center>
|
|
42
|
-
<Center padding={24}>
|
|
43
|
-
<Text align={'center'} type={'secondary'}>
|
|
44
|
-
© 2025 LobeHub. All rights reserved.
|
|
45
|
-
</Text>
|
|
46
|
-
</Center>
|
|
47
55
|
</Flexbox>
|
|
48
56
|
</Flexbox>
|
|
49
57
|
);
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { createStaticStyles } from 'antd-style';
|
|
2
2
|
|
|
3
|
+
import { isMacOSWithLargeWindowBorders } from '@/utils/platform';
|
|
4
|
+
|
|
3
5
|
export const styles = createStaticStyles(({ css, cssVar }) => ({
|
|
4
6
|
// Divider 样式
|
|
5
7
|
divider: css`
|
|
6
8
|
height: 24px;
|
|
7
9
|
`,
|
|
8
10
|
|
|
9
|
-
drag: css`
|
|
10
|
-
-webkit-app-region: drag;
|
|
11
|
-
`,
|
|
12
11
|
// 内层容器 - 深色模式
|
|
13
12
|
innerContainerDark: css`
|
|
14
13
|
position: relative;
|
|
@@ -16,7 +15,9 @@ export const styles = createStaticStyles(({ css, cssVar }) => ({
|
|
|
16
15
|
overflow: hidden;
|
|
17
16
|
|
|
18
17
|
border: 1px solid ${cssVar.colorBorderSecondary};
|
|
19
|
-
border-radius: ${
|
|
18
|
+
border-radius: ${!isMacOSWithLargeWindowBorders()
|
|
19
|
+
? cssVar.borderRadius
|
|
20
|
+
: `${cssVar.borderRadius} 12px ${cssVar.borderRadius} 12px`};
|
|
20
21
|
|
|
21
22
|
background: ${cssVar.colorBgContainer};
|
|
22
23
|
`,
|
|
@@ -28,7 +29,9 @@ export const styles = createStaticStyles(({ css, cssVar }) => ({
|
|
|
28
29
|
overflow: hidden;
|
|
29
30
|
|
|
30
31
|
border: 1px solid ${cssVar.colorBorder};
|
|
31
|
-
border-radius: ${
|
|
32
|
+
border-radius: ${!isMacOSWithLargeWindowBorders()
|
|
33
|
+
? cssVar.borderRadius
|
|
34
|
+
: `${cssVar.borderRadius} 12px ${cssVar.borderRadius} 12px`};
|
|
32
35
|
|
|
33
36
|
background: ${cssVar.colorBgContainer};
|
|
34
37
|
`,
|
|
@@ -6,7 +6,7 @@ import { isDesktop } from '@/const/version';
|
|
|
6
6
|
import { useIsDark } from '@/hooks/useIsDark';
|
|
7
7
|
import { useGlobalStore } from '@/store/global';
|
|
8
8
|
import { systemStatusSelectors } from '@/store/global/selectors';
|
|
9
|
-
import { isMacOSWithLargeWindowBorders } from '@/utils/platform';
|
|
9
|
+
import { getDarwinMajorVersion, isMacOSWithLargeWindowBorders } from '@/utils/platform';
|
|
10
10
|
|
|
11
11
|
import { styles } from './DesktopLayoutContainer/style';
|
|
12
12
|
|
|
@@ -24,8 +24,7 @@ const DesktopLayoutContainer: FC<PropsWithChildren> = ({ children }) => {
|
|
|
24
24
|
);
|
|
25
25
|
|
|
26
26
|
const innerCssVariables = useMemo<Record<string, string>>(() => {
|
|
27
|
-
const darwinMajorVersion =
|
|
28
|
-
typeof window !== 'undefined' ? (window.lobeEnv?.darwinMajorVersion ?? 0) : 0;
|
|
27
|
+
const darwinMajorVersion = getDarwinMajorVersion();
|
|
29
28
|
|
|
30
29
|
const borderRadius = darwinMajorVersion >= 25 ? '12px' : cssVar.borderRadius;
|
|
31
30
|
const borderBottomRightRadius =
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Flexbox } from '@lobehub/ui';
|
|
4
|
+
import { type FC } from 'react';
|
|
5
|
+
|
|
6
|
+
import { ProductLogo } from '@/components/Branding/ProductLogo';
|
|
7
|
+
import { electronStylish } from '@/styles/electron';
|
|
8
|
+
|
|
9
|
+
import { TITLE_BAR_HEIGHT } from './const';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* A simple, minimal TitleBar for Electron windows.
|
|
13
|
+
* Provides draggable area without business logic (navigation, updates, etc.)
|
|
14
|
+
* Use this for secondary windows like onboarding, settings, etc.
|
|
15
|
+
*/
|
|
16
|
+
const SimpleTitleBar: FC = () => {
|
|
17
|
+
return (
|
|
18
|
+
<Flexbox
|
|
19
|
+
align={'center'}
|
|
20
|
+
className={electronStylish.draggable}
|
|
21
|
+
height={TITLE_BAR_HEIGHT}
|
|
22
|
+
horizontal
|
|
23
|
+
justify={'center'}
|
|
24
|
+
width={'100%'}
|
|
25
|
+
>
|
|
26
|
+
<ProductLogo size={16} type={'text'} />
|
|
27
|
+
</Flexbox>
|
|
28
|
+
);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export default SimpleTitleBar;
|