@lobehub/lobehub 2.0.0-next.277 → 2.0.0-next.279
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/.cursor/rules/db-migrations.mdc +1 -1
- package/.cursor/rules/debug-usage.mdc +7 -5
- package/.cursor/rules/desktop-controller-tests.mdc +2 -1
- package/.cursor/rules/desktop-feature-implementation.mdc +9 -5
- package/.cursor/rules/desktop-local-tools-implement.mdc +67 -66
- package/.cursor/rules/desktop-menu-configuration.mdc +21 -9
- package/.cursor/rules/desktop-window-management.mdc +17 -2
- package/.cursor/rules/drizzle-schema-style-guide.mdc +6 -6
- package/.cursor/rules/hotkey.mdc +1 -0
- package/.cursor/rules/i18n.mdc +1 -0
- package/.cursor/rules/project-structure.mdc +16 -3
- package/.cursor/rules/react.mdc +17 -5
- package/.cursor/rules/recent-data-usage.mdc +2 -1
- package/.cursor/rules/testing-guide/testing-guide.mdc +262 -238
- package/.cursor/rules/testing-guide/zustand-store-action-test.mdc +1 -1
- package/.cursor/rules/zustand-action-patterns.mdc +1 -1
- package/.cursor/rules/zustand-slice-organization.mdc +4 -4
- package/CHANGELOG.md +51 -0
- package/CLAUDE.md +1 -1
- package/GEMINI.md +1 -1
- package/changelog/v1.json +14 -0
- package/docs/development/database-schema.dbml +16 -0
- package/locales/en-US/chat.json +24 -0
- package/locales/zh-CN/chat.json +24 -0
- package/package.json +1 -1
- package/packages/business/const/src/index.ts +3 -0
- package/packages/database/migrations/0069_add_topic_shares_table.sql +22 -0
- package/packages/database/migrations/meta/0069_snapshot.json +9704 -0
- package/packages/database/migrations/meta/_journal.json +7 -0
- package/packages/database/src/models/__tests__/topicShare.test.ts +318 -0
- package/packages/database/src/models/topicShare.ts +177 -0
- package/packages/database/src/schemas/topic.ts +44 -2
- package/packages/types/src/conversation.ts +5 -0
- package/packages/types/src/topic/topic.ts +46 -0
- package/src/app/[variants]/(main)/agent/features/Conversation/Header/ShareButton/index.tsx +24 -9
- package/src/app/[variants]/(main)/agent/features/Conversation/ThreadHydration.tsx +2 -1
- package/src/app/[variants]/(main)/agent/features/Portal/_layout/Mobile.tsx +3 -3
- package/src/app/[variants]/(main)/group/_layout/Sidebar/GroupConfig/GroupMember.tsx +3 -2
- package/src/app/[variants]/(main)/group/features/Conversation/Header/ShareButton/index.tsx +26 -9
- package/src/app/[variants]/(main)/group/features/Conversation/ThreadHydration.tsx +2 -1
- package/src/app/[variants]/(main)/group/features/Portal/_layout/Mobile.tsx +3 -3
- package/src/app/[variants]/(main)/group/profile/features/MemberProfile/AgentTool.tsx +4 -1
- package/src/app/[variants]/(main)/image/_layout/ConfigPanel/components/AspectRatioSelect/index.tsx +1 -2
- package/src/app/[variants]/(main)/image/_layout/ConfigPanel/components/ImageNum.tsx +54 -173
- package/src/app/[variants]/(main)/image/_layout/ConfigPanel/components/ResolutionSelect.tsx +22 -67
- package/src/app/[variants]/(mobile)/router/mobileRouter.config.tsx +18 -0
- package/src/app/[variants]/router/desktopRouter.config.tsx +18 -0
- package/src/app/[variants]/share/t/[id]/SharedMessageList.tsx +54 -0
- package/src/app/[variants]/share/t/[id]/_layout/index.tsx +170 -0
- package/src/app/[variants]/share/t/[id]/features/Portal/index.tsx +66 -0
- package/src/app/[variants]/share/t/[id]/index.tsx +112 -0
- package/src/app/robots.tsx +1 -1
- package/src/business/client/BusinessMobileRoutes.tsx +1 -1
- package/src/features/Conversation/ChatList/index.tsx +17 -6
- package/src/features/Conversation/Messages/AssistantGroup/Tool/Render/index.tsx +8 -4
- package/src/features/Conversation/Messages/AssistantGroup/Tool/index.tsx +15 -10
- package/src/features/Conversation/Messages/AssistantGroup/Tools.tsx +3 -1
- package/src/features/Conversation/Messages/AssistantGroup/components/ContentBlock.tsx +3 -2
- package/src/features/Conversation/Messages/AssistantGroup/components/GroupItem.tsx +2 -2
- package/src/features/Conversation/Messages/Supervisor/components/ContentBlock.tsx +25 -26
- package/src/features/Conversation/Messages/Supervisor/components/Group.tsx +4 -2
- package/src/features/Conversation/Messages/Tool/Tool/index.tsx +16 -12
- package/src/features/Conversation/Messages/Tool/index.tsx +20 -11
- package/src/features/Conversation/Messages/index.tsx +1 -1
- package/src/features/Conversation/store/slices/data/action.test.ts +42 -0
- package/src/features/Conversation/store/slices/data/action.ts +4 -2
- package/src/features/Portal/GroupThread/Header/index.tsx +2 -2
- package/src/features/Portal/MessageDetail/Body/index.tsx +3 -3
- package/src/features/Portal/components/Header.tsx +3 -3
- package/src/features/ProfileEditor/AgentTool.tsx +50 -19
- package/src/features/SharePopover/index.tsx +215 -0
- package/src/features/SharePopover/style.ts +10 -0
- package/src/hooks/useNavigateToAgent.ts +3 -3
- package/src/libs/next/proxy/define-config.ts +4 -1
- package/src/locales/default/chat.ts +26 -0
- package/src/proxy.ts +1 -0
- package/src/server/routers/lambda/__tests__/message.test.ts +152 -0
- package/src/server/routers/lambda/__tests__/share.test.ts +227 -0
- package/src/server/routers/lambda/__tests__/topic.test.ts +174 -0
- package/src/server/routers/lambda/index.ts +2 -0
- package/src/server/routers/lambda/message.ts +37 -4
- package/src/server/routers/lambda/share.ts +55 -0
- package/src/server/routers/lambda/topic.ts +45 -0
- package/src/services/message/index.ts +1 -0
- package/src/services/topic/index.ts +16 -0
- package/src/store/chat/slices/portal/action.test.ts +0 -41
- package/src/store/chat/slices/portal/action.ts +0 -25
- package/src/store/chat/slices/thread/action.test.ts +10 -6
- package/src/store/chat/slices/thread/action.ts +10 -3
- package/src/app/[variants]/(main)/group/features/Portal/features/Portal.tsx +0 -105
- package/src/app/[variants]/(main)/group/features/Portal/features/PortalPanel.tsx +0 -23
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Flexbox } from '@lobehub/ui';
|
|
4
|
+
import { memo, useCallback, useMemo } from 'react';
|
|
5
|
+
|
|
6
|
+
import { ChatList, ConversationProvider, MessageItem } from '@/features/Conversation';
|
|
7
|
+
import { useChatStore } from '@/store/chat';
|
|
8
|
+
import { messageMapKey } from '@/store/chat/utils/messageMapKey';
|
|
9
|
+
|
|
10
|
+
interface SharedMessageListProps {
|
|
11
|
+
agentId: string | null;
|
|
12
|
+
groupId: string | null;
|
|
13
|
+
shareId: string;
|
|
14
|
+
topicId: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const SharedMessageList = memo<SharedMessageListProps>(({ agentId, groupId, shareId, topicId }) => {
|
|
18
|
+
const context = useMemo(
|
|
19
|
+
() => ({
|
|
20
|
+
agentId: agentId ?? '',
|
|
21
|
+
groupId: groupId ?? undefined,
|
|
22
|
+
topicId,
|
|
23
|
+
topicShareId: shareId,
|
|
24
|
+
}),
|
|
25
|
+
[agentId, groupId, shareId, topicId],
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
// Sync messages to chatStore for artifact selectors to work
|
|
29
|
+
const chatKey = useMemo(() => messageMapKey(context), [context]);
|
|
30
|
+
const replaceMessages = useChatStore((s) => s.replaceMessages);
|
|
31
|
+
const messages = useChatStore((s) => s.dbMessagesMap[chatKey]);
|
|
32
|
+
|
|
33
|
+
const itemContent = useCallback(
|
|
34
|
+
(index: number, id: string) => <MessageItem disableEditing id={id} index={index} key={id} />,
|
|
35
|
+
[],
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<ConversationProvider
|
|
40
|
+
context={context}
|
|
41
|
+
hasInitMessages={!!messages}
|
|
42
|
+
messages={messages}
|
|
43
|
+
onMessagesChange={(messages) => {
|
|
44
|
+
replaceMessages(messages, { context });
|
|
45
|
+
}}
|
|
46
|
+
>
|
|
47
|
+
<Flexbox flex={1}>
|
|
48
|
+
<ChatList disableActionsBar itemContent={itemContent} />
|
|
49
|
+
</Flexbox>
|
|
50
|
+
</ConversationProvider>
|
|
51
|
+
);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
export default SharedMessageList;
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Avatar, Flexbox } from '@lobehub/ui';
|
|
4
|
+
import { Typography } from 'antd';
|
|
5
|
+
import { createStyles, cssVar } from 'antd-style';
|
|
6
|
+
import NextLink from 'next/link';
|
|
7
|
+
import { PropsWithChildren, memo, useEffect, useMemo } from 'react';
|
|
8
|
+
import { useTranslation } from 'react-i18next';
|
|
9
|
+
import { Link, Outlet, useParams } from 'react-router-dom';
|
|
10
|
+
import useSWR from 'swr';
|
|
11
|
+
|
|
12
|
+
import { ProductLogo } from '@/components/Branding';
|
|
13
|
+
import { DEFAULT_AVATAR } from '@/const/meta';
|
|
14
|
+
import GroupAvatar from '@/features/GroupAvatar';
|
|
15
|
+
import UserAvatar from '@/features/User/UserAvatar';
|
|
16
|
+
import { lambdaClient } from '@/libs/trpc/client';
|
|
17
|
+
import { useAgentStore } from '@/store/agent';
|
|
18
|
+
import { useUserStore } from '@/store/user';
|
|
19
|
+
import { authSelectors } from '@/store/user/slices/auth/selectors';
|
|
20
|
+
|
|
21
|
+
import SharePortal from '../features/Portal';
|
|
22
|
+
|
|
23
|
+
const useStyles = createStyles(({ css, token }) => ({
|
|
24
|
+
container: css`
|
|
25
|
+
width: 100vw;
|
|
26
|
+
min-height: 100vh;
|
|
27
|
+
background: ${token.colorBgLayout};
|
|
28
|
+
`,
|
|
29
|
+
content: css`
|
|
30
|
+
flex: 1;
|
|
31
|
+
width: 100%;
|
|
32
|
+
padding-block: 24px;
|
|
33
|
+
padding-inline: 24px;
|
|
34
|
+
`,
|
|
35
|
+
footer: css`
|
|
36
|
+
padding-block: 16px;
|
|
37
|
+
padding-inline: 24px;
|
|
38
|
+
color: ${token.colorTextTertiary};
|
|
39
|
+
text-align: center;
|
|
40
|
+
`,
|
|
41
|
+
header: css`
|
|
42
|
+
height: 52px;
|
|
43
|
+
padding: 8px;
|
|
44
|
+
`,
|
|
45
|
+
}));
|
|
46
|
+
|
|
47
|
+
const ShareTopicLayout = memo<PropsWithChildren>(({ children }) => {
|
|
48
|
+
const { styles } = useStyles();
|
|
49
|
+
const { t } = useTranslation('chat');
|
|
50
|
+
const { id } = useParams<{ id: string }>();
|
|
51
|
+
const dispatchAgentMap = useAgentStore((s) => s.internal_dispatchAgentMap);
|
|
52
|
+
const isLogin = useUserStore(authSelectors.isLogin);
|
|
53
|
+
|
|
54
|
+
const { data } = useSWR(
|
|
55
|
+
id ? ['shared-topic', id] : null,
|
|
56
|
+
() => lambdaClient.share.getSharedTopic.query({ shareId: id! }),
|
|
57
|
+
{ revalidateOnFocus: false },
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
// Set agent meta to agentStore for avatar display
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
if (data?.agentId && data.agentMeta) {
|
|
63
|
+
const meta = {
|
|
64
|
+
avatar: data.agentMeta.avatar ?? undefined,
|
|
65
|
+
backgroundColor: data.agentMeta.backgroundColor ?? undefined,
|
|
66
|
+
title: data.agentMeta.title ?? undefined,
|
|
67
|
+
};
|
|
68
|
+
dispatchAgentMap(data.agentId, meta);
|
|
69
|
+
}
|
|
70
|
+
}, [data?.agentId, data?.agentMeta, dispatchAgentMap]);
|
|
71
|
+
|
|
72
|
+
const isGroup = !!data?.groupId;
|
|
73
|
+
const isInboxAgent = !isGroup && data?.agentMeta?.slug === 'inbox';
|
|
74
|
+
const agentOrGroupTitle =
|
|
75
|
+
data?.groupMeta?.title || (isInboxAgent ? 'LobeAI' : data?.agentMeta?.title);
|
|
76
|
+
const agentMarketIdentifier = data?.agentMeta?.marketIdentifier;
|
|
77
|
+
|
|
78
|
+
// Build group avatars for GroupAvatar component
|
|
79
|
+
const groupAvatars = useMemo(() => {
|
|
80
|
+
if (!isGroup || !data?.groupMeta?.members) return [];
|
|
81
|
+
return data.groupMeta.members.map((member) => ({
|
|
82
|
+
avatar: member.avatar || DEFAULT_AVATAR,
|
|
83
|
+
backgroundColor: member.backgroundColor || undefined,
|
|
84
|
+
}));
|
|
85
|
+
}, [isGroup, data?.groupMeta?.members]);
|
|
86
|
+
|
|
87
|
+
const renderAgentOrGroupAvatar = () => {
|
|
88
|
+
// For group: use GroupAvatar with members
|
|
89
|
+
if (isGroup && groupAvatars.length > 0) {
|
|
90
|
+
return <GroupAvatar avatars={groupAvatars} size={24} />;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// For inbox agent: skip avatar as it's the same as product icon
|
|
94
|
+
if (isInboxAgent) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// For agent: use single Avatar
|
|
99
|
+
if (data?.agentMeta?.avatar) {
|
|
100
|
+
return (
|
|
101
|
+
<Avatar
|
|
102
|
+
avatar={data.agentMeta.avatar}
|
|
103
|
+
background={data.agentMeta.backgroundColor || cssVar.colorFillTertiary}
|
|
104
|
+
shape="square"
|
|
105
|
+
size={24}
|
|
106
|
+
/>
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return null;
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const renderAgentOrGroupTitle = () => {
|
|
114
|
+
if (!agentOrGroupTitle) return null;
|
|
115
|
+
|
|
116
|
+
// If agent has marketIdentifier, render as link to assistant page
|
|
117
|
+
if (agentMarketIdentifier && !data?.groupMeta?.title) {
|
|
118
|
+
return (
|
|
119
|
+
<a href={`/community/assistant/${agentMarketIdentifier}`} rel="noreferrer" target="_blank">
|
|
120
|
+
<Typography.Text ellipsis strong>
|
|
121
|
+
{agentOrGroupTitle}
|
|
122
|
+
</Typography.Text>
|
|
123
|
+
</a>
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return (
|
|
128
|
+
<Typography.Text ellipsis strong>
|
|
129
|
+
{agentOrGroupTitle}
|
|
130
|
+
</Typography.Text>
|
|
131
|
+
);
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
return (
|
|
135
|
+
<Flexbox className={styles.container}>
|
|
136
|
+
<Flexbox align="center" className={styles.header} gap={12} horizontal justify="space-between">
|
|
137
|
+
<Flexbox align="center" flex={1} gap={12} horizontal>
|
|
138
|
+
{isLogin ? (
|
|
139
|
+
<Link to="/">
|
|
140
|
+
<ProductLogo size={24} />
|
|
141
|
+
</Link>
|
|
142
|
+
) : (
|
|
143
|
+
<NextLink href="/login">
|
|
144
|
+
<ProductLogo size={24} />
|
|
145
|
+
</NextLink>
|
|
146
|
+
)}
|
|
147
|
+
{renderAgentOrGroupAvatar()}
|
|
148
|
+
{renderAgentOrGroupTitle()}
|
|
149
|
+
</Flexbox>
|
|
150
|
+
{data?.title && (
|
|
151
|
+
<Typography.Text ellipsis strong style={{ textAlign: 'center' }}>
|
|
152
|
+
{data.title}
|
|
153
|
+
</Typography.Text>
|
|
154
|
+
)}
|
|
155
|
+
<Flexbox align="center" flex={1} horizontal justify="flex-end">
|
|
156
|
+
{isLogin && <UserAvatar size={24} />}
|
|
157
|
+
</Flexbox>
|
|
158
|
+
</Flexbox>
|
|
159
|
+
<Flexbox className={styles.content} horizontal style={{ overflow: 'hidden' }}>
|
|
160
|
+
<Flexbox flex={1} style={{ overflow: 'hidden' }}>
|
|
161
|
+
{children ?? <Outlet />}
|
|
162
|
+
</Flexbox>
|
|
163
|
+
<SharePortal />
|
|
164
|
+
</Flexbox>
|
|
165
|
+
<Typography.Text className={styles.footer}>{t('sharePageDisclaimer')}</Typography.Text>
|
|
166
|
+
</Flexbox>
|
|
167
|
+
);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
export default ShareTopicLayout;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { DraggablePanel } from '@lobehub/ui';
|
|
4
|
+
import { createStyles } from 'antd-style';
|
|
5
|
+
import { memo } from 'react';
|
|
6
|
+
|
|
7
|
+
import { CHAT_PORTAL_TOOL_UI_WIDTH } from '@/const/layoutTokens';
|
|
8
|
+
import { PortalContent } from '@/features/Portal/router';
|
|
9
|
+
import { useChatStore } from '@/store/chat';
|
|
10
|
+
import { chatPortalSelectors } from '@/store/chat/selectors';
|
|
11
|
+
|
|
12
|
+
const useStyles = createStyles(({ css, token }) => ({
|
|
13
|
+
body: css`
|
|
14
|
+
overflow: hidden;
|
|
15
|
+
display: flex;
|
|
16
|
+
flex: 1;
|
|
17
|
+
flex-direction: column;
|
|
18
|
+
|
|
19
|
+
height: 0;
|
|
20
|
+
padding-block-end: 12px;
|
|
21
|
+
`,
|
|
22
|
+
content: css`
|
|
23
|
+
position: relative;
|
|
24
|
+
|
|
25
|
+
overflow: hidden;
|
|
26
|
+
display: flex;
|
|
27
|
+
flex-direction: column;
|
|
28
|
+
|
|
29
|
+
height: 100%;
|
|
30
|
+
min-height: 100%;
|
|
31
|
+
max-height: 100%;
|
|
32
|
+
|
|
33
|
+
background: ${token.colorBgContainer};
|
|
34
|
+
`,
|
|
35
|
+
drawer: css`
|
|
36
|
+
z-index: 10;
|
|
37
|
+
height: 100%;
|
|
38
|
+
background: ${token.colorBgContainer};
|
|
39
|
+
`,
|
|
40
|
+
}));
|
|
41
|
+
|
|
42
|
+
const SharePortal = memo(() => {
|
|
43
|
+
const { styles } = useStyles();
|
|
44
|
+
const showPortal = useChatStore(chatPortalSelectors.showPortal);
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<DraggablePanel
|
|
48
|
+
className={styles.drawer}
|
|
49
|
+
classNames={{ content: styles.content }}
|
|
50
|
+
defaultSize={{ width: CHAT_PORTAL_TOOL_UI_WIDTH }}
|
|
51
|
+
expand={showPortal}
|
|
52
|
+
expandable={false}
|
|
53
|
+
minWidth={CHAT_PORTAL_TOOL_UI_WIDTH}
|
|
54
|
+
placement="right"
|
|
55
|
+
showHandleWhenCollapsed={false}
|
|
56
|
+
showHandleWideArea={false}
|
|
57
|
+
size={{ height: '100%', width: CHAT_PORTAL_TOOL_UI_WIDTH }}
|
|
58
|
+
>
|
|
59
|
+
<PortalContent renderBody={(body) => <div className={styles.body}>{body}</div>} />
|
|
60
|
+
</DraggablePanel>
|
|
61
|
+
);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
SharePortal.displayName = 'SharePortal';
|
|
65
|
+
|
|
66
|
+
export default SharePortal;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Flexbox } from '@lobehub/ui';
|
|
4
|
+
import { TRPCClientError } from '@trpc/client';
|
|
5
|
+
import { Button, Result, Skeleton } from 'antd';
|
|
6
|
+
import { createStyles } from 'antd-style';
|
|
7
|
+
import { memo } from 'react';
|
|
8
|
+
import { useTranslation } from 'react-i18next';
|
|
9
|
+
import { useParams } from 'react-router-dom';
|
|
10
|
+
import useSWR from 'swr';
|
|
11
|
+
|
|
12
|
+
import { lambdaClient } from '@/libs/trpc/client';
|
|
13
|
+
|
|
14
|
+
import SharedMessageList from './SharedMessageList';
|
|
15
|
+
|
|
16
|
+
const useStyles = createStyles(({ css }) => ({
|
|
17
|
+
container: css`
|
|
18
|
+
flex: 1;
|
|
19
|
+
`,
|
|
20
|
+
errorContainer: css`
|
|
21
|
+
display: flex;
|
|
22
|
+
flex-direction: column;
|
|
23
|
+
align-items: center;
|
|
24
|
+
justify-content: center;
|
|
25
|
+
|
|
26
|
+
min-height: 400px;
|
|
27
|
+
padding: 48px;
|
|
28
|
+
|
|
29
|
+
text-align: center;
|
|
30
|
+
`,
|
|
31
|
+
}));
|
|
32
|
+
|
|
33
|
+
const ShareTopicPage = memo(() => {
|
|
34
|
+
const { styles } = useStyles();
|
|
35
|
+
const { t } = useTranslation('chat');
|
|
36
|
+
const { id } = useParams<{ id: string }>();
|
|
37
|
+
|
|
38
|
+
const { data, error, isLoading } = useSWR(
|
|
39
|
+
id ? ['shared-topic', id] : null,
|
|
40
|
+
() => lambdaClient.share.getSharedTopic.query({ shareId: id! }),
|
|
41
|
+
{ revalidateOnFocus: false },
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
if (isLoading) {
|
|
45
|
+
return (
|
|
46
|
+
<Flexbox className={styles.container} gap={16}>
|
|
47
|
+
<Skeleton active paragraph={{ rows: 1 }} title={false} />
|
|
48
|
+
<Skeleton active paragraph={{ rows: 6 }} />
|
|
49
|
+
</Flexbox>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (error) {
|
|
54
|
+
const trpcError = error instanceof TRPCClientError ? error : null;
|
|
55
|
+
const errorCode = trpcError?.data?.code;
|
|
56
|
+
|
|
57
|
+
if (errorCode === 'UNAUTHORIZED') {
|
|
58
|
+
return (
|
|
59
|
+
<Flexbox className={styles.errorContainer}>
|
|
60
|
+
<Result
|
|
61
|
+
extra={
|
|
62
|
+
<Button href="/login" type="primary">
|
|
63
|
+
{t('sharePage.error.unauthorized.action')}
|
|
64
|
+
</Button>
|
|
65
|
+
}
|
|
66
|
+
status="403"
|
|
67
|
+
subTitle={t('sharePage.error.unauthorized.subtitle')}
|
|
68
|
+
title={t('sharePage.error.unauthorized.title')}
|
|
69
|
+
/>
|
|
70
|
+
</Flexbox>
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (errorCode === 'FORBIDDEN') {
|
|
75
|
+
return (
|
|
76
|
+
<Flexbox className={styles.errorContainer}>
|
|
77
|
+
<Result
|
|
78
|
+
status="403"
|
|
79
|
+
subTitle={t('sharePage.error.forbidden.subtitle')}
|
|
80
|
+
title={t('sharePage.error.forbidden.title')}
|
|
81
|
+
/>
|
|
82
|
+
</Flexbox>
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// NOT_FOUND or other errors
|
|
87
|
+
return (
|
|
88
|
+
<Flexbox className={styles.errorContainer}>
|
|
89
|
+
<Result
|
|
90
|
+
status="404"
|
|
91
|
+
subTitle={t('sharePage.error.notFound.subtitle')}
|
|
92
|
+
title={t('sharePage.error.notFound.title')}
|
|
93
|
+
/>
|
|
94
|
+
</Flexbox>
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (!data) return null;
|
|
99
|
+
|
|
100
|
+
return (
|
|
101
|
+
<Flexbox className={styles.container}>
|
|
102
|
+
<SharedMessageList
|
|
103
|
+
agentId={data.agentId}
|
|
104
|
+
groupId={data.groupId}
|
|
105
|
+
shareId={data.shareId}
|
|
106
|
+
topicId={data.topicId}
|
|
107
|
+
/>
|
|
108
|
+
</Flexbox>
|
|
109
|
+
);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
export default ShareTopicPage;
|
package/src/app/robots.tsx
CHANGED
|
@@ -15,6 +15,10 @@ import { dataSelectors, useConversationStore } from '../store';
|
|
|
15
15
|
import VirtualizedList from './components/VirtualizedList';
|
|
16
16
|
|
|
17
17
|
export interface ChatListProps {
|
|
18
|
+
/**
|
|
19
|
+
* Disable the actions bar for all messages (e.g., in share page)
|
|
20
|
+
*/
|
|
21
|
+
disableActionsBar?: boolean;
|
|
18
22
|
/**
|
|
19
23
|
* Custom item renderer. If not provided, uses default ChatItem.
|
|
20
24
|
*/
|
|
@@ -29,7 +33,7 @@ export interface ChatListProps {
|
|
|
29
33
|
*
|
|
30
34
|
* Uses ConversationStore for message data and fetching.
|
|
31
35
|
*/
|
|
32
|
-
const ChatList = memo<ChatListProps>(({ welcome, itemContent }) => {
|
|
36
|
+
const ChatList = memo<ChatListProps>(({ disableActionsBar, welcome, itemContent }) => {
|
|
33
37
|
// Fetch messages (SWR key is null when skipFetch is true)
|
|
34
38
|
const context = useConversationStore((s) => s.context);
|
|
35
39
|
const enableUserMemories = useUserStore(settingsSelectors.memoryEnabled);
|
|
@@ -39,9 +43,12 @@ const ChatList = memo<ChatListProps>(({ welcome, itemContent }) => {
|
|
|
39
43
|
]);
|
|
40
44
|
useFetchMessages(context, skipFetch);
|
|
41
45
|
|
|
42
|
-
//
|
|
43
|
-
|
|
44
|
-
|
|
46
|
+
// Skip fetching notebook and memories for share pages (they require authentication)
|
|
47
|
+
const isSharePage = !!context.topicShareId;
|
|
48
|
+
|
|
49
|
+
// Fetch notebook documents when topic is selected (skip for share pages)
|
|
50
|
+
useFetchNotebookDocuments(isSharePage ? undefined : context.topicId!);
|
|
51
|
+
useFetchTopicMemories(enableUserMemories && !isSharePage ? context.topicId : undefined);
|
|
45
52
|
|
|
46
53
|
// Use selectors for data
|
|
47
54
|
|
|
@@ -56,7 +63,11 @@ const ChatList = memo<ChatListProps>(({ welcome, itemContent }) => {
|
|
|
56
63
|
);
|
|
57
64
|
const messagesInit = useConversationStore(dataSelectors.messagesInit);
|
|
58
65
|
|
|
59
|
-
|
|
66
|
+
// When topicId is null (new conversation), show welcome directly without waiting for fetch
|
|
67
|
+
// because there's no server data to fetch - only local optimistic updates exist
|
|
68
|
+
const isNewConversation = !context.topicId;
|
|
69
|
+
|
|
70
|
+
if (!messagesInit && !isNewConversation) {
|
|
60
71
|
return <SkeletonList />;
|
|
61
72
|
}
|
|
62
73
|
|
|
@@ -77,7 +88,7 @@ const ChatList = memo<ChatListProps>(({ welcome, itemContent }) => {
|
|
|
77
88
|
}
|
|
78
89
|
|
|
79
90
|
return (
|
|
80
|
-
<MessageActionProvider withSingletonActionsBar>
|
|
91
|
+
<MessageActionProvider withSingletonActionsBar={!disableActionsBar}>
|
|
81
92
|
<VirtualizedList
|
|
82
93
|
dataSource={displayMessageIds}
|
|
83
94
|
// isGenerating={isGenerating}
|
|
@@ -16,6 +16,7 @@ import RejectedResponse from './RejectedResponse';
|
|
|
16
16
|
interface RenderProps {
|
|
17
17
|
apiName: string;
|
|
18
18
|
arguments?: string;
|
|
19
|
+
disableEditing?: boolean;
|
|
19
20
|
identifier: string;
|
|
20
21
|
intervention?: ToolIntervention;
|
|
21
22
|
isArgumentsStreaming?: boolean;
|
|
@@ -43,6 +44,7 @@ const Render = memo<RenderProps>(
|
|
|
43
44
|
toolCallId,
|
|
44
45
|
messageId,
|
|
45
46
|
arguments: requestArgs,
|
|
47
|
+
disableEditing,
|
|
46
48
|
showPluginRender,
|
|
47
49
|
setShowPluginRender,
|
|
48
50
|
identifier,
|
|
@@ -54,7 +56,7 @@ const Render = memo<RenderProps>(
|
|
|
54
56
|
isArgumentsStreaming,
|
|
55
57
|
isToolCalling,
|
|
56
58
|
}) => {
|
|
57
|
-
if (toolMessageId && intervention?.status === 'pending') {
|
|
59
|
+
if (toolMessageId && intervention?.status === 'pending' && !disableEditing) {
|
|
58
60
|
return (
|
|
59
61
|
<Intervention
|
|
60
62
|
apiName={apiName}
|
|
@@ -150,9 +152,11 @@ const Render = memo<RenderProps>(
|
|
|
150
152
|
showPluginRender={showPluginRender}
|
|
151
153
|
toolCallId={toolCallId}
|
|
152
154
|
/>
|
|
153
|
-
|
|
154
|
-
<
|
|
155
|
-
|
|
155
|
+
{!disableEditing && (
|
|
156
|
+
<div>
|
|
157
|
+
<ModeSelector />
|
|
158
|
+
</div>
|
|
159
|
+
)}
|
|
156
160
|
</Flexbox>
|
|
157
161
|
</Suspense>
|
|
158
162
|
);
|
|
@@ -30,6 +30,7 @@ export interface GroupToolProps {
|
|
|
30
30
|
apiName: string;
|
|
31
31
|
arguments?: string;
|
|
32
32
|
assistantMessageId: string;
|
|
33
|
+
disableEditing?: boolean;
|
|
33
34
|
id: string;
|
|
34
35
|
identifier: string;
|
|
35
36
|
intervention?: ToolIntervention;
|
|
@@ -43,6 +44,7 @@ const Tool = memo<GroupToolProps>(
|
|
|
43
44
|
arguments: requestArgs,
|
|
44
45
|
apiName,
|
|
45
46
|
assistantMessageId,
|
|
47
|
+
disableEditing,
|
|
46
48
|
id,
|
|
47
49
|
intervention,
|
|
48
50
|
identifier,
|
|
@@ -106,16 +108,18 @@ const Tool = memo<GroupToolProps>(
|
|
|
106
108
|
return (
|
|
107
109
|
<AccordionItem
|
|
108
110
|
action={
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
111
|
+
!disableEditing && (
|
|
112
|
+
<Actions
|
|
113
|
+
assistantMessageId={assistantMessageId}
|
|
114
|
+
handleExpand={handleExpand}
|
|
115
|
+
identifier={identifier}
|
|
116
|
+
setShowDebug={setShowDebug}
|
|
117
|
+
setShowPluginRender={setShowPluginRender}
|
|
118
|
+
showCustomPluginRender={showCustomPluginRender}
|
|
119
|
+
showDebug={showDebug}
|
|
120
|
+
showPluginRender={showPluginRender}
|
|
121
|
+
/>
|
|
122
|
+
)
|
|
119
123
|
}
|
|
120
124
|
allowExpand={hasCustomRender}
|
|
121
125
|
expand={isToolRenderExpand}
|
|
@@ -150,6 +154,7 @@ const Tool = memo<GroupToolProps>(
|
|
|
150
154
|
<Render
|
|
151
155
|
apiName={apiName}
|
|
152
156
|
arguments={requestArgs}
|
|
157
|
+
disableEditing={disableEditing}
|
|
153
158
|
identifier={identifier}
|
|
154
159
|
intervention={intervention}
|
|
155
160
|
isArgumentsStreaming={isArgumentsStreaming}
|
|
@@ -5,11 +5,12 @@ import { memo } from 'react';
|
|
|
5
5
|
import Tool from './Tool';
|
|
6
6
|
|
|
7
7
|
interface ToolsRendererProps {
|
|
8
|
+
disableEditing?: boolean;
|
|
8
9
|
messageId: string;
|
|
9
10
|
tools: ChatToolPayloadWithResult[];
|
|
10
11
|
}
|
|
11
12
|
|
|
12
|
-
export const Tools = memo<ToolsRendererProps>(({ messageId, tools }) => {
|
|
13
|
+
export const Tools = memo<ToolsRendererProps>(({ disableEditing, messageId, tools }) => {
|
|
13
14
|
if (!tools || tools.length === 0) return null;
|
|
14
15
|
|
|
15
16
|
return (
|
|
@@ -19,6 +20,7 @@ export const Tools = memo<ToolsRendererProps>(({ messageId, tools }) => {
|
|
|
19
20
|
apiName={tool.apiName}
|
|
20
21
|
arguments={tool.arguments}
|
|
21
22
|
assistantMessageId={messageId}
|
|
23
|
+
disableEditing={disableEditing}
|
|
22
24
|
id={tool.id}
|
|
23
25
|
identifier={tool.identifier}
|
|
24
26
|
intervention={tool.intervention}
|
|
@@ -14,9 +14,10 @@ import MessageContent from './MessageContent';
|
|
|
14
14
|
|
|
15
15
|
interface ContentBlockProps extends AssistantContentBlock {
|
|
16
16
|
assistantId: string;
|
|
17
|
+
disableEditing?: boolean;
|
|
17
18
|
}
|
|
18
19
|
const ContentBlock = memo<ContentBlockProps>(
|
|
19
|
-
({ id, tools, content, imageList, reasoning, error, assistantId }) => {
|
|
20
|
+
({ id, tools, content, imageList, reasoning, error, assistantId, disableEditing }) => {
|
|
20
21
|
const errorContent = useErrorContent(error);
|
|
21
22
|
const showImageItems = !!imageList && imageList.length > 0;
|
|
22
23
|
const [isReasoning, deleteMessage, continueGeneration] = useConversationStore((s) => [
|
|
@@ -70,7 +71,7 @@ const ContentBlock = memo<ContentBlockProps>(
|
|
|
70
71
|
{showImageItems && <ImageFileListViewer items={imageList} />}
|
|
71
72
|
|
|
72
73
|
{/* Tools */}
|
|
73
|
-
{hasTools && <Tools messageId={id} tools={tools} />}
|
|
74
|
+
{hasTools && <Tools disableEditing={disableEditing} messageId={id} tools={tools} />}
|
|
74
75
|
</Flexbox>
|
|
75
76
|
);
|
|
76
77
|
},
|
|
@@ -25,10 +25,10 @@ const GroupItem = memo<GroupItemProps>(
|
|
|
25
25
|
toggleMessageEditing(item.id, true);
|
|
26
26
|
}}
|
|
27
27
|
>
|
|
28
|
-
<ContentBlock {...item} assistantId={assistantId} error={error} />
|
|
28
|
+
<ContentBlock {...item} assistantId={assistantId} disableEditing={disableEditing} error={error} />
|
|
29
29
|
</Flexbox>
|
|
30
30
|
) : (
|
|
31
|
-
<ContentBlock {...item} assistantId={assistantId} error={error} />
|
|
31
|
+
<ContentBlock {...item} assistantId={assistantId} disableEditing={disableEditing} error={error} />
|
|
32
32
|
);
|
|
33
33
|
},
|
|
34
34
|
isEqual,
|