@lobehub/lobehub 2.0.0-next.285 → 2.0.0-next.286
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/changelog/v1.json +9 -0
- package/package.json +1 -1
- package/packages/business/const/src/index.ts +0 -3
- package/src/app/[variants]/(main)/agent/features/Conversation/AgentWelcome/AddButton.tsx +2 -1
- package/src/app/[variants]/(main)/agent/features/Conversation/Header/ShareButton/index.tsx +5 -3
- package/src/app/[variants]/(main)/community/(detail)/provider/features/Details/Nav.tsx +27 -18
- package/src/app/[variants]/(main)/group/features/Conversation/Header/ShareButton/index.tsx +5 -3
- package/src/app/[variants]/onboarding/_layout/style.ts +10 -20
- package/src/store/user/slices/settings/action.test.ts +25 -0
- package/src/store/user/slices/settings/action.ts +11 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
## [Version 2.0.0-next.286](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.285...v2.0.0-next.286)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2026-01-14**</sup>
|
|
8
|
+
|
|
9
|
+
#### 🐛 Bug Fixes
|
|
10
|
+
|
|
11
|
+
- **misc**: Prevent auto navigation to profile when clicking topic.
|
|
12
|
+
|
|
13
|
+
<br/>
|
|
14
|
+
|
|
15
|
+
<details>
|
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
17
|
+
|
|
18
|
+
#### What's fixed
|
|
19
|
+
|
|
20
|
+
- **misc**: Prevent auto navigation to profile when clicking topic, closes [#11500](https://github.com/lobehub/lobe-chat/issues/11500) ([1e03005](https://github.com/lobehub/lobe-chat/commit/1e03005))
|
|
21
|
+
|
|
22
|
+
</details>
|
|
23
|
+
|
|
24
|
+
<div align="right">
|
|
25
|
+
|
|
26
|
+
[](#readme-top)
|
|
27
|
+
|
|
28
|
+
</div>
|
|
29
|
+
|
|
5
30
|
## [Version 2.0.0-next.285](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.284...v2.0.0-next.285)
|
|
6
31
|
|
|
7
32
|
<sup>Released on **2026-01-14**</sup>
|
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.286",
|
|
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",
|
|
@@ -9,7 +9,8 @@ import { useAgentStore } from '@/store/agent';
|
|
|
9
9
|
const AddButton = memo(() => {
|
|
10
10
|
const navigate = useNavigate();
|
|
11
11
|
const createAgent = useAgentStore((s) => s.createAgent);
|
|
12
|
-
|
|
12
|
+
// Use a unique SWR key to avoid conflicts with useCreateMenuItems which uses 'agent.createAgent'
|
|
13
|
+
const { mutate, isValidating } = useActionSWR('agent.createAgentFromWelcome', async () => {
|
|
13
14
|
const result = await createAgent({});
|
|
14
15
|
navigate(`/agent/${result.agentId}/profile`);
|
|
15
16
|
return result;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { ENABLE_TOPIC_LINK_SHARE } from '@lobechat/business-const';
|
|
4
3
|
import { ActionIcon } from '@lobehub/ui';
|
|
5
4
|
import { Share2 } from 'lucide-react';
|
|
6
5
|
import dynamic from 'next/dynamic';
|
|
@@ -10,6 +9,8 @@ import { useTranslation } from 'react-i18next';
|
|
|
10
9
|
import { DESKTOP_HEADER_ICON_SIZE, MOBILE_HEADER_ICON_SIZE } from '@/const/layoutTokens';
|
|
11
10
|
import { useWorkspaceModal } from '@/hooks/useWorkspaceModal';
|
|
12
11
|
import { useChatStore } from '@/store/chat';
|
|
12
|
+
import { useServerConfigStore } from '@/store/serverConfig';
|
|
13
|
+
import { serverConfigSelectors } from '@/store/serverConfig/selectors';
|
|
13
14
|
|
|
14
15
|
const ShareModal = dynamic(() => import('@/features/ShareModal'));
|
|
15
16
|
const SharePopover = dynamic(() => import('@/features/SharePopover'));
|
|
@@ -24,6 +25,7 @@ const ShareButton = memo<ShareButtonProps>(({ mobile, setOpen, open }) => {
|
|
|
24
25
|
const [isModalOpen, setIsModalOpen] = useWorkspaceModal(open, setOpen);
|
|
25
26
|
const { t } = useTranslation('common');
|
|
26
27
|
const activeTopicId = useChatStore((s) => s.activeTopicId);
|
|
28
|
+
const enableTopicLinkShare = useServerConfigStore(serverConfigSelectors.enableBusinessFeatures);
|
|
27
29
|
|
|
28
30
|
// Hide share button when no topic exists (no messages sent yet)
|
|
29
31
|
if (!activeTopicId) return null;
|
|
@@ -31,7 +33,7 @@ const ShareButton = memo<ShareButtonProps>(({ mobile, setOpen, open }) => {
|
|
|
31
33
|
const iconButton = (
|
|
32
34
|
<ActionIcon
|
|
33
35
|
icon={Share2}
|
|
34
|
-
onClick={
|
|
36
|
+
onClick={enableTopicLinkShare ? undefined : () => setIsModalOpen(true)}
|
|
35
37
|
size={mobile ? MOBILE_HEADER_ICON_SIZE : DESKTOP_HEADER_ICON_SIZE}
|
|
36
38
|
title={t('share')}
|
|
37
39
|
tooltipProps={{
|
|
@@ -42,7 +44,7 @@ const ShareButton = memo<ShareButtonProps>(({ mobile, setOpen, open }) => {
|
|
|
42
44
|
|
|
43
45
|
return (
|
|
44
46
|
<>
|
|
45
|
-
{
|
|
47
|
+
{enableTopicLinkShare ? (
|
|
46
48
|
<SharePopover onOpenModal={() => setIsModalOpen(true)}>{iconButton}</SharePopover>
|
|
47
49
|
) : (
|
|
48
50
|
iconButton
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { SOCIAL_URL } from '@lobechat/business-const';
|
|
3
|
+
import { BRANDING_PROVIDER, SOCIAL_URL } from '@lobechat/business-const';
|
|
4
4
|
import { Flexbox, Icon, Tabs } from '@lobehub/ui';
|
|
5
5
|
import { createStaticStyles } from 'antd-style';
|
|
6
6
|
import { BookOpenIcon, BrainCircuitIcon, ListIcon } from 'lucide-react';
|
|
@@ -38,27 +38,36 @@ const Nav = memo<NavProps>(({ mobile, setActiveTab, activeTab = ProviderNavKey.O
|
|
|
38
38
|
const { t } = useTranslation('discover');
|
|
39
39
|
const { identifier } = useDetailContext();
|
|
40
40
|
|
|
41
|
+
// Hide Guide tab for branding provider as it doesn't have integration docs
|
|
42
|
+
const showGuideTab = identifier !== BRANDING_PROVIDER;
|
|
43
|
+
|
|
44
|
+
const items = [
|
|
45
|
+
{
|
|
46
|
+
icon: <Icon icon={BookOpenIcon} size={16} />,
|
|
47
|
+
key: ProviderNavKey.Overview,
|
|
48
|
+
label: t('providers.details.overview.title'),
|
|
49
|
+
},
|
|
50
|
+
...(showGuideTab
|
|
51
|
+
? [
|
|
52
|
+
{
|
|
53
|
+
icon: <Icon icon={BrainCircuitIcon} size={16} />,
|
|
54
|
+
key: ProviderNavKey.Guide,
|
|
55
|
+
label: t('providers.details.guide.title'),
|
|
56
|
+
},
|
|
57
|
+
]
|
|
58
|
+
: []),
|
|
59
|
+
{
|
|
60
|
+
icon: <Icon icon={ListIcon} size={16} />,
|
|
61
|
+
key: ProviderNavKey.Related,
|
|
62
|
+
label: t('providers.details.related.title'),
|
|
63
|
+
},
|
|
64
|
+
];
|
|
65
|
+
|
|
41
66
|
const nav = (
|
|
42
67
|
<Tabs
|
|
43
68
|
activeKey={activeTab}
|
|
44
69
|
compact={mobile}
|
|
45
|
-
items={
|
|
46
|
-
{
|
|
47
|
-
icon: <Icon icon={BookOpenIcon} size={16} />,
|
|
48
|
-
key: ProviderNavKey.Overview,
|
|
49
|
-
label: t('providers.details.overview.title'),
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
icon: <Icon icon={BrainCircuitIcon} size={16} />,
|
|
53
|
-
key: ProviderNavKey.Guide,
|
|
54
|
-
label: t('providers.details.guide.title'),
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
icon: <Icon icon={ListIcon} size={16} />,
|
|
58
|
-
key: ProviderNavKey.Related,
|
|
59
|
-
label: t('providers.details.related.title'),
|
|
60
|
-
},
|
|
61
|
-
]}
|
|
70
|
+
items={items}
|
|
62
71
|
onChange={(key) => setActiveTab?.(key as ProviderNavKey)}
|
|
63
72
|
/>
|
|
64
73
|
);
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { ENABLE_TOPIC_LINK_SHARE } from '@lobechat/business-const';
|
|
4
3
|
import { ActionIcon } from '@lobehub/ui';
|
|
5
4
|
import { Share2 } from 'lucide-react';
|
|
6
5
|
import dynamic from 'next/dynamic';
|
|
@@ -10,6 +9,8 @@ import { useTranslation } from 'react-i18next';
|
|
|
10
9
|
import { DESKTOP_HEADER_ICON_SIZE, MOBILE_HEADER_ICON_SIZE } from '@/const/layoutTokens';
|
|
11
10
|
import { useWorkspaceModal } from '@/hooks/useWorkspaceModal';
|
|
12
11
|
import { useChatStore } from '@/store/chat';
|
|
12
|
+
import { useServerConfigStore } from '@/store/serverConfig';
|
|
13
|
+
import { serverConfigSelectors } from '@/store/serverConfig/selectors';
|
|
13
14
|
|
|
14
15
|
const ShareModal = dynamic(() => import('@/features/ShareModal'));
|
|
15
16
|
const SharePopover = dynamic(() => import('@/features/SharePopover'));
|
|
@@ -24,6 +25,7 @@ const ShareButton = memo<ShareButtonProps>(({ mobile, setOpen, open }) => {
|
|
|
24
25
|
const [isModalOpen, setIsModalOpen] = useWorkspaceModal(open, setOpen);
|
|
25
26
|
const { t } = useTranslation('common');
|
|
26
27
|
const activeTopicId = useChatStore((s) => s.activeTopicId);
|
|
28
|
+
const enableTopicLinkShare = useServerConfigStore(serverConfigSelectors.enableBusinessFeatures);
|
|
27
29
|
|
|
28
30
|
// Hide share button when no topic exists (no messages sent yet)
|
|
29
31
|
if (!activeTopicId) return null;
|
|
@@ -31,7 +33,7 @@ const ShareButton = memo<ShareButtonProps>(({ mobile, setOpen, open }) => {
|
|
|
31
33
|
const iconButton = (
|
|
32
34
|
<ActionIcon
|
|
33
35
|
icon={Share2}
|
|
34
|
-
onClick={
|
|
36
|
+
onClick={enableTopicLinkShare ? undefined : () => setIsModalOpen(true)}
|
|
35
37
|
size={mobile ? MOBILE_HEADER_ICON_SIZE : DESKTOP_HEADER_ICON_SIZE}
|
|
36
38
|
title={t('share')}
|
|
37
39
|
tooltipProps={{
|
|
@@ -42,7 +44,7 @@ const ShareButton = memo<ShareButtonProps>(({ mobile, setOpen, open }) => {
|
|
|
42
44
|
|
|
43
45
|
return (
|
|
44
46
|
<>
|
|
45
|
-
{
|
|
47
|
+
{enableTopicLinkShare ? (
|
|
46
48
|
<SharePopover onOpenModal={() => setIsModalOpen(true)}>{iconButton}</SharePopover>
|
|
47
49
|
) : (
|
|
48
50
|
iconButton
|
|
@@ -1,21 +1,16 @@
|
|
|
1
1
|
import { createStaticStyles } from 'antd-style';
|
|
2
2
|
|
|
3
3
|
export const styles = createStaticStyles(({ css, cssVar }) => ({
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
// Divider 样式
|
|
7
|
-
divider: css`
|
|
4
|
+
// Divider 样式
|
|
5
|
+
divider: css`
|
|
8
6
|
height: 24px;
|
|
9
7
|
`,
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
// 内层容器 - 深色模式
|
|
15
|
-
innerContainerDark: css`
|
|
9
|
+
// 内层容器 - 深色模式
|
|
10
|
+
innerContainerDark: css`
|
|
16
11
|
position: relative;
|
|
17
12
|
|
|
18
|
-
overflow: hidden;
|
|
13
|
+
overflow: hidden auto;
|
|
19
14
|
|
|
20
15
|
border: 1px solid ${cssVar.colorBorderSecondary};
|
|
21
16
|
border-radius: ${cssVar.borderRadius};
|
|
@@ -23,14 +18,11 @@ innerContainerDark: css`
|
|
|
23
18
|
background: ${cssVar.colorBgContainer};
|
|
24
19
|
`,
|
|
25
20
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
// 内层容器 - 浅色模式
|
|
30
|
-
innerContainerLight: css`
|
|
21
|
+
// 内层容器 - 浅色模式
|
|
22
|
+
innerContainerLight: css`
|
|
31
23
|
position: relative;
|
|
32
24
|
|
|
33
|
-
overflow: hidden;
|
|
25
|
+
overflow: hidden auto;
|
|
34
26
|
|
|
35
27
|
border: 1px solid ${cssVar.colorBorder};
|
|
36
28
|
border-radius: ${cssVar.borderRadius};
|
|
@@ -38,10 +30,8 @@ innerContainerLight: css`
|
|
|
38
30
|
background: ${cssVar.colorBgContainer};
|
|
39
31
|
`,
|
|
40
32
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
// 外层容器
|
|
44
|
-
outerContainer: css`
|
|
33
|
+
// 外层容器
|
|
34
|
+
outerContainer: css`
|
|
45
35
|
position: relative;
|
|
46
36
|
`,
|
|
47
37
|
}));
|
|
@@ -84,6 +84,31 @@ describe('SettingsAction', () => {
|
|
|
84
84
|
expect.any(AbortSignal),
|
|
85
85
|
);
|
|
86
86
|
});
|
|
87
|
+
|
|
88
|
+
it('should include field in diffs when user resets it to default value', async () => {
|
|
89
|
+
const { result } = renderHook(() => useUserStore());
|
|
90
|
+
|
|
91
|
+
// First, set memory.enabled to false (non-default value)
|
|
92
|
+
await act(async () => {
|
|
93
|
+
await result.current.setSettings({ memory: { enabled: false } });
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
expect(userService.updateUserSettings).toHaveBeenLastCalledWith(
|
|
97
|
+
expect.objectContaining({ memory: { enabled: false } }),
|
|
98
|
+
expect.any(AbortSignal),
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
// Then, reset memory.enabled back to true (default value)
|
|
102
|
+
// This should still include memory in the diffs to override the previously saved value
|
|
103
|
+
await act(async () => {
|
|
104
|
+
await result.current.setSettings({ memory: { enabled: true } });
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
expect(userService.updateUserSettings).toHaveBeenLastCalledWith(
|
|
108
|
+
expect.objectContaining({ memory: { enabled: true } }),
|
|
109
|
+
expect.any(AbortSignal),
|
|
110
|
+
);
|
|
111
|
+
});
|
|
87
112
|
});
|
|
88
113
|
|
|
89
114
|
describe('updateDefaultAgent', () => {
|
|
@@ -103,6 +103,17 @@ export const createSettingsSlice: StateCreator<
|
|
|
103
103
|
if (isEqual(prevSetting, nextSettings)) return;
|
|
104
104
|
|
|
105
105
|
const diffs = difference(nextSettings, defaultSettings);
|
|
106
|
+
|
|
107
|
+
// When user resets a field to default value, we need to explicitly include it in diffs
|
|
108
|
+
// to override the previously saved non-default value in the backend
|
|
109
|
+
const changedFields = difference(nextSettings, prevSetting);
|
|
110
|
+
for (const key of Object.keys(changedFields)) {
|
|
111
|
+
// Only handle fields that were previously set by user (exist in prevSetting)
|
|
112
|
+
if (key in prevSetting && !(key in diffs)) {
|
|
113
|
+
(diffs as any)[key] = (nextSettings as any)[key];
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
106
117
|
set({ settings: diffs }, false, 'optimistic_updateSettings');
|
|
107
118
|
|
|
108
119
|
const abortController = get().internal_createSignal();
|