@lobehub/lobehub 2.0.0-next.236 → 2.0.0-next.238
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 +66 -0
- package/changelog/v1.json +17 -0
- package/locales/en-US/oauth.json +1 -0
- package/locales/zh-CN/oauth.json +1 -0
- package/locales/zh-CN/subscription.json +1 -1
- package/package.json +1 -1
- package/packages/const/src/klavis.ts +1 -7
- package/packages/types/src/user/onboarding.ts +3 -1
- package/src/app/[variants]/(auth)/oauth/callback/success/page.tsx +44 -2
- package/src/app/[variants]/(main)/chat/_layout/Sidebar/Header/Nav.tsx +19 -1
- package/src/app/[variants]/(main)/chat/_layout/Sidebar/Header/index.tsx +1 -2
- package/src/app/[variants]/(main)/chat/_layout/Sidebar/Topic/List/Item/Editing.tsx +18 -9
- package/src/app/[variants]/(main)/settings/profile/features/KlavisAuthorizationList/index.tsx +6 -24
- package/src/app/[variants]/(main)/settings/profile/index.tsx +17 -3
- package/src/app/[variants]/onboarding/features/FullNameStep.tsx +18 -5
- package/src/app/[variants]/onboarding/features/InterestsStep.tsx +19 -7
- package/src/app/[variants]/onboarding/features/ProSettingsStep.tsx +30 -8
- package/src/app/[variants]/onboarding/features/ResponseLanguageStep.tsx +18 -4
- package/src/app/[variants]/onboarding/features/TelemetryStep.tsx +14 -5
- package/src/app/[variants]/onboarding/index.tsx +2 -1
- package/src/features/ChatInput/ActionBar/Tools/LobehubSkillServerItem.tsx +42 -2
- package/src/server/services/search/impls/exa/index.ts +1 -1
- package/src/server/services/search/impls/search1api/index.ts +1 -1
- package/src/server/services/search/impls/tavily/index.ts +1 -1
- package/src/store/tool/slices/klavisStore/action.test.ts +167 -2
- package/src/store/tool/slices/klavisStore/action.ts +9 -8
- package/src/store/tool/slices/klavisStore/initialState.ts +3 -0
- package/src/store/user/slices/auth/action.ts +1 -0
- package/src/store/user/slices/onboarding/action.test.ts +342 -0
- package/src/store/user/slices/onboarding/action.ts +4 -9
- package/src/store/user/slices/onboarding/selectors.test.ts +222 -0
- package/src/store/user/slices/onboarding/selectors.ts +6 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,72 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
## [Version 2.0.0-next.238](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.237...v2.0.0-next.238)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2026-01-08**</sup>
|
|
8
|
+
|
|
9
|
+
#### ✨ Features
|
|
10
|
+
|
|
11
|
+
- **misc**: Change the klavis Linear to LobeHub oauth Linear.
|
|
12
|
+
|
|
13
|
+
#### 🐛 Bug Fixes
|
|
14
|
+
|
|
15
|
+
- **misc**: Topic renaming input focus issue in context menu.
|
|
16
|
+
|
|
17
|
+
<br/>
|
|
18
|
+
|
|
19
|
+
<details>
|
|
20
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
21
|
+
|
|
22
|
+
#### What's improved
|
|
23
|
+
|
|
24
|
+
- **misc**: Change the klavis Linear to LobeHub oauth Linear, closes [#11339](https://github.com/lobehub/lobe-chat/issues/11339) ([ec8ff26](https://github.com/lobehub/lobe-chat/commit/ec8ff26))
|
|
25
|
+
|
|
26
|
+
#### What's fixed
|
|
27
|
+
|
|
28
|
+
- **misc**: Topic renaming input focus issue in context menu, closes [#11323](https://github.com/lobehub/lobe-chat/issues/11323) ([dd065fc](https://github.com/lobehub/lobe-chat/commit/dd065fc))
|
|
29
|
+
|
|
30
|
+
</details>
|
|
31
|
+
|
|
32
|
+
<div align="right">
|
|
33
|
+
|
|
34
|
+
[](#readme-top)
|
|
35
|
+
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
## [Version 2.0.0-next.237](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.236...v2.0.0-next.237)
|
|
39
|
+
|
|
40
|
+
<sup>Released on **2026-01-08**</sup>
|
|
41
|
+
|
|
42
|
+
#### ✨ Features
|
|
43
|
+
|
|
44
|
+
- **ui**: Move new topic button to navigation panel.
|
|
45
|
+
|
|
46
|
+
#### 🐛 Bug Fixes
|
|
47
|
+
|
|
48
|
+
- **onboarding**: Prevent step overflow and misc improvements.
|
|
49
|
+
|
|
50
|
+
<br/>
|
|
51
|
+
|
|
52
|
+
<details>
|
|
53
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
54
|
+
|
|
55
|
+
#### What's improved
|
|
56
|
+
|
|
57
|
+
- **ui**: Move new topic button to navigation panel, closes [#11325](https://github.com/lobehub/lobe-chat/issues/11325) ([3d6b399](https://github.com/lobehub/lobe-chat/commit/3d6b399))
|
|
58
|
+
|
|
59
|
+
#### What's fixed
|
|
60
|
+
|
|
61
|
+
- **onboarding**: Prevent step overflow and misc improvements, closes [#11322](https://github.com/lobehub/lobe-chat/issues/11322) ([8586fd4](https://github.com/lobehub/lobe-chat/commit/8586fd4))
|
|
62
|
+
|
|
63
|
+
</details>
|
|
64
|
+
|
|
65
|
+
<div align="right">
|
|
66
|
+
|
|
67
|
+
[](#readme-top)
|
|
68
|
+
|
|
69
|
+
</div>
|
|
70
|
+
|
|
5
71
|
## [Version 2.0.0-next.236](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.235...v2.0.0-next.236)
|
|
6
72
|
|
|
7
73
|
<sup>Released on **2026-01-08**</sup>
|
package/changelog/v1.json
CHANGED
|
@@ -1,4 +1,21 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"children": {
|
|
4
|
+
"features": [
|
|
5
|
+
"Change the klavis Linear to LobeHub oauth Linear."
|
|
6
|
+
],
|
|
7
|
+
"fixes": [
|
|
8
|
+
"Topic renaming input focus issue in context menu."
|
|
9
|
+
]
|
|
10
|
+
},
|
|
11
|
+
"date": "2026-01-08",
|
|
12
|
+
"version": "2.0.0-next.238"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"children": {},
|
|
16
|
+
"date": "2026-01-08",
|
|
17
|
+
"version": "2.0.0-next.237"
|
|
18
|
+
},
|
|
2
19
|
{
|
|
3
20
|
"children": {},
|
|
4
21
|
"date": "2026-01-08",
|
package/locales/en-US/oauth.json
CHANGED
|
@@ -32,5 +32,6 @@
|
|
|
32
32
|
"login.title": "Login to {{clientName}}",
|
|
33
33
|
"login.userWelcome": "Welcome back, ",
|
|
34
34
|
"success.subTitle": "You have successfully authorized the application to access your account. You may now close this page.",
|
|
35
|
+
"success.subTitleWithCountdown": "Authorization successful. Auto-closing in {{countdown}}s...",
|
|
35
36
|
"success.title": "Authorization Successful"
|
|
36
37
|
}
|
package/locales/zh-CN/oauth.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.238",
|
|
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",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IconType, SiCaldotcom, SiGithub
|
|
1
|
+
import { IconType, SiCaldotcom, SiGithub } from '@icons-pack/react-simple-icons';
|
|
2
2
|
import { Klavis } from 'klavis';
|
|
3
3
|
|
|
4
4
|
export interface KlavisServerType {
|
|
@@ -40,12 +40,6 @@ export const KLAVIS_SERVER_TYPES: KlavisServerType[] = [
|
|
|
40
40
|
label: 'Airtable',
|
|
41
41
|
serverName: Klavis.McpServerName.Airtable,
|
|
42
42
|
},
|
|
43
|
-
{
|
|
44
|
-
icon: SiLinear,
|
|
45
|
-
identifier: 'linear',
|
|
46
|
-
label: 'Linear',
|
|
47
|
-
serverName: Klavis.McpServerName.Linear,
|
|
48
|
-
},
|
|
49
43
|
{
|
|
50
44
|
icon: 'https://hub-apac-1.lobeobjects.space/assets/logos/googlesheets.svg',
|
|
51
45
|
identifier: 'google-sheets',
|
|
@@ -9,8 +9,10 @@ export interface UserOnboarding {
|
|
|
9
9
|
version: number;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
export const MAX_ONBOARDING_STEPS = 5;
|
|
13
|
+
|
|
12
14
|
export const UserOnboardingSchema = z.object({
|
|
13
|
-
currentStep: z.number().optional(),
|
|
15
|
+
currentStep: z.number().min(1).max(MAX_ONBOARDING_STEPS).optional(),
|
|
14
16
|
finishedAt: z.string().optional(),
|
|
15
17
|
version: z.number(),
|
|
16
18
|
});
|
|
@@ -2,11 +2,48 @@
|
|
|
2
2
|
|
|
3
3
|
import { FluentEmoji, Text } from '@lobehub/ui';
|
|
4
4
|
import { Result } from 'antd';
|
|
5
|
-
import
|
|
5
|
+
import { useSearchParams } from 'next/navigation';
|
|
6
|
+
import React, { memo, useEffect, useState } from 'react';
|
|
6
7
|
import { useTranslation } from 'react-i18next';
|
|
7
8
|
|
|
8
9
|
const SuccessPage = memo(() => {
|
|
9
10
|
const { t } = useTranslation('oauth');
|
|
11
|
+
const searchParams = useSearchParams();
|
|
12
|
+
const [countdown, setCountdown] = useState(3);
|
|
13
|
+
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
// Check if this is a LobeHub Skill OAuth callback
|
|
16
|
+
const provider = searchParams.get('provider');
|
|
17
|
+
|
|
18
|
+
if (provider && window.opener) {
|
|
19
|
+
// Notify parent window about successful OAuth
|
|
20
|
+
window.opener.postMessage(
|
|
21
|
+
{
|
|
22
|
+
provider,
|
|
23
|
+
type: 'LOBEHUB_SKILL_AUTH_SUCCESS',
|
|
24
|
+
},
|
|
25
|
+
window.location.origin,
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
// Start countdown and close window after 3 seconds
|
|
29
|
+
let timeLeft = 3;
|
|
30
|
+
setCountdown(timeLeft);
|
|
31
|
+
|
|
32
|
+
const countdownTimer = setInterval(() => {
|
|
33
|
+
timeLeft -= 1;
|
|
34
|
+
setCountdown(timeLeft);
|
|
35
|
+
|
|
36
|
+
if (timeLeft <= 0) {
|
|
37
|
+
clearInterval(countdownTimer);
|
|
38
|
+
window.close();
|
|
39
|
+
}
|
|
40
|
+
}, 1000);
|
|
41
|
+
|
|
42
|
+
return () => clearInterval(countdownTimer);
|
|
43
|
+
}
|
|
44
|
+
}, [searchParams]);
|
|
45
|
+
|
|
46
|
+
const provider = searchParams.get('provider');
|
|
10
47
|
|
|
11
48
|
return (
|
|
12
49
|
<Result
|
|
@@ -14,7 +51,12 @@ const SuccessPage = memo(() => {
|
|
|
14
51
|
status="success"
|
|
15
52
|
subTitle={
|
|
16
53
|
<Text fontSize={16} type="secondary">
|
|
17
|
-
{
|
|
54
|
+
{provider
|
|
55
|
+
? t('success.subTitleWithCountdown', {
|
|
56
|
+
countdown,
|
|
57
|
+
defaultValue: `You may close this page. Auto-closing in ${countdown}s...`,
|
|
58
|
+
})
|
|
59
|
+
: t('success.subTitle')}
|
|
18
60
|
</Text>
|
|
19
61
|
}
|
|
20
62
|
title={
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { Flexbox } from '@lobehub/ui';
|
|
4
4
|
import { BotPromptIcon } from '@lobehub/ui/icons';
|
|
5
|
-
import { SearchIcon } from 'lucide-react';
|
|
5
|
+
import { MessageSquarePlusIcon, SearchIcon } from 'lucide-react';
|
|
6
6
|
import { usePathname } from 'next/navigation';
|
|
7
7
|
import { memo } from 'react';
|
|
8
8
|
import { useTranslation } from 'react-i18next';
|
|
@@ -11,6 +11,7 @@ import urlJoin from 'url-join';
|
|
|
11
11
|
|
|
12
12
|
import NavItem from '@/features/NavPanel/components/NavItem';
|
|
13
13
|
import { useQueryRoute } from '@/hooks/useQueryRoute';
|
|
14
|
+
import { useActionSWR } from '@/libs/swr';
|
|
14
15
|
import { useAgentStore } from '@/store/agent';
|
|
15
16
|
import { builtinAgentSelectors } from '@/store/agent/selectors';
|
|
16
17
|
import { useChatStore } from '@/store/chat';
|
|
@@ -19,6 +20,7 @@ import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfi
|
|
|
19
20
|
|
|
20
21
|
const Nav = memo(() => {
|
|
21
22
|
const { t } = useTranslation('chat');
|
|
23
|
+
const { t: tTopic } = useTranslation('topic');
|
|
22
24
|
const isInbox = useAgentStore(builtinAgentSelectors.isInboxAgent);
|
|
23
25
|
const params = useParams();
|
|
24
26
|
const agentId = params.aid;
|
|
@@ -29,9 +31,25 @@ const Nav = memo(() => {
|
|
|
29
31
|
const toggleCommandMenu = useGlobalStore((s) => s.toggleCommandMenu);
|
|
30
32
|
const hideProfile = isInbox || !isAgentEditable;
|
|
31
33
|
const switchTopic = useChatStore((s) => s.switchTopic);
|
|
34
|
+
const [openNewTopicOrSaveTopic] = useChatStore((s) => [s.openNewTopicOrSaveTopic]);
|
|
35
|
+
|
|
36
|
+
const { mutate, isValidating } = useActionSWR('openNewTopicOrSaveTopic', openNewTopicOrSaveTopic);
|
|
37
|
+
const handleNewTopic = () => {
|
|
38
|
+
// If in agent sub-route, navigate back to agent chat first
|
|
39
|
+
if (isProfileActive && agentId) {
|
|
40
|
+
router.push(urlJoin('/agent', agentId));
|
|
41
|
+
}
|
|
42
|
+
mutate();
|
|
43
|
+
};
|
|
32
44
|
|
|
33
45
|
return (
|
|
34
46
|
<Flexbox gap={1} paddingInline={4}>
|
|
47
|
+
<NavItem
|
|
48
|
+
icon={MessageSquarePlusIcon}
|
|
49
|
+
loading={isValidating}
|
|
50
|
+
onClick={handleNewTopic}
|
|
51
|
+
title={tTopic('actions.addNewTopic')}
|
|
52
|
+
/>
|
|
35
53
|
{!hideProfile && (
|
|
36
54
|
<NavItem
|
|
37
55
|
active={isProfileActive}
|
|
@@ -4,14 +4,13 @@ import { type PropsWithChildren, memo } from 'react';
|
|
|
4
4
|
|
|
5
5
|
import SideBarHeaderLayout from '@/features/NavPanel/SideBarHeaderLayout';
|
|
6
6
|
|
|
7
|
-
import AddTopicButon from './AddTopicButon';
|
|
8
7
|
import Agent from './Agent';
|
|
9
8
|
import Nav from './Nav';
|
|
10
9
|
|
|
11
10
|
const HeaderInfo = memo<PropsWithChildren>(() => {
|
|
12
11
|
return (
|
|
13
12
|
<>
|
|
14
|
-
<SideBarHeaderLayout left={<Agent />}
|
|
13
|
+
<SideBarHeaderLayout left={<Agent />} />
|
|
15
14
|
<Nav />
|
|
16
15
|
</>
|
|
17
16
|
);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { Input, Popover } from '@lobehub/ui';
|
|
2
|
-
import {
|
|
1
|
+
import { Input, type InputProps, Popover } from '@lobehub/ui';
|
|
2
|
+
import type { InputRef } from 'antd';
|
|
3
|
+
import { memo, useCallback, useEffect, useRef, useState } from 'react';
|
|
3
4
|
|
|
4
5
|
import { useChatStore } from '@/store/chat';
|
|
5
6
|
|
|
@@ -9,6 +10,18 @@ interface EditingProps {
|
|
|
9
10
|
toggleEditing: (visible?: boolean) => void;
|
|
10
11
|
}
|
|
11
12
|
|
|
13
|
+
function FocusableInput({ ...props }: InputProps) {
|
|
14
|
+
const ref = useRef<InputRef>(null);
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
queueMicrotask(() => {
|
|
17
|
+
if (ref.current) {
|
|
18
|
+
ref.current.input?.focus();
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
}, []);
|
|
22
|
+
return <Input {...props} ref={ref} />;
|
|
23
|
+
}
|
|
24
|
+
|
|
12
25
|
const Editing = memo<EditingProps>(({ id, title, toggleEditing }) => {
|
|
13
26
|
const [newTitle, setNewTitle] = useState(title);
|
|
14
27
|
const [editing, updateTopicTitle] = useChatStore((s) => [
|
|
@@ -41,19 +54,14 @@ const Editing = memo<EditingProps>(({ id, title, toggleEditing }) => {
|
|
|
41
54
|
);
|
|
42
55
|
}
|
|
43
56
|
}
|
|
44
|
-
toggleEditing(false);
|
|
45
57
|
}, [newTitle, title, id, updateTopicTitle, toggleEditing]);
|
|
46
58
|
|
|
47
59
|
return (
|
|
48
60
|
<Popover
|
|
49
61
|
content={
|
|
50
|
-
<
|
|
51
|
-
autoFocus
|
|
62
|
+
<FocusableInput
|
|
52
63
|
defaultValue={title}
|
|
53
|
-
onBlur={
|
|
54
|
-
handleUpdate();
|
|
55
|
-
toggleEditing(false);
|
|
56
|
-
}}
|
|
64
|
+
onBlur={handleUpdate}
|
|
57
65
|
onChange={(e) => setNewTitle(e.target.value)}
|
|
58
66
|
onClick={(e) => e.stopPropagation()}
|
|
59
67
|
onPressEnter={() => {
|
|
@@ -64,6 +72,7 @@ const Editing = memo<EditingProps>(({ id, title, toggleEditing }) => {
|
|
|
64
72
|
}
|
|
65
73
|
onOpenChange={(open) => {
|
|
66
74
|
if (!open) handleUpdate();
|
|
75
|
+
|
|
67
76
|
toggleEditing(open);
|
|
68
77
|
}}
|
|
69
78
|
open={editing}
|
package/src/app/[variants]/(main)/settings/profile/features/KlavisAuthorizationList/index.tsx
CHANGED
|
@@ -4,11 +4,8 @@ import { memo, useCallback, useState } from 'react';
|
|
|
4
4
|
import { useTranslation } from 'react-i18next';
|
|
5
5
|
|
|
6
6
|
import { modal } from '@/components/AntdStaticMethods';
|
|
7
|
-
import { useServerConfigStore } from '@/store/serverConfig';
|
|
8
|
-
import { serverConfigSelectors } from '@/store/serverConfig/selectors';
|
|
9
7
|
import { useToolStore } from '@/store/tool';
|
|
10
|
-
import { type KlavisServer
|
|
11
|
-
import { type ToolStore } from '@/store/tool/store';
|
|
8
|
+
import { type KlavisServer } from '@/store/tool/slices/klavisStore';
|
|
12
9
|
|
|
13
10
|
interface KlavisAuthItemProps {
|
|
14
11
|
server: KlavisServer;
|
|
@@ -54,11 +51,6 @@ const KlavisAuthItem = memo<KlavisAuthItemProps>(({ server }) => {
|
|
|
54
51
|
return <IconComponent size={14} />;
|
|
55
52
|
};
|
|
56
53
|
|
|
57
|
-
// 只显示已连接的服务器
|
|
58
|
-
if (server.status !== KlavisServerStatus.CONNECTED) {
|
|
59
|
-
return null;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
54
|
return (
|
|
63
55
|
<Tag closable onClose={handleRevoke}>
|
|
64
56
|
<Flexbox align="center" gap={4} horizontal style={{ opacity: isRevoking ? 0.5 : 1 }}>
|
|
@@ -69,24 +61,14 @@ const KlavisAuthItem = memo<KlavisAuthItemProps>(({ server }) => {
|
|
|
69
61
|
);
|
|
70
62
|
});
|
|
71
63
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
const servers = useToolStore((s: ToolStore) => s.servers);
|
|
76
|
-
|
|
77
|
-
// 获取已授权的服务器列表
|
|
78
|
-
useFetchUserKlavisServers(enableKlavis);
|
|
79
|
-
|
|
80
|
-
// 只显示已连接的服务器
|
|
81
|
-
const connectedServers = servers.filter((s) => s.status === KlavisServerStatus.CONNECTED);
|
|
82
|
-
|
|
83
|
-
if (!enableKlavis || connectedServers.length === 0) {
|
|
84
|
-
return null;
|
|
85
|
-
}
|
|
64
|
+
interface KlavisAuthorizationListProps {
|
|
65
|
+
servers: KlavisServer[];
|
|
66
|
+
}
|
|
86
67
|
|
|
68
|
+
export const KlavisAuthorizationList = memo<KlavisAuthorizationListProps>(({ servers }) => {
|
|
87
69
|
return (
|
|
88
70
|
<Flexbox gap={8} horizontal wrap="wrap">
|
|
89
|
-
{
|
|
71
|
+
{servers.map((server) => (
|
|
90
72
|
<KlavisAuthItem key={server.identifier} server={server} />
|
|
91
73
|
))}
|
|
92
74
|
</Flexbox>
|
|
@@ -9,6 +9,8 @@ import { useTranslation } from 'react-i18next';
|
|
|
9
9
|
import SettingHeader from '@/app/[variants]/(main)/settings/features/SettingHeader';
|
|
10
10
|
import { useServerConfigStore } from '@/store/serverConfig';
|
|
11
11
|
import { serverConfigSelectors } from '@/store/serverConfig/selectors';
|
|
12
|
+
import { useToolStore } from '@/store/tool';
|
|
13
|
+
import { KlavisServerStatus } from '@/store/tool/slices/klavisStore';
|
|
12
14
|
import { useUserStore } from '@/store/user';
|
|
13
15
|
import { authSelectors, userProfileSelectors } from '@/store/user/selectors';
|
|
14
16
|
|
|
@@ -37,9 +39,21 @@ const ProfileSetting = ({ mobile }: ProfileSettingProps) => {
|
|
|
37
39
|
const isLoadedAuthProviders = useUserStore(authSelectors.isLoadedAuthProviders);
|
|
38
40
|
const fetchAuthProviders = useUserStore((s) => s.fetchAuthProviders);
|
|
39
41
|
const enableKlavis = useServerConfigStore(serverConfigSelectors.enableKlavis);
|
|
42
|
+
const [servers, isServersInit, useFetchUserKlavisServers] = useToolStore((s) => [
|
|
43
|
+
s.servers,
|
|
44
|
+
s.isServersInit,
|
|
45
|
+
s.useFetchUserKlavisServers,
|
|
46
|
+
]);
|
|
47
|
+
const connectedServers = servers.filter((s) => s.status === KlavisServerStatus.CONNECTED);
|
|
48
|
+
|
|
49
|
+
// Fetch Klavis servers
|
|
50
|
+
useFetchUserKlavisServers(enableKlavis);
|
|
40
51
|
|
|
41
52
|
const isLoginWithAuth = isLoginWithNextAuth || isLoginWithBetterAuth;
|
|
42
|
-
const isLoading =
|
|
53
|
+
const isLoading =
|
|
54
|
+
!isUserLoaded ||
|
|
55
|
+
(isLoginWithAuth && !isLoadedAuthProviders) ||
|
|
56
|
+
(enableKlavis && !isServersInit);
|
|
43
57
|
|
|
44
58
|
useEffect(() => {
|
|
45
59
|
if (isLoginWithAuth) {
|
|
@@ -110,11 +124,11 @@ const ProfileSetting = ({ mobile }: ProfileSettingProps) => {
|
|
|
110
124
|
)}
|
|
111
125
|
|
|
112
126
|
{/* Klavis Authorizations Row */}
|
|
113
|
-
{enableKlavis && (
|
|
127
|
+
{enableKlavis && connectedServers.length > 0 && (
|
|
114
128
|
<>
|
|
115
129
|
<Divider style={{ margin: 0 }} />
|
|
116
130
|
<ProfileRow label={t('profile.authorizations.title')} mobile={mobile}>
|
|
117
|
-
<KlavisAuthorizationList />
|
|
131
|
+
<KlavisAuthorizationList servers={connectedServers} />
|
|
118
132
|
</ProfileRow>
|
|
119
133
|
</>
|
|
120
134
|
)}
|
|
@@ -4,7 +4,7 @@ import { SendButton } from '@lobehub/editor/react';
|
|
|
4
4
|
import { Button, Flexbox, Icon, Input } from '@lobehub/ui';
|
|
5
5
|
import { cssVar } from 'antd-style';
|
|
6
6
|
import { SignatureIcon, Undo2Icon } from 'lucide-react';
|
|
7
|
-
import { memo, useState } from 'react';
|
|
7
|
+
import { memo, useCallback, useRef, useState } from 'react';
|
|
8
8
|
import { useTranslation } from 'react-i18next';
|
|
9
9
|
|
|
10
10
|
import { useUserStore } from '@/store/user';
|
|
@@ -23,13 +23,25 @@ const FullNameStep = memo<FullNameStepProps>(({ onBack, onNext }) => {
|
|
|
23
23
|
const updateFullName = useUserStore((s) => s.updateFullName);
|
|
24
24
|
|
|
25
25
|
const [value, setValue] = useState(existingFullName || '');
|
|
26
|
+
const [isNavigating, setIsNavigating] = useState(false);
|
|
27
|
+
const isNavigatingRef = useRef(false);
|
|
26
28
|
|
|
27
|
-
const handleNext = () => {
|
|
29
|
+
const handleNext = useCallback(() => {
|
|
30
|
+
if (isNavigatingRef.current) return;
|
|
31
|
+
isNavigatingRef.current = true;
|
|
32
|
+
setIsNavigating(true);
|
|
28
33
|
if (value.trim()) {
|
|
29
34
|
updateFullName(value.trim());
|
|
30
35
|
}
|
|
31
36
|
onNext();
|
|
32
|
-
};
|
|
37
|
+
}, [value, updateFullName, onNext]);
|
|
38
|
+
|
|
39
|
+
const handleBack = useCallback(() => {
|
|
40
|
+
if (isNavigatingRef.current) return;
|
|
41
|
+
isNavigatingRef.current = true;
|
|
42
|
+
setIsNavigating(true);
|
|
43
|
+
onBack();
|
|
44
|
+
}, [onBack]);
|
|
33
45
|
|
|
34
46
|
return (
|
|
35
47
|
<Flexbox gap={16}>
|
|
@@ -59,7 +71,7 @@ const FullNameStep = memo<FullNameStepProps>(({ onBack, onNext }) => {
|
|
|
59
71
|
}}
|
|
60
72
|
suffix={
|
|
61
73
|
<SendButton
|
|
62
|
-
disabled={!value?.trim()}
|
|
74
|
+
disabled={!value?.trim() || isNavigating}
|
|
63
75
|
onClick={handleNext}
|
|
64
76
|
style={{
|
|
65
77
|
zoom: 1.5,
|
|
@@ -73,8 +85,9 @@ const FullNameStep = memo<FullNameStepProps>(({ onBack, onNext }) => {
|
|
|
73
85
|
</Flexbox>
|
|
74
86
|
<Flexbox horizontal justify={'flex-start'} style={{ marginTop: 32 }}>
|
|
75
87
|
<Button
|
|
88
|
+
disabled={isNavigating}
|
|
76
89
|
icon={Undo2Icon}
|
|
77
|
-
onClick={
|
|
90
|
+
onClick={handleBack}
|
|
78
91
|
style={{
|
|
79
92
|
color: cssVar.colorTextDescription,
|
|
80
93
|
}}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { Block, Button, Flexbox, Icon, Input, Text } from '@lobehub/ui';
|
|
4
4
|
import { cssVar } from 'antd-style';
|
|
5
5
|
import { BriefcaseIcon, Undo2Icon } from 'lucide-react';
|
|
6
|
-
import { memo, useCallback, useMemo, useState } from 'react';
|
|
6
|
+
import { memo, useCallback, useMemo, useRef, useState } from 'react';
|
|
7
7
|
import { useTranslation } from 'react-i18next';
|
|
8
8
|
|
|
9
9
|
import { useUserStore } from '@/store/user';
|
|
@@ -25,6 +25,8 @@ const InterestsStep = memo<InterestsStepProps>(({ onBack, onNext }) => {
|
|
|
25
25
|
const [selectedInterests, setSelectedInterests] = useState<string[]>(existingInterests);
|
|
26
26
|
const [customInput, setCustomInput] = useState('');
|
|
27
27
|
const [showCustomInput, setShowCustomInput] = useState(false);
|
|
28
|
+
const [isNavigating, setIsNavigating] = useState(false);
|
|
29
|
+
const isNavigatingRef = useRef(false);
|
|
28
30
|
|
|
29
31
|
const areas = useMemo(
|
|
30
32
|
() =>
|
|
@@ -49,7 +51,11 @@ const InterestsStep = memo<InterestsStepProps>(({ onBack, onNext }) => {
|
|
|
49
51
|
}
|
|
50
52
|
}, [customInput, selectedInterests]);
|
|
51
53
|
|
|
52
|
-
const handleNext = useCallback(
|
|
54
|
+
const handleNext = useCallback(() => {
|
|
55
|
+
if (isNavigatingRef.current) return;
|
|
56
|
+
isNavigatingRef.current = true;
|
|
57
|
+
setIsNavigating(true);
|
|
58
|
+
|
|
53
59
|
// Include custom input value if "other" is active and has content
|
|
54
60
|
const finalInterests = [...selectedInterests];
|
|
55
61
|
const trimmedCustom = customInput.trim();
|
|
@@ -60,12 +66,17 @@ const InterestsStep = memo<InterestsStepProps>(({ onBack, onNext }) => {
|
|
|
60
66
|
// Deduplicate
|
|
61
67
|
const uniqueInterests = [...new Set(finalInterests)];
|
|
62
68
|
|
|
63
|
-
|
|
64
|
-
await updateInterests(uniqueInterests);
|
|
65
|
-
}
|
|
69
|
+
updateInterests(uniqueInterests);
|
|
66
70
|
onNext();
|
|
67
71
|
}, [selectedInterests, customInput, showCustomInput, updateInterests, onNext]);
|
|
68
72
|
|
|
73
|
+
const handleBack = useCallback(() => {
|
|
74
|
+
if (isNavigatingRef.current) return;
|
|
75
|
+
isNavigatingRef.current = true;
|
|
76
|
+
setIsNavigating(true);
|
|
77
|
+
onBack();
|
|
78
|
+
}, [onBack]);
|
|
79
|
+
|
|
69
80
|
return (
|
|
70
81
|
<Flexbox gap={16}>
|
|
71
82
|
<LobeMessage
|
|
@@ -138,14 +149,15 @@ const InterestsStep = memo<InterestsStepProps>(({ onBack, onNext }) => {
|
|
|
138
149
|
)}
|
|
139
150
|
<Flexbox horizontal justify={'space-between'} style={{ marginTop: 32 }}>
|
|
140
151
|
<Button
|
|
152
|
+
disabled={isNavigating}
|
|
141
153
|
icon={Undo2Icon}
|
|
142
|
-
onClick={
|
|
154
|
+
onClick={handleBack}
|
|
143
155
|
style={{ color: cssVar.colorTextDescription }}
|
|
144
156
|
type={'text'}
|
|
145
157
|
>
|
|
146
158
|
{t('back')}
|
|
147
159
|
</Button>
|
|
148
|
-
<Button onClick={handleNext} type={'primary'}>
|
|
160
|
+
<Button disabled={isNavigating} onClick={handleNext} type={'primary'}>
|
|
149
161
|
{t('next')}
|
|
150
162
|
</Button>
|
|
151
163
|
</Flexbox>
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { Button, Flexbox, Text } from '@lobehub/ui';
|
|
4
4
|
import { cssVar } from 'antd-style';
|
|
5
5
|
import { Undo2Icon } from 'lucide-react';
|
|
6
|
-
import { memo } from 'react';
|
|
6
|
+
import { memo, useCallback, useRef, useState } from 'react';
|
|
7
7
|
import { useTranslation } from 'react-i18next';
|
|
8
8
|
import { useNavigate } from 'react-router-dom';
|
|
9
9
|
|
|
@@ -34,14 +34,30 @@ const ProSettingsStep = memo<ProSettingsStepProps>(({ onBack }) => {
|
|
|
34
34
|
(s) => settingsSelectors.currentSettings(s).defaultAgent?.config,
|
|
35
35
|
);
|
|
36
36
|
|
|
37
|
-
const
|
|
37
|
+
const [isNavigating, setIsNavigating] = useState(false);
|
|
38
|
+
const isNavigatingRef = useRef(false);
|
|
39
|
+
|
|
40
|
+
const handleFinish = useCallback(() => {
|
|
41
|
+
if (isNavigatingRef.current) return;
|
|
42
|
+
isNavigatingRef.current = true;
|
|
43
|
+
setIsNavigating(true);
|
|
38
44
|
finishOnboarding();
|
|
39
45
|
navigate('/');
|
|
40
|
-
};
|
|
46
|
+
}, [finishOnboarding, navigate]);
|
|
47
|
+
|
|
48
|
+
const handleBack = useCallback(() => {
|
|
49
|
+
if (isNavigatingRef.current) return;
|
|
50
|
+
isNavigatingRef.current = true;
|
|
51
|
+
setIsNavigating(true);
|
|
52
|
+
onBack();
|
|
53
|
+
}, [onBack]);
|
|
41
54
|
|
|
42
|
-
const handleModelChange = (
|
|
43
|
-
|
|
44
|
-
|
|
55
|
+
const handleModelChange = useCallback(
|
|
56
|
+
({ model, provider }: { model: string; provider: string }) => {
|
|
57
|
+
updateDefaultModel(model, provider);
|
|
58
|
+
},
|
|
59
|
+
[updateDefaultModel],
|
|
60
|
+
);
|
|
45
61
|
|
|
46
62
|
return (
|
|
47
63
|
<Flexbox gap={16}>
|
|
@@ -70,8 +86,9 @@ const ProSettingsStep = memo<ProSettingsStepProps>(({ onBack }) => {
|
|
|
70
86
|
|
|
71
87
|
<Flexbox align={'center'} horizontal justify={'space-between'} style={{ marginTop: 16 }}>
|
|
72
88
|
<Button
|
|
89
|
+
disabled={isNavigating}
|
|
73
90
|
icon={Undo2Icon}
|
|
74
|
-
onClick={
|
|
91
|
+
onClick={handleBack}
|
|
75
92
|
style={{
|
|
76
93
|
color: cssVar.colorTextDescription,
|
|
77
94
|
}}
|
|
@@ -79,7 +96,12 @@ const ProSettingsStep = memo<ProSettingsStepProps>(({ onBack }) => {
|
|
|
79
96
|
>
|
|
80
97
|
{t('back')}
|
|
81
98
|
</Button>
|
|
82
|
-
<Button
|
|
99
|
+
<Button
|
|
100
|
+
disabled={isNavigating}
|
|
101
|
+
onClick={handleFinish}
|
|
102
|
+
style={{ minWidth: 120 }}
|
|
103
|
+
type="primary"
|
|
104
|
+
>
|
|
83
105
|
{t('finish')}
|
|
84
106
|
</Button>
|
|
85
107
|
</Flexbox>
|