diff --git a/package.json b/package.json index 105c979e..2aaaed00 100644 --- a/package.json +++ b/package.json @@ -78,6 +78,7 @@ "rc-util": "^5.38.2", "react-intersection-observer": "^9.8.1", "react-layout-kit": "^1.7.4", + "react-markdown": "^8.0.7", "zustand": "^4.5.1", "zustand-utils": "^1.3.2" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9638e25b..9d75977f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -59,6 +59,9 @@ dependencies: react-layout-kit: specifier: ^1.7.4 version: 1.7.4(react@18.2.0) + react-markdown: + specifier: ^8.0.7 + version: 8.0.7(@types/react@18.2.31)(react@18.2.0) zustand: specifier: ^4.5.1 version: 4.5.1(@types/react@18.2.31)(immer@10.0.3)(react@18.2.0) diff --git a/src/ChatItem/components/MessageContent.tsx b/src/ChatItem/components/MessageContent.tsx index c9b1bc30..fad80eb9 100644 --- a/src/ChatItem/components/MessageContent.tsx +++ b/src/ChatItem/components/MessageContent.tsx @@ -6,6 +6,7 @@ import { ChatItemProps } from '@/ChatItem'; import EditableMessage from '@/EditableMessage'; import { ConfigProvider } from 'antd'; +import { MarkdownProps } from '@ant-design/pro-editor'; import { useStyles } from '../style'; export interface MessageContentProps { @@ -21,6 +22,7 @@ export interface MessageContentProps { text?: ChatItemProps['text']; type?: ChatItemProps['type']; className?: string; + markdownProps?: MarkdownProps; } const MessageContent = memo( @@ -34,6 +36,7 @@ const MessageContent = memo( placement, messageExtra, renderMessage, + markdownProps, type, primary, onDoubleClick, @@ -55,6 +58,7 @@ const MessageContent = memo( openModal={mobile ? editing : undefined} text={text} value={String(message || '...')} + markdownProps={markdownProps} /> ); const messageContent = renderMessage ? renderMessage(content) : content; diff --git a/src/ChatItem/index.tsx b/src/ChatItem/index.tsx index 3a234fd7..759d8371 100644 --- a/src/ChatItem/index.tsx +++ b/src/ChatItem/index.tsx @@ -37,6 +37,7 @@ const ChatItem = memo((props) => { text, errorMessage, chatItemRenderConfig, + markdownProps, onDoubleClick, originData, ...restProps @@ -87,6 +88,7 @@ const ChatItem = memo((props) => { renderMessage={renderMessage} text={text} type={type} + markdownProps={markdownProps} /> ); return chatItemRenderConfig?.contentRender?.(props, dom) || dom; diff --git a/src/ChatItem/type.ts b/src/ChatItem/type.ts index 4cf682f5..dbbdb7fa 100644 --- a/src/ChatItem/type.ts +++ b/src/ChatItem/type.ts @@ -3,6 +3,7 @@ import { ReactNode } from 'react'; import { EditableMessageProps } from '@/EditableMessage'; import { DivProps, MetaData } from '@/types'; +import { MarkdownProps } from '@ant-design/pro-editor'; export type WithFalse = T | false; @@ -75,6 +76,12 @@ export interface ChatItemProps> { */ type?: 'block' | 'pure'; + /** + * @description The configuration for the markdown component + * @default {} + */ + markdownProps?: MarkdownProps; + /** * @description 聊天项渲染函数,为了保证性能他是惰性的,只有在列表项内容修改的时候才会重新执行 * @default (props, defaultDom) => defaultDom diff --git a/src/ChatList/ChatListItem.tsx b/src/ChatList/ChatListItem.tsx index 1b05b8d5..613e8dd9 100644 --- a/src/ChatList/ChatListItem.tsx +++ b/src/ChatList/ChatListItem.tsx @@ -8,6 +8,7 @@ import { LLMRoleType } from '@/types/llm'; import { ChatMessage } from '@/types/message'; import { useRefFunction } from '@/ProChat/hooks/useRefFunction'; +import { MarkdownProps } from '@ant-design/pro-editor'; import ActionsBar, { type ActionsBarProps } from './ActionsBar'; export type OnMessageChange = (id: string, content: string) => void; @@ -99,6 +100,10 @@ export interface ListItemProps> { * 聊天项的渲染函数。 */ chatItemRenderConfig?: ChatItemProps['chatItemRenderConfig']; + /** + * markdown组件的配置。 + */ + markdownProps?: MarkdownProps; /** * 原始数据。 */ @@ -133,6 +138,7 @@ const ChatListItem = (props: ChatListItemProps) => { renderItems, chatItemRenderConfig, chatItemClassName, + markdownProps, ...item } = props; @@ -285,6 +291,7 @@ const ChatListItem = (props: ChatListItemProps) => { time={item.updateAt || item.createAt} type={type === 'chat' ? 'block' : 'pure'} chatItemRenderConfig={chatItemRenderConfig} + markdownProps={markdownProps} /> ); return dom; diff --git a/src/ChatList/index.tsx b/src/ChatList/index.tsx index 1190fb82..39f61c9c 100644 --- a/src/ChatList/index.tsx +++ b/src/ChatList/index.tsx @@ -71,6 +71,7 @@ const ChatList = memo( renderActions, historyCount = 0, chatItemRenderConfig, + markdownProps, ...props }) => { const { cx, styles } = useStyles(); @@ -114,6 +115,7 @@ const ChatList = memo( {...item} originData={item} chatItemRenderConfig={chatItemRenderConfig} + markdownProps={markdownProps} /> diff --git a/src/EditableMessage/index.tsx b/src/EditableMessage/index.tsx index 1c8e1be0..2fc4162d 100644 --- a/src/EditableMessage/index.tsx +++ b/src/EditableMessage/index.tsx @@ -3,7 +3,7 @@ import { CSSProperties, memo } from 'react'; import MessageInput, { type MessageInputProps } from '@/MessageInput'; import MessageModal, { type MessageModalProps } from '@/MessageModal'; -import { Markdown } from '@ant-design/pro-editor'; +import { Markdown, MarkdownProps } from '@ant-design/pro-editor'; export interface EditableMessageProps { /** @@ -14,10 +14,6 @@ export interface EditableMessageProps { * @title The class name for the MessageInput component */ input?: string; - /** - * @title The class name for the Markdown component - */ - markdown?: string; textarea?: string; }; editButtonSize?: MessageInputProps['editButtonSize']; @@ -64,16 +60,16 @@ export interface EditableMessageProps { * @title The style for the MessageInput component */ input?: CSSProperties; - /** - * @title The style for the Markdown component - */ - markdown?: CSSProperties; }; text?: MessageModalProps['text']; /** * @title The current text value */ value: string; + /** + * @title The props for the Markdown component + */ + markdownProps?: MarkdownProps; } const EditableMessage = memo( @@ -93,6 +89,7 @@ const EditableMessage = memo( editButtonSize, text, model, + markdownProps, }) => { const [isEdit, setTyping] = useMergedState(false, { onChange: onEditingChange, @@ -134,12 +131,12 @@ const EditableMessage = memo( input ) : ( {value || placeholder} @@ -156,6 +153,7 @@ const EditableMessage = memo( setExpand(e); setTyping(false); }} + markdownProps={markdownProps} open={expand} placeholder={placeholder} text={text} diff --git a/src/MessageModal/index.tsx b/src/MessageModal/index.tsx index 512540ea..a7437598 100644 --- a/src/MessageModal/index.tsx +++ b/src/MessageModal/index.tsx @@ -3,7 +3,7 @@ import { useMergedState } from 'rc-util'; import { CSSProperties, ReactNode, memo } from 'react'; import MessageInput, { type MessageInputProps } from '@/MessageInput'; -import { Markdown } from '@ant-design/pro-editor'; +import { Markdown, MarkdownProps } from '@ant-design/pro-editor'; import { Modal, type ModalProps } from 'antd'; export interface MessageModalProps extends Pick { @@ -41,6 +41,10 @@ export interface MessageModalProps extends Pick { * @description The value of the message content */ value: string; + /** + * @description The props for the Markdown component + */ + markdownProps?: MarkdownProps; } const MessageModal = memo( @@ -56,6 +60,7 @@ const MessageModal = memo( text, footer, extra, + markdownProps, }) => { const { mobile } = useResponsive(); @@ -106,7 +111,18 @@ const MessageModal = memo( ) : ( <> {extra} - + {String(value || placeholder)} diff --git a/src/ProChat/components/ChatList/index.tsx b/src/ProChat/components/ChatList/index.tsx index 458de7c9..71e460fa 100644 --- a/src/ProChat/components/ChatList/index.tsx +++ b/src/ProChat/components/ChatList/index.tsx @@ -17,89 +17,92 @@ interface ListProps extends Partial { itemShouldUpdate?: (prevProps: ChatListItemProps, nextProps: ChatListItemProps) => boolean; } -const List = memo(({ showTitle, itemShouldUpdate, chatItemRenderConfig }) => { - const data = useStore(chatSelectors.currentChatsWithGuideMessage, isEqual); +const List = memo( + ({ showTitle, itemShouldUpdate, chatItemRenderConfig, markdownProps }) => { + const data = useStore(chatSelectors.currentChatsWithGuideMessage, isEqual); - const [ - init, - displayMode, - enableHistoryCount, - historyCount, - chatLoadingId, - deleteMessage, - resendMessage, - dispatchMessage, - ] = useStore((s) => { - const config = s.config; + const [ + init, + displayMode, + enableHistoryCount, + historyCount, + chatLoadingId, + deleteMessage, + resendMessage, + dispatchMessage, + ] = useStore((s) => { + const config = s.config; - return [ - s.init, - s.displayMode, - config.enableHistoryCount, - config.historyCount, - s.chatLoadingId, - s.deleteMessage, - s.resendMessage, - s.dispatchMessage, - ]; - }); + return [ + s.init, + s.displayMode, + config.enableHistoryCount, + config.historyCount, + s.chatLoadingId, + s.deleteMessage, + s.resendMessage, + s.dispatchMessage, + ]; + }); - const onActionsClick = useRefFunction((action, { id, error }) => { - switch (action.key) { - case 'del': { - deleteMessage(id); - break; - } - case 'regenerate': { - resendMessage(id); + const onActionsClick = useRefFunction((action, { id, error }) => { + switch (action.key) { + case 'del': { + deleteMessage(id); + break; + } + case 'regenerate': { + resendMessage(id); - // if this message is an error message, we need to delete it - if (error) deleteMessage(id); - break; + // if this message is an error message, we need to delete it + if (error) deleteMessage(id); + break; + } } - } - // TODO: need a custom callback - }); + // TODO: need a custom callback + }); - const onMessageChange = useRefFunction((id, content) => - dispatchMessage({ id, key: 'content', type: 'updateMessage', value: content }), - ); + const onMessageChange = useRefFunction((id, content) => + dispatchMessage({ id, key: 'content', type: 'updateMessage', value: content }), + ); - const textObj = useMemo(() => { - return { - cancel: '取消', - confirm: '确认', - copy: '复制', - copySuccess: '复制成功', - delete: '删除', - edit: '编辑', - history: '历史范围', - regenerate: '重新生成', - }; - }, []); - if (!init) return ; + const textObj = useMemo(() => { + return { + cancel: '取消', + confirm: '确认', + copy: '复制', + copySuccess: '复制成功', + delete: '删除', + edit: '编辑', + history: '历史范围', + regenerate: '重新生成', + }; + }, []); + if (!init) return ; - return ( - - ); -}); + return ( + + ); + }, +); export default List; diff --git a/src/ProChat/container/App.tsx b/src/ProChat/container/App.tsx index 64c0601e..5f66e886 100644 --- a/src/ProChat/container/App.tsx +++ b/src/ProChat/container/App.tsx @@ -45,6 +45,7 @@ const App = memo( itemShouldUpdate, chatItemRenderConfig, backToBottomConfig, + markdownProps, }) => { const ref = useRef(null); const areaHtml = useRef(null); @@ -96,6 +97,7 @@ const App = memo( showTitle={showTitle} itemShouldUpdate={itemShouldUpdate} chatItemRenderConfig={chatItemRenderConfig} + markdownProps={markdownProps} /> diff --git a/src/ProChat/container/index.tsx b/src/ProChat/container/index.tsx index 8ef9f2fd..1237fc73 100644 --- a/src/ProChat/container/index.tsx +++ b/src/ProChat/container/index.tsx @@ -33,6 +33,7 @@ export function ProChat = Record>({ chatItemRenderConfig, backToBottomConfig, appStyle, + markdownProps, ...props }: ProChatProps) { return ( @@ -54,6 +55,7 @@ export function ProChat = Record>({ style={style} backToBottomConfig={backToBottomConfig} className={className} + markdownProps={markdownProps} /> diff --git a/src/ProChat/store/store.ts b/src/ProChat/store/store.ts index cfaa7982..51586e64 100644 --- a/src/ProChat/store/store.ts +++ b/src/ProChat/store/store.ts @@ -2,6 +2,7 @@ import { StateCreator } from 'zustand/vanilla'; import { ChatListProps } from '@/ChatList'; import { MetaData } from '@/ProChat/types/meta'; +import { MarkdownProps } from '@ant-design/pro-editor'; import isEqual from 'fast-deep-equal'; import { merge } from 'lodash-es'; import { optionalDevtools } from 'zustand-utils'; @@ -21,6 +22,10 @@ export interface ChatProps = Record> * @description 聊天项的渲染函数 */ chatItemRenderConfig?: ChatListProps['chatItemRenderConfig']; + /** + * @description markdown组件的参数 + */ + markdownProps?: MarkdownProps; /** * @description 判断聊天项的更新函数 */