mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-08 20:42:30 +08:00
feat: render message reference and add avatar to MessageItem (#73)
* feat: add temporary conversation * feat: add avatar to MessageItem * feat: render message reference
This commit is contained in:
@ -1,11 +1,34 @@
|
||||
.chatContainer {
|
||||
padding: 0 24px 24px;
|
||||
.messageContainer {
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.messageItem {
|
||||
.messageItemContent {
|
||||
padding: 24px 0;
|
||||
.messageItemSection {
|
||||
display: inline-block;
|
||||
width: 300px;
|
||||
}
|
||||
.messageItemSectionLeft {
|
||||
width: 70%;
|
||||
}
|
||||
.messageItemSectionRight {
|
||||
width: 30%;
|
||||
}
|
||||
.messageItemContent {
|
||||
display: inline-flex;
|
||||
gap: 20px;
|
||||
}
|
||||
.messageItemContentReverse {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
.messageText {
|
||||
padding: 0 14px;
|
||||
background-color: rgba(249, 250, 251, 1);
|
||||
}
|
||||
.referenceIcon {
|
||||
padding: 0 6px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,17 +1,59 @@
|
||||
import { Button, Flex, Input, Typography } from 'antd';
|
||||
import { ChangeEventHandler, useState } from 'react';
|
||||
|
||||
import { Message } from '@/interfaces/database/chat';
|
||||
import classNames from 'classnames';
|
||||
import { useFetchConversation, useSendMessage } from '../hooks';
|
||||
|
||||
import { ReactComponent as AssistantIcon } from '@/assets/svg/assistant.svg';
|
||||
import { MessageType } from '@/constants/chat';
|
||||
import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
|
||||
import { useSelectUserInfo } from '@/hooks/userSettingHook';
|
||||
import { IReference, Message } from '@/interfaces/database/chat';
|
||||
import { Avatar, Button, Flex, Input, Popover } from 'antd';
|
||||
import classNames from 'classnames';
|
||||
import { ChangeEventHandler, useCallback, useMemo, useState } from 'react';
|
||||
import reactStringReplace from 'react-string-replace';
|
||||
import { useFetchConversation, useSendMessage } from '../hooks';
|
||||
import { IClientConversation } from '../interface';
|
||||
|
||||
import { InfoCircleOutlined } from '@ant-design/icons';
|
||||
import Markdown from 'react-markdown';
|
||||
import { visitParents } from 'unist-util-visit-parents';
|
||||
import styles from './index.less';
|
||||
|
||||
const { Paragraph } = Typography;
|
||||
const rehypeWrapReference = () => {
|
||||
return function wrapTextTransform(tree: any) {
|
||||
visitParents(tree, 'text', (node, ancestors) => {
|
||||
if (ancestors.at(-1).tagName !== 'custom-typography') {
|
||||
node.type = 'element';
|
||||
node.tagName = 'custom-typography';
|
||||
node.properties = {};
|
||||
node.children = [{ type: 'text', value: node.value }];
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
const MessageItem = ({ item }: { item: Message; references: IReference[] }) => {
|
||||
const userInfo = useSelectUserInfo();
|
||||
|
||||
const popoverContent = useMemo(
|
||||
() => (
|
||||
<div>
|
||||
<p>Content</p>
|
||||
<p>Content</p>
|
||||
</div>
|
||||
),
|
||||
[],
|
||||
);
|
||||
|
||||
const renderReference = useCallback(
|
||||
(text: string) => {
|
||||
return reactStringReplace(text, /#{2}\d{1,}\${2}/g, (match, i) => {
|
||||
return (
|
||||
<Popover content={popoverContent}>
|
||||
<InfoCircleOutlined key={i} className={styles.referenceIcon} />
|
||||
</Popover>
|
||||
);
|
||||
});
|
||||
},
|
||||
[popoverContent],
|
||||
);
|
||||
|
||||
const MessageItem = ({ item }: { item: Message }) => {
|
||||
return (
|
||||
<div
|
||||
className={classNames(styles.messageItem, {
|
||||
@ -19,11 +61,50 @@ const MessageItem = ({ item }: { item: Message }) => {
|
||||
[styles.messageItemRight]: item.role === MessageType.User,
|
||||
})}
|
||||
>
|
||||
<span className={styles.messageItemContent}>
|
||||
<Paragraph ellipsis={{ tooltip: item.content, rows: 3 }}>
|
||||
{item.content}
|
||||
</Paragraph>
|
||||
</span>
|
||||
<section
|
||||
className={classNames(styles.messageItemSection, {
|
||||
[styles.messageItemSectionLeft]: item.role === MessageType.Assistant,
|
||||
[styles.messageItemSectionRight]: item.role === MessageType.User,
|
||||
})}
|
||||
>
|
||||
<div
|
||||
className={classNames(styles.messageItemContent, {
|
||||
[styles.messageItemContentReverse]: item.role === MessageType.User,
|
||||
})}
|
||||
>
|
||||
{item.role === MessageType.User ? (
|
||||
userInfo.avatar ?? (
|
||||
<Avatar
|
||||
size={40}
|
||||
src={
|
||||
userInfo.avatar ??
|
||||
'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png'
|
||||
}
|
||||
/>
|
||||
)
|
||||
) : (
|
||||
<AssistantIcon></AssistantIcon>
|
||||
)}
|
||||
<Flex vertical gap={8} flex={1}>
|
||||
<b>
|
||||
{item.role === MessageType.Assistant ? 'Resume Assistant' : 'You'}
|
||||
</b>
|
||||
<div className={styles.messageText}>
|
||||
<Markdown
|
||||
rehypePlugins={[rehypeWrapReference]}
|
||||
components={
|
||||
{
|
||||
'custom-typography': ({ children }: { children: string }) =>
|
||||
renderReference(children),
|
||||
} as any
|
||||
}
|
||||
>
|
||||
{item.content}
|
||||
</Markdown>
|
||||
</div>
|
||||
</Flex>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -32,9 +113,13 @@ const ChatContainer = () => {
|
||||
const [value, setValue] = useState('');
|
||||
const conversation: IClientConversation = useFetchConversation();
|
||||
const { sendMessage } = useSendMessage();
|
||||
const loading = useOneNamespaceEffectsLoading('chatModel', [
|
||||
'completeConversation',
|
||||
'getConversation',
|
||||
]);
|
||||
|
||||
const handlePressEnter = () => {
|
||||
console.info(value);
|
||||
setValue('');
|
||||
sendMessage(value);
|
||||
};
|
||||
|
||||
@ -44,17 +129,23 @@ const ChatContainer = () => {
|
||||
|
||||
return (
|
||||
<Flex flex={1} className={styles.chatContainer} vertical>
|
||||
<Flex flex={1} vertical>
|
||||
{conversation?.message?.map((message) => (
|
||||
<MessageItem key={message.id} item={message}></MessageItem>
|
||||
))}
|
||||
<Flex flex={1} vertical className={styles.messageContainer}>
|
||||
<div>
|
||||
{conversation?.message?.map((message) => (
|
||||
<MessageItem
|
||||
key={message.id}
|
||||
item={message}
|
||||
references={conversation.reference}
|
||||
></MessageItem>
|
||||
))}
|
||||
</div>
|
||||
</Flex>
|
||||
<Input
|
||||
size="large"
|
||||
placeholder="Message Resume Assistant..."
|
||||
value={value}
|
||||
suffix={
|
||||
<Button type="primary" onClick={handlePressEnter}>
|
||||
<Button type="primary" onClick={handlePressEnter} loading={loading}>
|
||||
Send
|
||||
</Button>
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user