实现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
组件中渲染消息文本时能够正确处理换行符。
修改目标:
- 支持多行显示:当用户发送多行消息时,确保文本能够按行显示,而不仅仅是将换行符替换为空格。
- 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
样式的改进:
关键改动:
- 保留换行符: 使用
white-space: pre-line
来保留文本中的换行符(\n
),并且自动换行。 - 避免内容溢出: 适当设置
word-break
和overflow-wrap
属性,以确保长单词或无空格的长文本能够正确换行,避免溢出。 - 简化重复的
.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;
}
实际对话消息,显示如下:
相关文章:

实现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…...

Pointnet++改进71:添加LFE模块|高效长距离注意力网络
简介:1.该教程提供大量的首发改进的方式,降低上手难度,多种结构改进,助力寻找创新点!2.本篇文章对Pointnet++特征提取模块进行改进,加入LFE模块,提升性能。3.专栏持续更新,紧随最新的研究内容。 目录 1.理论介绍 2.修改步骤 2.1 步骤一 2.2 步骤二 2.3 步骤三 1.理…...

C++STL容器vector容器大小相关函数
目录 前言 主要参考 vector::size vector::max_size vector::resize vector::capacity vector::empty vector::reserve vector::shrink_to_fit 共勉 前言 本文将讨论STL容器vector中与迭代器相关的函数,模板参数T为int类型。 主要参考 cpluscplus.com 侯…...

阿里云CPU超载解决记录
现象:阿里云CPU使用率超90%连续5分钟告警,项目日志error.log中存在heap/gc/limit等内存耗尽等信息,阿里云慢查询日志每日有查询时间很长的参数一直不变的慢sql,linux服务器使用top命令并按c可以看到cpu过大是哪个命令行造成的 分…...

【工具变量】上市公司企业商业信用融资数据(2003-2022年)
一、测算方式:参考《会计研究》张新民老师的做法 净商业信用NTC(应付账款应付票据预收账款)-(应收账款应收票据预付账款),用总资产标准化; 应付账款AP应付账款应付票据预收账款,用总资产标准化 一年以上应付账款比例LAP是企业一年以上(包括一…...

2024数字科技生态大会 | 紫光展锐携手中国电信助力数字科技高质量发展
2024年12月3日至5日,中国电信2024数字科技生态大会在广州举行,通过主题峰会、多场分论坛、重要签约及合作发布等环节,与合作伙伴共绘数字科技发展新愿景。紫光展锐作为中国电信的战略合作伙伴受邀参会,全面呈现了技术、产品创新进…...

ES语法(一)概括
一、语法 1、请求方式 Elasticsearch(ES)使用基于 JSON 的查询 DSL(领域特定语言)来与数据交互。 一个 ElasticSearch 请求和任何 HTTP 请求一样由若干相同的部件组成: curl -X<VERB> <PROTOCOL>://&l…...

(vue)el-cascader多选级联选择器,值取最后一级的数据
(vue)el-cascader多选级联选择器,取值取最后一级的数据 获取到:[“养殖区”,“鸡棚”,“E5001”] 期望:[“E5001”] 问题: 解决方法 增加change事件方法,处理选中的value值 1.单选 <el-cascaderv-model"tags2":o…...

友思特方案 | 精密制程的光影贴合:半导体制造中的高功率紫外光源
导读 为新能源锂电行业赋能第四站:半导体制造中的高功率紫外光源!稳定输出、灵活控制的曝光设备是新能源/半导体行业高端生产中减少误差、提高效率的核心技术,友思特 ALE 系列 UV LED 紫外光源集合6大优势,为精密制造的健康发展提…...

README写作技巧
做一个项目,首先第一眼看上去要美观,这样才有看下去的动力。做项目亦是如此,如果每一步应付做的话,我想动力也不会太大,最终很大概率会放弃或者进度缓慢。 1.README组成 README是对项目的一个说明,它对观看…...

【密码学】分组密码的工作模式
1.电码本模式(ECB) 优点: 每个数据块独立加密,可并行加密,实现简单。 缺点: 相同明文会产生相同密文,不具备数据完整保护性。 适用于短消息的加密传输 (如一个加密密钥)。 工作流程:用相同的密钥分别对…...

SQL 和 NoSQL 有什么区别?
SQL(Structured Query Language,结构化查询语言)和NoSQL数据库是两种不同类型的数据库管理系统,它们在多个方面存在显著的区别。以下是对SQL和NoSQL主要区别的详细分析: 一、数据存储与模型 SQL数据库 使用关系模型来…...

提升网站流量的关键:AI在SEO关键词优化中的应用
内容概要 在当今数字时代,提升网站流量已成为每个网站管理员的首要任务。而人工智能的技术进步,为搜索引擎优化(SEO)提供了强有力的支持,尤其是在关键词优化方面。关键词是连接用户需求与网站内容的桥梁,其…...

Harnessing Large Language Models for Training-free Video Anomaly Detection
标题:利用大型语言模型实现无训练的视频异常检测 原文链接:https://openaccess.thecvf.com/content/CVPR2024/papers/Zanella_Harnessing_Large_Language_Models_for_Training-free_Video_Anomaly_Detection_CVPR_2024_paper.pdf 源码链接:ht…...

如何通过自学成长为一名后端开发工程师?
大家好,我是袁庭新。最近,有星友向我提出了一个很好的问题:如何通过自学成为一名后端开发工程师? 为了解答这个疑问,我特意制作了一个视频来详细分享我的看法和建议。 戳链接:如何通过自学成长为一名后端开…...

HDR视频技术之六:色调映射
图像显示技术的最终目的就是使得显示的图像效果尽量接近人们在自然界中观察到的对应的场景。 HDR 图像与视频有着更高的亮度、更深的位深、更广的色域,因此它无法在常见的普通显示器上显示。 入门级的显示器与播放设备(例如普通人家使用的电视࿰…...

(洛谷题目)P11060 【MX-X4-T0】「Jason-1」x!
思路: 理解问题:首先,我们要理解题目的要求,即判断一个非负整数n的阶乘n!是否是n1的倍数。 阶乘的定义:根据阶乘的定义,n!是所有小于等于n的正整数的乘积。特别地,0!被定义为1。 特殊情况处理…...

TEXT2SQL工具vanna本地化安装和应用
TEXT2SQL工具vanna本地化安装和应用 Vanna和Text2SQL环境安装和数据准备 conda虚拟环境安装数据准备ollama环境准备 ollama安装和运行ollama下载模型测试下API方式正常使用 chromaDB的默认的embedding模型准备 vanna脚本跑起来 Vanna和Text2SQL TEXT2SQL即文本转SQL…...

Bloom 效果
1、Bloom 效果是什么 Bloom效果(中文也可以叫做高光溢出效果),是一种使画面中亮度较高的区域产生一种光晕或发光效果的图像处理技术,Bloom效果的主要目的是模拟现实世界中强光源在相机镜头或人眼中造成的散射和反射现象ÿ…...

AWS 机器学习,推动 AI 技术的健康发展
目录 一、AI 正在改变生产方式二、从炒作走向务实1、选对场景2、重视数据3、产品思维4、持续优化 三、人才是最稀缺的资源四、负责任的 AI 开发五、未来已来六、启示与思考七、结语 如果说传统软件开发是手工作坊,那么 AI 就像工业革命带来的机器生产。 在最新的一…...

MCPTT 与BTC
MCPTT(Mission Critical Push-to-Talk)和B-TrunC(宽带集群)是两种关键通信标准,它们分别由不同的组织制定和推广。 MCPTT(Mission Critical Push-to-Talk)标准由3GPP(第三代合作伙伴…...

Jackson - JsonGenerator创建JSON、JsonParser解析JSON
以下是关于如何使用Jackson的JsonGenerator类来创建JSON内容以及如何使用JsonParser类来读取JSON内容的教程。 依赖项 首先,在pom.xml文件中添加以下依赖项以引入Jackson库: <dependency><groupId>com.fasterxml.jackson.core</groupI…...

Linux-音频应用编程
ALPHA I.MX6U 开发板支持音频,板上搭载了音频编解码芯片 WM8960,支持播放以及录音功能!本章我们来学习 Linux 下的音频应用编程,音频应用编程相比于前面几个章节所介绍的内容、其难度有所上升,但是笔者仅向大家介绍 Li…...

《QT 示例宝库:探索丰富的编程世界》
《QT 示例宝库:探索丰富的编程世界》 一、QT 基础示例(一)QRadioButton 示例(二)拦截关闭事件示例 二、QT 常用代码示例(一)QObject 相关操作(二)Qt 基本容器遍历&#x…...

腾讯云流式湖仓统一存储实践
点击蓝字⬆ 关注我们 本文共计5107 预计阅读时长16分钟 * 本文将分享腾讯云流式湖仓的架构与实践。主要内容包括: 流计算Oceanus介绍腾讯云流式湖仓架构腾讯云流式湖仓实践腾讯云流式湖仓发展规划 一、流计算Oceanus介绍 随着大数据技术的发展࿰…...

18 设计模式之迭代器模式(书籍遍历案例)
一、什么是迭代器模式 迭代器模式(Iterator Pattern)是一种行为型设计模式,允许客户端通过统一的接口顺序访问一个集合对象中的元素,而无需暴露集合对象的内部实现。这个模式主要用于访问聚合对象(如集合、数组等&…...

超清4K视频素材哪里找?优质下载资源网站分享
我是你们的自媒体UP主小李。现在是高清、4K视频大行其道的时代,想要制作出吸引眼球的优质内容,超清4K视频素材必不可少。今天就为大家分享几个宝藏网站,让你的视频创作更轻松、更出彩! 蛙学网 首先推荐 蛙学网,这是国内…...

刷题日志【1】
目录 1.全排列【力扣】 代码1: 代码2: 2、子集【力扣】 3、全排列Ⅱ【力扣】 4、组合【力扣】 1.全排列【力扣】 代码1: class Solution {bool check[7];vector <int> path;vector<vector<int>> ret;public:vecto…...

【C++算法】32.前缀和_矩阵区域和
文章目录 题目链接:题目描述:解法C 算法代码: 题目链接: 1314. 矩阵区域和 题目描述: 解法 防止有人看不明白题目,先解释一下题目 二维前缀和思想: 使用前缀和矩阵 ret [x1,y1]~[x2,y2] D …...

使用堆栈(Stack)
集合类型(Collection)下篇_xml collection-CSDN博客 以上是堆栈的简单介绍,下方是堆栈的使用 题目:给定一个逆波兰表达式(后缀表达式)的字符串数组tokens,其中每个元素是一个操作数(数字&…...