Skip to content

Commit

Permalink
Feat/chat markdown render (#2679)
Browse files Browse the repository at this point in the history
* docs: remove repet change log

* feat: Chat add markdownRenderProps API
  • Loading branch information
YyumeiZhang authored Jan 20, 2025
1 parent 8440e5c commit b0a54ec
Show file tree
Hide file tree
Showing 11 changed files with 65 additions and 14 deletions.
7 changes: 4 additions & 3 deletions content/plus/chat/index-en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -1617,8 +1617,9 @@ render(DefaultChat);
| inputBoxStyle | Input box style | CSSProperties | - |
| inputBoxCls | Input box className | string | - |
| sendHotKey | Keyboard shortcut for sending content, supports `enter` \| `shift+enter`. The former will send the message in the input box when you press enter alone. When the shift and enter keys are pressed at the same time, it will only wrap the line and not send it. The latter is the opposite | string | `enter` |
| markdownRenderProps | This parameter will be passed to the MarkdownRender component used for dialog rendering. For details, see [MarkdownRenderProps](/en-US/plus/markdownrender#API)| MarkdownRenderProps |-|
| mode | Conversation mode, support `bubble` \| `noBubble` \| `userBubble` | string | `bubble` |
| roleConfig | Role information configuration, see[RoleConfig](#RoleConfig) | RoleConfig | - |
| roleConfig | Role information configuration, see [RoleConfig](#RoleConfig) | RoleConfig | - |
| renderDivider | Custom render divider, supported since v2.67.0 | (message?: Message) => ReactNode | - |
| renderHintBox | Custom rendering prompt information | (props: {content: string; index: number,onHintClick: () => void}) => React.ReactNode| - |
| onChatsChange | Triggered when the conversation list changes | (chats: Message[]) => void | - |
Expand All @@ -1637,8 +1638,8 @@ render(DefaultChat);
| showClearContext | Whether to display the clear context button| boolean | false |
| showStopGenerate | Whether to display the stop generation button| boolean | false |
| topSlot | top slot for chat | React.ReactNode | - |
| uploadProps | Upload component properties, refer to details [Upload](/zh-CN/input/upload#API%20%E5%8F%82%E8%80%83) | UploadProps | - |
| uploadTipProps | Upload component prompt attribute, refer to details [Tooltip](/zh-CN/show/tooltip#API%20%E5%8F%82%E8%80%83) | TooltipProps | - |
| uploadProps | Upload component properties, refer to details [Upload](/en-US/input/upload#API%20%E5%8F%82%E8%80%83) | UploadProps | - |
| uploadTipProps | Upload component prompt attribute, refer to details [Tooltip](/en-US/show/tooltip#API%20%E5%8F%82%E8%80%83) | TooltipProps | - |


#### RoleConfig
Expand Down
3 changes: 2 additions & 1 deletion content/plus/chat/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -1620,8 +1620,9 @@ render(DefaultChat);
| inputBoxStyle | 输入框样式 | CSSProperties | - |
| inputBoxCls | 输入框类名 | string | - |
| sendHotKey | 发送输入内容的键盘快捷键,支持 `enter` \| `shift+enter`。前者在单独按下 enter 将发送输入框中的消息, shift 和 enter 按键同时按下时,仅换行,不发送。后者相反 | string | `enter` |
| markdownRenderProps | 该参数将透传给对话框渲染所用的 MarkdownRender 组件,详见 [MarkdownRenderProps](/zh-CN/plus/markdownrender#API)| MarkdownRenderProps |-|
| mode | 对话模式,支持 `bubble` \| `noBubble` \| `userBubble` | string | `bubble` |
| roleConfig | 角色信息配置,具体见[RoleConfig](#RoleConfig) | RoleConfig | - |
| roleConfig | 角色信息配置,具体见 [RoleConfig](#RoleConfig) | RoleConfig | - |
| renderDivider | 自定义渲染分割线, 自 v2.67.0 支持 | (message?: Message) => ReactNode | - |
| renderHintBox | 自定义渲染提示信息 | (props: {content: string; index: number,onHintClick: () => void}) => React.ReactNode| - |
| onChatsChange | 对话列表变化时触发 | (chats: Message[]) => void | - |
Expand Down
1 change: 0 additions & 1 deletion content/start/changelog/index-en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ Version:Major.Minor.Patch (follow the **Semver** specification)
- 【Fix】
- Fixed the problem of incorrect onChange callback result in Tree component treeDataSimpleJson mode [#2508 ](https://github.com/DouyinFE/semi-design/issues/2508)
- fixed the issue that the display of disabled subNavItem in vertical Navigation does not meet expectations when it is collapsed
- Set the max-width of the img node of the image preview to none to avoid enlargement display errors when using tailwind at the same time.[#2624](https://github.com/DouyinFE/semi-design/pull/2624)

#### 🎉 2.71.2 (2024-12-13)
- 【Fix】
Expand Down
1 change: 0 additions & 1 deletion content/start/changelog/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ Semi 版本号遵循 **Semver** 规范(主版本号-次版本号-修订版本
- 【Fix】
- 修复 Tree 组件 treeDataSimpleJson 模式下,onChange 回调结果错误问题 [#2508 ](https://github.com/DouyinFE/semi-design/issues/2508) [#2601](https://github.com/DouyinFE/semi-design/pull/2601)
- 修复竖向 Navigation 在收起状态下 disabled subNavItem 展示不符合预期问题 [#2637](https://github.com/DouyinFE/semi-design/pull/2637)
- 设置图片预览的 img 节点的 max-width 为none,避免同时使用 tailwind 时放大显示错误问题[#2624](https://github.com/DouyinFE/semi-design/pull/2624)_


#### 🎉 2.71.2 (2024-12-13)
Expand Down
29 changes: 28 additions & 1 deletion packages/semi-ui/chat/_story/chat.stories.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import React, { useState, useCallback, useRef, useEffect, useMemo } from 'react'
import { Form, Button, Avatar, Dropdown, Radio, RadioGroup, Switch, Collapsible, AvatarGroup, Divider } from '@douyinfe/semi-ui';
import { IconUpload, IconForward, IconMoreStroked, IconArrowRight, IconChevronUp } from '@douyinfe/semi-icons';
import MarkdownRender from '../../markdownRender';
import { initMessage, roleInfo, commonOuterStyle, hintsExample, infoWithAttachment, simpleInitMessage, semiCode, infoWithDivider } from './constant';
import { initMessage, roleInfo, commonOuterStyle, hintsExample, infoWithAttachment, simpleInitMessage, semiCode, infoWithDivider, infoWithJSX } from './constant';

export default {
title: 'Chat',
Expand Down Expand Up @@ -900,3 +900,30 @@ export const CustomRenderDivider = () => {
</div>
);
}

export const MarkdownRenderProps = () => {
const [message, setMessage] = useState(infoWithJSX);
const components = {};
components['MyButton'] = ({ children,onClick }) => {
return <Button type={"primary"} onClick={onClick} style={{marginBottom:"12px"}}> {children} </Button>
}
const markdownRenderProps = {
format: 'mdx',
components: {...MarkdownRender.defaultComponents,...components}
}

return (
<div
style={{ height: 600}}
>
<Chat
placeholder={'不处理输入信息,仅用于展示附件'}
style={commonOuterStyle}
chats={message}
roleConfig={roleInfo}
uploadProps={uploadProps}
markdownRenderProps={markdownRenderProps}
/>
</div>
)
}
17 changes: 16 additions & 1 deletion packages/semi-ui/chat/_story/constant.js
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,20 @@ const simpleInitMessage = [
},
];

const infoWithJSX = [
{
role: 'system',
id: '1',
createAt: 1715676751919,
content: `因为用的是 mdx 模式,因此对于部分符号需要转义 \\{\\} \\<\\> ...
#### 下面是一个渲染在 Markdown 中的按钮
<MyButton onClick={()=>alert("点击了 MyButton")}>MyButton 点我</MyButton>
直接在 Markdown 中书写 JSX 即可
`
},
]

export {
initMessage,
roleInfo,
Expand All @@ -164,5 +178,6 @@ export {
infoWithAttachment,
simpleInitMessage,
semiCode,
infoWithDivider
infoWithDivider,
infoWithJSX
};
8 changes: 5 additions & 3 deletions packages/semi-ui/chat/chatBox/chatBoxContent.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { ReactElement, ReactNode, useMemo } from 'react';
import cls from 'classnames';
import { Message, Metadata, RenderContentProps } from '../interface';
import MarkdownRender from '../../markdownRender';
import MarkdownRender, { MarkdownRenderProps } from '../../markdownRender';
import { cssClasses, strings } from '@douyinfe/semi-foundation/chat/constants';
import { MDXProps } from 'mdx/types';
import { FileAttachment, ImageAttachment } from '../attachment';
Expand All @@ -16,11 +16,12 @@ interface ChatBoxContentProps {
children?: string;
role?: Metadata;
message?: Message;
customRenderFunc?: (props: RenderContentProps) => ReactNode
customRenderFunc?: (props: RenderContentProps) => ReactNode;
markdownRenderProps?: MarkdownRenderProps;
}

const ChatBoxContent = (props: ChatBoxContentProps) => {
const { message = {}, customRenderFunc, role: roleInfo, customMarkDownComponents, mode } = props;
const { message = {}, customRenderFunc, role: roleInfo, customMarkDownComponents, mode, markdownRenderProps } = props;
const { content, role, status } = message;

const markdownComponents = useMemo(() => ({
Expand Down Expand Up @@ -53,6 +54,7 @@ const ChatBoxContent = (props: ChatBoxContentProps) => {
format='md'
raw={content}
components={markdownComponents as any}
{...markdownRenderProps}
/>;
} else if (Array.isArray(content)) {
realContent = content.map((item, index) => {
Expand Down
2 changes: 2 additions & 0 deletions packages/semi-ui/chat/chatBox/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const ChatBox = React.memo((props: ChatBoxProps) => {
chatBoxRenderConfig = {},
customMarkDownComponents,
previousMessage,
markdownRenderProps
} = props;
const { renderChatBoxAvatar, renderChatBoxAction,
renderChatBoxContent, renderChatBoxTitle,
Expand Down Expand Up @@ -64,6 +65,7 @@ const ChatBox = React.memo((props: ChatBoxProps) => {
message={message}
customMarkDownComponents={customMarkDownComponents}
customRenderFunc={renderChatBoxContent}
markdownRenderProps={markdownRenderProps}
/>);
}, [message, info, renderChatBoxContent, mode]);

Expand Down
3 changes: 2 additions & 1 deletion packages/semi-ui/chat/chatContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const ChatContent = React.memo((props: ChatContentProps) => {
const { chats, onMessageBadFeedback, onMessageCopy, mode,
onChatsChange, onMessageDelete, onMessageGoodFeedback,
onMessageReset, roleConfig, chatBoxRenderConfig, align,
customMarkDownComponents, renderDivider,
customMarkDownComponents, renderDivider, markdownRenderProps
} = props;

const [toast, contextHolder] = Toast.useToast();
Expand Down Expand Up @@ -49,6 +49,7 @@ const ChatContent = React.memo((props: ChatContentProps) => {
lastChat={lastMessage}
customMarkDownComponents={customMarkDownComponents}
chatBoxRenderConfig={chatBoxRenderConfig}
markdownRenderProps={markdownRenderProps}
/>;
})}
<div className={`${PREFIX}-toast`}>{contextHolder as any}</div>
Expand Down
4 changes: 3 additions & 1 deletion packages/semi-ui/chat/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ class Chat extends BaseComponent<ChatProps, ChatState> {
uploadProps: PropTypes.object,
uploadTipProps: PropTypes.object,
mode: PropTypes.string,
markdownRenderProps: PropTypes.object,
};

static defaultProps = getDefaultPropsFromGlobalConfig(Chat.__SemiComponentName__, {
Expand Down Expand Up @@ -289,7 +290,7 @@ class Chat extends BaseComponent<ChatProps, ChatState> {
customMarkDownComponents, mode, showClearContext,
placeholder, inputBoxCls, inputBoxStyle,
hintStyle, hintCls, uploadProps, uploadTipProps,
sendHotKey, renderDivider
sendHotKey, renderDivider, markdownRenderProps
} = this.props;
const { backBottomVisible, chats, wheelScroll, uploadAreaVisible } = this.state;
let showStopGenerateFlag = false;
Expand Down Expand Up @@ -347,6 +348,7 @@ class Chat extends BaseComponent<ChatProps, ChatState> {
onMessageCopy={onMessageCopy}
chatBoxRenderConfig={chatBoxRenderConfig}
renderDivider={renderDivider}
markdownRenderProps={markdownRenderProps}
/>
{/* hint area */}
{!!hints?.length && <Hint
Expand Down
4 changes: 3 additions & 1 deletion packages/semi-ui/chat/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Upload } from '../index';
import type { FileItem, UploadProps } from '../upload';
import { Message } from '@douyinfe/semi-foundation/chat/foundation';
import type { TooltipProps } from '../tooltip';
import { MarkdownRenderProps } from '../markdownRender';

export { Message };
export interface CommonChatsProps {
Expand All @@ -19,7 +20,8 @@ export interface CommonChatsProps {
onMessageCopy?: (message?: Message) => void;
chatBoxRenderConfig?: ChatBoxRenderConfig;
customMarkDownComponents?: MDXProps['components'];
renderDivider?: (message?: Message) => ReactNode
renderDivider?: (message?: Message) => ReactNode;
markdownRenderProps?: MarkdownRenderProps
}

export interface ChatProps extends CommonChatsProps {
Expand Down

0 comments on commit b0a54ec

Please sign in to comment.