实现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.rhkb.cn/news/486402.html

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

相关文章

【LeetCode】80.删除有序数组中的重复项II

题目链接&#xff1a; 80.删除有序数组中的重复项II 题目描述&#xff1a; 解题思路&#xff1a; 按照题目中要求&#xff0c;必须在原来数组中进行修改&#xff0c;并且在O(1)额外空间条件下完成。因此我们可以使用双指针算法&#xff0c;算法具体流程如下&#xff1a; 如…

国产GPU中,VLLM0.5.0发布Qwen2.5-14B-Instruct-GPTQ-Int8模型,请求返回结果乱码

概述 国产GPU: DCU Z100 推理框架&#xff1a; vllm0.5.0 docker容器化部署 运行如下代码&#xff1a; python -m vllm.entrypoints.openai.api_server --model /app/models/Qwen2.5-14B-Instruct-GPTQ-Int8 --served-model-name qwen-gptq --trust-remote-code --enforce…

[Redis#18] 哨兵机制 | docker 部署实验 | 选举机制(leader主)

目录 基本概念 工作原理 哨兵节点的作用与配置 监控与心跳检测 ⭕故障恢复流程 Docker 部署 Redis Sentinel 场景介绍 Docker 简介 Docker 安装 Docker Compose 编排 Redis 主从及哨兵节点 编排 Redis 主从节点 编排 Redis 哨兵节点 创建哨兵配置文件 启动服务 …

网站打开速度测试工具:互联网优化的得力助手

在信息飞速流转的互联网时代&#xff0c;网站如同企业与用户对话的窗口&#xff0c;其打开速度直接关乎用户体验&#xff0c;乃至业务的成败。所幸&#xff0c;一系列专业的网站打开速度测试工具应运而生&#xff0c;它们宛如幕后的技术侦探&#xff0c;精准剖析网站性能&#…

字节高频算法面试题:小于 n 的最大数

问题描述&#xff08;感觉n的位数需要大于等于2&#xff0c;因为n的位数1的话会有点问题&#xff0c;“且无重复”是指nums中存在重复&#xff0c;但是最后返回的小于n最大数是可以重复使用nums中的元素的&#xff09;&#xff1a; 思路&#xff1a; 先对nums倒序排序 暴力回…

基于springboot+vue实现的农场管理平台 (源码+L文+ppt)4-110

第四章 系统设计 4.1 系统总体结构设计 本系统是基于B/S架构的网站系统&#xff0c;分为系统前台和系统后台&#xff0c;前台主要是提供给注册用户和未注册登录的游客使用的&#xff0c;包括网站首页、农业生产资料、农业质量方法、农业种植计划、农业新闻、通知公告、在线咨…

最长最短单词

最长最短单词 C语言实现C实现Java实现Python实现 &#x1f490;The Begin&#x1f490;点点关注&#xff0c;收藏不迷路&#x1f490; 输入1行句子&#xff08;不多于200个单词&#xff0c;每个单词长度不超过100&#xff09;&#xff0c;只包含字母、空格和逗号。单词由至少一…

时频转换 | Matlab梅尔频谱图Mel spectrogram一维数据转二维图像方法

目录 基本介绍程序设计参考资料获取方式 基本介绍 时频转换 | Matlab梅尔频谱图Mel spectrogram一维数据转二维图像方法 程序设计 clear clc % close all load 130.mat % 导入数据 x X130_DE_time; % 本数据只选择5120个点进行分析 x x(1:120000,:); fs 12000 ; % 数据…

蓝牙键鼠无法被电脑识别

起因是我的键鼠是三模的&#xff0c;但是我蓝牙模式我只用过几次&#xff0c;基本一直使用的是有线模式&#xff0c;最近突然要用无线连接&#xff0c;如果使用收发器就显得过于繁琐&#xff0c;还占用usb口&#xff0c;因此想用蓝牙连&#xff0c;但是由于 win10更新了英特尔…

网络命令配置

随笔记录 目录 1. 背景介绍 2. 配置网络命令空间 3 验证 3.1 未网络命令空间外网卡配置IP 3.2 验证配置 3.2.1 在网络命令空间外接口启动iperf3 3.2.2 网络命令空间内启动iperf3 client 1. 背景介绍 2. 配置网络命令空间 1. 配置前[rootlocalhost SDK-V1.10.1.7]# ip…

Megatron 和 deepspeed 大模型训练框架

文章目录 相同点不同点开发团队&#xff1a;专长领域&#xff1a;GPU优化&#xff1a;功能特性&#xff1a;框架支持&#xff1a; 优势比较Megatron优势&#xff1a;DeepSpeed优势&#xff1a;Megatron训练框架最主要的特点、优缺点优点:缺点: 扩展对比深入探究其他训练框架各种…

STM32进阶 定时器3 通用定时器 案例1:LED呼吸灯——PWM脉冲

功能 它有基本定时器所有功能&#xff0c;还增加以下功能 TIM2、TIM3、TIM4、TIM5 多种时钟源&#xff1a; 外部时钟源模式1&#xff1a; 每个定时器有四个输入通道 只有通道1和通道2的信号可以作为时钟信号源 通道1 和通道2 的信号经过输入滤液和边缘检测器 外部时钟源…

详解Vue设计模式

详解 vue 设计模式 ​ Vue.js 作为一个流行的前端框架&#xff0c;拥有许多设计模式&#xff0c;这些设计模式帮助开发者更好地组织和管理代码&#xff0c;提升代码的可维护性、可扩展性和可读性。Vue 设计模式主要体现在以下几个方面&#xff1a; 1. 组件化设计模式 (Compon…

Tomcat使用教程

下载地址&#xff1a;https://tomcat.apache.org/ 配置环境变量 变量名: CATALINA_HOME 变量值: D:\tools\apache-tomcat-9.0.97 Path: %CATALINA_HOME%\bin 启动Tomcat(打开命令提示符) startup.bat 解决乱码问题(打开conf\logging.properties) java.util.logging.Conso…

免押租赁系统助力资源共享新模式开创便捷租赁体验

内容概要 免押租赁系统&#xff0c;听起来是不是很酷&#xff1f;这个新模式不仅仅是为了让你少花点钱&#xff0c;它的到来简直就是个革命&#xff01;以前&#xff0c;租东西时首先想到的就是那个令人心痛的押金&#xff0c;对吧&#xff1f;但现在&#xff0c;免押租赁系统…

17. Threejs案例-Three.js创建多个立方体

17. Threejs案例-Three.js创建多个立方体 实现效果 知识点 WebGLRenderer (WebGL渲染器) WebGLRenderer 是 Three.js 中用于渲染 WebGL 场景的核心类。它负责将场景中的对象渲染到画布上。 构造器 new THREE.WebGLRenderer(parameters) 参数类型描述parametersObject可选…

【kettle】mysql数据抽取至kafka/消费kafka数据存入mysql

目录 一、mysql数据抽取至kafka1、表输入2、json output3、kafka producer4、启动转换&#xff0c;查看是否可以消费 二、消费kafka数据存入mysql1、Kafka consumer2、Get records from stream3、字段选择4、JSON input5、表输出 一、mysql数据抽取至kafka 1、表输入 点击新建…

如何让谷歌外链看起来更真实?

在SEO优化过程中&#xff0c;外链的自然性往往会被忽视&#xff0c;尤其是在一些急于见效的策略中&#xff0c;外链往往集中在高权重的少数几个网站上&#xff0c;导致外链结构单一且缺乏多样性。这样的外链网络容易让搜索引擎怀疑其真实性&#xff0c;进而影响网站排名。如何才…

【Qt移植LVGL】QWidget手搓LVGL软件仿真模拟器(非直接运行图形库)

【Qt移植LVGL】QWidget手搓LVGL软件仿真模拟器&#xff08;非直接运行图形库&#xff09; 打包开源地址&#xff1a; Qt函数库gitee地址 更新以gitee为准 移植后的demo工程&#xff1a; gitee 有些没实现的 后续我会继续优化 文章目录 别碰瓷看清楚&#xff1a;是移植&#…

Spring Data Elasticsearch

简介说明 spring-data-elasticsearch是比较好用的一个elasticsearch客户端&#xff0c;本文介绍如何使用它来操作ES。本文使用spring-boot-starter-data-elasticsearch&#xff0c;它内部会引入spring-data-elasticsearch。 Spring Data ElasticSearch有下边这几种方法操作El…