实现RAGFlow-0.14.1的输入框多行输入和消息框的多行显示

 一、Chat页面输入框的修改


1. macOS配置

我使用MacBook Pro,chip 是 Apple M3 Pro,Memory是18GB,macOS是 Sonoma 14.6.1。

2. 修改chat输入框代码

目前RAGFlow前端的chat功能,输入的内容是单行的,不能主动使用Shift+Enter实现分行。根据 src/pages/chat/index.tsx 文件,可以看出该文件是聊天页面的主入口,整体结构是将聊天内容通过 <ChatContainer /> 组件呈现。因此,如果要实现多行文本框功能,主要修改点会在 ChatContainer 组件的实现中。

chat/chat-container/index.tsx 中,可以看到消息输入功能是通过 <MessageInput /> 组件实现的。如果需要将单行输入框改为支持多行输入的 TextArea,需要修改 MessageInput 组件的实现。

修改src/components/message-input/index.tsx的代码如下:

return (<FlexclassName={styles.messageInputWrapper}style={{backgroundColor: '#f7f8fa', // 淡灰色背景border: '1px solid #e0e0e0', // 外部边框颜色borderRadius: '12px', // 圆角增加为原来的 1.5 倍padding: '10px 12px', // 内边距boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)', // 添加阴影}}vertical>{/* 输入框 */}<Input.TextAreasize="large"placeholder={t('sendPlaceholder')}value={value}disabled={disabled}autoSize={{ minRows: 1, maxRows: 6 }} // 默认一行,自动调整至 6 行style={{flex: 1,border: 'none', // 禁用自带边框outline: 'none', // 去掉选中高亮boxShadow: 'none', // 禁用焦点样式resize: 'none', // 禁用用户手动调整大小fontSize: '14px',lineHeight: '20px', // 行高,保证单行内容视觉效果padding: '0', // 去掉多余的填充// overflow: 'hidden', // 禁止滚动条显示backgroundColor: '#f7f8fa', // 与外层背景色一致}}onPressEnter={(e) => {if (!e.shiftKey) {e.preventDefault();handlePressEnter();}}}onChange={onInputChange as ChangeEventHandler<HTMLTextAreaElement>}/>{/* 按钮区域 */}<Flexjustify="space-between"align="center"style={{marginTop: '8px',}}>{showUploadIcon && (<UploadonPreview={handlePreview}onChange={handleChange}multiple={false}onRemove={handleRemove}showUploadList={false}beforeUpload={() => {return false;}}><Buttontype={'text'}disabled={disabled}icon={<SvgIconname="paper-clip"width={18}height={22}disabled={disabled}></SvgIcon>}></Button></Upload>)}<Buttontype="primary"onClick={handlePressEnter}loading={sendLoading}disabled={sendDisabled || isUploadingFile}style={{height: '40px',borderRadius: '12px', // 按钮圆角同步调整padding: '0 16px',}}>{t('send')}</Button></Flex>

 实际页面输入效果如下:

2.1 替换Input为 Input.TextArea

Input 替换为 Input.TextArea,并添加 autoSize 属性,以实现多行输入框的自动伸缩功能。

2.2 修改发送逻辑

在原有逻辑中,按 Enter 会直接触发消息发送。对于多行输入框,需要支持:

  • Shift + Enter 换行。

  • Enter 发送消息。

上面代码中,onPressEnter 事件已经处理了此逻辑。

  

二、Agent Flow页面中输入框的修改

项目的Agent页面上还有chat,改了component下的message-input,对这个chat不起作用。修改src/pages/flow/box.tsx,关键点说明:

  • Input.TextArea 的使用

    • 替换了原来的 Input,支持多行输入。
    • autoSize 参数允许输入框高度根据内容自动扩展。
  • Shift + Enter 处理

    • 检测 e.shiftKey 是否被按下。
    • Shift 被按下时,不触发消息发送,只换行。
    • 当未按下 Shift 时,发送消息并阻止默认行为。
  • suffix 按钮

    • 保留了发送按钮的逻辑,用户也可以点击按钮发送消息。
return (<><Flex flex={1} className={styles.chatContainer} vertical><Flex flex={1} vertical className={styles.messageContainer}><div><Spin spinning={loading}>{derivedMessages?.map((message, i) => {return (<MessageItemloading={message.role === MessageType.Assistant &&sendLoading &&derivedMessages.length - 1 === i}key={message.id}nickname={userInfo.nickname}avatar={userInfo.avatar}item={message}reference={buildMessageItemReference({ message: derivedMessages, reference },message,)}clickDocumentButton={clickDocumentButton}index={i}showLikeButton={false}sendLoading={sendLoading}></MessageItem>);})}</Spin></div><div ref={ref} /></Flex><Flexalign="flex-start" // 改为 flex-start,使内容顶部对齐style={{padding: '12px 20px',backgroundColor: '#ffffff', // 白色背景borderTop: '1px solid #e8e8e8', // 分割线颜色position: 'sticky', // 固定在底部bottom: 0,zIndex: 100, // 确保浮于内容上方}}><Input.TextAreaplaceholder={t('sendPlaceholder')}value={value}autoSize={{ minRows: 1, maxRows: 6 }} // 自动调整高度onChange={handleInputChange as React.ChangeEventHandler<HTMLTextAreaElement>}onPressEnter={(e) => {if (!e.shiftKey) { // Shift+Enter 换行e.preventDefault();handlePressEnter();}}}style={{flex: 1,border: '1px solid #e0e0e0', // 边框颜色borderRadius: '8px', // 圆角边框padding: '10px 12px',fontSize: '14px',lineHeight: '20px',boxShadow: 'none', // 去除阴影resize: 'none', // 禁止拖动调整大小}}/><Buttontype="primary"onClick={handlePressEnter}loading={sendLoading}style={{marginLeft: '10px',borderRadius: '8px',padding: '0 16px',height: '40px',fontSize: '14px',display: 'flex',alignItems: 'center', // 保持内容居中justifyContent: 'center',marginTop: 'auto', // 自动保持按钮与输入框底部对齐}}>{t('send')}</Button></Flex></Flex><PdfDrawervisible={visible}hideModal={hideModal}documentId={documentId}chunk={selectedChunk}></PdfDrawer></>);

实际页面输入效果如下:

三、消息框中的显示内容的修改

虽然对话的多行输入没有问题了,对话chat上的消息显示没有跟随输入分行,只是将分行的地方加了一个空格,显得很怪异,现在将chat的消息显示也适配一下多行。


1. 修改src/components/message-item/index.tsx:

要实现消息内容中的换行处理,确保用户输入的内容能够正确地显示多行,我们需要确保在 MessageItem 组件中渲染消息文本时能够正确处理换行符。

修改目标:

  1. 支持多行显示:当用户发送多行消息时,确保文本能够按行显示,而不仅仅是将换行符替换为空格。
  2. CSS 样式处理:通过合适的 CSS 属性(如 white-space: pre-line)来保留换行符。

主要改动:

  • MessageItem 组件中确保显示消息的部分使用正确的 white-space 样式。
  • 如果 item.content 包含换行符,它们将被正确处理并显示为多行。
import { ReactComponent as AssistantIcon } from '@/assets/svg/assistant.svg';
import { MessageType } from '@/constants/chat';
import { useSetModalState } from '@/hooks/common-hooks';
import { IReference } from '@/interfaces/database/chat';
import { IChunk } from '@/interfaces/database/knowledge';
import classNames from 'classnames';
import { memo, useCallback, useEffect, useMemo, useState } from 'react';import {useFetchDocumentInfosByIds,useFetchDocumentThumbnailsByIds,
} from '@/hooks/document-hooks';
import { IRegenerateMessage, IRemoveMessageById } from '@/hooks/logic-hooks';
import { IMessage } from '@/pages/chat/interface';
import MarkdownContent from '@/pages/chat/markdown-content';
import { getExtension, isImage } from '@/utils/document-util';
import { Avatar, Button, Flex, List, Space, Typography } from 'antd';
import FileIcon from '../file-icon';
import IndentedTreeModal from '../indented-tree/modal';
import NewDocumentLink from '../new-document-link';
import { AssistantGroupButton, UserGroupButton } from './group-button';
import styles from './index.less';const { Text } = Typography;interface IProps extends Partial<IRemoveMessageById>, IRegenerateMessage {item: IMessage;reference: IReference;loading?: boolean;sendLoading?: boolean;nickname?: string;avatar?: string;clickDocumentButton?: (documentId: string, chunk: IChunk) => void;index: number;showLikeButton?: boolean;
}const MessageItem = ({item,reference,loading = false,avatar = '',sendLoading = false,clickDocumentButton,index,removeMessageById,regenerateMessage,showLikeButton = true,
}: IProps) => {const isAssistant = item.role === MessageType.Assistant;const isUser = item.role === MessageType.User;const { data: documentList, setDocumentIds } = useFetchDocumentInfosByIds();const { data: documentThumbnails, setDocumentIds: setIds } =useFetchDocumentThumbnailsByIds();const { visible, hideModal, showModal } = useSetModalState();const [clickedDocumentId, setClickedDocumentId] = useState('');const referenceDocumentList = useMemo(() => {return reference?.doc_aggs ?? [];}, [reference?.doc_aggs]);const handleUserDocumentClick = useCallback((id: string) => () => {setClickedDocumentId(id);showModal();},[showModal],);const handleRegenerateMessage = useCallback(() => {regenerateMessage?.(item);}, [regenerateMessage, item]);useEffect(() => {const ids = item?.doc_ids ?? [];if (ids.length) {setDocumentIds(ids);const documentIds = ids.filter((x) => !(x in documentThumbnails));if (documentIds.length) {setIds(documentIds);}}}, [item.doc_ids, setDocumentIds, setIds, documentThumbnails]);return (<divclassName={classNames(styles.messageItem, {[styles.messageItemLeft]: item.role === MessageType.Assistant,[styles.messageItemRight]: item.role === MessageType.User,})}><sectionclassName={classNames(styles.messageItemSection, {[styles.messageItemSectionLeft]: item.role === MessageType.Assistant,[styles.messageItemSectionRight]: item.role === MessageType.User,})}><divclassName={classNames(styles.messageItemContent, {[styles.messageItemContentReverse]: item.role === MessageType.User,})}>{item.role === MessageType.User ? (<Avatarsize={40}src={avatar ??'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png'}/>) : (<AssistantIcon></AssistantIcon>)}<Flex vertical gap={8} flex={1}><Space>{isAssistant ? (index !== 0 && (<AssistantGroupButtonmessageId={item.id}content={item.content}prompt={item.prompt}showLikeButton={showLikeButton}audioBinary={item.audio_binary}></AssistantGroupButton>)) : (<UserGroupButtoncontent={item.content}messageId={item.id}removeMessageById={removeMessageById}regenerateMessage={regenerateMessage && handleRegenerateMessage}sendLoading={sendLoading}></UserGroupButton>)}{/* <b>{isAssistant ? '' : nickname}</b> */}</Space><divclassName={isAssistant ? styles.messageText : styles.messageUserText}style={{ whiteSpace: 'pre-line' }} // 保留换行符并自动换行><MarkdownContentloading={loading}content={item.content}reference={reference}clickDocumentButton={clickDocumentButton}></MarkdownContent></div>{isAssistant && referenceDocumentList.length > 0 && (<ListbordereddataSource={referenceDocumentList}renderItem={(item) => {return (<List.Item><Flex gap={'small'} align="center"><FileIconid={item.doc_id}name={item.doc_name}></FileIcon><NewDocumentLinkdocumentId={item.doc_id}documentName={item.doc_name}prefix="document">{item.doc_name}</NewDocumentLink></Flex></List.Item>);}}/>)}{isUser && documentList.length > 0 && (<ListbordereddataSource={documentList}renderItem={(item) => {// TODO:const fileThumbnail =documentThumbnails[item.id] || documentThumbnails[item.id];const fileExtension = getExtension(item.name);return (<List.Item><Flex gap={'small'} align="center"><FileIcon id={item.id} name={item.name}></FileIcon>{isImage(fileExtension) ? (<NewDocumentLinkdocumentId={item.id}documentName={item.name}prefix="document">{item.name}</NewDocumentLink>) : (<Buttontype={'text'}onClick={handleUserDocumentClick(item.id)}><Textstyle={{ maxWidth: '40vw' }}ellipsis={{ tooltip: item.name }}>{item.name}</Text></Button>)}</Flex></List.Item>);}}/>)}</Flex></div></section>{visible && (<IndentedTreeModalvisible={visible}hideModal={hideModal}documentId={clickedDocumentId}></IndentedTreeModal>)}</div>);
};export default memo(MessageItem);

 

2. 修改src/components/message-item/index.less:

要确保文本内容(特别是多行消息)能够正确显示换行符并且样式合理,我们可以对现有的 .messageText.messageUserText 样式做一些调整。以下是针对 index.less 样式的改进:

关键改动:

  1. 保留换行符: 使用 white-space: pre-line 来保留文本中的换行符(\n),并且自动换行。
  2. 避免内容溢出: 适当设置 word-breakoverflow-wrap 属性,以确保长单词或无空格的长文本能够正确换行,避免溢出。
  3. 简化重复的 .messageText.messageUserText 样式: 让这两者有一个统一的基础样式,便于管理。
.messageItem {padding: 24px 0;.messageItemSection {display: inline-block;}.messageItemSectionLeft {width: 80%;}.messageItemSectionRight {// width: 80%;// max-width: 50vw;}.messageItemContent {display: inline-flex;gap: 20px;flex-wrap: wrap;  // 允许内容换行}.messageItemContentReverse {flex-direction: row-reverse;}.messageText {.chunkText();padding: 0 14px;background-color: rgba(249, 250, 251, 1);word-break: break-all;}/* 共同的文本样式基础 */.messageTextBase {padding: 6px 10px;border-radius: 8px;word-wrap: break-word;  // 强制长单词换行overflow-wrap: break-word;  // 强制长单词换行white-space: pre-line;  // 保留换行符并换行}/* Assistant 消息文本样式 */.messageText {.chunkText();.messageTextBase();background-color: #e6f4ff;word-break: break-word;  // 自动换行}/* User 消息文本样式 */.messageUserText {.chunkText();.messageTextBase();background-color: rgb(248, 247, 247);word-break: break-word;  // 自动换行text-align: justify;  // 用户消息文本两端对齐}.messageEmpty {width: 300px;}.thumbnailImg {max-width: 20px;}
}.messageItemLeft {text-align: left;
}.messageItemRight {text-align: right;
}

实际对话消息,显示如下:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/63414.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

11.17【大数据】Hadoop【DEBUG】

列出hdfs文件系统所有的目录和文件 主节点上 子结点 是一样的 *为什么能登进 slave 02 的主机,但是 master 当中依然显示 slave 02 为 DeadNode?* hadoop坏死节点的重启_hadoop3 子节点重启-CSDN博客 注意hadoop-daemon.sh 实际上位于 Hadoop 的 sbin 目录中&#xff0c;而不…

网络测速工具

1. SPEEDTEST https://www.speedtest.net/ 2. 测速网 测速网 - 专业测网速, 网速测试, 宽带提速, 游戏测速, 直播测速, 5G测速, 物联网监测,Wi-Fi 7,Wi-Fi 6,FTTR,全屋Wi-Fi - SpeedTest.cn 3. 字节比特换算 bps&#xff08;bits per second&#xff09; 字节和比特的换算…

快速构建NLP理论知识体系

NLP理论知识体系 一句话解释NLPNLP模型及原理简述1、Rag 一句话解释NLP 如果我们要实现机器翻译、情感分析、问答系统、文本摘要、聊天机器人、构造智能化的辅助文件填写模板&#xff0c;NLP可以通过现成的模型对输入的语音、文字、图片进行处理&#xff08;分词、标词性、去停…

AI - RAG中的状态化管理聊天记录

AI - RAG中的状态化管理聊天记录 大家好&#xff0c;今天我们来聊聊LangChain和LLM中一个重要的话题——状态化管理聊天记录。在使用大语言模型(LLM)的时候&#xff0c;聊天记录&#xff08;History&#xff09;和状态&#xff08;State&#xff09;管理是非常关键的。那我们先…

【AI学习】Mamba学习(二十):Mamba是一种线性注意力

论文《Demystify Mamba in Vision: A Linear Attention Perspective 》从线性注意力的视角&#xff0c;对Mamba 进行了阐释。 论文名称&#xff1a;Demystify Mamba in Vision: A Linear Attention Perspective (Arxiv 2024.05) 论文地址&#xff1a;https://arxiv.org/pdf/24…

POI遍历行所有单元格的两种方式,getPhysicalNumberOfCells方式有问题,勿用

今天看POI源码的时候&#xff0c;发现HSSFWorkbook类型的工作簿&#xff0c;行数据是用TreeMap<Integer, HSSFRow>存储的&#xff0c;列数据是用HSSFCell[]数组来存的&#xff1b;XSSFWorkbook类型的工作簿&#xff0c;行数据是用SortedMap<Integer, XSSFRow>存储的…

DataSophon集成CMAK KafkaManager

本次集成基于DDP1.2.1 集成CMAK-3.0.0.6 设计的json和tar包我放网盘了. 通过网盘分享的文件&#xff1a;DDP集成CMAK 链接: https://pan.baidu.com/s/1BR70Ajj9FxvjBlsOX4Ivhw?pwdcpmc 提取码: cpmc CMAK github上提供了zip压缩包.将压缩包解压之后 在根目录下加入启动脚本…

ArrayList常见操作源码逐句剖析

目录 前言 正文 1.需要了解的一些字段属性 1.存储 ArrayList 元素的数组缓冲区。 2.集合的大小 3.默认集合容量大小 2.ArrayList对象创建 1.无参构造 2.有参构造1 3.有参构造2 3.添加元素add(E e)以及扩容机制 ​编辑 后言 前言 源码的剖析有助于理解设计模式&…

重磅更新:CnosDB 2.3.5.4 版本上线, 性能提升,问题修复一网打尽

&#x1f4e2; 重磅更新&#xff1a;CnosDB 2.3.5.4 版本上线, 性能提升&#xff0c;问题修复一网打尽 &#x1f4e2; 我们很高兴地向大家介绍最新版本的更新&#xff0c;以下是本次更新的主要内容&#xff1a; &#x1f539; 版本号&#xff1a;2.3.5.4 &#x1f539; 发布…

SD-WAN 2.0 在金融行业的典型应用场景

目录 全扁平化组网 场景需求 应用方案 SD-WAN 2.0 在金融行业的创新实践 SD-WAN5G提高金融行业网络接入可靠性 全扁平化组网 随着金融机构数字化转型的推进&#xff0c;机构业务的多样性、复杂性、 个性化等要求&#xff0c;对现有的金融机构网络架构与网管人员运维模式提出…

如何延长相机电池续航时间

如果你曾在拍摄过程中突然发现相机电池电量不足&#xff0c;就会知道那有多让人紧张和沮丧了。无论你是在拍摄小朋友的生日派对、家庭聚会&#xff0c;还是作为一名专业摄影师在工作&#xff0c;保持电池有电都是至关重要的。否则&#xff0c;你就有可能错过精彩瞬间&#xff0…

C#开发-集合使用和技巧(十)Union用法-并集

在 C# 中&#xff0c;IEnumerable 的 Union 方法用于返回两个序列的并集。Union 方法会去除重复的元素&#xff0c;确保结果集中每个元素都是唯一的。以下是 Union 方法的基本用法&#xff1a; 基本语法 public static IEnumerable<TSource> Union<TSource>(this…

轻量化特征融合 | YOLOv11 引入一种基于增强层间特征相关性的轻量级特征融合网络 | 北理工新作

本改进已同步到Magic框架 摘要—无人机图像中的小目标检测由于分辨率低和背景融合等因素具有挑战性,导致特征信息有限。多尺度特征融合可以通过捕获不同尺度的信息来增强检测,但传统策略效果不佳。简单的连接或加法操作无法充分利用多尺度融合的优势,导致特征之间的相关性不…

ABAP 系统变量SY-INDEX与SY-TABIX的区别

ABAP系统变量SY-INDEX与SY-TABIX都是在循环中使用&#xff1a; SY-INDEX在Do...EndDo和While...EndWhile中起作用&#xff1b; SY-TABIX在Loop...EndLoop中有效。 详见如下实例&#xff1a; REPORT ztest_index_tabix.DATA:lit_vbak TYPE STANDARD TABLE OF vbak,lwa_vbak …

方案拆解 | 打击矩阵新规频出!2025矩阵营销该怎么玩?

社媒平台的矩阵营销又要“变天”了&#xff1f;&#xff01; 11月18日&#xff0c;小红书官方发表了被安全薯 称为“小红书史上最严打击黑灰产专项”新规&#xff0c;其中就包括黑灰产矩阵号的公告。 ▲ 图源&#xff1a;小红书 实际上&#xff0c;不包括这次&#xff0c;今年…

Lua语言入门 - Lua 数组

Lua 数组 数组&#xff0c;就是相同数据类型的元素按一定顺序排列的集合&#xff0c;可以是一维数组和多维数组。 在 Lua 中&#xff0c;数组不是一种特定的数据类型&#xff0c;而是一种用来存储一组值的数据结构。 实际上&#xff0c;Lua 中并没有专门的数组类型&#xff…

SVM的基本思想

一、SVM的基本思想 SVM的基本思想是在样本的向量空间中寻找一个超平面&#xff0c;使得两类样本被分割在平面的两端。这样的平面理论上有无穷多个&#xff0c;但SVM的目标是找到一个最优的超平面&#xff0c;即两侧距离超平面最近的样本点到超平面的距离被最大化的超平面。这个…

Java基于SpringBoot的网上订餐系统,附源码

博主介绍&#xff1a;✌Java老徐、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&…

【 工具变量】IPCC碳排放因子数据测算表

一、数据简介&#xff1a; 排放因子法是IPCC提出的一种碳排放估算方法&#xff0c;也是目前适用范围最广、应用最为普遍的方法。将各类能源消耗的实物统计量转变为标准统计量&#xff0c;再乘以各自的碳排放因子&#xff0c;加总之后就可以得到碳排放总量。如果按照ISO14064标…

备忘录模式的理解和实践

引言 在软件开发中&#xff0c;我们经常会遇到需要保存对象状态并在某个时间点恢复到该状态的需求。这种需求类似于我们平时说的“后悔药”&#xff0c;即允许用户撤销之前的操作&#xff0c;恢复到某个之前的状态。备忘录模式&#xff08;Memento Pattern&#xff09;正是为了…