@lobehub/chat 1.135.3 → 1.135.5
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 +58 -0
- package/changelog/v1.json +18 -0
- package/locales/ar/models.json +9 -0
- package/locales/bg-BG/models.json +9 -0
- package/locales/de-DE/models.json +9 -0
- package/locales/en-US/models.json +9 -0
- package/locales/es-ES/models.json +9 -0
- package/locales/fa-IR/models.json +9 -0
- package/locales/fr-FR/models.json +9 -0
- package/locales/it-IT/models.json +9 -0
- package/locales/ja-JP/models.json +9 -0
- package/locales/ko-KR/models.json +9 -0
- package/locales/nl-NL/models.json +9 -0
- package/locales/pl-PL/models.json +9 -0
- package/locales/pt-BR/models.json +9 -0
- package/locales/ru-RU/models.json +9 -0
- package/locales/tr-TR/models.json +9 -0
- package/locales/vi-VN/models.json +9 -0
- package/locales/zh-CN/models.json +9 -0
- package/locales/zh-TW/models.json +9 -0
- package/package.json +2 -2
- package/packages/const/src/index.ts +1 -0
- package/packages/const/src/settings/index.ts +1 -0
- package/packages/model-bank/src/aiModels/aihubmix.ts +25 -0
- package/packages/model-bank/src/aiModels/nvidia.ts +17 -1
- package/packages/model-bank/src/aiModels/openai.ts +80 -1
- package/packages/model-runtime/src/const/models.ts +2 -0
- package/packages/model-runtime/src/providers/openai/index.ts +15 -11
- package/packages/model-runtime/src/providers/openrouter/index.ts +7 -2
- package/packages/model-runtime/src/providers/openrouter/type.ts +4 -2
- package/packages/model-runtime/src/providers/vercelaigateway/index.ts +7 -0
- package/packages/model-runtime/src/utils/modelParse.ts +31 -3
- package/packages/types/src/aiProvider.ts +1 -2
- package/packages/types/src/discover/models.ts +3 -2
- package/packages/types/src/discover/providers.ts +3 -2
- package/packages/types/src/message/chat.ts +13 -0
- package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatList/ChatItem/index.tsx +1 -5
- package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatList/WelcomeChatItem/WelcomeMessage.tsx +1 -1
- package/src/app/[variants]/(main)/settings/provider/features/ModelList/CreateNewModelModal/Form.tsx +1 -2
- package/src/app/[variants]/(main)/settings/provider/features/ModelList/ModelItem.tsx +1 -4
- package/src/app/[variants]/(main)/settings/provider/features/ModelList/SortModelModal/ListItem.tsx +1 -2
- package/src/app/[variants]/(main)/settings/provider/features/ModelList/SortModelModal/index.tsx +1 -1
- package/src/{components → features}/ChatItem/ChatItem.tsx +5 -35
- package/src/{components → features}/ChatItem/components/MessageContent.tsx +27 -7
- package/src/features/Conversation/{components/MarkdownElements → MarkdownElements}/LobeArtifact/Render/index.tsx +1 -1
- package/src/features/Conversation/{components/MarkdownElements → MarkdownElements}/remarkPlugins/getNodeContent.test.ts +1 -1
- package/src/features/Conversation/{Actions → Messages/Assistant/Actions}/Error.tsx +1 -1
- package/src/features/Conversation/{components/ChatItem/ActionsBar.tsx → Messages/Assistant/Actions/index.tsx} +71 -44
- package/src/features/Conversation/Messages/Assistant/Block.tsx +63 -0
- package/src/features/Conversation/{Extras/Assistant.test.tsx → Messages/Assistant/Extra/index.test.tsx} +36 -31
- package/src/features/Conversation/{Extras/Assistant.tsx → Messages/Assistant/Extra/index.tsx} +13 -7
- package/src/features/Conversation/Messages/Assistant/MessageContent.tsx +102 -0
- package/src/features/Conversation/Messages/Assistant/index.tsx +235 -84
- package/src/features/Conversation/Messages/User/Actions.tsx +153 -0
- package/src/features/Conversation/Messages/User/BelowMessage.tsx +7 -2
- package/src/features/Conversation/{Extras/User.tsx → Messages/User/Extra.tsx} +9 -7
- package/src/features/Conversation/Messages/User/MessageContent.tsx +31 -0
- package/src/features/Conversation/Messages/User/index.tsx +127 -24
- package/src/features/Conversation/Messages/index.tsx +152 -0
- package/src/features/Conversation/{Extras → components/Extras}/Usage/UsageDetail/ModelCard.tsx +4 -3
- package/src/features/Conversation/{Extras → components/Extras}/Usage/UsageDetail/pricing.ts +3 -2
- package/src/features/Conversation/{Extras → components/Extras}/Usage/UsageDetail/tokens.test.ts +2 -3
- package/src/features/Conversation/components/{ChatItem/ShareMessageModal → ShareMessageModal}/ShareImage/Preview.tsx +1 -1
- package/src/features/Conversation/components/{ChatItem/ShareMessageModal → ShareMessageModal}/ShareText/index.tsx +1 -1
- package/src/features/Conversation/components/VirtualizedList/index.tsx +1 -0
- package/src/features/Conversation/hooks/useChatListActionsBar.tsx +29 -1
- package/src/features/Conversation/hooks/useDoubleClickEdit.ts +42 -0
- package/src/features/Conversation/index.ts +1 -1
- package/src/features/Conversation/types/{index.tsx → index.ts} +0 -7
- package/src/features/Portal/Thread/Chat/ChatItem.tsx +0 -7
- package/src/hooks/useUserAvatar.test.ts +129 -0
- package/src/hooks/useUserAvatar.ts +19 -0
- package/src/server/routers/lambda/aiModel.ts +7 -8
- package/src/store/user/slices/settings/selectors/settings.ts +6 -5
- package/src/features/ChatItem/index.tsx +0 -58
- package/src/features/Conversation/Actions/Assistant.tsx +0 -68
- package/src/features/Conversation/Actions/Fallback.tsx +0 -19
- package/src/features/Conversation/Actions/Tool.tsx +0 -33
- package/src/features/Conversation/Actions/User.tsx +0 -39
- package/src/features/Conversation/Actions/customAction.ts +0 -37
- package/src/features/Conversation/Actions/index.ts +0 -14
- package/src/features/Conversation/Extras/index.ts +0 -8
- package/src/features/Conversation/Extras/type.ts +0 -5
- package/src/features/Conversation/Messages/index.ts +0 -45
- package/src/features/Conversation/components/ChatItem/index.tsx +0 -358
- /package/src/{components → features}/ChatItem/components/Actions.tsx +0 -0
- /package/src/{components → features}/ChatItem/components/Avatar.tsx +0 -0
- /package/src/{components → features}/ChatItem/components/BorderSpacing.tsx +0 -0
- /package/src/{components → features}/ChatItem/components/ErrorContent.tsx +0 -0
- /package/src/{components → features}/ChatItem/components/Loading.tsx +0 -0
- /package/src/{components → features}/ChatItem/components/Title.tsx +0 -0
- /package/src/{components → features}/ChatItem/index.ts +0 -0
- /package/src/{components → features}/ChatItem/style.ts +0 -0
- /package/src/{components → features}/ChatItem/type.ts +0 -0
- /package/src/features/Conversation/{components/MarkdownElements → MarkdownElements}/LobeArtifact/Render/Icon.tsx +0 -0
- /package/src/features/Conversation/{components/MarkdownElements → MarkdownElements}/LobeArtifact/index.ts +0 -0
- /package/src/features/Conversation/{components/MarkdownElements → MarkdownElements}/LobeArtifact/rehypePlugin.test.ts +0 -0
- /package/src/features/Conversation/{components/MarkdownElements → MarkdownElements}/LobeArtifact/rehypePlugin.ts +0 -0
- /package/src/features/Conversation/{components/MarkdownElements → MarkdownElements}/LobeThinking/Render.tsx +0 -0
- /package/src/features/Conversation/{components/MarkdownElements → MarkdownElements}/LobeThinking/index.ts +0 -0
- /package/src/features/Conversation/{components/MarkdownElements → MarkdownElements}/LocalFile/Render/index.tsx +0 -0
- /package/src/features/Conversation/{components/MarkdownElements → MarkdownElements}/LocalFile/index.ts +0 -0
- /package/src/features/Conversation/{components/MarkdownElements → MarkdownElements}/Thinking/Render.tsx +0 -0
- /package/src/features/Conversation/{components/MarkdownElements → MarkdownElements}/Thinking/index.ts +0 -0
- /package/src/features/Conversation/{components/MarkdownElements → MarkdownElements}/index.ts +0 -0
- /package/src/features/Conversation/{components/MarkdownElements → MarkdownElements}/remarkPlugins/__snapshots__/createRemarkSelfClosingTagPlugin.test.ts.snap +0 -0
- /package/src/features/Conversation/{components/MarkdownElements → MarkdownElements}/remarkPlugins/createRemarkCustomTagPlugin.ts +0 -0
- /package/src/features/Conversation/{components/MarkdownElements → MarkdownElements}/remarkPlugins/createRemarkSelfClosingTagPlugin.test.ts +0 -0
- /package/src/features/Conversation/{components/MarkdownElements → MarkdownElements}/remarkPlugins/createRemarkSelfClosingTagPlugin.ts +0 -0
- /package/src/features/Conversation/{components/MarkdownElements → MarkdownElements}/remarkPlugins/getNodeContent.ts +0 -0
- /package/src/features/Conversation/{components/MarkdownElements → MarkdownElements}/type.ts +0 -0
- /package/src/features/Conversation/{components/MarkdownElements → MarkdownElements}/utils.ts +0 -0
- /package/src/features/Conversation/{Extras → components/Extras}/ExtraContainer.tsx +0 -0
- /package/src/features/Conversation/{Extras → components/Extras}/TTS/FilePlayer.tsx +0 -0
- /package/src/features/Conversation/{Extras → components/Extras}/TTS/InitPlayer.tsx +0 -0
- /package/src/features/Conversation/{Extras → components/Extras}/TTS/Player.tsx +0 -0
- /package/src/features/Conversation/{Extras → components/Extras}/TTS/index.tsx +0 -0
- /package/src/features/Conversation/{Extras → components/Extras}/Translate.tsx +0 -0
- /package/src/features/Conversation/{Extras → components/Extras}/Usage/UsageDetail/TokenProgress.tsx +0 -0
- /package/src/features/Conversation/{Extras → components/Extras}/Usage/UsageDetail/index.tsx +0 -0
- /package/src/features/Conversation/{Extras → components/Extras}/Usage/UsageDetail/tokens.ts +0 -0
- /package/src/features/Conversation/{Extras → components/Extras}/Usage/index.tsx +0 -0
- /package/src/features/Conversation/components/{ChatItem/ShareMessageModal → ShareMessageModal}/ShareImage/index.tsx +0 -0
- /package/src/features/Conversation/components/{ChatItem/ShareMessageModal → ShareMessageModal}/ShareImage/style.ts +0 -0
- /package/src/features/Conversation/components/{ChatItem/ShareMessageModal → ShareMessageModal}/ShareImage/type.ts +0 -0
- /package/src/features/Conversation/components/{ChatItem/ShareMessageModal → ShareMessageModal}/ShareText/Preview.tsx +0 -0
- /package/src/features/Conversation/components/{ChatItem/ShareMessageModal → ShareMessageModal}/ShareText/template.test.ts +0 -0
- /package/src/features/Conversation/components/{ChatItem/ShareMessageModal → ShareMessageModal}/ShareText/template.ts +0 -0
- /package/src/features/Conversation/components/{ChatItem/ShareMessageModal → ShareMessageModal}/ShareText/type.ts +0 -0
- /package/src/features/Conversation/components/{ChatItem/ShareMessageModal → ShareMessageModal}/index.tsx +0 -0
- /package/src/features/Conversation/components/{ChatItem/ShareMessageModal → ShareMessageModal}/style.ts +0 -0
- /package/src/features/Conversation/{components/ChatItem → context}/InPortalThreadContext.ts +0 -0
- /package/src/features/Conversation/{components/ChatItem/utils.test.ts → utils.test.ts} +0 -0
- /package/src/features/Conversation/{components/ChatItem/utils.ts → utils.ts} +0 -0
|
@@ -1,34 +1,137 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ChatMessage } from '@lobechat/types';
|
|
2
|
+
import { useResponsive } from 'antd-style';
|
|
3
|
+
import { ReactNode, memo, useCallback, useMemo } from 'react';
|
|
2
4
|
import { Flexbox } from 'react-layout-kit';
|
|
3
5
|
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
6
|
+
import Avatar from '@/features/ChatItem/components/Avatar';
|
|
7
|
+
import BorderSpacing from '@/features/ChatItem/components/BorderSpacing';
|
|
8
|
+
import MessageContent from '@/features/ChatItem/components/MessageContent';
|
|
9
|
+
import Title from '@/features/ChatItem/components/Title';
|
|
10
|
+
import { useStyles } from '@/features/ChatItem/style';
|
|
11
|
+
import { useUserAvatar } from '@/hooks/useUserAvatar';
|
|
12
|
+
import { useAgentStore } from '@/store/agent';
|
|
13
|
+
import { agentChatConfigSelectors } from '@/store/agent/selectors';
|
|
14
|
+
import { useChatStore } from '@/store/chat';
|
|
15
|
+
import { chatSelectors } from '@/store/chat/selectors';
|
|
16
|
+
import { useUserStore } from '@/store/user';
|
|
17
|
+
import { userProfileSelectors } from '@/store/user/selectors';
|
|
7
18
|
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import
|
|
19
|
+
import { useDoubleClickEdit } from '../../hooks/useDoubleClickEdit';
|
|
20
|
+
import { UserActionsBar } from './Actions';
|
|
21
|
+
import { UserBelowMessage } from './BelowMessage';
|
|
22
|
+
import { UserMessageExtra } from './Extra';
|
|
23
|
+
import { MarkdownRender as UserMarkdownRender } from './MarkdownRender';
|
|
24
|
+
import { UserMessageContent } from './MessageContent';
|
|
11
25
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
26
|
+
interface UserMessageProps extends ChatMessage {
|
|
27
|
+
disableEditing?: boolean;
|
|
28
|
+
index: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const UserMessage = memo<UserMessageProps>((props) => {
|
|
32
|
+
const { id, ragQuery, content, createdAt, error, role, index, extra, disableEditing } = props;
|
|
33
|
+
|
|
34
|
+
const { mobile } = useResponsive();
|
|
35
|
+
const avatar = useUserAvatar();
|
|
36
|
+
const title = useUserStore(userProfileSelectors.displayUserName);
|
|
37
|
+
|
|
38
|
+
const displayMode = useAgentStore(agentChatConfigSelectors.displayMode);
|
|
39
|
+
|
|
40
|
+
const [editing, generating, isInRAGFlow] = useChatStore((s) => [
|
|
41
|
+
chatSelectors.isMessageEditing(id)(s),
|
|
42
|
+
chatSelectors.isMessageGenerating(id)(s),
|
|
43
|
+
chatSelectors.isMessageInRAGFlow(id)(s),
|
|
44
|
+
]);
|
|
45
|
+
|
|
46
|
+
const loading = isInRAGFlow || generating;
|
|
47
|
+
|
|
48
|
+
const placement = displayMode === 'chat' ? 'right' : 'left';
|
|
49
|
+
const variant = displayMode === 'chat' ? 'bubble' : 'docs';
|
|
50
|
+
|
|
51
|
+
const { styles } = useStyles({
|
|
52
|
+
editing,
|
|
53
|
+
placement,
|
|
54
|
+
primary: true,
|
|
55
|
+
showTitle: false,
|
|
56
|
+
time: createdAt,
|
|
57
|
+
title,
|
|
58
|
+
variant,
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const onDoubleClick = useDoubleClickEdit({ disableEditing, error, id, index, role });
|
|
62
|
+
|
|
63
|
+
const renderMessage = useCallback(
|
|
64
|
+
(editableContent: ReactNode) => (
|
|
65
|
+
<UserMessageContent {...props} editableContent={editableContent} />
|
|
66
|
+
),
|
|
67
|
+
[props],
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
const markdownProps = useMemo(
|
|
71
|
+
() => ({
|
|
72
|
+
customRender: (dom: ReactNode, { text }: { text: string }) => (
|
|
73
|
+
<UserMarkdownRender displayMode={displayMode} dom={dom} id={id} text={text} />
|
|
74
|
+
),
|
|
75
|
+
}),
|
|
76
|
+
[displayMode],
|
|
77
|
+
);
|
|
18
78
|
|
|
19
79
|
return (
|
|
20
|
-
<Flexbox
|
|
21
|
-
{
|
|
22
|
-
{
|
|
23
|
-
{
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
80
|
+
<Flexbox
|
|
81
|
+
className={styles.container}
|
|
82
|
+
direction={placement === 'left' ? 'horizontal' : 'horizontal-reverse'}
|
|
83
|
+
gap={mobile ? 6 : 12}
|
|
84
|
+
>
|
|
85
|
+
<Avatar
|
|
86
|
+
alt={title}
|
|
87
|
+
avatar={{ avatar, title }}
|
|
88
|
+
loading={loading}
|
|
89
|
+
placement={placement}
|
|
90
|
+
size={mobile ? 32 : undefined}
|
|
91
|
+
style={{ marginTop: 6 }}
|
|
92
|
+
/>
|
|
93
|
+
<Flexbox
|
|
94
|
+
align={placement === 'left' ? 'flex-start' : 'flex-end'}
|
|
95
|
+
className={styles.messageContainer}
|
|
96
|
+
>
|
|
97
|
+
<Title
|
|
98
|
+
avatar={{ avatar, title }}
|
|
99
|
+
placement={placement}
|
|
100
|
+
showTitle={false}
|
|
101
|
+
time={createdAt}
|
|
102
|
+
/>
|
|
103
|
+
<Flexbox
|
|
104
|
+
align={placement === 'left' ? 'flex-start' : 'flex-end'}
|
|
105
|
+
className={styles.messageContent}
|
|
106
|
+
direction={placement === 'left' ? 'horizontal' : 'horizontal-reverse'}
|
|
107
|
+
gap={8}
|
|
108
|
+
>
|
|
109
|
+
<Flexbox width={'100%'}>
|
|
110
|
+
<MessageContent
|
|
111
|
+
editing={editing}
|
|
112
|
+
id={id}
|
|
113
|
+
markdownProps={markdownProps}
|
|
114
|
+
message={content}
|
|
115
|
+
messageExtra={<UserMessageExtra content={content} extra={extra} id={id} />}
|
|
116
|
+
onDoubleClick={onDoubleClick}
|
|
117
|
+
placement={placement}
|
|
118
|
+
primary
|
|
119
|
+
renderMessage={renderMessage}
|
|
120
|
+
variant={variant}
|
|
121
|
+
/>
|
|
122
|
+
</Flexbox>
|
|
123
|
+
|
|
124
|
+
{!disableEditing && (
|
|
125
|
+
<Flexbox align={'flex-start'} className={styles.actions} role="menubar">
|
|
126
|
+
<UserActionsBar data={props} id={id} index={index} />
|
|
127
|
+
</Flexbox>
|
|
128
|
+
)}
|
|
129
|
+
</Flexbox>
|
|
130
|
+
<UserBelowMessage content={content} id={id} ragQuery={ragQuery} />
|
|
131
|
+
</Flexbox>
|
|
132
|
+
{mobile && variant === 'bubble' && <BorderSpacing borderSpacing={32} />}
|
|
29
133
|
</Flexbox>
|
|
30
134
|
);
|
|
31
135
|
});
|
|
32
136
|
|
|
33
|
-
export
|
|
34
|
-
export { MarkdownRender as UserMarkdownRender } from './MarkdownRender';
|
|
137
|
+
export default UserMessage;
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { createStyles } from 'antd-style';
|
|
4
|
+
import isEqual from 'fast-deep-equal';
|
|
5
|
+
import { ReactNode, memo, useCallback, useEffect, useMemo, useRef } from 'react';
|
|
6
|
+
import { Flexbox } from 'react-layout-kit';
|
|
7
|
+
|
|
8
|
+
import { isDesktop } from '@/const/version';
|
|
9
|
+
import {
|
|
10
|
+
removeVirtuosoVisibleItem,
|
|
11
|
+
upsertVirtuosoVisibleItem,
|
|
12
|
+
} from '@/features/Conversation/components/VirtualizedList/VirtuosoContext';
|
|
13
|
+
import { useChatStore } from '@/store/chat';
|
|
14
|
+
import { chatSelectors } from '@/store/chat/selectors';
|
|
15
|
+
|
|
16
|
+
import History from '../components/History';
|
|
17
|
+
import { InPortalThreadContext } from '../context/InPortalThreadContext';
|
|
18
|
+
import AssistantMessage from './Assistant';
|
|
19
|
+
import UserMessage from './User';
|
|
20
|
+
|
|
21
|
+
const useStyles = createStyles(({ css, prefixCls }) => ({
|
|
22
|
+
loading: css`
|
|
23
|
+
opacity: 0.6;
|
|
24
|
+
`,
|
|
25
|
+
message: css`
|
|
26
|
+
position: relative;
|
|
27
|
+
// prevent the textarea too long
|
|
28
|
+
.${prefixCls}-input {
|
|
29
|
+
max-height: 900px;
|
|
30
|
+
}
|
|
31
|
+
`,
|
|
32
|
+
}));
|
|
33
|
+
|
|
34
|
+
export interface ChatListItemProps {
|
|
35
|
+
className?: string;
|
|
36
|
+
disableEditing?: boolean;
|
|
37
|
+
enableHistoryDivider?: boolean;
|
|
38
|
+
endRender?: ReactNode;
|
|
39
|
+
id: string;
|
|
40
|
+
inPortalThread?: boolean;
|
|
41
|
+
index: number;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const Item = memo<ChatListItemProps>(
|
|
45
|
+
({
|
|
46
|
+
className,
|
|
47
|
+
enableHistoryDivider,
|
|
48
|
+
id,
|
|
49
|
+
endRender,
|
|
50
|
+
disableEditing,
|
|
51
|
+
inPortalThread = false,
|
|
52
|
+
index,
|
|
53
|
+
}) => {
|
|
54
|
+
const { styles, cx } = useStyles();
|
|
55
|
+
const containerRef = useRef<HTMLDivElement | null>(null);
|
|
56
|
+
|
|
57
|
+
const item = useChatStore(chatSelectors.getMessageById(id), isEqual);
|
|
58
|
+
|
|
59
|
+
const [isMessageLoading] = useChatStore((s) => [chatSelectors.isMessageLoading(id)(s)]);
|
|
60
|
+
|
|
61
|
+
// ======================= Performance Optimization ======================= //
|
|
62
|
+
// these useMemo/useCallback are all for the performance optimization
|
|
63
|
+
// maybe we can remove it in React 19
|
|
64
|
+
// ======================================================================== //
|
|
65
|
+
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
if (typeof window === 'undefined' || typeof IntersectionObserver === 'undefined') return;
|
|
68
|
+
|
|
69
|
+
const element = containerRef.current;
|
|
70
|
+
if (!element) return;
|
|
71
|
+
|
|
72
|
+
const root = element.closest('[data-virtuoso-scroller]');
|
|
73
|
+
const thresholds = [0, 0.01, 0.1, 0.25, 0.5, 0.75, 0.9, 1];
|
|
74
|
+
const options: any = { threshold: thresholds };
|
|
75
|
+
|
|
76
|
+
if (root instanceof Element) options.root = root;
|
|
77
|
+
|
|
78
|
+
const observer = new IntersectionObserver((entries) => {
|
|
79
|
+
entries.forEach((entry) => {
|
|
80
|
+
if (entry.target !== element) return;
|
|
81
|
+
|
|
82
|
+
if (entry.isIntersecting) {
|
|
83
|
+
const { bottom, top } = entry.intersectionRect;
|
|
84
|
+
|
|
85
|
+
upsertVirtuosoVisibleItem(index, {
|
|
86
|
+
bottom,
|
|
87
|
+
ratio: entry.intersectionRatio,
|
|
88
|
+
top,
|
|
89
|
+
});
|
|
90
|
+
} else {
|
|
91
|
+
removeVirtuosoVisibleItem(index);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
}, options);
|
|
95
|
+
|
|
96
|
+
observer.observe(element);
|
|
97
|
+
|
|
98
|
+
return () => {
|
|
99
|
+
observer.disconnect();
|
|
100
|
+
removeVirtuosoVisibleItem(index);
|
|
101
|
+
};
|
|
102
|
+
}, [index]);
|
|
103
|
+
|
|
104
|
+
const onContextMenu = useCallback(async () => {
|
|
105
|
+
if (isDesktop && item) {
|
|
106
|
+
const { electronSystemService } = await import('@/services/electron/system');
|
|
107
|
+
|
|
108
|
+
electronSystemService.showContextMenu('chat', {
|
|
109
|
+
content: item.content,
|
|
110
|
+
hasError: !!item.error,
|
|
111
|
+
messageId: id,
|
|
112
|
+
role: item.role,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}, [id, item]);
|
|
116
|
+
|
|
117
|
+
const renderContent = useMemo(() => {
|
|
118
|
+
switch (item?.role) {
|
|
119
|
+
case 'user': {
|
|
120
|
+
return <UserMessage {...item} disableEditing={disableEditing} index={index} />;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
case 'assistant': {
|
|
124
|
+
return <AssistantMessage {...item} disableEditing={disableEditing} index={index} />;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return null;
|
|
129
|
+
}, [item]);
|
|
130
|
+
|
|
131
|
+
if (!item) return;
|
|
132
|
+
|
|
133
|
+
return (
|
|
134
|
+
<InPortalThreadContext.Provider value={inPortalThread}>
|
|
135
|
+
{enableHistoryDivider && <History />}
|
|
136
|
+
<Flexbox
|
|
137
|
+
className={cx(styles.message, className, isMessageLoading && styles.loading)}
|
|
138
|
+
data-index={index}
|
|
139
|
+
onContextMenu={onContextMenu}
|
|
140
|
+
ref={containerRef}
|
|
141
|
+
>
|
|
142
|
+
{renderContent}
|
|
143
|
+
{endRender}
|
|
144
|
+
</Flexbox>
|
|
145
|
+
</InPortalThreadContext.Provider>
|
|
146
|
+
);
|
|
147
|
+
},
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
Item.displayName = 'ChatItem';
|
|
151
|
+
|
|
152
|
+
export default Item;
|
package/src/features/Conversation/{Extras → components/Extras}/Usage/UsageDetail/ModelCard.tsx
RENAMED
|
@@ -1,16 +1,17 @@
|
|
|
1
|
+
import { getCachedTextInputUnitRate, getWriteCacheInputUnitRate } from '@lobechat/utils';
|
|
1
2
|
import { ModelIcon } from '@lobehub/icons';
|
|
2
3
|
import { Icon, Segmented, Tooltip } from '@lobehub/ui';
|
|
3
4
|
import { createStyles } from 'antd-style';
|
|
4
5
|
import { ArrowDownToDot, ArrowUpFromDot, BookUp2Icon, CircleFadingArrowUp } from 'lucide-react';
|
|
6
|
+
import { LobeDefaultAiModelListItem } from 'model-bank';
|
|
5
7
|
import { memo } from 'react';
|
|
6
8
|
import { useTranslation } from 'react-i18next';
|
|
7
9
|
import { Flexbox } from 'react-layout-kit';
|
|
8
10
|
|
|
9
|
-
import { getPrice } from '@/features/Conversation/Extras/Usage/UsageDetail/pricing';
|
|
10
11
|
import { useGlobalStore } from '@/store/global';
|
|
11
12
|
import { systemStatusSelectors } from '@/store/global/selectors';
|
|
12
|
-
|
|
13
|
-
import {
|
|
13
|
+
|
|
14
|
+
import { getPrice } from './pricing';
|
|
14
15
|
|
|
15
16
|
export const useStyles = createStyles(({ css, token }) => {
|
|
16
17
|
return {
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
+
import { ModelPriceCurrency, Pricing } from 'model-bank';
|
|
2
|
+
|
|
1
3
|
import {
|
|
2
4
|
formatPriceByCurrency,
|
|
3
5
|
getCachedTextInputUnitRate,
|
|
4
6
|
getTextInputUnitRate,
|
|
5
7
|
getTextOutputUnitRate,
|
|
6
8
|
getWriteCacheInputUnitRate,
|
|
7
|
-
} from '
|
|
8
|
-
import { ModelPriceCurrency, Pricing } from 'model-bank';
|
|
9
|
+
} from '@/utils/index';
|
|
9
10
|
|
|
10
11
|
export const getPrice = (pricing: Pricing) => {
|
|
11
12
|
const inputRate = getTextInputUnitRate(pricing);
|
package/src/features/Conversation/{Extras → components/Extras}/Usage/UsageDetail/tokens.test.ts
RENAMED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
+
import { ModelUsage } from '@lobechat/types';
|
|
2
|
+
import { LobeDefaultAiModelListItem } from 'model-bank';
|
|
1
3
|
import { describe, expect, it } from 'vitest';
|
|
2
4
|
|
|
3
|
-
import { ModelUsage } from '@/types/message';
|
|
4
|
-
|
|
5
|
-
import { LobeDefaultAiModelListItem } from '../../../../../../packages/model-bank/src/types/aiModel';
|
|
6
5
|
import { getDetailsToken } from './tokens';
|
|
7
6
|
|
|
8
7
|
describe('getDetailsToken', () => {
|
|
@@ -5,7 +5,6 @@ import { memo } from 'react';
|
|
|
5
5
|
import { useTranslation } from 'react-i18next';
|
|
6
6
|
import { Flexbox } from 'react-layout-kit';
|
|
7
7
|
|
|
8
|
-
import pkg from '@/../package.json';
|
|
9
8
|
import { ProductLogo } from '@/components/Branding';
|
|
10
9
|
import { ChatItem } from '@/features/Conversation';
|
|
11
10
|
import PluginTag from '@/features/PluginTag';
|
|
@@ -15,6 +14,7 @@ import { useSessionStore } from '@/store/session';
|
|
|
15
14
|
import { sessionMetaSelectors, sessionSelectors } from '@/store/session/selectors';
|
|
16
15
|
import { ChatMessage } from '@/types/message';
|
|
17
16
|
|
|
17
|
+
import pkg from '../../../../../../package.json';
|
|
18
18
|
import { useContainerStyles } from '../style';
|
|
19
19
|
import { useStyles } from './style';
|
|
20
20
|
import { FieldType } from './type';
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { exportFile } from '@lobechat/utils/client';
|
|
2
1
|
import { Button, copyToClipboard } from '@lobehub/ui';
|
|
3
2
|
import { App } from 'antd';
|
|
4
3
|
import isEqual from 'fast-deep-equal';
|
|
@@ -11,6 +10,7 @@ import { useIsMobile } from '@/hooks/useIsMobile';
|
|
|
11
10
|
import { useChatStore } from '@/store/chat';
|
|
12
11
|
import { topicSelectors } from '@/store/chat/selectors';
|
|
13
12
|
import { ChatMessage } from '@/types/message';
|
|
13
|
+
import { exportFile } from '@/utils/client';
|
|
14
14
|
|
|
15
15
|
import { useStyles } from '../style';
|
|
16
16
|
import Preview from './Preview';
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import type { ActionIconGroupItemType } from '@lobehub/ui';
|
|
2
|
+
import { css, cx } from 'antd-style';
|
|
2
3
|
import {
|
|
3
4
|
Copy,
|
|
4
5
|
DownloadIcon,
|
|
5
6
|
Edit,
|
|
7
|
+
LanguagesIcon,
|
|
6
8
|
ListRestart,
|
|
9
|
+
Play,
|
|
7
10
|
RotateCcw,
|
|
8
11
|
Share2,
|
|
9
12
|
Split,
|
|
@@ -13,6 +16,14 @@ import { useMemo } from 'react';
|
|
|
13
16
|
import { useTranslation } from 'react-i18next';
|
|
14
17
|
|
|
15
18
|
import { isDeprecatedEdition } from '@/const/version';
|
|
19
|
+
import { localeOptions } from '@/locales/resources';
|
|
20
|
+
|
|
21
|
+
const translateStyle = css`
|
|
22
|
+
.ant-dropdown-menu-sub {
|
|
23
|
+
overflow-y: scroll;
|
|
24
|
+
max-height: 400px;
|
|
25
|
+
}
|
|
26
|
+
`;
|
|
16
27
|
|
|
17
28
|
interface ChatListActionsBar {
|
|
18
29
|
branching: ActionIconGroupItemType;
|
|
@@ -24,12 +35,14 @@ interface ChatListActionsBar {
|
|
|
24
35
|
export: ActionIconGroupItemType;
|
|
25
36
|
regenerate: ActionIconGroupItemType;
|
|
26
37
|
share: ActionIconGroupItemType;
|
|
38
|
+
translate: ActionIconGroupItemType;
|
|
39
|
+
tts: ActionIconGroupItemType;
|
|
27
40
|
}
|
|
28
41
|
|
|
29
42
|
export const useChatListActionsBar = ({
|
|
30
43
|
hasThread,
|
|
31
44
|
}: { hasThread?: boolean } = {}): ChatListActionsBar => {
|
|
32
|
-
const { t } = useTranslation('common');
|
|
45
|
+
const { t } = useTranslation(['common', 'chat']);
|
|
33
46
|
|
|
34
47
|
return useMemo(
|
|
35
48
|
() => ({
|
|
@@ -85,6 +98,21 @@ export const useChatListActionsBar = ({
|
|
|
85
98
|
key: 'share',
|
|
86
99
|
label: t('share', { defaultValue: 'Share' }),
|
|
87
100
|
},
|
|
101
|
+
translate: {
|
|
102
|
+
children: localeOptions.map((i) => ({
|
|
103
|
+
key: i.value,
|
|
104
|
+
label: t(`lang.${i.value}`),
|
|
105
|
+
})),
|
|
106
|
+
icon: LanguagesIcon,
|
|
107
|
+
key: 'translate',
|
|
108
|
+
label: t('translate.action', { ns: 'chat' }),
|
|
109
|
+
popupClassName: cx(translateStyle),
|
|
110
|
+
},
|
|
111
|
+
tts: {
|
|
112
|
+
icon: Play,
|
|
113
|
+
key: 'tts',
|
|
114
|
+
label: t('tts.action', { ns: 'chat' }),
|
|
115
|
+
},
|
|
88
116
|
}),
|
|
89
117
|
[hasThread],
|
|
90
118
|
);
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { MouseEventHandler, use, useCallback } from 'react';
|
|
2
|
+
|
|
3
|
+
import { useChatStore } from '@/store/chat';
|
|
4
|
+
|
|
5
|
+
import { VirtuosoContext } from '../components/VirtualizedList/VirtuosoContext';
|
|
6
|
+
|
|
7
|
+
interface UseDoubleClickEditProps {
|
|
8
|
+
disableEditing?: boolean;
|
|
9
|
+
error: any;
|
|
10
|
+
id: string;
|
|
11
|
+
index: number;
|
|
12
|
+
role: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const useDoubleClickEdit = ({
|
|
16
|
+
disableEditing,
|
|
17
|
+
role,
|
|
18
|
+
error,
|
|
19
|
+
id,
|
|
20
|
+
index,
|
|
21
|
+
}: UseDoubleClickEditProps) => {
|
|
22
|
+
const [toggleMessageEditing] = useChatStore((s) => [s.toggleMessageEditing]);
|
|
23
|
+
const virtuosoRef = use(VirtuosoContext);
|
|
24
|
+
|
|
25
|
+
return useCallback<MouseEventHandler<HTMLDivElement>>(
|
|
26
|
+
(e) => {
|
|
27
|
+
if (
|
|
28
|
+
disableEditing ||
|
|
29
|
+
error ||
|
|
30
|
+
id === 'default' ||
|
|
31
|
+
!e.altKey ||
|
|
32
|
+
!['assistant', 'user'].includes(role)
|
|
33
|
+
)
|
|
34
|
+
return;
|
|
35
|
+
|
|
36
|
+
toggleMessageEditing(id, true);
|
|
37
|
+
|
|
38
|
+
virtuosoRef?.current?.scrollIntoView({ align: 'start', behavior: 'auto', index });
|
|
39
|
+
},
|
|
40
|
+
[role, disableEditing],
|
|
41
|
+
);
|
|
42
|
+
};
|
|
@@ -1,14 +1,9 @@
|
|
|
1
|
-
import { type ActionIconGroupEvent } from '@lobehub/ui';
|
|
2
1
|
import { type ChatItemProps } from '@lobehub/ui/chat';
|
|
3
2
|
import { FC, ReactNode } from 'react';
|
|
4
3
|
|
|
5
4
|
import { LLMRoleType } from '@/types/llm';
|
|
6
5
|
import { ChatMessage } from '@/types/message';
|
|
7
6
|
|
|
8
|
-
import { type ActionsBarProps } from '../components/ChatItem/ActionsBar';
|
|
9
|
-
|
|
10
|
-
export type OnActionsClick = (action: ActionIconGroupEvent, message: ChatMessage) => void;
|
|
11
|
-
export type OnAvatarsClick = (role: RenderRole) => ChatItemProps['onAvatarClick'];
|
|
12
7
|
export type RenderRole = LLMRoleType | 'default' | 'history' | string;
|
|
13
8
|
export type RenderMessage = FC<ChatMessage & { editableContent: ReactNode }>;
|
|
14
9
|
export type RenderBelowMessage = FC<ChatMessage>;
|
|
@@ -20,8 +15,6 @@ export type MarkdownCustomRender = (props: {
|
|
|
20
15
|
text: string;
|
|
21
16
|
}) => ReactNode;
|
|
22
17
|
|
|
23
|
-
export type RenderAction = FC<ActionsBarProps & ChatMessage>;
|
|
24
|
-
|
|
25
18
|
export type RenderItem = FC<{ key: string } & ChatMessage & ListItemProps>;
|
|
26
19
|
|
|
27
20
|
export interface ListItemProps {
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import React, { memo, useMemo } from 'react';
|
|
2
2
|
|
|
3
3
|
import { ChatItem } from '@/features/Conversation';
|
|
4
|
-
import ActionsBar from '@/features/Conversation/components/ChatItem/ActionsBar';
|
|
5
4
|
import { useAgentStore } from '@/store/agent';
|
|
6
5
|
import { agentChatConfigSelectors } from '@/store/agent/selectors';
|
|
7
6
|
import { useChatStore } from '@/store/chat';
|
|
@@ -30,18 +29,12 @@ const ThreadChatItem = memo<ThreadChatItemProps>(({ id, index }) => {
|
|
|
30
29
|
|
|
31
30
|
const isParentMessage = index <= threadStartMessageIndex;
|
|
32
31
|
|
|
33
|
-
const actionBar = useMemo(
|
|
34
|
-
() => !isParentMessage && <ActionsBar id={id} inPortalThread index={index} />,
|
|
35
|
-
[id, isParentMessage],
|
|
36
|
-
);
|
|
37
|
-
|
|
38
32
|
const enableHistoryDivider = useAgentStore(
|
|
39
33
|
agentChatConfigSelectors.enableHistoryDivider(historyLength, index),
|
|
40
34
|
);
|
|
41
35
|
|
|
42
36
|
return (
|
|
43
37
|
<ChatItem
|
|
44
|
-
actionBar={actionBar}
|
|
45
38
|
disableEditing={isParentMessage}
|
|
46
39
|
enableHistoryDivider={enableHistoryDivider}
|
|
47
40
|
endRender={endRender}
|