@lobehub/lobehub 2.0.0-next.312 → 2.0.0-next.314
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 +51 -0
- package/apps/desktop/src/main/appBrowsers.ts +4 -1
- package/apps/desktop/src/main/controllers/AuthCtr.ts +75 -7
- 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/docs/usage/providers/internlm.mdx +2 -2
- package/docs/usage/providers/internlm.zh-CN.mdx +3 -3
- 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/locales/en-US/error.json +10 -1
- package/locales/en-US/subscription.json +1 -1
- package/locales/zh-CN/desktop-onboarding.json +5 -0
- package/locales/zh-CN/error.json +10 -1
- package/locales/zh-CN/subscription.json +1 -1
- package/package.json +1 -1
- package/packages/agent-runtime/src/agents/GeneralChatAgent.ts +14 -2
- package/packages/agent-runtime/src/agents/__tests__/GeneralChatAgent.test.ts +275 -1
- package/packages/builtin-tool-cloud-sandbox/package.json +1 -0
- package/packages/builtin-tool-cloud-sandbox/src/ExecutionRuntime/index.ts +105 -134
- package/packages/builtin-tool-cloud-sandbox/src/executor/index.ts +254 -0
- package/packages/builtin-tool-cloud-sandbox/src/index.ts +1 -0
- package/packages/builtin-tool-cloud-sandbox/src/types/api.ts +22 -0
- package/packages/builtin-tool-cloud-sandbox/src/types/index.ts +4 -0
- package/packages/builtin-tool-cloud-sandbox/src/types/params.ts +85 -0
- package/packages/builtin-tool-cloud-sandbox/src/types/service.ts +48 -0
- package/packages/builtin-tool-cloud-sandbox/src/{types.ts → types/state.ts} +0 -23
- package/packages/builtin-tool-memory/src/manifest.ts +5 -5
- package/packages/desktop-bridge/src/index.ts +5 -0
- package/packages/editor-runtime/src/__tests__/EditorRuntime.real.test.ts +1 -1
- package/packages/editor-runtime/src/__tests__/EditorRuntime.test.ts +1 -1
- package/packages/electron-client-ipc/src/events/index.ts +5 -1
- package/packages/electron-client-ipc/src/events/remoteServer.ts +23 -0
- package/packages/electron-client-ipc/src/types/window.ts +3 -2
- package/packages/memory-user-memory/src/schemas/index.ts +0 -1
- package/packages/model-bank/src/modelProviders/internlm.ts +1 -1
- package/packages/model-runtime/src/core/RouterRuntime/createRuntime.ts +5 -15
- package/packages/model-runtime/src/providers/internlm/index.test.ts +15 -15
- package/packages/model-runtime/src/providers/internlm/index.ts +1 -1
- package/packages/types/src/tool/intervention.ts +4 -2
- package/packages/types/src/user/preference.ts +1 -0
- 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 +121 -29
- 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/[variants]/(main)/_layout/DesktopAutoOidcOnFirstOpen.tsx +4 -0
- package/src/app/manifest.ts +1 -1
- package/src/business/server/user.ts +4 -0
- package/src/features/Conversation/Messages/Task/Actions/index.tsx +0 -2
- package/src/features/Conversation/Messages/Task/index.tsx +1 -1
- package/src/features/Conversation/Messages/Tasks/shared/ProcessingState.tsx +0 -2
- package/src/features/Electron/titlebar/NavigationBar.tsx +1 -2
- package/src/features/NavPanel/components/NavPanelDraggable.tsx +0 -14
- package/src/features/ResourceManager/components/Explorer/ItemDropdown/useFileItemDropdown.tsx +4 -3
- package/src/features/SharePopover/index.tsx +5 -3
- package/src/hooks/useAppOrigin.ts +16 -0
- package/src/layout/GlobalProvider/useUserStateRedirect.ts +37 -24
- package/src/libs/trusted-client/index.ts +2 -5
- package/src/locales/default/desktop-onboarding.ts +5 -0
- package/src/locales/default/error.ts +11 -0
- package/src/locales/default/subscription.ts +1 -1
- package/src/server/manifest.ts +2 -2
- package/src/server/modules/AgentRuntime/RuntimeExecutors.ts +2 -0
- package/src/server/routers/lambda/user.ts +24 -10
- package/src/server/services/agentRuntime/AgentRuntimeService.test.ts +3 -0
- package/src/server/services/agentRuntime/AgentRuntimeService.ts +8 -5
- package/src/server/services/agentRuntime/types.ts +7 -0
- package/src/server/services/aiAgent/__tests__/execGroupSubAgentTask.test.ts +3 -0
- package/src/server/services/aiAgent/index.ts +10 -4
- package/src/server/services/market/index.ts +20 -0
- package/src/server/services/sandbox/index.ts +186 -0
- package/src/server/services/toolExecution/builtin.ts +12 -18
- package/src/server/services/toolExecution/index.ts +1 -1
- package/src/server/services/toolExecution/serverRuntimes/cloudSandbox.ts +38 -0
- package/src/server/services/toolExecution/serverRuntimes/index.ts +55 -0
- package/src/server/services/toolExecution/serverRuntimes/types.ts +14 -0
- package/src/server/services/toolExecution/serverRuntimes/webBrowsing.ts +20 -0
- package/src/server/services/toolExecution/types.ts +2 -0
- package/src/services/{codeInterpreter.ts → cloudSandbox.ts} +3 -3
- package/src/services/electron/remoteServer.ts +8 -0
- package/src/services/electron/system.ts +5 -5
- package/src/store/chat/agents/GroupOrchestration/__tests__/batch-exec-async-tasks.test.ts +626 -0
- package/src/store/chat/agents/GroupOrchestration/createGroupOrchestrationExecutors.ts +294 -0
- package/src/store/chat/slices/plugin/action.test.ts +0 -48
- package/src/store/chat/slices/plugin/actions/pluginTypes.ts +0 -131
- package/src/store/tool/slices/builtin/executors/index.ts +2 -0
- package/src/store/user/slices/settings/selectors/toolIntervention.test.ts +143 -0
- package/src/store/user/slices/settings/selectors/toolIntervention.ts +11 -2
- package/packages/memory-user-memory/src/schemas/jsonSchemas.ts +0 -37
|
@@ -9,7 +9,7 @@ import { LobeInternLMAI, params } from './index';
|
|
|
9
9
|
testProvider({
|
|
10
10
|
Runtime: LobeInternLMAI,
|
|
11
11
|
provider: ModelProvider.InternLM,
|
|
12
|
-
defaultBaseURL: 'https://
|
|
12
|
+
defaultBaseURL: 'https://chat.intern-ai.org.cn/api/v1',
|
|
13
13
|
chatDebugEnv: 'DEBUG_INTERNLM_CHAT_COMPLETION',
|
|
14
14
|
chatModel: 'internlm2_5-7b-chat',
|
|
15
15
|
test: {
|
|
@@ -30,7 +30,7 @@ describe('LobeInternLMAI - custom features', () => {
|
|
|
30
30
|
|
|
31
31
|
describe('params object', () => {
|
|
32
32
|
it('should export params with correct baseURL', () => {
|
|
33
|
-
expect(params.baseURL).toBe('https://
|
|
33
|
+
expect(params.baseURL).toBe('https://chat.intern-ai.org.cn/api/v1');
|
|
34
34
|
});
|
|
35
35
|
|
|
36
36
|
it('should have correct provider', () => {
|
|
@@ -116,7 +116,7 @@ describe('LobeInternLMAI - custom features', () => {
|
|
|
116
116
|
describe('models', () => {
|
|
117
117
|
it('should fetch and process models', async () => {
|
|
118
118
|
const mockClient = {
|
|
119
|
-
baseURL: 'https://
|
|
119
|
+
baseURL: 'https://chat.intern-ai.org.cn/api/v1',
|
|
120
120
|
apiKey: 'test',
|
|
121
121
|
models: {
|
|
122
122
|
list: vi.fn().mockResolvedValue({
|
|
@@ -137,7 +137,7 @@ describe('LobeInternLMAI - custom features', () => {
|
|
|
137
137
|
|
|
138
138
|
it('should detect function call capability from model name', async () => {
|
|
139
139
|
const mockClient = {
|
|
140
|
-
baseURL: 'https://
|
|
140
|
+
baseURL: 'https://chat.intern-ai.org.cn/api/v1',
|
|
141
141
|
apiKey: 'test',
|
|
142
142
|
models: {
|
|
143
143
|
list: vi.fn().mockResolvedValue({
|
|
@@ -156,7 +156,7 @@ describe('LobeInternLMAI - custom features', () => {
|
|
|
156
156
|
|
|
157
157
|
it('should detect vision capability from model name', async () => {
|
|
158
158
|
const mockClient = {
|
|
159
|
-
baseURL: 'https://
|
|
159
|
+
baseURL: 'https://chat.intern-ai.org.cn/api/v1',
|
|
160
160
|
apiKey: 'test',
|
|
161
161
|
models: {
|
|
162
162
|
list: vi.fn().mockResolvedValue({
|
|
@@ -175,7 +175,7 @@ describe('LobeInternLMAI - custom features', () => {
|
|
|
175
175
|
|
|
176
176
|
it('should handle case-insensitive keyword matching', async () => {
|
|
177
177
|
const mockClient = {
|
|
178
|
-
baseURL: 'https://
|
|
178
|
+
baseURL: 'https://chat.intern-ai.org.cn/api/v1',
|
|
179
179
|
apiKey: 'test',
|
|
180
180
|
models: {
|
|
181
181
|
list: vi.fn().mockResolvedValue({
|
|
@@ -194,7 +194,7 @@ describe('LobeInternLMAI - custom features', () => {
|
|
|
194
194
|
|
|
195
195
|
it('should merge with known model data from model-bank', async () => {
|
|
196
196
|
const mockClient = {
|
|
197
|
-
baseURL: 'https://
|
|
197
|
+
baseURL: 'https://chat.intern-ai.org.cn/api/v1',
|
|
198
198
|
apiKey: 'test',
|
|
199
199
|
models: {
|
|
200
200
|
list: vi.fn().mockResolvedValue({
|
|
@@ -215,7 +215,7 @@ describe('LobeInternLMAI - custom features', () => {
|
|
|
215
215
|
|
|
216
216
|
it('should handle models not in model-bank', async () => {
|
|
217
217
|
const mockClient = {
|
|
218
|
-
baseURL: 'https://
|
|
218
|
+
baseURL: 'https://chat.intern-ai.org.cn/api/v1',
|
|
219
219
|
apiKey: 'test',
|
|
220
220
|
models: {
|
|
221
221
|
list: vi.fn().mockResolvedValue({
|
|
@@ -238,7 +238,7 @@ describe('LobeInternLMAI - custom features', () => {
|
|
|
238
238
|
|
|
239
239
|
it('should set enabled flag from known model', async () => {
|
|
240
240
|
const mockClient = {
|
|
241
|
-
baseURL: 'https://
|
|
241
|
+
baseURL: 'https://chat.intern-ai.org.cn/api/v1',
|
|
242
242
|
apiKey: 'test',
|
|
243
243
|
models: {
|
|
244
244
|
list: vi.fn().mockResolvedValue({
|
|
@@ -255,7 +255,7 @@ describe('LobeInternLMAI - custom features', () => {
|
|
|
255
255
|
|
|
256
256
|
it('should inherit abilities from known model', async () => {
|
|
257
257
|
const mockClient = {
|
|
258
|
-
baseURL: 'https://
|
|
258
|
+
baseURL: 'https://chat.intern-ai.org.cn/api/v1',
|
|
259
259
|
apiKey: 'test',
|
|
260
260
|
models: {
|
|
261
261
|
list: vi.fn().mockResolvedValue({
|
|
@@ -291,7 +291,7 @@ describe('LobeInternLMAI - custom features', () => {
|
|
|
291
291
|
|
|
292
292
|
it('should handle empty model list', async () => {
|
|
293
293
|
const mockClient = {
|
|
294
|
-
baseURL: 'https://
|
|
294
|
+
baseURL: 'https://chat.intern-ai.org.cn/api/v1',
|
|
295
295
|
apiKey: 'test',
|
|
296
296
|
models: {
|
|
297
297
|
list: vi.fn().mockResolvedValue({
|
|
@@ -306,7 +306,7 @@ describe('LobeInternLMAI - custom features', () => {
|
|
|
306
306
|
|
|
307
307
|
it('should filter out null/undefined models', async () => {
|
|
308
308
|
const mockClient = {
|
|
309
|
-
baseURL: 'https://
|
|
309
|
+
baseURL: 'https://chat.intern-ai.org.cn/api/v1',
|
|
310
310
|
apiKey: 'test',
|
|
311
311
|
models: {
|
|
312
312
|
list: vi.fn().mockResolvedValue({
|
|
@@ -322,7 +322,7 @@ describe('LobeInternLMAI - custom features', () => {
|
|
|
322
322
|
|
|
323
323
|
it('should set contextWindowTokens from known model', async () => {
|
|
324
324
|
const mockClient = {
|
|
325
|
-
baseURL: 'https://
|
|
325
|
+
baseURL: 'https://chat.intern-ai.org.cn/api/v1',
|
|
326
326
|
apiKey: 'test',
|
|
327
327
|
models: {
|
|
328
328
|
list: vi.fn().mockResolvedValue({
|
|
@@ -340,7 +340,7 @@ describe('LobeInternLMAI - custom features', () => {
|
|
|
340
340
|
|
|
341
341
|
it('should set displayName from known model', async () => {
|
|
342
342
|
const mockClient = {
|
|
343
|
-
baseURL: 'https://
|
|
343
|
+
baseURL: 'https://chat.intern-ai.org.cn/api/v1',
|
|
344
344
|
apiKey: 'test',
|
|
345
345
|
models: {
|
|
346
346
|
list: vi.fn().mockResolvedValue({
|
|
@@ -358,7 +358,7 @@ describe('LobeInternLMAI - custom features', () => {
|
|
|
358
358
|
|
|
359
359
|
it('should combine keyword detection with known model abilities', async () => {
|
|
360
360
|
const mockClient = {
|
|
361
|
-
baseURL: 'https://
|
|
361
|
+
baseURL: 'https://chat.intern-ai.org.cn/api/v1',
|
|
362
362
|
apiKey: 'test',
|
|
363
363
|
models: {
|
|
364
364
|
list: vi.fn().mockResolvedValue({
|
|
@@ -138,13 +138,15 @@ export interface UserInterventionConfig {
|
|
|
138
138
|
* - auto-run: Automatically approve all tools without user consent
|
|
139
139
|
* - allow-list: Only approve tools in the allow list
|
|
140
140
|
* - manual: Use tool's own humanIntervention config (default)
|
|
141
|
+
* - headless: Fully automated mode for async tasks - all tools execute automatically,
|
|
142
|
+
* security blacklist tools are skipped (not blocked)
|
|
141
143
|
*/
|
|
142
|
-
approvalMode: 'auto-run' | 'allow-list' | 'manual';
|
|
144
|
+
approvalMode: 'auto-run' | 'allow-list' | 'manual' | 'headless';
|
|
143
145
|
}
|
|
144
146
|
|
|
145
147
|
export const UserInterventionConfigSchema = z.object({
|
|
146
148
|
allowList: z.array(z.string()).optional(),
|
|
147
|
-
approvalMode: z.enum(['auto-run', 'allow-list', 'manual']),
|
|
149
|
+
approvalMode: z.enum(['auto-run', 'allow-list', 'manual', 'headless']),
|
|
148
150
|
});
|
|
149
151
|
|
|
150
152
|
/**
|
|
@@ -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,8 @@ 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';
|
|
17
|
+
import { remoteServerService } from '@/services/electron/remoteServer';
|
|
12
18
|
import { useElectronStore } from '@/store/electron';
|
|
13
19
|
import { setDesktopAutoOidcFirstOpenHandled } from '@/utils/electron/autoOidc';
|
|
14
20
|
|
|
@@ -20,6 +26,13 @@ type LoginMethod = 'cloud' | 'selfhost';
|
|
|
20
26
|
// 登录状态类型
|
|
21
27
|
type LoginStatus = 'idle' | 'loading' | 'success' | 'error';
|
|
22
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
|
+
|
|
23
36
|
const loginMethodMetas = {
|
|
24
37
|
cloud: {
|
|
25
38
|
descriptionKey: 'screen5.methods.cloud.description',
|
|
@@ -44,6 +57,7 @@ const LoginStep = memo<LoginStepProps>(({ onBack, onNext }) => {
|
|
|
44
57
|
const { t } = useTranslation('desktop-onboarding');
|
|
45
58
|
const [endpoint, setEndpoint] = useState('');
|
|
46
59
|
const [cloudLoginStatus, setCloudLoginStatus] = useState<LoginStatus>('idle');
|
|
60
|
+
const [authProgress, setAuthProgress] = useState<AuthorizationProgress | null>(null);
|
|
47
61
|
const [selfhostLoginStatus, setSelfhostLoginStatus] = useState<LoginStatus>('idle');
|
|
48
62
|
const [remoteError, setRemoteError] = useState<string | null>(null);
|
|
49
63
|
const [isSigningOut, setIsSigningOut] = useState(false);
|
|
@@ -164,29 +178,61 @@ const LoginStep = memo<LoginStepProps>(({ onBack, onNext }) => {
|
|
|
164
178
|
useWatchBroadcast('authorizationSuccessful', async () => {
|
|
165
179
|
setRemoteError(null);
|
|
166
180
|
clearRemoteServerSyncError();
|
|
181
|
+
setAuthProgress(null);
|
|
167
182
|
await refreshServerConfig();
|
|
168
183
|
});
|
|
169
184
|
|
|
170
185
|
useWatchBroadcast('authorizationFailed', ({ error }) => {
|
|
171
186
|
setRemoteError(error);
|
|
187
|
+
setAuthProgress(null);
|
|
172
188
|
if (cloudLoginStatus === 'loading') setCloudLoginStatus('error');
|
|
173
189
|
if (selfhostLoginStatus === 'loading') setSelfhostLoginStatus('error');
|
|
174
190
|
});
|
|
175
191
|
|
|
192
|
+
useWatchBroadcast('authorizationProgress', (progress) => {
|
|
193
|
+
setAuthProgress(progress);
|
|
194
|
+
if (progress.phase === 'cancelled') {
|
|
195
|
+
setCloudLoginStatus('idle');
|
|
196
|
+
setSelfhostLoginStatus('idle');
|
|
197
|
+
setAuthProgress(null);
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
const handleCancelAuth = async () => {
|
|
202
|
+
await remoteServerService.cancelAuthorization();
|
|
203
|
+
setCloudLoginStatus('idle');
|
|
204
|
+
setSelfhostLoginStatus('idle');
|
|
205
|
+
setAuthProgress(null);
|
|
206
|
+
};
|
|
207
|
+
|
|
176
208
|
// 渲染 Cloud 登录内容
|
|
177
209
|
const renderCloudContent = () => {
|
|
178
210
|
if (cloudLoginStatus === 'success') {
|
|
179
211
|
return (
|
|
180
|
-
<
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
212
|
+
<Flexbox gap={16} style={{ width: '100%' }}>
|
|
213
|
+
<Alert
|
|
214
|
+
description={t('authResult.success.desc')}
|
|
215
|
+
style={{ width: '100%' }}
|
|
216
|
+
title={t('authResult.success.title')}
|
|
217
|
+
type={'success'}
|
|
218
|
+
/>
|
|
219
|
+
<UserInfo
|
|
220
|
+
style={{
|
|
221
|
+
background: cssVar.colorFillSecondary,
|
|
222
|
+
borderRadius: 8,
|
|
223
|
+
}}
|
|
224
|
+
/>
|
|
225
|
+
<Button
|
|
226
|
+
block
|
|
227
|
+
disabled={isSigningOut || isConnectingServer}
|
|
228
|
+
icon={Cloud}
|
|
229
|
+
onClick={handleSignOut}
|
|
230
|
+
size={'large'}
|
|
231
|
+
type={'default'}
|
|
232
|
+
>
|
|
233
|
+
{isSigningOut ? t('screen5.actions.signingOut') : t('screen5.actions.signOut')}
|
|
234
|
+
</Button>
|
|
235
|
+
</Flexbox>
|
|
190
236
|
);
|
|
191
237
|
}
|
|
192
238
|
|
|
@@ -212,19 +258,51 @@ const LoginStep = memo<LoginStepProps>(({ onBack, onNext }) => {
|
|
|
212
258
|
);
|
|
213
259
|
}
|
|
214
260
|
|
|
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
|
+
|
|
269
|
+
return (
|
|
270
|
+
<Flexbox gap={8} style={{ width: '100%' }}>
|
|
271
|
+
<Button block disabled={true} icon={Cloud} loading={true} size={'large'} type={'primary'}>
|
|
272
|
+
{t('screen5.actions.signingIn')}
|
|
273
|
+
</Button>
|
|
274
|
+
<Text style={{ color: cssVar.colorTextDescription }} type={'secondary'}>
|
|
275
|
+
{phaseText}
|
|
276
|
+
</Text>
|
|
277
|
+
<Flexbox align={'center'} horizontal justify={'space-between'}>
|
|
278
|
+
{remainingSeconds !== null ? (
|
|
279
|
+
<Text style={{ color: cssVar.colorTextDescription }} type={'secondary'}>
|
|
280
|
+
{t('screen5.auth.remaining', {
|
|
281
|
+
time: remainingSeconds,
|
|
282
|
+
})}
|
|
283
|
+
</Text>
|
|
284
|
+
) : (
|
|
285
|
+
<div />
|
|
286
|
+
)}
|
|
287
|
+
<Button onClick={handleCancelAuth} size={'small'} type={'text'}>
|
|
288
|
+
{t('screen5.actions.cancel')}
|
|
289
|
+
</Button>
|
|
290
|
+
</Flexbox>
|
|
291
|
+
</Flexbox>
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
|
|
215
295
|
return (
|
|
216
296
|
<Button
|
|
217
297
|
block
|
|
218
|
-
disabled={
|
|
298
|
+
disabled={isConnectingServer}
|
|
219
299
|
icon={Cloud}
|
|
220
|
-
loading={
|
|
300
|
+
loading={false}
|
|
221
301
|
onClick={handleCloudLogin}
|
|
222
302
|
size={'large'}
|
|
223
303
|
type={'primary'}
|
|
224
304
|
>
|
|
225
|
-
{
|
|
226
|
-
? t('screen5.actions.signingIn')
|
|
227
|
-
: t('screen5.actions.signInCloud')}
|
|
305
|
+
{t('screen5.actions.signInCloud')}
|
|
228
306
|
</Button>
|
|
229
307
|
);
|
|
230
308
|
};
|
|
@@ -233,16 +311,30 @@ const LoginStep = memo<LoginStepProps>(({ onBack, onNext }) => {
|
|
|
233
311
|
const renderSelfhostContent = () => {
|
|
234
312
|
if (selfhostLoginStatus === 'success') {
|
|
235
313
|
return (
|
|
236
|
-
<
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
314
|
+
<Flexbox gap={16} style={{ width: '100%' }}>
|
|
315
|
+
<Alert
|
|
316
|
+
description={t('authResult.success.desc')}
|
|
317
|
+
style={{ width: '100%' }}
|
|
318
|
+
title={t('authResult.success.title')}
|
|
319
|
+
type={'success'}
|
|
320
|
+
/>
|
|
321
|
+
<UserInfo
|
|
322
|
+
style={{
|
|
323
|
+
background: cssVar.colorFillSecondary,
|
|
324
|
+
borderRadius: 8,
|
|
325
|
+
}}
|
|
326
|
+
/>
|
|
327
|
+
<Button
|
|
328
|
+
block
|
|
329
|
+
disabled={isSigningOut || isConnectingServer}
|
|
330
|
+
icon={Server}
|
|
331
|
+
onClick={handleSignOut}
|
|
332
|
+
size={'large'}
|
|
333
|
+
type={'default'}
|
|
334
|
+
>
|
|
335
|
+
{isSigningOut ? t('screen5.actions.signingOut') : t('screen5.actions.signOut')}
|
|
336
|
+
</Button>
|
|
337
|
+
</Flexbox>
|
|
246
338
|
);
|
|
247
339
|
}
|
|
248
340
|
|
|
@@ -296,8 +388,8 @@ const LoginStep = memo<LoginStepProps>(({ onBack, onNext }) => {
|
|
|
296
388
|
};
|
|
297
389
|
|
|
298
390
|
return (
|
|
299
|
-
<
|
|
300
|
-
<Flexbox>
|
|
391
|
+
<Center gap={32} style={{ height: '100%', minHeight: '100%' }}>
|
|
392
|
+
<Flexbox align={'flex-start'} justify={'flex-start'} style={{ width: '100%' }}>
|
|
301
393
|
<LobeMessage sentences={[t('screen5.title'), t('screen5.title2'), t('screen5.title3')]} />
|
|
302
394
|
<Text as={'p'}>{t('screen5.description')}</Text>
|
|
303
395
|
</Flexbox>
|
|
@@ -343,7 +435,7 @@ const LoginStep = memo<LoginStepProps>(({ onBack, onNext }) => {
|
|
|
343
435
|
</Button>
|
|
344
436
|
</Flexbox>
|
|
345
437
|
)}
|
|
346
|
-
</
|
|
438
|
+
</Center>
|
|
347
439
|
);
|
|
348
440
|
});
|
|
349
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
|
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
+
import { APP_WINDOW_MIN_SIZE } from '@lobechat/desktop-bridge';
|
|
3
4
|
import { Flexbox, Skeleton } from '@lobehub/ui';
|
|
4
5
|
import { Suspense, memo, useCallback, useEffect, useState } from 'react';
|
|
5
6
|
import { useSearchParams } from 'react-router-dom';
|
|
@@ -50,12 +51,11 @@ const DesktopOnboardingPage = memo(() => {
|
|
|
50
51
|
|
|
51
52
|
// 设置窗口大小和可调整性
|
|
52
53
|
useEffect(() => {
|
|
53
|
-
const
|
|
54
|
+
const minimumSize = { height: 900, width: 1200 };
|
|
54
55
|
|
|
55
56
|
const applyWindowSettings = async () => {
|
|
56
57
|
try {
|
|
57
|
-
await electronSystemService.
|
|
58
|
-
await electronSystemService.setWindowResizable({ resizable: false });
|
|
58
|
+
await electronSystemService.setWindowMinimumSize(minimumSize);
|
|
59
59
|
} catch (error) {
|
|
60
60
|
console.error('[DesktopOnboarding] Failed to apply window settings:', error);
|
|
61
61
|
}
|
|
@@ -64,7 +64,8 @@ const DesktopOnboardingPage = memo(() => {
|
|
|
64
64
|
applyWindowSettings();
|
|
65
65
|
|
|
66
66
|
return () => {
|
|
67
|
-
|
|
67
|
+
// Restore to app-level default minimum size preset
|
|
68
|
+
electronSystemService.setWindowMinimumSize(APP_WINDOW_MIN_SIZE).catch((error) => {
|
|
68
69
|
console.error('[DesktopOnboarding] Failed to restore window settings:', error);
|
|
69
70
|
});
|
|
70
71
|
};
|
|
@@ -127,9 +128,9 @@ const DesktopOnboardingPage = memo(() => {
|
|
|
127
128
|
// 如果是第4步(LoginStep),完成 onboarding
|
|
128
129
|
setDesktopOnboardingCompleted();
|
|
129
130
|
clearDesktopOnboardingStep(); // Clear persisted step since onboarding is complete
|
|
130
|
-
// Restore window
|
|
131
|
+
// Restore window minimum size before hard reload (cleanup won't run due to hard navigation)
|
|
131
132
|
electronSystemService
|
|
132
|
-
.
|
|
133
|
+
.setWindowMinimumSize(APP_WINDOW_MIN_SIZE)
|
|
133
134
|
.catch(console.error)
|
|
134
135
|
.finally(() => {
|
|
135
136
|
// Use hard reload instead of SPA navigation to ensure the app boots with the new desktop state.
|
|
@@ -196,7 +197,7 @@ const DesktopOnboardingPage = memo(() => {
|
|
|
196
197
|
|
|
197
198
|
return (
|
|
198
199
|
<OnboardingContainer>
|
|
199
|
-
<Flexbox gap={24} style={{ maxWidth: 560, width: '100%' }}>
|
|
200
|
+
<Flexbox gap={24} style={{ maxWidth: 560, minHeight: '100%', width: '100%' }}>
|
|
200
201
|
<Suspense
|
|
201
202
|
fallback={
|
|
202
203
|
<Flexbox gap={8}>
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { memo, useEffect } from 'react';
|
|
4
4
|
|
|
5
|
+
import { getDesktopOnboardingCompleted } from '@/app/[variants]/(desktop)/desktop-onboarding/storage';
|
|
5
6
|
import { useElectronStore } from '@/store/electron';
|
|
6
7
|
import {
|
|
7
8
|
getDesktopAutoOidcFirstOpenHandled,
|
|
@@ -28,6 +29,9 @@ const DesktopAutoOidcOnFirstOpen = memo(() => {
|
|
|
28
29
|
useEffect(() => {
|
|
29
30
|
if (!isInitRemoteServerConfig) return;
|
|
30
31
|
|
|
32
|
+
// Don't auto-trigger during onboarding flow.
|
|
33
|
+
if (!getDesktopOnboardingCompleted()) return;
|
|
34
|
+
|
|
31
35
|
// If already connected or not in cloud mode, don't auto-trigger.
|
|
32
36
|
if (dataSyncConfig.active) return;
|
|
33
37
|
if (dataSyncConfig.storageMode !== 'cloud') return;
|