@lobehub/lobehub 2.0.0-next.277 → 2.0.0-next.278
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 +25 -0
- package/CLAUDE.md +1 -1
- package/GEMINI.md +1 -1
- package/changelog/v1.json +5 -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)/group/features/Conversation/Header/ShareButton/index.tsx +26 -9
- 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 +12 -5
- 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.ts +2 -1
- package/src/features/SharePopover/index.tsx +215 -0
- package/src/features/SharePopover/style.ts +10 -0
- 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
|
@@ -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
|
|
|
@@ -77,7 +84,7 @@ const ChatList = memo<ChatListProps>(({ welcome, itemContent }) => {
|
|
|
77
84
|
}
|
|
78
85
|
|
|
79
86
|
return (
|
|
80
|
-
<MessageActionProvider withSingletonActionsBar>
|
|
87
|
+
<MessageActionProvider withSingletonActionsBar={!disableActionsBar}>
|
|
81
88
|
<VirtualizedList
|
|
82
89
|
dataSource={displayMessageIds}
|
|
83
90
|
// 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,
|
|
@@ -11,34 +11,33 @@ import { Tools } from '../../AssistantGroup/Tools';
|
|
|
11
11
|
import Reasoning from '../../components/Reasoning';
|
|
12
12
|
import MessageContent from './MessageContent';
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
errorContent && error && (content === LOADING_FLAT || !content) ? errorContent : undefined
|
|
26
|
-
}
|
|
27
|
-
id={id}
|
|
28
|
-
/>
|
|
29
|
-
);
|
|
14
|
+
interface ContentBlockProps extends AssistantContentBlock {
|
|
15
|
+
disableEditing?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const ContentBlock = memo<ContentBlockProps>(
|
|
19
|
+
({ id, tools, content, reasoning, error, disableEditing }) => {
|
|
20
|
+
const errorContent = useErrorContent(error);
|
|
21
|
+
const isReasoning = useConversationStore(messageStateSelectors.isMessageInReasoning(id));
|
|
22
|
+
const hasTools = tools && tools.length > 0;
|
|
23
|
+
const showReasoning =
|
|
24
|
+
(!!reasoning && reasoning.content?.trim() !== '') || (!reasoning && isReasoning);
|
|
30
25
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
{showReasoning && <Reasoning {...reasoning} id={id} />}
|
|
26
|
+
if (error && (content === LOADING_FLAT || !content))
|
|
27
|
+
return <ErrorContent error={errorContent} id={id} />;
|
|
34
28
|
|
|
35
|
-
|
|
36
|
-
<
|
|
29
|
+
return (
|
|
30
|
+
<Flexbox gap={8} id={id}>
|
|
31
|
+
{showReasoning && <Reasoning {...reasoning} id={id} />}
|
|
32
|
+
|
|
33
|
+
{/* Content - markdown text */}
|
|
34
|
+
<MessageContent content={content} hasTools={hasTools} id={id} />
|
|
37
35
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
36
|
+
{/* Tools */}
|
|
37
|
+
{hasTools && <Tools disableEditing={disableEditing} messageId={id} tools={tools} />}
|
|
38
|
+
</Flexbox>
|
|
39
|
+
);
|
|
40
|
+
},
|
|
41
|
+
);
|
|
43
42
|
|
|
44
43
|
export default ContentBlock;
|
|
@@ -29,7 +29,7 @@ interface GroupChildrenProps {
|
|
|
29
29
|
messageIndex: number;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
const Group = memo<GroupChildrenProps>(({ blocks, id, content }) => {
|
|
32
|
+
const Group = memo<GroupChildrenProps>(({ blocks, id, content, disableEditing }) => {
|
|
33
33
|
const isCollapsed = useConversationStore(messageStateSelectors.isMessageCollapsed(id));
|
|
34
34
|
const contextValue = useMemo(() => ({ assistantGroupId: id }), [id]);
|
|
35
35
|
|
|
@@ -46,7 +46,9 @@ const Group = memo<GroupChildrenProps>(({ blocks, id, content }) => {
|
|
|
46
46
|
<MessageAggregationContext value={contextValue}>
|
|
47
47
|
<Flexbox className={styles.container} gap={8}>
|
|
48
48
|
{blocks.map((item) => {
|
|
49
|
-
return
|
|
49
|
+
return (
|
|
50
|
+
<ContentBlock {...item} disableEditing={disableEditing} key={id + '.' + item.id} />
|
|
51
|
+
);
|
|
50
52
|
})}
|
|
51
53
|
</Flexbox>
|
|
52
54
|
</MessageAggregationContext>
|