diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json index 1aa5059f..624d6b5f 100644 --- a/src/renderer/src/i18n/locales/en-us.json +++ b/src/renderer/src/i18n/locales/en-us.json @@ -86,6 +86,7 @@ "message.new.branch.created": "New Branch Created", "message.regenerate.model": "Switch Model", "message.new.context": "New Context", + "message.useful": "Helpful", "save": "Save", "settings.code_collapsible": "Code block collapsible", "settings.context_count": "Context", diff --git a/src/renderer/src/i18n/locales/ja-jp.json b/src/renderer/src/i18n/locales/ja-jp.json index 748d5079..6006e2da 100644 --- a/src/renderer/src/i18n/locales/ja-jp.json +++ b/src/renderer/src/i18n/locales/ja-jp.json @@ -86,6 +86,7 @@ "message.new.branch.created": "新しいブランチが作成されました", "message.regenerate.model": "モデルを切り替え", "message.new.context": "新しいコンテキスト", + "message.useful": "役立つ", "save": "保存", "settings.code_collapsible": "コードブロックを折りたたむ", "settings.context_count": "コンテキスト", diff --git a/src/renderer/src/i18n/locales/ru-ru.json b/src/renderer/src/i18n/locales/ru-ru.json index 1b962735..f32fb5fd 100644 --- a/src/renderer/src/i18n/locales/ru-ru.json +++ b/src/renderer/src/i18n/locales/ru-ru.json @@ -86,6 +86,7 @@ "message.new.branch.created": "Новая ветка создана", "message.regenerate.model": "Переключить модель", "message.new.context": "Новый контекст", + "message.useful": "Полезно", "save": "Сохранить", "settings.code_collapsible": "Блок кода свернут", "settings.context_count": "Контекст", diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json index d9fe9f19..73d54567 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -86,6 +86,7 @@ "message.new.branch.created": "新分支已创建", "message.regenerate.model": "切换模型", "message.new.context": "清除上下文", + "message.useful": "有用", "save": "保存", "settings.code_collapsible": "代码块可折叠", "settings.context_count": "上下文数", @@ -253,6 +254,10 @@ "message.style": "消息样式", "message.style.bubble": "气泡", "message.style.plain": "简洁", + "message.multi_model_style": "多模型回答样式", + "message.multi_model_style.horizontal": "水平", + "message.multi_model_style.vertical": "垂直", + "message.multi_model_style.fold": "折叠", "reset.confirm.content": "确定要重置所有数据吗?", "reset.double.confirm.content": "你的全部数据都会丢失,如果没有备份数据,将无法恢复,确定要继续吗?", "reset.double.confirm.title": "数据丢失!!!", diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index 249ce35d..2330db85 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -86,6 +86,7 @@ "message.new.branch.created": "新分支已建立", "message.regenerate.model": "切換模型", "message.new.context": "新上下文", + "message.useful": "有用", "save": "保存", "settings.code_collapsible": "代码块可折叠", "settings.context_count": "上下文", diff --git a/src/renderer/src/pages/home/Inputbar/Inputbar.tsx b/src/renderer/src/pages/home/Inputbar/Inputbar.tsx index 27341c1b..48057322 100644 --- a/src/renderer/src/pages/home/Inputbar/Inputbar.tsx +++ b/src/renderer/src/pages/home/Inputbar/Inputbar.tsx @@ -139,7 +139,6 @@ const Inputbar: FC = ({ assistant: _assistant, setActiveTopic }) => { setText('') setFiles([]) - setMentionModels([]) setTimeout(() => setText(''), 500) setTimeout(() => resizeTextArea(), 0) diff --git a/src/renderer/src/pages/home/Messages/Message.tsx b/src/renderer/src/pages/home/Messages/Message.tsx index 146effa9..4a3a71bd 100644 --- a/src/renderer/src/pages/home/Messages/Message.tsx +++ b/src/renderer/src/pages/home/Messages/Message.tsx @@ -194,7 +194,6 @@ const MessageItem: FC = ({ const MessageContainer = styled.div` display: flex; flex-direction: column; - padding: 15px 20px 0 20px; position: relative; transition: background-color 0.3s ease; &.message-highlight { diff --git a/src/renderer/src/pages/home/Messages/MessageGroup.tsx b/src/renderer/src/pages/home/Messages/MessageGroup.tsx new file mode 100644 index 00000000..943b4a8f --- /dev/null +++ b/src/renderer/src/pages/home/Messages/MessageGroup.tsx @@ -0,0 +1,88 @@ +import { useSettings } from '@renderer/hooks/useSettings' +import { Message, Topic } from '@renderer/types' +import { Segmented } from 'antd' +import { Dispatch, FC, SetStateAction, useState } from 'react' +import styled from 'styled-components' + +import MessageItem from './Message' + +interface Props { + messages: (Message & { index: number })[] + topic?: Topic + hidePresetMessages?: boolean + onGetMessages?: () => Message[] + onSetMessages?: Dispatch> + onDeleteMessage?: (message: Message) => void +} + +const MessageGroup: FC = ({ + messages, + topic, + hidePresetMessages, + onDeleteMessage, + onSetMessages, + onGetMessages +}) => { + const { multiModelMessageStyle } = useSettings() + const messageLength = messages.length + const [selectedIndex, setSelectedIndex] = useState(0) + + return ( + + {messageLength > 1 && multiModelMessageStyle === 'fold' && ( + setSelectedIndex(Number(value))} + options={messages.map((message, index) => ({ + label: `@${message.modelId}`, + value: index.toString() + }))} + size="small" + /> + )} + + {messages.map((message, index) => ( + + + + ))} + + + ) +} + +const GroupContainer = styled.div`` + +const GridContainer = styled.div<{ $count: number; $layout: 'fold' | 'horizontal' | 'vertical' }>` + width: 100%; + overflow-x: auto; + display: grid; + grid-template-columns: repeat( + ${(props) => (['fold', 'vertical'].includes(props.$layout) ? 1 : props.$count)}, + minmax(400px, 1fr) + ); + gap: 16px; +` + +const MessageWrapper = styled.div<{ $layout: 'fold' | 'horizontal' | 'vertical'; $selected: boolean }>` + width: 100%; + display: ${(props) => { + if (props.$layout === 'fold') { + return props.$selected ? 'block' : 'none' + } + if (props.$layout === 'horizontal') { + return 'inline-block' + } + return 'block' + }}; +` + +export default MessageGroup diff --git a/src/renderer/src/pages/home/Messages/MessageMenubar.tsx b/src/renderer/src/pages/home/Messages/MessageMenubar.tsx index 6ea12ed8..a21d3ab4 100644 --- a/src/renderer/src/pages/home/Messages/MessageMenubar.tsx +++ b/src/renderer/src/pages/home/Messages/MessageMenubar.tsx @@ -3,6 +3,8 @@ import { DeleteOutlined, EditOutlined, ForkOutlined, + LikeFilled, + LikeOutlined, MenuOutlined, QuestionCircleOutlined, SaveOutlined, @@ -43,7 +45,6 @@ const MessageMenubar: FC = (props) => { isLastMessage, isAssistantMessage, assistantModel, - setModel, onEditMessage, onDeleteMessage, onGetMessages @@ -53,7 +54,6 @@ const MessageMenubar: FC = (props) => { const [isTranslating, setIsTranslating] = useState(false) const isUserMessage = message.role === 'user' - const canRegenerate = isLastMessage && isAssistantMessage const onCopy = useCallback(() => { navigator.clipboard.writeText(removeTrailingDoubleSpaces(message.content)) @@ -62,14 +62,6 @@ const MessageMenubar: FC = (props) => { setTimeout(() => setCopied(false), 2000) }, [message.content, t]) - const onRegenerate = useCallback( - (model: Model) => { - setModel(model) - setTimeout(() => EventEmitter.emit(EVENT_NAMES.REGENERATE_MESSAGE, model), 100) - }, - [setModel] - ) - const onNewBranch = useCallback(async () => { await modelGenerating() EventEmitter.emit(EVENT_NAMES.NEW_BRANCH, index) @@ -175,25 +167,27 @@ const MessageMenubar: FC = (props) => { [message, onEdit, onNewBranch, t] ) - const onAtModelRegenerate = async () => { - await modelGenerating() - const selectedModel = await SelectModelPopup.show({ model }) - selectedModel && onRegenerate(selectedModel) - } - const onDeleteAndRegenerate = async () => { await modelGenerating() + const selectedModel = await SelectModelPopup.show({ model }) + if (!selectedModel) return + onEditMessage?.({ ...message, content: '', reasoning_content: undefined, metrics: undefined, status: 'sending', - modelId: assistantModel?.id || model?.id, + modelId: selectedModel.id || assistantModel?.id || model?.id, + model: selectedModel, translatedContent: undefined }) } + const onUseful = useCallback(() => { + onEditMessage?.({ ...message, useful: !message.useful }) + }, [message, onEditMessage]) + return ( {message.role === 'user' && ( @@ -210,23 +204,9 @@ const MessageMenubar: FC = (props) => { {isAssistantMessage && ( - } - onConfirm={onDeleteAndRegenerate}> - - - - - - - )} - {canRegenerate && ( - - - + + + )} @@ -281,6 +261,14 @@ const MessageMenubar: FC = (props) => { )} + {isAssistantMessage && ( + + + {message.useful ? : } + + + )} + ` const ScrollContainer = styled.div` display: flex; flex-direction: column-reverse; + padding: 16px 16px; + gap: 32px; ` interface ContainerProps { - right?: boolean + $right?: boolean } const Container = styled(Scrollbar)` @@ -79,6 +82,8 @@ const Messages: FC = ({ assistant, topic, setActiveTopic }) => { const { updateTopic, addTopic } = useAssistant(assistant.id) const { showTopics, topicPosition, showAssistants, enableTopicNaming } = useSettings() + const groupedMessages = getGroupedMessages(displayMessages) + const INITIAL_MESSAGES_COUNT = 20 const LOAD_MORE_COUNT = 20 @@ -102,10 +107,13 @@ const Messages: FC = ({ assistant, topic, setActiveTopic }) => { message.mentions.forEach((m) => { const assistantMessage = getAssistantMessage({ assistant: { ...assistant, model: m }, topic }) assistantMessage.model = m + assistantMessage.askId = message.id assistantMessages.push(assistantMessage) }) } else { - assistantMessages.push(getAssistantMessage({ assistant, topic })) + const assistantMessage = getAssistantMessage({ assistant, topic }) + assistantMessage.askId = message.id + assistantMessages.push(assistantMessage) } setMessages((prev) => { @@ -214,7 +222,7 @@ const Messages: FC = ({ assistant, topic, setActiveTopic }) => { setActiveTopic(newTopic) autoRenameTopic() - // 由于复制了消���,消息中附带的文件的总数变了,需要更新 + // 由于复制了消息,消息中附带的文件的总数变了,需要更新 const filesArr = branchMessages.map((m) => m.files) const files = flatten(filesArr).filter(Boolean) files.map(async (f) => { @@ -293,7 +301,7 @@ const Messages: FC = ({ assistant, topic, setActiveTopic }) => { style={{ maxWidth }} key={assistant.id} ref={containerRef} - right={topicPosition === 'left'}> + $right={topicPosition === 'left'}> = ({ assistant, topic, setActiveTopic }) => { - {displayMessages.map((message, index) => ( - ( + = (props) => { codeCollapsible, mathEngine, autoTranslateWithSpace, - pasteLongTextThreshold + pasteLongTextThreshold, + multiModelMessageStyle } = useSettings() const onUpdateAssistantSettings = (settings: Partial) => { @@ -255,6 +257,19 @@ const SettingsTab: FC = (props) => { + + {t('message.message.multi_model_style')} + + + {t('message.message.code_style')}