@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.
Files changed (134) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/changelog/v1.json +18 -0
  3. package/locales/ar/models.json +9 -0
  4. package/locales/bg-BG/models.json +9 -0
  5. package/locales/de-DE/models.json +9 -0
  6. package/locales/en-US/models.json +9 -0
  7. package/locales/es-ES/models.json +9 -0
  8. package/locales/fa-IR/models.json +9 -0
  9. package/locales/fr-FR/models.json +9 -0
  10. package/locales/it-IT/models.json +9 -0
  11. package/locales/ja-JP/models.json +9 -0
  12. package/locales/ko-KR/models.json +9 -0
  13. package/locales/nl-NL/models.json +9 -0
  14. package/locales/pl-PL/models.json +9 -0
  15. package/locales/pt-BR/models.json +9 -0
  16. package/locales/ru-RU/models.json +9 -0
  17. package/locales/tr-TR/models.json +9 -0
  18. package/locales/vi-VN/models.json +9 -0
  19. package/locales/zh-CN/models.json +9 -0
  20. package/locales/zh-TW/models.json +9 -0
  21. package/package.json +2 -2
  22. package/packages/const/src/index.ts +1 -0
  23. package/packages/const/src/settings/index.ts +1 -0
  24. package/packages/model-bank/src/aiModels/aihubmix.ts +25 -0
  25. package/packages/model-bank/src/aiModels/nvidia.ts +17 -1
  26. package/packages/model-bank/src/aiModels/openai.ts +80 -1
  27. package/packages/model-runtime/src/const/models.ts +2 -0
  28. package/packages/model-runtime/src/providers/openai/index.ts +15 -11
  29. package/packages/model-runtime/src/providers/openrouter/index.ts +7 -2
  30. package/packages/model-runtime/src/providers/openrouter/type.ts +4 -2
  31. package/packages/model-runtime/src/providers/vercelaigateway/index.ts +7 -0
  32. package/packages/model-runtime/src/utils/modelParse.ts +31 -3
  33. package/packages/types/src/aiProvider.ts +1 -2
  34. package/packages/types/src/discover/models.ts +3 -2
  35. package/packages/types/src/discover/providers.ts +3 -2
  36. package/packages/types/src/message/chat.ts +13 -0
  37. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatList/ChatItem/index.tsx +1 -5
  38. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatList/WelcomeChatItem/WelcomeMessage.tsx +1 -1
  39. package/src/app/[variants]/(main)/settings/provider/features/ModelList/CreateNewModelModal/Form.tsx +1 -2
  40. package/src/app/[variants]/(main)/settings/provider/features/ModelList/ModelItem.tsx +1 -4
  41. package/src/app/[variants]/(main)/settings/provider/features/ModelList/SortModelModal/ListItem.tsx +1 -2
  42. package/src/app/[variants]/(main)/settings/provider/features/ModelList/SortModelModal/index.tsx +1 -1
  43. package/src/{components → features}/ChatItem/ChatItem.tsx +5 -35
  44. package/src/{components → features}/ChatItem/components/MessageContent.tsx +27 -7
  45. package/src/features/Conversation/{components/MarkdownElements → MarkdownElements}/LobeArtifact/Render/index.tsx +1 -1
  46. package/src/features/Conversation/{components/MarkdownElements → MarkdownElements}/remarkPlugins/getNodeContent.test.ts +1 -1
  47. package/src/features/Conversation/{Actions → Messages/Assistant/Actions}/Error.tsx +1 -1
  48. package/src/features/Conversation/{components/ChatItem/ActionsBar.tsx → Messages/Assistant/Actions/index.tsx} +71 -44
  49. package/src/features/Conversation/Messages/Assistant/Block.tsx +63 -0
  50. package/src/features/Conversation/{Extras/Assistant.test.tsx → Messages/Assistant/Extra/index.test.tsx} +36 -31
  51. package/src/features/Conversation/{Extras/Assistant.tsx → Messages/Assistant/Extra/index.tsx} +13 -7
  52. package/src/features/Conversation/Messages/Assistant/MessageContent.tsx +102 -0
  53. package/src/features/Conversation/Messages/Assistant/index.tsx +235 -84
  54. package/src/features/Conversation/Messages/User/Actions.tsx +153 -0
  55. package/src/features/Conversation/Messages/User/BelowMessage.tsx +7 -2
  56. package/src/features/Conversation/{Extras/User.tsx → Messages/User/Extra.tsx} +9 -7
  57. package/src/features/Conversation/Messages/User/MessageContent.tsx +31 -0
  58. package/src/features/Conversation/Messages/User/index.tsx +127 -24
  59. package/src/features/Conversation/Messages/index.tsx +152 -0
  60. package/src/features/Conversation/{Extras → components/Extras}/Usage/UsageDetail/ModelCard.tsx +4 -3
  61. package/src/features/Conversation/{Extras → components/Extras}/Usage/UsageDetail/pricing.ts +3 -2
  62. package/src/features/Conversation/{Extras → components/Extras}/Usage/UsageDetail/tokens.test.ts +2 -3
  63. package/src/features/Conversation/components/{ChatItem/ShareMessageModal → ShareMessageModal}/ShareImage/Preview.tsx +1 -1
  64. package/src/features/Conversation/components/{ChatItem/ShareMessageModal → ShareMessageModal}/ShareText/index.tsx +1 -1
  65. package/src/features/Conversation/components/VirtualizedList/index.tsx +1 -0
  66. package/src/features/Conversation/hooks/useChatListActionsBar.tsx +29 -1
  67. package/src/features/Conversation/hooks/useDoubleClickEdit.ts +42 -0
  68. package/src/features/Conversation/index.ts +1 -1
  69. package/src/features/Conversation/types/{index.tsx → index.ts} +0 -7
  70. package/src/features/Portal/Thread/Chat/ChatItem.tsx +0 -7
  71. package/src/hooks/useUserAvatar.test.ts +129 -0
  72. package/src/hooks/useUserAvatar.ts +19 -0
  73. package/src/server/routers/lambda/aiModel.ts +7 -8
  74. package/src/store/user/slices/settings/selectors/settings.ts +6 -5
  75. package/src/features/ChatItem/index.tsx +0 -58
  76. package/src/features/Conversation/Actions/Assistant.tsx +0 -68
  77. package/src/features/Conversation/Actions/Fallback.tsx +0 -19
  78. package/src/features/Conversation/Actions/Tool.tsx +0 -33
  79. package/src/features/Conversation/Actions/User.tsx +0 -39
  80. package/src/features/Conversation/Actions/customAction.ts +0 -37
  81. package/src/features/Conversation/Actions/index.ts +0 -14
  82. package/src/features/Conversation/Extras/index.ts +0 -8
  83. package/src/features/Conversation/Extras/type.ts +0 -5
  84. package/src/features/Conversation/Messages/index.ts +0 -45
  85. package/src/features/Conversation/components/ChatItem/index.tsx +0 -358
  86. /package/src/{components → features}/ChatItem/components/Actions.tsx +0 -0
  87. /package/src/{components → features}/ChatItem/components/Avatar.tsx +0 -0
  88. /package/src/{components → features}/ChatItem/components/BorderSpacing.tsx +0 -0
  89. /package/src/{components → features}/ChatItem/components/ErrorContent.tsx +0 -0
  90. /package/src/{components → features}/ChatItem/components/Loading.tsx +0 -0
  91. /package/src/{components → features}/ChatItem/components/Title.tsx +0 -0
  92. /package/src/{components → features}/ChatItem/index.ts +0 -0
  93. /package/src/{components → features}/ChatItem/style.ts +0 -0
  94. /package/src/{components → features}/ChatItem/type.ts +0 -0
  95. /package/src/features/Conversation/{components/MarkdownElements → MarkdownElements}/LobeArtifact/Render/Icon.tsx +0 -0
  96. /package/src/features/Conversation/{components/MarkdownElements → MarkdownElements}/LobeArtifact/index.ts +0 -0
  97. /package/src/features/Conversation/{components/MarkdownElements → MarkdownElements}/LobeArtifact/rehypePlugin.test.ts +0 -0
  98. /package/src/features/Conversation/{components/MarkdownElements → MarkdownElements}/LobeArtifact/rehypePlugin.ts +0 -0
  99. /package/src/features/Conversation/{components/MarkdownElements → MarkdownElements}/LobeThinking/Render.tsx +0 -0
  100. /package/src/features/Conversation/{components/MarkdownElements → MarkdownElements}/LobeThinking/index.ts +0 -0
  101. /package/src/features/Conversation/{components/MarkdownElements → MarkdownElements}/LocalFile/Render/index.tsx +0 -0
  102. /package/src/features/Conversation/{components/MarkdownElements → MarkdownElements}/LocalFile/index.ts +0 -0
  103. /package/src/features/Conversation/{components/MarkdownElements → MarkdownElements}/Thinking/Render.tsx +0 -0
  104. /package/src/features/Conversation/{components/MarkdownElements → MarkdownElements}/Thinking/index.ts +0 -0
  105. /package/src/features/Conversation/{components/MarkdownElements → MarkdownElements}/index.ts +0 -0
  106. /package/src/features/Conversation/{components/MarkdownElements → MarkdownElements}/remarkPlugins/__snapshots__/createRemarkSelfClosingTagPlugin.test.ts.snap +0 -0
  107. /package/src/features/Conversation/{components/MarkdownElements → MarkdownElements}/remarkPlugins/createRemarkCustomTagPlugin.ts +0 -0
  108. /package/src/features/Conversation/{components/MarkdownElements → MarkdownElements}/remarkPlugins/createRemarkSelfClosingTagPlugin.test.ts +0 -0
  109. /package/src/features/Conversation/{components/MarkdownElements → MarkdownElements}/remarkPlugins/createRemarkSelfClosingTagPlugin.ts +0 -0
  110. /package/src/features/Conversation/{components/MarkdownElements → MarkdownElements}/remarkPlugins/getNodeContent.ts +0 -0
  111. /package/src/features/Conversation/{components/MarkdownElements → MarkdownElements}/type.ts +0 -0
  112. /package/src/features/Conversation/{components/MarkdownElements → MarkdownElements}/utils.ts +0 -0
  113. /package/src/features/Conversation/{Extras → components/Extras}/ExtraContainer.tsx +0 -0
  114. /package/src/features/Conversation/{Extras → components/Extras}/TTS/FilePlayer.tsx +0 -0
  115. /package/src/features/Conversation/{Extras → components/Extras}/TTS/InitPlayer.tsx +0 -0
  116. /package/src/features/Conversation/{Extras → components/Extras}/TTS/Player.tsx +0 -0
  117. /package/src/features/Conversation/{Extras → components/Extras}/TTS/index.tsx +0 -0
  118. /package/src/features/Conversation/{Extras → components/Extras}/Translate.tsx +0 -0
  119. /package/src/features/Conversation/{Extras → components/Extras}/Usage/UsageDetail/TokenProgress.tsx +0 -0
  120. /package/src/features/Conversation/{Extras → components/Extras}/Usage/UsageDetail/index.tsx +0 -0
  121. /package/src/features/Conversation/{Extras → components/Extras}/Usage/UsageDetail/tokens.ts +0 -0
  122. /package/src/features/Conversation/{Extras → components/Extras}/Usage/index.tsx +0 -0
  123. /package/src/features/Conversation/components/{ChatItem/ShareMessageModal → ShareMessageModal}/ShareImage/index.tsx +0 -0
  124. /package/src/features/Conversation/components/{ChatItem/ShareMessageModal → ShareMessageModal}/ShareImage/style.ts +0 -0
  125. /package/src/features/Conversation/components/{ChatItem/ShareMessageModal → ShareMessageModal}/ShareImage/type.ts +0 -0
  126. /package/src/features/Conversation/components/{ChatItem/ShareMessageModal → ShareMessageModal}/ShareText/Preview.tsx +0 -0
  127. /package/src/features/Conversation/components/{ChatItem/ShareMessageModal → ShareMessageModal}/ShareText/template.test.ts +0 -0
  128. /package/src/features/Conversation/components/{ChatItem/ShareMessageModal → ShareMessageModal}/ShareText/template.ts +0 -0
  129. /package/src/features/Conversation/components/{ChatItem/ShareMessageModal → ShareMessageModal}/ShareText/type.ts +0 -0
  130. /package/src/features/Conversation/components/{ChatItem/ShareMessageModal → ShareMessageModal}/index.tsx +0 -0
  131. /package/src/features/Conversation/components/{ChatItem/ShareMessageModal → ShareMessageModal}/style.ts +0 -0
  132. /package/src/features/Conversation/{components/ChatItem → context}/InPortalThreadContext.ts +0 -0
  133. /package/src/features/Conversation/{components/ChatItem/utils.test.ts → utils.test.ts} +0 -0
  134. /package/src/features/Conversation/{components/ChatItem/utils.ts → utils.ts} +0 -0
@@ -1,34 +1,137 @@
1
- import { ReactNode, memo } from 'react';
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 BubblesLoading from '@/components/BubblesLoading';
5
- import { LOADING_FLAT } from '@/const/message';
6
- import { ChatMessage } from '@/types/message';
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 FileListViewer from './FileListViewer';
9
- import ImageFileListViewer from './ImageFileListViewer';
10
- import VideoFileListViewer from './VideoFileListViewer';
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
- export const UserMessage = memo<
13
- ChatMessage & {
14
- editableContent: ReactNode;
15
- }
16
- >(({ id, editableContent, content, imageList, videoList, fileList }) => {
17
- if (content === LOADING_FLAT) return <BubblesLoading />;
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 gap={8} id={id}>
21
- {editableContent}
22
- {imageList && imageList?.length > 0 && <ImageFileListViewer items={imageList} />}
23
- {videoList && videoList?.length > 0 && <VideoFileListViewer items={videoList} />}
24
- {fileList && fileList?.length > 0 && (
25
- <div style={{ marginTop: 8 }}>
26
- <FileListViewer items={fileList} />
27
- </div>
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 * from './BelowMessage';
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;
@@ -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
- import { LobeDefaultAiModelListItem } from '../../../../../../packages/model-bank/src/types/aiModel';
13
- import { getCachedTextInputUnitRate, getWriteCacheInputUnitRate } from '@/utils/pricing';
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 '@lobechat/utils';
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);
@@ -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';
@@ -85,6 +85,7 @@ const VirtualizedList = memo<VirtualizedListProps>(({ mobile, dataSource, itemCo
85
85
  components={{
86
86
  List,
87
87
  }}
88
+
88
89
  computeItemKey={(_, item) => item}
89
90
  data={dataSource}
90
91
  followOutput={getFollowOutput}
@@ -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,3 +1,3 @@
1
- export { default as ChatItem } from './components/ChatItem';
2
1
  export { default as SkeletonList } from './components/SkeletonList';
3
2
  export { default as VirtualizedList } from './components/VirtualizedList';
3
+ export { default as ChatItem } from './Messages';
@@ -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}