@lobehub/lobehub 2.0.0-next.313 → 2.0.0-next.315
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 +50 -0
- package/apps/desktop/src/main/appBrowsers.ts +4 -1
- package/apps/desktop/src/main/controllers/BrowserWindowsCtr.ts +15 -3
- package/apps/desktop/src/main/core/browser/Browser.ts +14 -4
- package/apps/desktop/src/main/core/browser/BrowserManager.ts +7 -2
- package/changelog/v1.json +18 -0
- package/e2e/src/steps/community/detail-pages.steps.ts +2 -2
- package/e2e/src/steps/community/interactions.steps.ts +6 -6
- package/e2e/src/steps/hooks.ts +19 -3
- package/package.json +1 -1
- package/packages/desktop-bridge/src/index.ts +5 -0
- package/packages/electron-client-ipc/src/types/window.ts +3 -2
- package/src/app/[variants]/(desktop)/desktop-onboarding/_layout/index.tsx +6 -3
- package/src/app/[variants]/(desktop)/desktop-onboarding/components/OnboardingFooterActions.tsx +38 -0
- package/src/app/[variants]/(desktop)/desktop-onboarding/features/DataModeStep.tsx +19 -14
- package/src/app/[variants]/(desktop)/desktop-onboarding/features/LoginStep.tsx +53 -19
- package/src/app/[variants]/(desktop)/desktop-onboarding/features/PermissionsStep.tsx +19 -14
- package/src/app/[variants]/(desktop)/desktop-onboarding/index.tsx +8 -7
- package/src/app/manifest.ts +1 -1
- package/src/features/Electron/titlebar/NavigationBar.tsx +1 -2
- package/src/server/manifest.ts +2 -2
- package/src/server/services/aiAgent/index.ts +28 -11
- package/src/server/services/klavis/index.ts +228 -0
- package/src/server/services/market/index.ts +13 -0
- package/src/server/services/sandbox/index.ts +84 -18
- package/src/server/services/toolExecution/builtin.ts +12 -0
- package/src/server/services/toolExecution/index.ts +70 -4
- package/src/server/services/toolExecution/serverRuntimes/cloudSandbox.ts +7 -0
- package/src/services/electron/system.ts +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,56 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
## [Version 2.0.0-next.315](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.314...v2.0.0-next.315)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2026-01-19**</sup>
|
|
8
|
+
|
|
9
|
+
#### ✨ Features
|
|
10
|
+
|
|
11
|
+
- **misc**: Add the cloudEndpoint & Klavis Tools Call in Excuation Task.
|
|
12
|
+
|
|
13
|
+
<br/>
|
|
14
|
+
|
|
15
|
+
<details>
|
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
17
|
+
|
|
18
|
+
#### What's improved
|
|
19
|
+
|
|
20
|
+
- **misc**: Add the cloudEndpoint & Klavis Tools Call in Excuation Task, closes [#11627](https://github.com/lobehub/lobe-chat/issues/11627) ([0ffe6c4](https://github.com/lobehub/lobe-chat/commit/0ffe6c4))
|
|
21
|
+
|
|
22
|
+
</details>
|
|
23
|
+
|
|
24
|
+
<div align="right">
|
|
25
|
+
|
|
26
|
+
[](#readme-top)
|
|
27
|
+
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
## [Version 2.0.0-next.314](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.313...v2.0.0-next.314)
|
|
31
|
+
|
|
32
|
+
<sup>Released on **2026-01-19**</sup>
|
|
33
|
+
|
|
34
|
+
#### ✨ Features
|
|
35
|
+
|
|
36
|
+
- **misc**: Improve desktop onboarding window management and footer actions.
|
|
37
|
+
|
|
38
|
+
<br/>
|
|
39
|
+
|
|
40
|
+
<details>
|
|
41
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
42
|
+
|
|
43
|
+
#### What's improved
|
|
44
|
+
|
|
45
|
+
- **misc**: Improve desktop onboarding window management and footer actions, closes [#11619](https://github.com/lobehub/lobe-chat/issues/11619) ([6ed280e](https://github.com/lobehub/lobe-chat/commit/6ed280e))
|
|
46
|
+
|
|
47
|
+
</details>
|
|
48
|
+
|
|
49
|
+
<div align="right">
|
|
50
|
+
|
|
51
|
+
[](#readme-top)
|
|
52
|
+
|
|
53
|
+
</div>
|
|
54
|
+
|
|
5
55
|
## [Version 2.0.0-next.313](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.312...v2.0.0-next.313)
|
|
6
56
|
|
|
7
57
|
<sup>Released on **2026-01-19**</sup>
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { APP_WINDOW_MIN_SIZE } from '@lobechat/desktop-bridge';
|
|
2
|
+
|
|
1
3
|
import type { BrowserWindowOpts } from './core/browser/Browser';
|
|
2
4
|
|
|
3
5
|
export const BrowsersIdentifiers = {
|
|
@@ -11,7 +13,8 @@ export const appBrowsers = {
|
|
|
11
13
|
height: 800,
|
|
12
14
|
identifier: 'app',
|
|
13
15
|
keepAlive: true,
|
|
14
|
-
|
|
16
|
+
minHeight: APP_WINDOW_MIN_SIZE.height,
|
|
17
|
+
minWidth: APP_WINDOW_MIN_SIZE.width,
|
|
15
18
|
path: '/',
|
|
16
19
|
showOnInit: true,
|
|
17
20
|
titleBarStyle: 'hidden',
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
InterceptRouteParams,
|
|
3
3
|
OpenSettingsWindowOptions,
|
|
4
|
-
|
|
4
|
+
WindowMinimumSizeParams,
|
|
5
5
|
WindowSizeParams,
|
|
6
6
|
} from '@lobechat/electron-client-ipc';
|
|
7
7
|
import { findMatchingRoute } from '~common/routes';
|
|
@@ -81,9 +81,21 @@ export default class BrowserWindowsCtr extends ControllerModule {
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
@IpcMethod()
|
|
84
|
-
|
|
84
|
+
setWindowMinimumSize(params: WindowMinimumSizeParams) {
|
|
85
85
|
this.withSenderIdentifier((identifier) => {
|
|
86
|
-
this.app.browserManager.
|
|
86
|
+
const currentSize = this.app.browserManager.getWindowSize(identifier);
|
|
87
|
+
const nextWindowSize = {
|
|
88
|
+
...currentSize,
|
|
89
|
+
};
|
|
90
|
+
if (params.height) {
|
|
91
|
+
nextWindowSize.height = Math.max(currentSize.height, params.height);
|
|
92
|
+
}
|
|
93
|
+
if (params.width) {
|
|
94
|
+
nextWindowSize.width = Math.max(currentSize.width, params.width);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
this.app.browserManager.setWindowSize(identifier, nextWindowSize);
|
|
98
|
+
this.app.browserManager.setWindowMinimumSize(identifier, params);
|
|
87
99
|
});
|
|
88
100
|
}
|
|
89
101
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TITLE_BAR_HEIGHT } from '@lobechat/desktop-bridge';
|
|
1
|
+
import { APP_WINDOW_MIN_SIZE, TITLE_BAR_HEIGHT } from '@lobechat/desktop-bridge';
|
|
2
2
|
import { MainBroadcastEventKey, MainBroadcastParams } from '@lobechat/electron-client-ipc';
|
|
3
3
|
import {
|
|
4
4
|
BrowserWindow,
|
|
@@ -291,9 +291,19 @@ export default class Browser {
|
|
|
291
291
|
});
|
|
292
292
|
}
|
|
293
293
|
|
|
294
|
-
|
|
295
|
-
logger.debug(`[${this.identifier}] Setting window
|
|
296
|
-
|
|
294
|
+
setWindowMinimumSize(size: { height?: number; width?: number }): void {
|
|
295
|
+
logger.debug(`[${this.identifier}] Setting window minimum size: ${JSON.stringify(size)}`);
|
|
296
|
+
|
|
297
|
+
const currentMinimumSize = this._browserWindow?.getMinimumSize?.() ?? [0, 0];
|
|
298
|
+
const rawWidth = size.width ?? currentMinimumSize[0];
|
|
299
|
+
const rawHeight = size.height ?? currentMinimumSize[1];
|
|
300
|
+
|
|
301
|
+
// Electron doesn't "reset" minimum size with 0x0 reliably.
|
|
302
|
+
// Treat 0 / negative as fallback to app-level default preset.
|
|
303
|
+
const width = rawWidth > 0 ? rawWidth : APP_WINDOW_MIN_SIZE.width;
|
|
304
|
+
const height = rawHeight > 0 ? rawHeight : APP_WINDOW_MIN_SIZE.height;
|
|
305
|
+
|
|
306
|
+
this._browserWindow?.setMinimumSize?.(width, height);
|
|
297
307
|
}
|
|
298
308
|
|
|
299
309
|
// ==================== Window Position ====================
|
|
@@ -250,9 +250,14 @@ export class BrowserManager {
|
|
|
250
250
|
browser?.setWindowSize(size);
|
|
251
251
|
}
|
|
252
252
|
|
|
253
|
-
|
|
253
|
+
getWindowSize(identifier: string) {
|
|
254
254
|
const browser = this.browsers.get(identifier);
|
|
255
|
-
browser?.
|
|
255
|
+
return browser?.browserWindow.getBounds();
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
setWindowMinimumSize(identifier: string, size: { height?: number; width?: number }) {
|
|
259
|
+
const browser = this.browsers.get(identifier);
|
|
260
|
+
browser?.setWindowMinimumSize(size);
|
|
256
261
|
}
|
|
257
262
|
|
|
258
263
|
getIdentifierByWebContents(webContents: WebContents): string | null {
|
package/changelog/v1.json
CHANGED
|
@@ -1,4 +1,22 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"children": {
|
|
4
|
+
"features": [
|
|
5
|
+
"Add the cloudEndpoint & Klavis Tools Call in Excuation Task."
|
|
6
|
+
]
|
|
7
|
+
},
|
|
8
|
+
"date": "2026-01-19",
|
|
9
|
+
"version": "2.0.0-next.315"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"children": {
|
|
13
|
+
"features": [
|
|
14
|
+
"Improve desktop onboarding window management and footer actions."
|
|
15
|
+
]
|
|
16
|
+
},
|
|
17
|
+
"date": "2026-01-19",
|
|
18
|
+
"version": "2.0.0-next.314"
|
|
19
|
+
},
|
|
2
20
|
{
|
|
3
21
|
"children": {
|
|
4
22
|
"fixes": [
|
|
@@ -68,7 +68,7 @@ Then('I should be on an assistant detail page', async function (this: CustomWorl
|
|
|
68
68
|
|
|
69
69
|
const currentUrl = this.page.url();
|
|
70
70
|
// Check if URL matches assistant detail page pattern
|
|
71
|
-
const hasAssistantDetail = /\/community\/
|
|
71
|
+
const hasAssistantDetail = /\/community\/agent\/[^#?]+/.test(currentUrl);
|
|
72
72
|
expect(
|
|
73
73
|
hasAssistantDetail,
|
|
74
74
|
`Expected URL to match assistant detail page pattern, but got: ${currentUrl}`,
|
|
@@ -138,7 +138,7 @@ Then('I should be on the assistant list page', async function (this: CustomWorld
|
|
|
138
138
|
// After back navigation, URL should be /community/agent or /community
|
|
139
139
|
const isListPage =
|
|
140
140
|
(currentUrl.includes('/community/agent') &&
|
|
141
|
-
!/\/community\/
|
|
141
|
+
!/\/community\/agent\/[\dA-Za-z-]+$/.test(currentUrl)) ||
|
|
142
142
|
currentUrl.endsWith('/community') ||
|
|
143
143
|
currentUrl.includes('/community#');
|
|
144
144
|
|
|
@@ -230,10 +230,10 @@ When('I click on the sort dropdown', async function (this: CustomWorld) {
|
|
|
230
230
|
});
|
|
231
231
|
|
|
232
232
|
When('I select a sort option', async function (this: CustomWorld) {
|
|
233
|
-
await this.page.waitForTimeout(
|
|
233
|
+
await this.page.waitForTimeout(1000);
|
|
234
234
|
|
|
235
|
-
//
|
|
236
|
-
const sortOptions = this.page.locator('[role="
|
|
235
|
+
// The sort dropdown uses checkbox items with role="menuitemcheckbox"
|
|
236
|
+
const sortOptions = this.page.locator('[role="menuitemcheckbox"]');
|
|
237
237
|
|
|
238
238
|
// Wait for options to appear
|
|
239
239
|
await sortOptions.first().waitFor({ state: 'visible', timeout: 30_000 });
|
|
@@ -381,7 +381,7 @@ Then('the URL should contain the category parameter', async function (this: Cust
|
|
|
381
381
|
currentUrl.includes('category=') ||
|
|
382
382
|
currentUrl.includes('tag=') ||
|
|
383
383
|
// For path-based routing like /community/agent/category-name
|
|
384
|
-
/\/community\/
|
|
384
|
+
/\/community\/agent\/[^/?]+/.test(currentUrl);
|
|
385
385
|
|
|
386
386
|
expect(
|
|
387
387
|
hasCategory,
|
|
@@ -433,8 +433,8 @@ Then('I should be navigated to the assistant detail page', async function (this:
|
|
|
433
433
|
await this.page.waitForLoadState('networkidle', { timeout: 30_000 });
|
|
434
434
|
|
|
435
435
|
const currentUrl = this.page.url();
|
|
436
|
-
// Verify that URL changed and contains /
|
|
437
|
-
const hasAssistantDetail = /\/community\/
|
|
436
|
+
// Verify that URL changed and contains /agent/ followed by an identifier
|
|
437
|
+
const hasAssistantDetail = /\/community\/agent\/[^#?]+/.test(currentUrl);
|
|
438
438
|
const urlChanged = currentUrl !== this.testContext.previousUrl;
|
|
439
439
|
|
|
440
440
|
expect(
|
package/e2e/src/steps/hooks.ts
CHANGED
|
@@ -45,10 +45,24 @@ BeforeAll({ timeout: 600_000 }, async function () {
|
|
|
45
45
|
// Navigate to signin page
|
|
46
46
|
await page.goto(`${baseUrl}/signin`, { waitUntil: 'networkidle' });
|
|
47
47
|
|
|
48
|
+
// Wait for the page to fully hydrate
|
|
49
|
+
await page.waitForTimeout(2000);
|
|
50
|
+
|
|
51
|
+
// Check if we can find the email input
|
|
52
|
+
const emailInput = page
|
|
53
|
+
.locator('input[id="email"], input[name="email"], input[type="text"]')
|
|
54
|
+
.first();
|
|
55
|
+
const emailInputVisible = await emailInput.isVisible().catch(() => false);
|
|
56
|
+
|
|
57
|
+
if (!emailInputVisible) {
|
|
58
|
+
console.log(
|
|
59
|
+
'⚠️ Login form not available, skipping authentication (tests requiring auth may fail)',
|
|
60
|
+
);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
48
64
|
// Step 1: Enter email
|
|
49
65
|
console.log(' Step 1: Entering email...');
|
|
50
|
-
const emailInput = page.locator('input[id="email"]').first();
|
|
51
|
-
await emailInput.waitFor({ state: 'visible', timeout: 30_000 });
|
|
52
66
|
await emailInput.fill(TEST_USER.email);
|
|
53
67
|
|
|
54
68
|
// Click the next button
|
|
@@ -57,7 +71,9 @@ BeforeAll({ timeout: 600_000 }, async function () {
|
|
|
57
71
|
|
|
58
72
|
// Step 2: Wait for password step and enter password
|
|
59
73
|
console.log(' Step 2: Entering password...');
|
|
60
|
-
const passwordInput = page
|
|
74
|
+
const passwordInput = page
|
|
75
|
+
.locator('input[id="password"], input[name="password"], input[type="password"]')
|
|
76
|
+
.first();
|
|
61
77
|
await passwordInput.waitFor({ state: 'visible', timeout: 30_000 });
|
|
62
78
|
await passwordInput.fill(TEST_USER.password);
|
|
63
79
|
|
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.315",
|
|
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",
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { TITLE_BAR_HEIGHT } from '@lobechat/desktop-bridge';
|
|
4
4
|
import { Center, Flexbox, Text } from '@lobehub/ui';
|
|
5
5
|
import { Divider } from 'antd';
|
|
6
|
-
import { cx } from 'antd-style';
|
|
6
|
+
import { css, cx } from 'antd-style';
|
|
7
7
|
import type { FC, PropsWithChildren } from 'react';
|
|
8
8
|
|
|
9
9
|
import SimpleTitleBar from '@/features/Electron/titlebar/SimpleTitleBar';
|
|
@@ -13,6 +13,9 @@ import { useIsDark } from '@/hooks/useIsDark';
|
|
|
13
13
|
|
|
14
14
|
import { styles } from './style';
|
|
15
15
|
|
|
16
|
+
const contentContainer = css`
|
|
17
|
+
overflow: auto;
|
|
18
|
+
`;
|
|
16
19
|
const OnboardingContainer: FC<PropsWithChildren> = ({ children }) => {
|
|
17
20
|
const isDarkMode = useIsDark();
|
|
18
21
|
return (
|
|
@@ -44,9 +47,9 @@ const OnboardingContainer: FC<PropsWithChildren> = ({ children }) => {
|
|
|
44
47
|
<ThemeButton placement={'bottomRight'} size={18} />
|
|
45
48
|
</Flexbox>
|
|
46
49
|
</Flexbox>
|
|
47
|
-
<
|
|
50
|
+
<Flexbox align={'center'} className={cx(contentContainer)} height={'100%'} width={'100%'}>
|
|
48
51
|
{children}
|
|
49
|
-
</
|
|
52
|
+
</Flexbox>
|
|
50
53
|
<Center padding={24}>
|
|
51
54
|
<Text align={'center'} type={'secondary'}>
|
|
52
55
|
© 2025 LobeHub. All rights reserved.
|
package/src/app/[variants]/(desktop)/desktop-onboarding/components/OnboardingFooterActions.tsx
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Flexbox, type FlexboxProps } from '@lobehub/ui';
|
|
2
|
+
import { cssVar } from 'antd-style';
|
|
3
|
+
import { type ReactNode, memo } from 'react';
|
|
4
|
+
|
|
5
|
+
interface OnboardingFooterActionsProps extends Omit<FlexboxProps, 'children'> {
|
|
6
|
+
left?: ReactNode;
|
|
7
|
+
right?: ReactNode;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const OnboardingFooterActions = memo<OnboardingFooterActionsProps>(
|
|
11
|
+
({ left, right, style, ...rest }) => {
|
|
12
|
+
return (
|
|
13
|
+
<Flexbox
|
|
14
|
+
align={'center'}
|
|
15
|
+
horizontal
|
|
16
|
+
justify={'space-between'}
|
|
17
|
+
style={{
|
|
18
|
+
background: cssVar.colorBgContainer,
|
|
19
|
+
bottom: 0,
|
|
20
|
+
marginTop: 'auto',
|
|
21
|
+
paddingTop: 16,
|
|
22
|
+
position: 'sticky',
|
|
23
|
+
width: '100%',
|
|
24
|
+
zIndex: 10,
|
|
25
|
+
...style,
|
|
26
|
+
}}
|
|
27
|
+
{...rest}
|
|
28
|
+
>
|
|
29
|
+
<div>{left}</div>
|
|
30
|
+
<div>{right}</div>
|
|
31
|
+
</Flexbox>
|
|
32
|
+
);
|
|
33
|
+
},
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
OnboardingFooterActions.displayName = 'OnboardingFooterActions';
|
|
37
|
+
|
|
38
|
+
export default OnboardingFooterActions;
|
|
@@ -10,6 +10,7 @@ import { useUserStore } from '@/store/user';
|
|
|
10
10
|
import { userGeneralSettingsSelectors } from '@/store/user/selectors';
|
|
11
11
|
|
|
12
12
|
import LobeMessage from '../components/LobeMessage';
|
|
13
|
+
import OnboardingFooterActions from '../components/OnboardingFooterActions';
|
|
13
14
|
|
|
14
15
|
type DataMode = 'share' | 'privacy';
|
|
15
16
|
|
|
@@ -48,7 +49,7 @@ const DataModeStep = memo<DataModeStepProps>(({ onBack, onNext }) => {
|
|
|
48
49
|
);
|
|
49
50
|
|
|
50
51
|
return (
|
|
51
|
-
<Flexbox gap={16}>
|
|
52
|
+
<Flexbox gap={16} style={{ height: '100%', minHeight: '100%' }}>
|
|
52
53
|
<Flexbox>
|
|
53
54
|
<LobeMessage sentences={[t('screen4.title'), t('screen4.title2'), t('screen4.title3')]} />
|
|
54
55
|
<Text as={'p'}>{t('screen4.description')}</Text>
|
|
@@ -113,19 +114,23 @@ const DataModeStep = memo<DataModeStepProps>(({ onBack, onNext }) => {
|
|
|
113
114
|
<Text color={cssVar.colorTextSecondary} fontSize={12} style={{ marginTop: 16 }}>
|
|
114
115
|
{t('screen4.footerNote')}
|
|
115
116
|
</Text>
|
|
116
|
-
<
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
117
|
+
<OnboardingFooterActions
|
|
118
|
+
left={
|
|
119
|
+
<Button
|
|
120
|
+
icon={Undo2Icon}
|
|
121
|
+
onClick={onBack}
|
|
122
|
+
style={{ color: cssVar.colorTextDescription }}
|
|
123
|
+
type={'text'}
|
|
124
|
+
>
|
|
125
|
+
{t('back')}
|
|
126
|
+
</Button>
|
|
127
|
+
}
|
|
128
|
+
right={
|
|
129
|
+
<Button onClick={onNext} type={'primary'}>
|
|
130
|
+
{t('next')}
|
|
131
|
+
</Button>
|
|
132
|
+
}
|
|
133
|
+
/>
|
|
129
134
|
</Flexbox>
|
|
130
135
|
);
|
|
131
136
|
});
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
AuthorizationPhase,
|
|
5
|
+
AuthorizationProgress,
|
|
6
|
+
useWatchBroadcast,
|
|
7
|
+
} from '@lobechat/electron-client-ipc';
|
|
4
8
|
import { Alert, Button, Center, Flexbox, Icon, Input, Text } from '@lobehub/ui';
|
|
5
9
|
import { Divider } from 'antd';
|
|
6
10
|
import { cssVar } from 'antd-style';
|
|
@@ -9,6 +13,7 @@ import { memo, useEffect, useState } from 'react';
|
|
|
9
13
|
import { useTranslation } from 'react-i18next';
|
|
10
14
|
|
|
11
15
|
import { isDesktop } from '@/const/version';
|
|
16
|
+
import UserInfo from '@/features/User/UserInfo';
|
|
12
17
|
import { remoteServerService } from '@/services/electron/remoteServer';
|
|
13
18
|
import { useElectronStore } from '@/store/electron';
|
|
14
19
|
import { setDesktopAutoOidcFirstOpenHandled } from '@/utils/electron/autoOidc';
|
|
@@ -21,6 +26,13 @@ type LoginMethod = 'cloud' | 'selfhost';
|
|
|
21
26
|
// 登录状态类型
|
|
22
27
|
type LoginStatus = 'idle' | 'loading' | 'success' | 'error';
|
|
23
28
|
|
|
29
|
+
const authorizationPhaseI18nKeyMap: Record<AuthorizationPhase, string> = {
|
|
30
|
+
browser_opened: 'screen5.auth.phase.browserOpened',
|
|
31
|
+
cancelled: 'screen5.actions.cancel',
|
|
32
|
+
verifying: 'screen5.auth.phase.verifying',
|
|
33
|
+
waiting_for_auth: 'screen5.auth.phase.waitingForAuth',
|
|
34
|
+
};
|
|
35
|
+
|
|
24
36
|
const loginMethodMetas = {
|
|
25
37
|
cloud: {
|
|
26
38
|
descriptionKey: 'screen5.methods.cloud.description',
|
|
@@ -181,6 +193,7 @@ const LoginStep = memo<LoginStepProps>(({ onBack, onNext }) => {
|
|
|
181
193
|
setAuthProgress(progress);
|
|
182
194
|
if (progress.phase === 'cancelled') {
|
|
183
195
|
setCloudLoginStatus('idle');
|
|
196
|
+
setSelfhostLoginStatus('idle');
|
|
184
197
|
setAuthProgress(null);
|
|
185
198
|
}
|
|
186
199
|
});
|
|
@@ -188,6 +201,7 @@ const LoginStep = memo<LoginStepProps>(({ onBack, onNext }) => {
|
|
|
188
201
|
const handleCancelAuth = async () => {
|
|
189
202
|
await remoteServerService.cancelAuthorization();
|
|
190
203
|
setCloudLoginStatus('idle');
|
|
204
|
+
setSelfhostLoginStatus('idle');
|
|
191
205
|
setAuthProgress(null);
|
|
192
206
|
};
|
|
193
207
|
|
|
@@ -195,13 +209,19 @@ const LoginStep = memo<LoginStepProps>(({ onBack, onNext }) => {
|
|
|
195
209
|
const renderCloudContent = () => {
|
|
196
210
|
if (cloudLoginStatus === 'success') {
|
|
197
211
|
return (
|
|
198
|
-
<Flexbox gap={
|
|
212
|
+
<Flexbox gap={16} style={{ width: '100%' }}>
|
|
199
213
|
<Alert
|
|
200
214
|
description={t('authResult.success.desc')}
|
|
201
215
|
style={{ width: '100%' }}
|
|
202
216
|
title={t('authResult.success.title')}
|
|
203
217
|
type={'success'}
|
|
204
218
|
/>
|
|
219
|
+
<UserInfo
|
|
220
|
+
style={{
|
|
221
|
+
background: cssVar.colorFillSecondary,
|
|
222
|
+
borderRadius: 8,
|
|
223
|
+
}}
|
|
224
|
+
/>
|
|
205
225
|
<Button
|
|
206
226
|
block
|
|
207
227
|
disabled={isSigningOut || isConnectingServer}
|
|
@@ -239,27 +259,35 @@ const LoginStep = memo<LoginStepProps>(({ onBack, onNext }) => {
|
|
|
239
259
|
}
|
|
240
260
|
|
|
241
261
|
if (cloudLoginStatus === 'loading') {
|
|
262
|
+
const phaseText = t(authorizationPhaseI18nKeyMap[authProgress?.phase ?? 'browser_opened'], {
|
|
263
|
+
defaultValue: t('screen5.actions.signingIn'),
|
|
264
|
+
});
|
|
265
|
+
const remainingSeconds = authProgress
|
|
266
|
+
? Math.max(0, Math.ceil((authProgress.maxPollTime - authProgress.elapsed) / 1000))
|
|
267
|
+
: null;
|
|
268
|
+
|
|
242
269
|
return (
|
|
243
270
|
<Flexbox gap={8} style={{ width: '100%' }}>
|
|
244
271
|
<Button block disabled={true} icon={Cloud} loading={true} size={'large'} type={'primary'}>
|
|
245
|
-
{
|
|
246
|
-
? t(`screen5.auth.phase.${authProgress.phase}`, {
|
|
247
|
-
defaultValue: t('screen5.actions.signingIn'),
|
|
248
|
-
})
|
|
249
|
-
: t('screen5.actions.signingIn')}
|
|
272
|
+
{t('screen5.actions.signingIn')}
|
|
250
273
|
</Button>
|
|
251
|
-
{
|
|
252
|
-
|
|
274
|
+
<Text style={{ color: cssVar.colorTextDescription }} type={'secondary'}>
|
|
275
|
+
{phaseText}
|
|
276
|
+
</Text>
|
|
277
|
+
<Flexbox align={'center'} horizontal justify={'space-between'}>
|
|
278
|
+
{remainingSeconds !== null ? (
|
|
253
279
|
<Text style={{ color: cssVar.colorTextDescription }} type={'secondary'}>
|
|
254
280
|
{t('screen5.auth.remaining', {
|
|
255
|
-
time:
|
|
281
|
+
time: remainingSeconds,
|
|
256
282
|
})}
|
|
257
283
|
</Text>
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
284
|
+
) : (
|
|
285
|
+
<div />
|
|
286
|
+
)}
|
|
287
|
+
<Button onClick={handleCancelAuth} size={'small'} type={'text'}>
|
|
288
|
+
{t('screen5.actions.cancel')}
|
|
289
|
+
</Button>
|
|
290
|
+
</Flexbox>
|
|
263
291
|
</Flexbox>
|
|
264
292
|
);
|
|
265
293
|
}
|
|
@@ -283,13 +311,19 @@ const LoginStep = memo<LoginStepProps>(({ onBack, onNext }) => {
|
|
|
283
311
|
const renderSelfhostContent = () => {
|
|
284
312
|
if (selfhostLoginStatus === 'success') {
|
|
285
313
|
return (
|
|
286
|
-
<Flexbox gap={
|
|
314
|
+
<Flexbox gap={16} style={{ width: '100%' }}>
|
|
287
315
|
<Alert
|
|
288
316
|
description={t('authResult.success.desc')}
|
|
289
317
|
style={{ width: '100%' }}
|
|
290
318
|
title={t('authResult.success.title')}
|
|
291
319
|
type={'success'}
|
|
292
320
|
/>
|
|
321
|
+
<UserInfo
|
|
322
|
+
style={{
|
|
323
|
+
background: cssVar.colorFillSecondary,
|
|
324
|
+
borderRadius: 8,
|
|
325
|
+
}}
|
|
326
|
+
/>
|
|
293
327
|
<Button
|
|
294
328
|
block
|
|
295
329
|
disabled={isSigningOut || isConnectingServer}
|
|
@@ -354,8 +388,8 @@ const LoginStep = memo<LoginStepProps>(({ onBack, onNext }) => {
|
|
|
354
388
|
};
|
|
355
389
|
|
|
356
390
|
return (
|
|
357
|
-
<
|
|
358
|
-
<Flexbox>
|
|
391
|
+
<Center gap={32} style={{ height: '100%', minHeight: '100%' }}>
|
|
392
|
+
<Flexbox align={'flex-start'} justify={'flex-start'} style={{ width: '100%' }}>
|
|
359
393
|
<LobeMessage sentences={[t('screen5.title'), t('screen5.title2'), t('screen5.title3')]} />
|
|
360
394
|
<Text as={'p'}>{t('screen5.description')}</Text>
|
|
361
395
|
</Flexbox>
|
|
@@ -401,7 +435,7 @@ const LoginStep = memo<LoginStepProps>(({ onBack, onNext }) => {
|
|
|
401
435
|
</Button>
|
|
402
436
|
</Flexbox>
|
|
403
437
|
)}
|
|
404
|
-
</
|
|
438
|
+
</Center>
|
|
405
439
|
);
|
|
406
440
|
});
|
|
407
441
|
|
|
@@ -18,6 +18,7 @@ import { useTranslation } from 'react-i18next';
|
|
|
18
18
|
import { ensureElectronIpc } from '@/utils/electron/ipc';
|
|
19
19
|
|
|
20
20
|
import LobeMessage from '../components/LobeMessage';
|
|
21
|
+
import OnboardingFooterActions from '../components/OnboardingFooterActions';
|
|
21
22
|
|
|
22
23
|
type PermissionMeta = {
|
|
23
24
|
descriptionKey: string;
|
|
@@ -154,7 +155,7 @@ const PermissionsStep = memo<PermissionsStepProps>(({ onBack, onNext }) => {
|
|
|
154
155
|
};
|
|
155
156
|
|
|
156
157
|
return (
|
|
157
|
-
<Flexbox gap={16}>
|
|
158
|
+
<Flexbox gap={16} style={{ height: '100%', minHeight: '100%' }}>
|
|
158
159
|
<Flexbox>
|
|
159
160
|
<LobeMessage sentences={[t('screen3.title'), t('screen3.title2'), t('screen3.title3')]} />
|
|
160
161
|
<Text as={'p'}>{t('screen3.description')}</Text>
|
|
@@ -207,19 +208,23 @@ const PermissionsStep = memo<PermissionsStepProps>(({ onBack, onNext }) => {
|
|
|
207
208
|
</Block>
|
|
208
209
|
))}
|
|
209
210
|
</Block>
|
|
210
|
-
<
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
211
|
+
<OnboardingFooterActions
|
|
212
|
+
left={
|
|
213
|
+
<Button
|
|
214
|
+
icon={Undo2Icon}
|
|
215
|
+
onClick={onBack}
|
|
216
|
+
style={{ color: cssVar.colorTextDescription }}
|
|
217
|
+
type={'text'}
|
|
218
|
+
>
|
|
219
|
+
{t('back')}
|
|
220
|
+
</Button>
|
|
221
|
+
}
|
|
222
|
+
right={
|
|
223
|
+
<Button onClick={onNext} type={'primary'}>
|
|
224
|
+
{t('next')}
|
|
225
|
+
</Button>
|
|
226
|
+
}
|
|
227
|
+
/>
|
|
223
228
|
</Flexbox>
|
|
224
229
|
);
|
|
225
230
|
});
|