@lobehub/lobehub 2.0.0-next.66 → 2.0.0-next.67
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 +3 -2
- package/src/app/[variants]/(main)/chat/components/conversation/features/ChatList/ChatItem/index.tsx +1 -0
- package/src/app/[variants]/(main)/chat/components/conversation/features/ChatMinimap/index.tsx +21 -28
- package/src/features/ChatItem/style.ts +4 -0
- package/src/features/Conversation/Messages/Assistant/Actions/index.tsx +3 -3
- package/src/features/Conversation/Messages/Assistant/index.tsx +329 -230
- package/src/features/Conversation/Messages/Group/Actions/WithContentId.tsx +3 -3
- package/src/features/Conversation/Messages/Group/GroupItem.tsx +3 -5
- package/src/features/Conversation/Messages/Group/index.tsx +80 -13
- package/src/features/Conversation/Messages/User/Actions/ActionsBar.tsx +3 -3
- package/src/features/Conversation/Messages/index.tsx +24 -8
- package/src/features/Conversation/components/VirtualizedList/VirtuosoContext.ts +13 -13
- package/src/features/Conversation/components/VirtualizedList/index.tsx +92 -58
- package/src/features/Conversation/components/WideScreenContainer/index.tsx +10 -6
- package/src/features/Conversation/hooks/useDoubleClickEdit.ts +3 -3
- package/src/store/chat/slices/aiChat/actions/conversationLifecycle.ts +9 -1
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { LOADING_FLAT } from '@lobechat/const';
|
|
4
4
|
import { UIChatMessage } from '@lobechat/types';
|
|
5
5
|
import { Tag } from '@lobehub/ui';
|
|
6
|
-
import { css, cx, useResponsive } from 'antd-style';
|
|
6
|
+
import { createStyles, css, cx, useResponsive } from 'antd-style';
|
|
7
7
|
import isEqual from 'fast-deep-equal';
|
|
8
8
|
import { ReactNode, memo, useCallback, useMemo } from 'react';
|
|
9
9
|
import { useTranslation } from 'react-i18next';
|
|
@@ -15,7 +15,6 @@ import BorderSpacing from '@/features/ChatItem/components/BorderSpacing';
|
|
|
15
15
|
import ErrorContent from '@/features/ChatItem/components/ErrorContent';
|
|
16
16
|
import MessageContent from '@/features/ChatItem/components/MessageContent';
|
|
17
17
|
import Title from '@/features/ChatItem/components/Title';
|
|
18
|
-
import { useStyles } from '@/features/ChatItem/style';
|
|
19
18
|
import { useOpenChatSettings } from '@/hooks/useInterceptingRoutes';
|
|
20
19
|
import { useAgentStore } from '@/store/agent';
|
|
21
20
|
import { agentChatConfigSelectors } from '@/store/agent/selectors';
|
|
@@ -44,6 +43,103 @@ const messageContainer = cx(css`
|
|
|
44
43
|
background: none;
|
|
45
44
|
`);
|
|
46
45
|
|
|
46
|
+
export const useStyles = createStyles(
|
|
47
|
+
(
|
|
48
|
+
{ cx, css, token, responsive },
|
|
49
|
+
{
|
|
50
|
+
placement,
|
|
51
|
+
variant,
|
|
52
|
+
editing,
|
|
53
|
+
}: { editing?: boolean; placement?: 'left' | 'right'; variant?: 'bubble' | 'docs' },
|
|
54
|
+
) => {
|
|
55
|
+
const rawContainerStylish = css`
|
|
56
|
+
margin-block-end: -16px;
|
|
57
|
+
transition: background-color 100ms ${token.motionEaseOut};
|
|
58
|
+
`;
|
|
59
|
+
|
|
60
|
+
const editingStylish =
|
|
61
|
+
editing &&
|
|
62
|
+
css`
|
|
63
|
+
width: 100%;
|
|
64
|
+
`;
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
actions: cx(
|
|
68
|
+
css`
|
|
69
|
+
flex: none;
|
|
70
|
+
align-self: ${variant === 'bubble'
|
|
71
|
+
? 'flex-end'
|
|
72
|
+
: placement === 'left'
|
|
73
|
+
? 'flex-start'
|
|
74
|
+
: 'flex-end'};
|
|
75
|
+
justify-content: ${placement === 'left' ? 'flex-end' : 'flex-start'};
|
|
76
|
+
`,
|
|
77
|
+
editing &&
|
|
78
|
+
css`
|
|
79
|
+
pointer-events: none !important;
|
|
80
|
+
opacity: 0 !important;
|
|
81
|
+
`,
|
|
82
|
+
),
|
|
83
|
+
container: cx(
|
|
84
|
+
variant === 'docs' && rawContainerStylish,
|
|
85
|
+
css`
|
|
86
|
+
position: relative;
|
|
87
|
+
|
|
88
|
+
width: 100%;
|
|
89
|
+
max-width: 100vw;
|
|
90
|
+
padding-block: 24px 12px;
|
|
91
|
+
padding-inline: 12px;
|
|
92
|
+
|
|
93
|
+
@supports (content-visibility: auto) {
|
|
94
|
+
contain-intrinsic-size: auto 100lvh;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
time {
|
|
98
|
+
display: inline-block;
|
|
99
|
+
white-space: nowrap;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
div[role='menubar'] {
|
|
103
|
+
display: flex;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
time,
|
|
107
|
+
div[role='menubar'] {
|
|
108
|
+
pointer-events: none;
|
|
109
|
+
opacity: 0;
|
|
110
|
+
transition: opacity 200ms ${token.motionEaseOut};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
&:hover {
|
|
114
|
+
time,
|
|
115
|
+
div[role='menubar'] {
|
|
116
|
+
pointer-events: unset;
|
|
117
|
+
opacity: 1;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
${responsive.mobile} {
|
|
122
|
+
padding-block-start: ${variant === 'docs' ? '16px' : '12px'};
|
|
123
|
+
padding-inline: 8px;
|
|
124
|
+
}
|
|
125
|
+
`,
|
|
126
|
+
),
|
|
127
|
+
messageContent: cx(
|
|
128
|
+
editingStylish,
|
|
129
|
+
css`
|
|
130
|
+
position: relative;
|
|
131
|
+
overflow: hidden;
|
|
132
|
+
max-width: 100%;
|
|
133
|
+
|
|
134
|
+
${responsive.mobile} {
|
|
135
|
+
flex-direction: column !important;
|
|
136
|
+
}
|
|
137
|
+
`,
|
|
138
|
+
),
|
|
139
|
+
};
|
|
140
|
+
},
|
|
141
|
+
);
|
|
142
|
+
|
|
47
143
|
const isHtmlCode = (content: string, language: string) => {
|
|
48
144
|
return (
|
|
49
145
|
language === 'html' ||
|
|
@@ -57,244 +153,247 @@ interface AssistantMessageProps {
|
|
|
57
153
|
disableEditing?: boolean;
|
|
58
154
|
id: string;
|
|
59
155
|
index: number;
|
|
156
|
+
isLatestItem?: boolean;
|
|
60
157
|
}
|
|
61
158
|
|
|
62
|
-
const AssistantMessage = memo<AssistantMessageProps>(
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
{showHtmlPreview && <HtmlPreviewAction content={content} size={actionIconSize} />}
|
|
179
|
-
{originalNode}
|
|
180
|
-
</>
|
|
181
|
-
);
|
|
159
|
+
const AssistantMessage = memo<AssistantMessageProps>(
|
|
160
|
+
({ id, index, disableEditing, isLatestItem }) => {
|
|
161
|
+
const item = useChatStore(
|
|
162
|
+
displayMessageSelectors.getDisplayMessageById(id),
|
|
163
|
+
isEqual,
|
|
164
|
+
) as UIChatMessage;
|
|
165
|
+
|
|
166
|
+
const {
|
|
167
|
+
error,
|
|
168
|
+
role,
|
|
169
|
+
search,
|
|
170
|
+
content,
|
|
171
|
+
createdAt,
|
|
172
|
+
tools,
|
|
173
|
+
extra,
|
|
174
|
+
model,
|
|
175
|
+
provider,
|
|
176
|
+
meta,
|
|
177
|
+
targetId,
|
|
178
|
+
performance,
|
|
179
|
+
usage,
|
|
180
|
+
metadata,
|
|
181
|
+
} = item;
|
|
182
|
+
|
|
183
|
+
const avatar = meta;
|
|
184
|
+
const { t } = useTranslation('chat');
|
|
185
|
+
const { mobile } = useResponsive();
|
|
186
|
+
const placement = 'left';
|
|
187
|
+
const type = useAgentStore(agentChatConfigSelectors.displayMode);
|
|
188
|
+
const variant = type === 'chat' ? 'bubble' : 'docs';
|
|
189
|
+
|
|
190
|
+
const { transitionMode, highlighterTheme, mermaidTheme } = useUserStore(
|
|
191
|
+
userGeneralSettingsSelectors.config,
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
const [generating, isInRAGFlow, editing] = useChatStore((s) => [
|
|
195
|
+
messageStateSelectors.isMessageGenerating(id)(s),
|
|
196
|
+
messageStateSelectors.isMessageInRAGFlow(id)(s),
|
|
197
|
+
messageStateSelectors.isMessageEditing(id)(s),
|
|
198
|
+
]);
|
|
199
|
+
|
|
200
|
+
const { styles } = useStyles({
|
|
201
|
+
editing,
|
|
202
|
+
placement,
|
|
203
|
+
variant,
|
|
204
|
+
});
|
|
205
|
+
const errorContent = useErrorContent(error);
|
|
206
|
+
|
|
207
|
+
// remove line breaks in artifact tag to make the ast transform easier
|
|
208
|
+
const message = !editing ? normalizeThinkTags(processWithArtifact(content)) : content;
|
|
209
|
+
|
|
210
|
+
// when the message is in RAG flow or the AI generating, it should be in loading state
|
|
211
|
+
const loading = isInRAGFlow || generating;
|
|
212
|
+
|
|
213
|
+
const animated = transitionMode === 'fadeIn' && generating;
|
|
214
|
+
|
|
215
|
+
const isGroupSession = useSessionStore(sessionSelectors.isCurrentSessionGroupSession);
|
|
216
|
+
const currentSession = useSessionStore(sessionSelectors.currentSession);
|
|
217
|
+
const sessionId = isGroupSession && currentSession ? currentSession.id : '';
|
|
218
|
+
const groupConfig = useChatGroupStore(chatGroupSelectors.getGroupConfig(sessionId || ''));
|
|
219
|
+
|
|
220
|
+
const reducted =
|
|
221
|
+
isGroupSession && targetId !== null && targetId !== 'user' && !groupConfig?.revealDM;
|
|
222
|
+
|
|
223
|
+
// Get target name for DM indicator
|
|
224
|
+
const userName = useUserStore(userProfileSelectors.nickName) || 'User';
|
|
225
|
+
const agents = useSessionStore(sessionSelectors.currentGroupAgents);
|
|
226
|
+
|
|
227
|
+
const dmIndicator = useMemo(() => {
|
|
228
|
+
if (!targetId) return undefined;
|
|
229
|
+
|
|
230
|
+
let targetName = targetId;
|
|
231
|
+
if (targetId === 'user') {
|
|
232
|
+
targetName = t('dm.you');
|
|
233
|
+
} else {
|
|
234
|
+
const targetAgent = agents?.find((agent) => agent.id === targetId);
|
|
235
|
+
targetName = targetAgent?.title || targetId;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return <Tag>{t('dm.visibleTo', { target: targetName })}</Tag>;
|
|
239
|
+
}, [targetId, userName, agents, t]);
|
|
240
|
+
|
|
241
|
+
// ======================= Performance Optimization ======================= //
|
|
242
|
+
// these useMemo/useCallback are all for the performance optimization
|
|
243
|
+
// maybe we can remove it in React 19
|
|
244
|
+
// ======================================================================== //
|
|
245
|
+
|
|
246
|
+
const components = useMemo(
|
|
247
|
+
() =>
|
|
248
|
+
Object.fromEntries(
|
|
249
|
+
markdownElements.map((element) => {
|
|
250
|
+
const Component = element.Component;
|
|
251
|
+
|
|
252
|
+
return [element.tag, (props: any) => <Component {...props} id={id} />];
|
|
253
|
+
}),
|
|
254
|
+
),
|
|
255
|
+
[id],
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
const markdownProps = useMemo(
|
|
259
|
+
() => ({
|
|
260
|
+
animated,
|
|
261
|
+
citations: search?.citations,
|
|
262
|
+
componentProps: {
|
|
263
|
+
highlight: {
|
|
264
|
+
actionsRender: ({ content, actionIconSize, language, originalNode }: any) => {
|
|
265
|
+
const showHtmlPreview = isHtmlCode(content, language);
|
|
266
|
+
|
|
267
|
+
return (
|
|
268
|
+
<>
|
|
269
|
+
{showHtmlPreview && <HtmlPreviewAction content={content} size={actionIconSize} />}
|
|
270
|
+
{originalNode}
|
|
271
|
+
</>
|
|
272
|
+
);
|
|
273
|
+
},
|
|
274
|
+
theme: highlighterTheme,
|
|
182
275
|
},
|
|
183
|
-
theme:
|
|
276
|
+
mermaid: { theme: mermaidTheme },
|
|
184
277
|
},
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
)
|
|
219
|
-
|
|
220
|
-
);
|
|
221
|
-
const errorMessage = <ErrorMessageExtra data={item} />;
|
|
278
|
+
components,
|
|
279
|
+
enableCustomFootnotes: true,
|
|
280
|
+
rehypePlugins,
|
|
281
|
+
remarkPlugins,
|
|
282
|
+
showFootnotes:
|
|
283
|
+
search?.citations &&
|
|
284
|
+
// if the citations are all empty, we should not show the citations
|
|
285
|
+
search?.citations.length > 0 &&
|
|
286
|
+
// if the citations's url and title are all the same, we should not show the citations
|
|
287
|
+
search?.citations.every((item) => item.title !== item.url),
|
|
288
|
+
}),
|
|
289
|
+
[animated, components, role, search, highlighterTheme, mermaidTheme],
|
|
290
|
+
);
|
|
291
|
+
|
|
292
|
+
const [isInbox] = useSessionStore((s) => [sessionSelectors.isInboxSession(s)]);
|
|
293
|
+
const [toggleSystemRole] = useGlobalStore((s) => [s.toggleSystemRole]);
|
|
294
|
+
const openChatSettings = useOpenChatSettings();
|
|
295
|
+
|
|
296
|
+
const onAvatarClick = useCallback(() => {
|
|
297
|
+
if (!isInbox) {
|
|
298
|
+
toggleSystemRole(true);
|
|
299
|
+
} else {
|
|
300
|
+
openChatSettings();
|
|
301
|
+
}
|
|
302
|
+
}, [isInbox]);
|
|
303
|
+
|
|
304
|
+
const onDoubleClick = useDoubleClickEdit({ disableEditing, error, id, index, role });
|
|
305
|
+
|
|
306
|
+
const renderMessage = useCallback(
|
|
307
|
+
(editableContent: ReactNode) => (
|
|
308
|
+
<AssistantMessageContent {...item} editableContent={editableContent} />
|
|
309
|
+
),
|
|
310
|
+
[item],
|
|
311
|
+
);
|
|
312
|
+
const errorMessage = <ErrorMessageExtra data={item} />;
|
|
222
313
|
|
|
223
|
-
|
|
224
|
-
<Flexbox className={styles.container} gap={mobile ? 6 : 12}>
|
|
225
|
-
<Flexbox gap={4} horizontal>
|
|
226
|
-
<Avatar
|
|
227
|
-
alt={avatar.title || 'avatar'}
|
|
228
|
-
avatar={avatar}
|
|
229
|
-
loading={loading}
|
|
230
|
-
onClick={onAvatarClick}
|
|
231
|
-
placement={placement}
|
|
232
|
-
size={MOBILE_AVATAR_SIZE}
|
|
233
|
-
/>
|
|
234
|
-
<Title
|
|
235
|
-
avatar={avatar}
|
|
236
|
-
placement={placement}
|
|
237
|
-
showTitle
|
|
238
|
-
style={{ marginBlockEnd: 0 }}
|
|
239
|
-
time={createdAt}
|
|
240
|
-
titleAddon={dmIndicator}
|
|
241
|
-
/>
|
|
242
|
-
</Flexbox>
|
|
314
|
+
return (
|
|
243
315
|
<Flexbox
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
direction={'vertical'}
|
|
248
|
-
gap={8}
|
|
249
|
-
width={'fit-content'}
|
|
316
|
+
className={styles.container}
|
|
317
|
+
gap={mobile ? 6 : 12}
|
|
318
|
+
style={isLatestItem ? { minHeight: 'calc(-284px + 100dvh)' } : undefined}
|
|
250
319
|
>
|
|
251
|
-
<Flexbox
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
320
|
+
<Flexbox gap={4} horizontal>
|
|
321
|
+
<Avatar
|
|
322
|
+
alt={avatar.title || 'avatar'}
|
|
323
|
+
avatar={avatar}
|
|
324
|
+
loading={loading}
|
|
325
|
+
onClick={onAvatarClick}
|
|
326
|
+
placement={placement}
|
|
327
|
+
size={MOBILE_AVATAR_SIZE}
|
|
328
|
+
/>
|
|
329
|
+
<Title
|
|
330
|
+
avatar={avatar}
|
|
331
|
+
placement={placement}
|
|
332
|
+
showTitle
|
|
333
|
+
style={{ marginBlockEnd: 0 }}
|
|
334
|
+
time={createdAt}
|
|
335
|
+
titleAddon={dmIndicator}
|
|
336
|
+
/>
|
|
337
|
+
</Flexbox>
|
|
338
|
+
<Flexbox
|
|
339
|
+
align={'flex-start'}
|
|
340
|
+
className={styles.messageContent}
|
|
341
|
+
data-layout={'vertical'} // 添加数据属性以方便样式选择
|
|
342
|
+
direction={'vertical'}
|
|
343
|
+
gap={8}
|
|
344
|
+
width={'fit-content'}
|
|
345
|
+
>
|
|
346
|
+
<Flexbox style={{ flex: 1, maxWidth: '100%' }}>
|
|
347
|
+
{error && (message === LOADING_FLAT || !message) ? (
|
|
348
|
+
<ErrorContent error={errorContent} message={errorMessage} placement={placement} />
|
|
349
|
+
) : (
|
|
350
|
+
<MessageContent
|
|
351
|
+
className={messageContainer}
|
|
352
|
+
editing={editing}
|
|
353
|
+
id={id}
|
|
354
|
+
markdownProps={markdownProps}
|
|
355
|
+
message={reducted ? `*${t('hideForYou')}*` : message}
|
|
356
|
+
messageExtra={
|
|
357
|
+
<>
|
|
358
|
+
{errorContent && (
|
|
359
|
+
<ErrorContent
|
|
360
|
+
error={errorContent}
|
|
361
|
+
message={errorMessage}
|
|
362
|
+
placement={placement}
|
|
363
|
+
/>
|
|
364
|
+
)}
|
|
365
|
+
<AssistantMessageExtra
|
|
366
|
+
content={content}
|
|
367
|
+
extra={extra}
|
|
368
|
+
id={id}
|
|
369
|
+
model={model!}
|
|
370
|
+
performance={performance! || metadata}
|
|
371
|
+
provider={provider!}
|
|
372
|
+
tools={tools}
|
|
373
|
+
usage={usage! || metadata}
|
|
268
374
|
/>
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
onDoubleClick={onDoubleClick}
|
|
283
|
-
placement={placement}
|
|
284
|
-
renderMessage={renderMessage}
|
|
285
|
-
variant={variant}
|
|
286
|
-
/>
|
|
375
|
+
</>
|
|
376
|
+
}
|
|
377
|
+
onDoubleClick={onDoubleClick}
|
|
378
|
+
placement={placement}
|
|
379
|
+
renderMessage={renderMessage}
|
|
380
|
+
variant={variant}
|
|
381
|
+
/>
|
|
382
|
+
)}
|
|
383
|
+
</Flexbox>
|
|
384
|
+
{!disableEditing && !editing && (
|
|
385
|
+
<Flexbox align={'flex-start'} className={styles.actions} role="menubar">
|
|
386
|
+
<AssistantActionsBar data={item} id={id} index={index} />
|
|
387
|
+
</Flexbox>
|
|
287
388
|
)}
|
|
288
389
|
</Flexbox>
|
|
289
|
-
{
|
|
290
|
-
<Flexbox align={'flex-start'} className={styles.actions} role="menubar">
|
|
291
|
-
<AssistantActionsBar data={item} id={id} index={index} />
|
|
292
|
-
</Flexbox>
|
|
293
|
-
)}
|
|
390
|
+
{mobile && <BorderSpacing borderSpacing={MOBILE_AVATAR_SIZE} />}
|
|
294
391
|
</Flexbox>
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
392
|
+
);
|
|
393
|
+
},
|
|
394
|
+
isEqual,
|
|
395
|
+
);
|
|
396
|
+
|
|
397
|
+
AssistantMessage.displayName = 'AssistantMessage';
|
|
299
398
|
|
|
300
399
|
export default AssistantMessage;
|
|
@@ -6,7 +6,7 @@ import { memo, use, useCallback, useContext, useMemo, useState } from 'react';
|
|
|
6
6
|
import { useTranslation } from 'react-i18next';
|
|
7
7
|
|
|
8
8
|
import ShareMessageModal from '@/features/Conversation/components/ShareMessageModal';
|
|
9
|
-
import {
|
|
9
|
+
import { VirtuaContext } from '@/features/Conversation/components/VirtualizedList/VirtuosoContext';
|
|
10
10
|
import { useChatStore } from '@/store/chat';
|
|
11
11
|
import { messageStateSelectors, threadSelectors } from '@/store/chat/selectors';
|
|
12
12
|
import { useSessionStore } from '@/store/session';
|
|
@@ -89,7 +89,7 @@ const WithContentId = memo<GroupActionsProps>(({ id, data, index, contentBlock }
|
|
|
89
89
|
s.toggleMessageCollapsed,
|
|
90
90
|
]);
|
|
91
91
|
const { message } = App.useApp();
|
|
92
|
-
const
|
|
92
|
+
const virtuaRef = use(VirtuaContext);
|
|
93
93
|
|
|
94
94
|
const onActionClick = useCallback(
|
|
95
95
|
async (action: ActionIconGroupEvent) => {
|
|
@@ -97,7 +97,7 @@ const WithContentId = memo<GroupActionsProps>(({ id, data, index, contentBlock }
|
|
|
97
97
|
case 'edit': {
|
|
98
98
|
toggleMessageEditing(id, true);
|
|
99
99
|
|
|
100
|
-
|
|
100
|
+
virtuaRef?.current?.scrollToIndex(index, { align: 'start' });
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
103
|
if (!data) return;
|
|
@@ -4,7 +4,7 @@ import { memo, use } from 'react';
|
|
|
4
4
|
import { Flexbox } from 'react-layout-kit';
|
|
5
5
|
|
|
6
6
|
import { ContentBlock } from '@/features/Conversation/Messages/Group/ContentBlock';
|
|
7
|
-
import {
|
|
7
|
+
import { VirtuaContext } from '@/features/Conversation/components/VirtualizedList/VirtuosoContext';
|
|
8
8
|
import { useChatStore } from '@/store/chat';
|
|
9
9
|
|
|
10
10
|
interface GroupItemProps extends AssistantContentBlock {
|
|
@@ -17,7 +17,7 @@ interface GroupItemProps extends AssistantContentBlock {
|
|
|
17
17
|
const GroupItem = memo<GroupItemProps>(
|
|
18
18
|
({ contentId, messageIndex, index, disableEditing, error, ...item }) => {
|
|
19
19
|
const [toggleMessageEditing] = useChatStore((s) => [s.toggleMessageEditing]);
|
|
20
|
-
const
|
|
20
|
+
const virtuaRef = use(VirtuaContext);
|
|
21
21
|
|
|
22
22
|
return item.id === contentId ? (
|
|
23
23
|
<Flexbox
|
|
@@ -25,10 +25,8 @@ const GroupItem = memo<GroupItemProps>(
|
|
|
25
25
|
if (disableEditing || error || !e.altKey) return;
|
|
26
26
|
|
|
27
27
|
toggleMessageEditing(item.id, true);
|
|
28
|
-
|
|
28
|
+
virtuaRef?.current?.scrollToIndex(messageIndex, {
|
|
29
29
|
align: 'start',
|
|
30
|
-
behavior: 'auto',
|
|
31
|
-
index: messageIndex,
|
|
32
30
|
});
|
|
33
31
|
}}
|
|
34
32
|
>
|