@lobehub/lobehub 2.0.0-next.280 → 2.0.0-next.282
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/changelog/v1.json +18 -0
- package/e2e/src/steps/agent/conversation-mgmt.steps.ts +47 -3
- package/package.json +1 -1
- package/packages/builtin-tool-group-agent-builder/src/client/Render/BatchCreateAgents.tsx +19 -3
- package/packages/builtin-tool-group-agent-builder/src/client/Streaming/BatchCreateAgents/index.tsx +19 -4
- package/packages/builtin-tool-group-agent-builder/src/client/Streaming/UpdateAgentPrompt/index.tsx +3 -13
- package/packages/builtin-tool-group-agent-builder/src/client/Streaming/UpdateGroupPrompt/index.tsx +1 -1
- package/packages/builtin-tool-group-agent-builder/src/systemRole.ts +83 -121
- package/packages/builtin-tool-local-system/src/client/Inspector/ReadLocalFile/index.tsx +20 -2
- package/packages/observability-otel/src/node.ts +40 -3
- package/src/app/[variants]/(main)/agent/_layout/Sidebar/Topic/List/Item/index.tsx +9 -1
- package/src/app/[variants]/(main)/agent/features/Conversation/ConversationArea.tsx +1 -28
- package/src/app/[variants]/(main)/community/(detail)/features/MakedownRender.tsx +3 -3
- package/src/app/[variants]/(main)/group/_layout/Sidebar/GroupConfig/GroupMember.tsx +8 -1
- package/src/app/[variants]/(main)/group/_layout/Sidebar/GroupConfig/Header/Avatar.tsx +2 -13
- package/src/app/[variants]/(main)/group/_layout/Sidebar/Header/Agent/index.tsx +3 -4
- package/src/app/[variants]/(main)/group/_layout/Sidebar/Topic/List/Item/index.tsx +17 -8
- package/src/app/[variants]/(main)/group/features/Conversation/ConversationArea.tsx +1 -29
- package/src/app/[variants]/(main)/group/features/Conversation/Header/ShareButton/index.tsx +0 -2
- package/src/app/[variants]/(main)/group/features/GroupAvatar.tsx +17 -9
- package/src/app/[variants]/(main)/group/profile/features/AgentBuilder/TopicSelector.tsx +8 -5
- package/src/app/[variants]/(main)/group/profile/features/Header/ChromeTabs/index.tsx +20 -2
- package/src/app/[variants]/(main)/group/profile/features/MemberProfile/AgentTool.tsx +4 -2
- package/src/app/[variants]/(main)/group/profile/features/ProfileHydration.tsx +5 -25
- package/src/features/AgentGroupAvatar/index.tsx +38 -0
- package/src/features/Conversation/Messages/Supervisor/index.tsx +8 -2
- package/src/features/Conversation/Messages/User/useMarkdown.tsx +1 -2
- package/src/features/EditorModal/EditorCanvas.tsx +62 -0
- package/src/features/EditorModal/TextArea.tsx +30 -0
- package/src/features/EditorModal/Typobar.tsx +139 -0
- package/src/features/EditorModal/index.tsx +18 -8
- package/src/features/NavPanel/components/EmptyNavItem.tsx +2 -2
- package/src/features/NavPanel/components/NavItem.tsx +27 -3
- package/src/features/ToolTag/index.tsx +167 -0
- package/src/server/routers/lambda/topic.ts +8 -1
- package/src/services/chat/mecha/contextEngineering.test.ts +1 -1
- package/src/services/chat/mecha/contextEngineering.ts +3 -4
- package/src/services/chat/mecha/memoryManager.ts +9 -38
- package/src/store/agentGroup/initialState.ts +1 -1
- package/src/store/agentGroup/slices/lifecycle.ts +15 -2
- package/src/app/[variants]/(main)/group/_layout/Sidebar/Topic/hooks/useTopicNavigation.ts +0 -49
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { ActionIcon, Flexbox, Icon, Skeleton, Tag } from '@lobehub/ui';
|
|
2
2
|
import { cssVar } from 'antd-style';
|
|
3
3
|
import { MessageSquareDashed, Star } from 'lucide-react';
|
|
4
|
-
import { Suspense, memo, useCallback } from 'react';
|
|
4
|
+
import { Suspense, memo, useCallback, useMemo } from 'react';
|
|
5
5
|
import { useTranslation } from 'react-i18next';
|
|
6
|
+
import urlJoin from 'url-join';
|
|
6
7
|
|
|
7
8
|
import { isDesktop } from '@/const/version';
|
|
8
9
|
import NavItem from '@/features/NavPanel/components/NavItem';
|
|
@@ -29,6 +30,12 @@ const TopicItem = memo<TopicItemProps>(({ id, title, fav, active, threadId }) =>
|
|
|
29
30
|
const openTopicInNewWindow = useGlobalStore((s) => s.openTopicInNewWindow);
|
|
30
31
|
const activeAgentId = useAgentStore((s) => s.activeAgentId);
|
|
31
32
|
|
|
33
|
+
// Construct href for cmd+click support
|
|
34
|
+
const href = useMemo(() => {
|
|
35
|
+
if (!activeAgentId || !id) return undefined;
|
|
36
|
+
return urlJoin('/chat', `?agent=${activeAgentId}&topic=${id}`);
|
|
37
|
+
}, [activeAgentId, id]);
|
|
38
|
+
|
|
32
39
|
const [editing, isLoading] = useChatStore((s) => [
|
|
33
40
|
id ? s.topicRenamingId === id : false,
|
|
34
41
|
id ? s.topicLoadingIds.includes(id) : false,
|
|
@@ -97,6 +104,7 @@ const TopicItem = memo<TopicItemProps>(({ id, title, fav, active, threadId }) =>
|
|
|
97
104
|
active={active && !threadId && !isInAgentSubRoute}
|
|
98
105
|
contextMenuItems={dropdownMenu}
|
|
99
106
|
disabled={editing}
|
|
107
|
+
href={href}
|
|
100
108
|
icon={
|
|
101
109
|
<ActionIcon
|
|
102
110
|
color={fav ? cssVar.colorWarning : undefined}
|
|
@@ -1,20 +1,14 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { Flexbox } from '@lobehub/ui';
|
|
4
|
-
import { Suspense, memo,
|
|
4
|
+
import { Suspense, memo, useMemo } from 'react';
|
|
5
5
|
|
|
6
6
|
import ChatMiniMap from '@/features/ChatMiniMap';
|
|
7
7
|
import { ChatList, ConversationProvider, TodoProgress } from '@/features/Conversation';
|
|
8
8
|
import ZenModeToast from '@/features/ZenModeToast';
|
|
9
9
|
import { useOperationState } from '@/hooks/useOperationState';
|
|
10
|
-
import { useAgentStore } from '@/store/agent';
|
|
11
|
-
import { agentSelectors } from '@/store/agent/selectors';
|
|
12
10
|
import { useChatStore } from '@/store/chat';
|
|
13
|
-
import { topicSelectors } from '@/store/chat/selectors';
|
|
14
11
|
import { messageMapKey } from '@/store/chat/utils/messageMapKey';
|
|
15
|
-
import { useUserStore } from '@/store/user';
|
|
16
|
-
import { settingsSelectors } from '@/store/user/selectors';
|
|
17
|
-
import { useUserMemoryStore } from '@/store/userMemory';
|
|
18
12
|
|
|
19
13
|
import WelcomeChatItem from './AgentWelcome';
|
|
20
14
|
import ChatHydration from './ChatHydration';
|
|
@@ -33,27 +27,6 @@ import { useAgentContext } from './useAgentContext';
|
|
|
33
27
|
const Conversation = memo(() => {
|
|
34
28
|
const context = useAgentContext();
|
|
35
29
|
|
|
36
|
-
const [useFetchUserMemory, setActiveMemoryContext] = useUserMemoryStore((s) => [
|
|
37
|
-
s.useFetchUserMemory,
|
|
38
|
-
s.setActiveMemoryContext,
|
|
39
|
-
]);
|
|
40
|
-
const [currentAgentMeta, activeTopic] = [
|
|
41
|
-
useAgentStore(agentSelectors.currentAgentMeta),
|
|
42
|
-
useChatStore(topicSelectors.currentActiveTopic),
|
|
43
|
-
];
|
|
44
|
-
const enableUserMemories = useUserStore(settingsSelectors.memoryEnabled);
|
|
45
|
-
|
|
46
|
-
useEffect(() => {
|
|
47
|
-
if (!enableUserMemories) {
|
|
48
|
-
setActiveMemoryContext(undefined);
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
setActiveMemoryContext({ agent: currentAgentMeta, topic: activeTopic });
|
|
53
|
-
}, [activeTopic, currentAgentMeta, enableUserMemories, setActiveMemoryContext]);
|
|
54
|
-
|
|
55
|
-
useFetchUserMemory(Boolean(enableUserMemories && context.agentId));
|
|
56
|
-
|
|
57
30
|
// Get raw dbMessages from ChatStore for this context
|
|
58
31
|
// ConversationStore will parse them internally to generate displayMessages
|
|
59
32
|
const chatKey = useMemo(
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { Center, Empty, Markdown } from '@lobehub/ui';
|
|
4
4
|
import { FileText } from 'lucide-react';
|
|
5
5
|
import Link from 'next/link';
|
|
6
|
-
import { memo } from 'react';
|
|
6
|
+
import { type ReactNode, memo } from 'react';
|
|
7
7
|
import { useTranslation } from 'react-i18next';
|
|
8
8
|
|
|
9
9
|
import { H1, H2, H3, H4, H5 } from './Toc/Heading';
|
|
@@ -26,7 +26,7 @@ const MarkdownRender = memo<{ children?: string }>(({ children }) => {
|
|
|
26
26
|
<Markdown
|
|
27
27
|
allowHtml
|
|
28
28
|
components={{
|
|
29
|
-
a: ({ href, ...rest }) => {
|
|
29
|
+
a: ({ href, ...rest }: { children?: ReactNode; href?: string }) => {
|
|
30
30
|
if (href && href.startsWith('http'))
|
|
31
31
|
return <Link {...rest} href={href} target={'_blank'} />;
|
|
32
32
|
return rest?.children;
|
|
@@ -36,7 +36,7 @@ const MarkdownRender = memo<{ children?: string }>(({ children }) => {
|
|
|
36
36
|
h3: H3,
|
|
37
37
|
h4: H4,
|
|
38
38
|
h5: H5,
|
|
39
|
-
img: ({ src, ...rest }) => {
|
|
39
|
+
img: ({ src, ...rest }: { alt?: string; src?: string | Blob }) => {
|
|
40
40
|
// FIXME ignore experimental blob image prop passing
|
|
41
41
|
if (typeof src !== 'string') return null;
|
|
42
42
|
if (src.includes('glama.ai')) return null;
|
|
@@ -8,6 +8,7 @@ import { useTranslation } from 'react-i18next';
|
|
|
8
8
|
import { DEFAULT_AVATAR } from '@/const/meta';
|
|
9
9
|
import NavItem from '@/features/NavPanel/components/NavItem';
|
|
10
10
|
import UserAvatar from '@/features/User/UserAvatar';
|
|
11
|
+
import { useQueryRoute } from '@/hooks/useQueryRoute';
|
|
11
12
|
import { useAgentGroupStore } from '@/store/agentGroup';
|
|
12
13
|
import { agentGroupSelectors } from '@/store/agentGroup/selectors';
|
|
13
14
|
import { useChatStore } from '@/store/chat';
|
|
@@ -30,6 +31,7 @@ interface GroupMemberProps {
|
|
|
30
31
|
*/
|
|
31
32
|
const GroupMember = memo<GroupMemberProps>(({ addModalOpen, onAddModalOpenChange, groupId }) => {
|
|
32
33
|
const { t } = useTranslation('chat');
|
|
34
|
+
const router = useQueryRoute();
|
|
33
35
|
const [nickname, username] = useUserStore((s) => [
|
|
34
36
|
userProfileSelectors.nickName(s),
|
|
35
37
|
userProfileSelectors.username(s),
|
|
@@ -80,6 +82,11 @@ const GroupMember = memo<GroupMemberProps>(({ addModalOpen, onAddModalOpenChange
|
|
|
80
82
|
pushPortalView({ agentId, type: PortalViewType.GroupThread });
|
|
81
83
|
};
|
|
82
84
|
|
|
85
|
+
const handleMemberDoubleClick = (agentId: string) => {
|
|
86
|
+
if (!groupId) return;
|
|
87
|
+
router.push(`/group/${groupId}/profile`, { query: { tab: agentId }, replace: true });
|
|
88
|
+
};
|
|
89
|
+
|
|
83
90
|
return (
|
|
84
91
|
<>
|
|
85
92
|
<Flexbox gap={2}>
|
|
@@ -93,7 +100,7 @@ const GroupMember = memo<GroupMemberProps>(({ addModalOpen, onAddModalOpenChange
|
|
|
93
100
|
key={item.id}
|
|
94
101
|
onChat={() => handleMemberClick(item.id)}
|
|
95
102
|
>
|
|
96
|
-
<div>
|
|
103
|
+
<div onDoubleClick={() => handleMemberDoubleClick(item.id)}>
|
|
97
104
|
<GroupMemberItem
|
|
98
105
|
actions={
|
|
99
106
|
<ActionIcon
|
|
@@ -3,15 +3,10 @@
|
|
|
3
3
|
import { Block } from '@lobehub/ui';
|
|
4
4
|
import { memo } from 'react';
|
|
5
5
|
|
|
6
|
-
import
|
|
6
|
+
import SupervisorAvatar from '@/app/[variants]/(main)/group/features/GroupAvatar';
|
|
7
7
|
import { useOpenChatSettings } from '@/hooks/useInterceptingRoutes';
|
|
8
|
-
import { useAgentGroupStore } from '@/store/agentGroup';
|
|
9
|
-
import { agentGroupSelectors } from '@/store/agentGroup/selectors';
|
|
10
8
|
|
|
11
9
|
const HeaderAvatar = memo<{ size?: number }>(() => {
|
|
12
|
-
const currentGroup = useAgentGroupStore(agentGroupSelectors.currentGroup);
|
|
13
|
-
const agents = currentGroup?.agents || [];
|
|
14
|
-
|
|
15
10
|
const openChatSettings = useOpenChatSettings();
|
|
16
11
|
|
|
17
12
|
return (
|
|
@@ -30,13 +25,7 @@ const HeaderAvatar = memo<{ size?: number }>(() => {
|
|
|
30
25
|
variant={'borderless'}
|
|
31
26
|
width={32}
|
|
32
27
|
>
|
|
33
|
-
<
|
|
34
|
-
avatars={agents.map((agent) => ({
|
|
35
|
-
avatar: agent.avatar,
|
|
36
|
-
background: agent.backgroundColor || undefined,
|
|
37
|
-
}))}
|
|
38
|
-
size={28}
|
|
39
|
-
/>
|
|
28
|
+
<SupervisorAvatar size={28} />
|
|
40
29
|
</Block>
|
|
41
30
|
);
|
|
42
31
|
});
|
|
@@ -5,7 +5,7 @@ import { ChevronsUpDownIcon } from 'lucide-react';
|
|
|
5
5
|
import React, { type PropsWithChildren, memo } from 'react';
|
|
6
6
|
import { useTranslation } from 'react-i18next';
|
|
7
7
|
|
|
8
|
-
import
|
|
8
|
+
import SupervisorAvatar from '@/app/[variants]/(main)/group/features/GroupAvatar';
|
|
9
9
|
import { SkeletonItem } from '@/features/NavPanel/components/SkeletonList';
|
|
10
10
|
import { useAgentGroupStore } from '@/store/agentGroup';
|
|
11
11
|
import { agentGroupSelectors } from '@/store/agentGroup/selectors';
|
|
@@ -15,10 +15,9 @@ import SwitchPanel from './SwitchPanel';
|
|
|
15
15
|
const Agent = memo<PropsWithChildren>(() => {
|
|
16
16
|
const { t } = useTranslation(['chat', 'common']);
|
|
17
17
|
|
|
18
|
-
const [isGroupsInit, groupMeta
|
|
18
|
+
const [isGroupsInit, groupMeta] = useAgentGroupStore((s) => [
|
|
19
19
|
agentGroupSelectors.isGroupsInit(s),
|
|
20
20
|
agentGroupSelectors.currentGroupMeta(s),
|
|
21
|
-
agentGroupSelectors.currentGroupMemberAvatars(s),
|
|
22
21
|
]);
|
|
23
22
|
|
|
24
23
|
const displayTitle = groupMeta?.title || t('untitledGroup', { ns: 'chat' });
|
|
@@ -39,7 +38,7 @@ const Agent = memo<PropsWithChildren>(() => {
|
|
|
39
38
|
}}
|
|
40
39
|
variant={'borderless'}
|
|
41
40
|
>
|
|
42
|
-
<
|
|
41
|
+
<SupervisorAvatar size={28} />
|
|
43
42
|
<Text ellipsis weight={500}>
|
|
44
43
|
{displayTitle}
|
|
45
44
|
</Text>
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import { ActionIcon, Flexbox, Icon, Skeleton, Tag } from '@lobehub/ui';
|
|
2
2
|
import { cssVar } from 'antd-style';
|
|
3
3
|
import { MessageSquareDashed, Star } from 'lucide-react';
|
|
4
|
-
import { Suspense, memo, useCallback } from 'react';
|
|
4
|
+
import { Suspense, memo, useCallback, useMemo } from 'react';
|
|
5
5
|
import { useTranslation } from 'react-i18next';
|
|
6
|
+
import urlJoin from 'url-join';
|
|
6
7
|
|
|
7
8
|
import { isDesktop } from '@/const/version';
|
|
8
9
|
import NavItem from '@/features/NavPanel/components/NavItem';
|
|
9
10
|
import { useAgentStore } from '@/store/agent';
|
|
11
|
+
import { useAgentGroupStore } from '@/store/agentGroup';
|
|
10
12
|
import { useChatStore } from '@/store/chat';
|
|
11
13
|
import { useGlobalStore } from '@/store/global';
|
|
12
14
|
|
|
13
15
|
import ThreadList from '../../TopicListContent/ThreadList';
|
|
14
|
-
import { useTopicNavigation } from '../../hooks/useTopicNavigation';
|
|
15
16
|
import Actions from './Actions';
|
|
16
17
|
import Editing from './Editing';
|
|
17
18
|
import { useTopicItemDropdownMenu } from './useDropdownMenu';
|
|
@@ -27,7 +28,15 @@ interface TopicItemProps {
|
|
|
27
28
|
const TopicItem = memo<TopicItemProps>(({ id, title, fav, active, threadId }) => {
|
|
28
29
|
const { t } = useTranslation('topic');
|
|
29
30
|
const openTopicInNewWindow = useGlobalStore((s) => s.openTopicInNewWindow);
|
|
31
|
+
const toggleMobileTopic = useGlobalStore((s) => s.toggleMobileTopic);
|
|
30
32
|
const activeAgentId = useAgentStore((s) => s.activeAgentId);
|
|
33
|
+
const [activeGroupId, switchTopic] = useAgentGroupStore((s) => [s.activeGroupId, s.switchTopic]);
|
|
34
|
+
|
|
35
|
+
// Construct href for cmd+click support
|
|
36
|
+
const href = useMemo(() => {
|
|
37
|
+
if (!activeGroupId || !id) return undefined;
|
|
38
|
+
return urlJoin('/group', activeGroupId, `?topic=${id}`);
|
|
39
|
+
}, [activeGroupId, id]);
|
|
31
40
|
|
|
32
41
|
const [editing, isLoading] = useChatStore((s) => [
|
|
33
42
|
id ? s.topicRenamingId === id : false,
|
|
@@ -36,8 +45,6 @@ const TopicItem = memo<TopicItemProps>(({ id, title, fav, active, threadId }) =>
|
|
|
36
45
|
|
|
37
46
|
const [favoriteTopic] = useChatStore((s) => [s.favoriteTopic]);
|
|
38
47
|
|
|
39
|
-
const { navigateToTopic, isInAgentSubRoute } = useTopicNavigation();
|
|
40
|
-
|
|
41
48
|
const toggleEditing = useCallback(
|
|
42
49
|
(visible?: boolean) => {
|
|
43
50
|
useChatStore.setState({ topicRenamingId: visible && id ? id : '' });
|
|
@@ -47,8 +54,9 @@ const TopicItem = memo<TopicItemProps>(({ id, title, fav, active, threadId }) =>
|
|
|
47
54
|
|
|
48
55
|
const handleClick = useCallback(() => {
|
|
49
56
|
if (editing) return;
|
|
50
|
-
|
|
51
|
-
|
|
57
|
+
switchTopic(id);
|
|
58
|
+
toggleMobileTopic(false);
|
|
59
|
+
}, [editing, id, switchTopic, toggleMobileTopic]);
|
|
52
60
|
|
|
53
61
|
const handleDoubleClick = useCallback(() => {
|
|
54
62
|
if (!id || !activeAgentId) return;
|
|
@@ -66,7 +74,7 @@ const TopicItem = memo<TopicItemProps>(({ id, title, fav, active, threadId }) =>
|
|
|
66
74
|
if (!id) {
|
|
67
75
|
return (
|
|
68
76
|
<NavItem
|
|
69
|
-
active={active
|
|
77
|
+
active={active}
|
|
70
78
|
icon={
|
|
71
79
|
<Icon color={cssVar.colorTextDescription} icon={MessageSquareDashed} size={'small'} />
|
|
72
80
|
}
|
|
@@ -94,9 +102,10 @@ const TopicItem = memo<TopicItemProps>(({ id, title, fav, active, threadId }) =>
|
|
|
94
102
|
<Flexbox style={{ position: 'relative' }}>
|
|
95
103
|
<NavItem
|
|
96
104
|
actions={<Actions dropdownMenu={dropdownMenu} />}
|
|
97
|
-
active={active && !threadId
|
|
105
|
+
active={active && !threadId}
|
|
98
106
|
contextMenuItems={dropdownMenu}
|
|
99
107
|
disabled={editing}
|
|
108
|
+
href={!editing ? href : undefined}
|
|
100
109
|
icon={
|
|
101
110
|
<ActionIcon
|
|
102
111
|
color={fav ? cssVar.colorWarning : undefined}
|
|
@@ -1,20 +1,14 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { Flexbox } from '@lobehub/ui';
|
|
4
|
-
import { Suspense, memo,
|
|
4
|
+
import { Suspense, memo, useMemo } from 'react';
|
|
5
5
|
|
|
6
6
|
import ChatMiniMap from '@/features/ChatMiniMap';
|
|
7
7
|
import { ChatList, ConversationProvider } from '@/features/Conversation';
|
|
8
8
|
import ZenModeToast from '@/features/ZenModeToast';
|
|
9
9
|
import { useOperationState } from '@/hooks/useOperationState';
|
|
10
|
-
import { useAgentStore } from '@/store/agent';
|
|
11
|
-
import { agentSelectors } from '@/store/agent/selectors';
|
|
12
10
|
import { useChatStore } from '@/store/chat';
|
|
13
|
-
import { topicSelectors } from '@/store/chat/selectors';
|
|
14
11
|
import { messageMapKey } from '@/store/chat/utils/messageMapKey';
|
|
15
|
-
import { useUserStore } from '@/store/user';
|
|
16
|
-
import { settingsSelectors } from '@/store/user/selectors';
|
|
17
|
-
import { useUserMemoryStore } from '@/store/userMemory';
|
|
18
12
|
|
|
19
13
|
import WelcomeChatItem from './AgentWelcome';
|
|
20
14
|
import ChatHydration from './ChatHydration';
|
|
@@ -39,28 +33,6 @@ interface ConversationAreaProps {
|
|
|
39
33
|
const Conversation = memo<ConversationAreaProps>(({ mobile = false }) => {
|
|
40
34
|
const context = useGroupContext();
|
|
41
35
|
|
|
42
|
-
const [useFetchUserMemory, setActiveMemoryContext] = useUserMemoryStore((s) => [
|
|
43
|
-
s.useFetchUserMemory,
|
|
44
|
-
s.setActiveMemoryContext,
|
|
45
|
-
]);
|
|
46
|
-
const [currentAgentMeta, activeTopic] = [
|
|
47
|
-
useAgentStore(agentSelectors.currentAgentMeta),
|
|
48
|
-
useChatStore(topicSelectors.currentActiveTopic),
|
|
49
|
-
];
|
|
50
|
-
|
|
51
|
-
const enableUserMemories = useUserStore(settingsSelectors.memoryEnabled);
|
|
52
|
-
|
|
53
|
-
useEffect(() => {
|
|
54
|
-
if (!enableUserMemories) {
|
|
55
|
-
setActiveMemoryContext(undefined);
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
setActiveMemoryContext({ agent: currentAgentMeta, topic: activeTopic });
|
|
60
|
-
}, [activeTopic, currentAgentMeta, enableUserMemories, setActiveMemoryContext]);
|
|
61
|
-
|
|
62
|
-
useFetchUserMemory(Boolean(enableUserMemories && context.agentId));
|
|
63
|
-
|
|
64
36
|
// Get raw dbMessages from ChatStore for this context
|
|
65
37
|
// ConversationStore will parse them internally to generate displayMessages
|
|
66
38
|
const chatKey = useMemo(
|
|
@@ -11,8 +11,6 @@ import { DESKTOP_HEADER_ICON_SIZE, MOBILE_HEADER_ICON_SIZE } from '@/const/layou
|
|
|
11
11
|
import { useWorkspaceModal } from '@/hooks/useWorkspaceModal';
|
|
12
12
|
import { useChatStore } from '@/store/chat';
|
|
13
13
|
|
|
14
|
-
console.log('ENABLE_TOPIC_LINK_SHARE', ENABLE_TOPIC_LINK_SHARE);
|
|
15
|
-
|
|
16
14
|
const ShareModal = dynamic(() => import('@/features/ShareModal'));
|
|
17
15
|
const SharePopover = dynamic(() => import('@/features/SharePopover'));
|
|
18
16
|
|
|
@@ -1,19 +1,27 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import isEqual from 'fast-deep-equal';
|
|
4
|
-
import
|
|
4
|
+
import { memo } from 'react';
|
|
5
5
|
|
|
6
|
-
import
|
|
6
|
+
import AgentGroupAvatar from '@/features/AgentGroupAvatar';
|
|
7
7
|
import { useAgentGroupStore } from '@/store/agentGroup';
|
|
8
8
|
import { agentGroupSelectors } from '@/store/agentGroup/selectors';
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
);
|
|
10
|
+
/**
|
|
11
|
+
* Connected AgentGroupAvatar that reads from agentGroup store
|
|
12
|
+
*/
|
|
13
|
+
const CurrentAgentGroupAvatar = memo<{ size?: number }>(({ size = 28 }) => {
|
|
14
|
+
const groupMeta = useAgentGroupStore(agentGroupSelectors.currentGroupMeta, isEqual);
|
|
15
|
+
const memberAvatars = useAgentGroupStore(agentGroupSelectors.currentGroupMemberAvatars, isEqual);
|
|
15
16
|
|
|
16
|
-
return
|
|
17
|
+
return (
|
|
18
|
+
<AgentGroupAvatar
|
|
19
|
+
avatar={groupMeta.avatar}
|
|
20
|
+
backgroundColor={groupMeta.backgroundColor}
|
|
21
|
+
memberAvatars={memberAvatars}
|
|
22
|
+
size={size}
|
|
23
|
+
/>
|
|
24
|
+
);
|
|
17
25
|
});
|
|
18
26
|
|
|
19
|
-
export default
|
|
27
|
+
export default CurrentAgentGroupAvatar;
|
|
@@ -5,6 +5,7 @@ import { useTranslation } from 'react-i18next';
|
|
|
5
5
|
|
|
6
6
|
import { DESKTOP_HEADER_ICON_SIZE } from '@/const/layoutTokens';
|
|
7
7
|
import NavHeader from '@/features/NavHeader';
|
|
8
|
+
import { useQueryState } from '@/hooks/useQueryParam';
|
|
8
9
|
import { useChatStore } from '@/store/chat';
|
|
9
10
|
import { topicSelectors } from '@/store/chat/slices/topic/selectors';
|
|
10
11
|
|
|
@@ -18,16 +19,18 @@ const TopicSelector = memo<TopicSelectorProps>(({ agentId }) => {
|
|
|
18
19
|
// Fetch topics for the group agent builder
|
|
19
20
|
useChatStore((s) => s.useFetchTopics)(true, { agentId });
|
|
20
21
|
|
|
21
|
-
// Use activeTopicId from chatStore (synced
|
|
22
|
-
const
|
|
22
|
+
// Use activeTopicId from chatStore (synced from URL query 'bt' via ProfileHydration)
|
|
23
|
+
const activeTopicId = useChatStore((s) => s.activeTopicId);
|
|
23
24
|
const topics = useChatStore((s) => topicSelectors.getTopicsByAgentId(agentId)(s));
|
|
24
25
|
|
|
25
|
-
//
|
|
26
|
+
// Directly update URL query 'bt' to switch topic in profile page
|
|
27
|
+
const [, setBuilderTopicId] = useQueryState('bt');
|
|
28
|
+
|
|
26
29
|
const handleSwitchTopic = useCallback(
|
|
27
30
|
(topicId?: string) => {
|
|
28
|
-
|
|
31
|
+
setBuilderTopicId(topicId ?? null);
|
|
29
32
|
},
|
|
30
|
-
[
|
|
33
|
+
[setBuilderTopicId],
|
|
31
34
|
);
|
|
32
35
|
|
|
33
36
|
// Find active topic from the agent's topics list directly
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { Avatar, Flexbox } from '@lobehub/ui';
|
|
4
4
|
import { createStaticStyles, cx } from 'antd-style';
|
|
5
5
|
import { Plus } from 'lucide-react';
|
|
6
|
-
import { ReactNode, memo } from 'react';
|
|
6
|
+
import { ReactNode, memo, useEffect, useRef } from 'react';
|
|
7
7
|
import { useTranslation } from 'react-i18next';
|
|
8
8
|
|
|
9
9
|
const styles = createStaticStyles(({ css, cssVar: cv }) => ({
|
|
@@ -109,15 +109,33 @@ interface ChromeTabsProps {
|
|
|
109
109
|
|
|
110
110
|
const ChromeTabs = memo<ChromeTabsProps>(({ items, activeId, onChange, onAdd }) => {
|
|
111
111
|
const { t } = useTranslation('chat');
|
|
112
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
113
|
+
|
|
114
|
+
useEffect(() => {
|
|
115
|
+
if (!containerRef.current || !activeId) return;
|
|
116
|
+
|
|
117
|
+
const activeTab = containerRef.current.querySelector(`[data-tab-id="${activeId}"]`);
|
|
118
|
+
if (!activeTab) return;
|
|
119
|
+
|
|
120
|
+
const containerRect = containerRef.current.getBoundingClientRect();
|
|
121
|
+
const tabRect = activeTab.getBoundingClientRect();
|
|
122
|
+
|
|
123
|
+
const isVisible = tabRect.left >= containerRect.left && tabRect.right <= containerRect.right;
|
|
124
|
+
|
|
125
|
+
if (!isVisible) {
|
|
126
|
+
activeTab.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest' });
|
|
127
|
+
}
|
|
128
|
+
}, [activeId]);
|
|
112
129
|
|
|
113
130
|
return (
|
|
114
|
-
<div className={styles.container}>
|
|
131
|
+
<div className={styles.container} ref={containerRef}>
|
|
115
132
|
{items.map((item) => {
|
|
116
133
|
const isActive = item.id === activeId;
|
|
117
134
|
|
|
118
135
|
return (
|
|
119
136
|
<div
|
|
120
137
|
className={cx(styles.tab, isActive && styles.tabActive)}
|
|
138
|
+
data-tab-id={item.id}
|
|
121
139
|
key={item.id}
|
|
122
140
|
onClick={() => onChange(item.id)}
|
|
123
141
|
>
|
|
@@ -5,12 +5,14 @@ import { useGroupProfileStore } from '@/store/groupProfile';
|
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* AgentTool for group profile editor
|
|
8
|
-
* -
|
|
8
|
+
* - showWebBrowsing: Group member profile supports web browsing toggle
|
|
9
|
+
* - filterAvailableInWeb: Filter out desktop-only tools in web version
|
|
10
|
+
* - useAllMetaList: Use allMetaList to include hidden tools
|
|
9
11
|
* - Passes agentId from group profile store to display the correct member's plugins
|
|
10
12
|
*/
|
|
11
13
|
const AgentTool = () => {
|
|
12
14
|
const agentId = useGroupProfileStore((s) => s.activeTabId);
|
|
13
|
-
return <SharedAgentTool agentId={agentId} />;
|
|
15
|
+
return <SharedAgentTool agentId={agentId} filterAvailableInWeb showWebBrowsing useAllMetaList />;
|
|
14
16
|
};
|
|
15
17
|
|
|
16
18
|
export default AgentTool;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { useEditor, useEditorState } from '@lobehub/editor/react';
|
|
4
4
|
import { useUnmount } from 'ahooks';
|
|
5
|
-
import { memo, useEffect
|
|
5
|
+
import { memo, useEffect } from 'react';
|
|
6
6
|
import { createStoreUpdater } from 'zustand-utils';
|
|
7
7
|
|
|
8
8
|
import { useRegisterFilesHotkeys, useSaveDocumentHotkey } from '@/hooks/useHotkeys';
|
|
@@ -25,34 +25,15 @@ const ProfileHydration = memo(() => {
|
|
|
25
25
|
const [activeTabId] = useQueryState('tab', parseAsString.withDefault('group'));
|
|
26
26
|
storeUpdater('activeTabId', activeTabId);
|
|
27
27
|
|
|
28
|
-
//
|
|
29
|
-
|
|
30
|
-
const
|
|
28
|
+
// Sync URL query 'bt' → chatStore.activeTopicId (one-way only)
|
|
29
|
+
// Store → URL sync is handled directly by TopicSelector using setBuilderTopicId
|
|
30
|
+
const [builderTopicId] = useQueryState('bt');
|
|
31
31
|
|
|
32
|
-
// Track if the change came from URL to prevent sync loops
|
|
33
|
-
const isUrlChangeRef = useRef(false);
|
|
34
|
-
|
|
35
|
-
// Sync URL → Store (when URL changes)
|
|
36
32
|
useEffect(() => {
|
|
37
33
|
const urlTopicId = builderTopicId ?? undefined;
|
|
38
|
-
|
|
39
|
-
isUrlChangeRef.current = true;
|
|
40
|
-
useChatStore.setState({ activeTopicId: urlTopicId });
|
|
41
|
-
}
|
|
34
|
+
useChatStore.setState({ activeTopicId: urlTopicId });
|
|
42
35
|
}, [builderTopicId]);
|
|
43
36
|
|
|
44
|
-
// Sync Store → URL (when store changes, but not from URL)
|
|
45
|
-
useEffect(() => {
|
|
46
|
-
if (isUrlChangeRef.current) {
|
|
47
|
-
isUrlChangeRef.current = false;
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
const urlTopicId = builderTopicId ?? undefined;
|
|
51
|
-
if (activeTopicId !== urlTopicId) {
|
|
52
|
-
setBuilderTopicId(activeTopicId ?? null);
|
|
53
|
-
}
|
|
54
|
-
}, [activeTopicId]);
|
|
55
|
-
|
|
56
37
|
// Register hotkeys
|
|
57
38
|
useRegisterFilesHotkeys();
|
|
58
39
|
useSaveDocumentHotkey(flushSave);
|
|
@@ -65,7 +46,6 @@ const ProfileHydration = memo(() => {
|
|
|
65
46
|
editorState: undefined,
|
|
66
47
|
saveStateMap: {},
|
|
67
48
|
});
|
|
68
|
-
useChatStore.setState({ activeTopicId: undefined });
|
|
69
49
|
});
|
|
70
50
|
|
|
71
51
|
return null;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Avatar } from '@lobehub/ui';
|
|
4
|
+
import { memo } from 'react';
|
|
5
|
+
|
|
6
|
+
import GroupAvatar from '@/features/GroupAvatar';
|
|
7
|
+
|
|
8
|
+
export interface AgentGroupAvatarProps {
|
|
9
|
+
/**
|
|
10
|
+
* Custom avatar for the group (emoji or url)
|
|
11
|
+
*/
|
|
12
|
+
avatar?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Background color for custom avatar
|
|
15
|
+
*/
|
|
16
|
+
backgroundColor?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Member avatars to display when no custom avatar
|
|
19
|
+
*/
|
|
20
|
+
memberAvatars?: { avatar?: string; background?: string }[];
|
|
21
|
+
/**
|
|
22
|
+
* Avatar size
|
|
23
|
+
*/
|
|
24
|
+
size?: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const AgentGroupAvatar = memo<AgentGroupAvatarProps>(
|
|
28
|
+
({ avatar, backgroundColor, memberAvatars = [], size = 28 }) => {
|
|
29
|
+
// If group has custom avatar, show it; otherwise show member avatars composition
|
|
30
|
+
if (avatar) {
|
|
31
|
+
return <Avatar avatar={avatar} background={backgroundColor} shape="square" size={size} />;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return <GroupAvatar avatars={memberAvatars} size={size} />;
|
|
35
|
+
},
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
export default AgentGroupAvatar;
|
|
@@ -6,9 +6,9 @@ import { type MouseEventHandler, memo, useCallback } from 'react';
|
|
|
6
6
|
import { useTranslation } from 'react-i18next';
|
|
7
7
|
|
|
8
8
|
import { MESSAGE_ACTION_BAR_PORTAL_ATTRIBUTES } from '@/const/messageActionPortal';
|
|
9
|
+
import AgentGroupAvatar from '@/features/AgentGroupAvatar';
|
|
9
10
|
import { ChatItem } from '@/features/Conversation/ChatItem';
|
|
10
11
|
import { useNewScreen } from '@/features/Conversation/Messages/components/useNewScreen';
|
|
11
|
-
import GroupAvatar from '@/features/GroupAvatar';
|
|
12
12
|
import { useAgentGroupStore } from '@/store/agentGroup';
|
|
13
13
|
import { agentGroupSelectors } from '@/store/agentGroup/selectors';
|
|
14
14
|
|
|
@@ -90,7 +90,13 @@ const GroupMessage = memo<GroupMessageProps>(({ id, index, disableEditing, isLat
|
|
|
90
90
|
</>
|
|
91
91
|
}
|
|
92
92
|
avatar={{ ...avatar, title: groupMeta.title }}
|
|
93
|
-
customAvatarRender={() =>
|
|
93
|
+
customAvatarRender={() => (
|
|
94
|
+
<AgentGroupAvatar
|
|
95
|
+
avatar={groupMeta.avatar}
|
|
96
|
+
backgroundColor={groupMeta.backgroundColor}
|
|
97
|
+
memberAvatars={memberAvatars}
|
|
98
|
+
/>
|
|
99
|
+
)}
|
|
94
100
|
newScreen={newScreen}
|
|
95
101
|
onMouseEnter={onMouseEnter}
|
|
96
102
|
placement={'left'}
|
|
@@ -30,12 +30,11 @@ export const useMarkdown = (id: string): Partial<MarkdownProps> => {
|
|
|
30
30
|
() =>
|
|
31
31
|
({
|
|
32
32
|
components: Object.fromEntries(
|
|
33
|
-
// @ts-expect-error
|
|
34
33
|
markdownElements.map((element) => {
|
|
35
34
|
const Component = element.Component;
|
|
36
35
|
return [element.tag, (props: any) => <Component {...props} id={id} />];
|
|
37
36
|
}),
|
|
38
|
-
),
|
|
37
|
+
) as any,
|
|
39
38
|
customRender: (dom: ReactNode, { text }: { text: string }) => {
|
|
40
39
|
if (text.length > 30_000) return <ContentPreview content={text} id={id} />;
|
|
41
40
|
return dom;
|