Browse Source

feat: [Chat] Custom rendering function adds more node information par… (#2557)

* feat: [Chat] Custom rendering function adds more node information parameters
* docs: add FullChatBoxNodes info for Chat component
* fix: [Chat] Add backup condition in message comparison logic to prevent TypeError
---------

Co-authored-by: pointhalo <[email protected]>
YyumeiZhang 11 months ago
parent
commit
fe26172a8c

+ 89 - 10
content/plus/chat/index-en-US.md

@@ -543,17 +543,65 @@ render(DefaultChat);
 Pass in custom rendering configuration through `chatBoxRenderConfig`, the chatBoxRenderConfig type is as follows
 
 ```ts
-interface ChatBoxRenderConfig {
+export interface RenderTitleProps {
+    message?: Message;
+    role?: Metadata;
+    defaultTitle?: ReactNode
+}
+
+export interface RenderAvatarProps {
+    message?: Message;  /* Supported in  2.69.0*/
+    role?: Metadata, 
+    defaultAvatar?: ReactNode
+}
+
+export interface RenderContentProps {
+    message?: Message;
+    role?: Metadata;
+    defaultContent?: ReactNode | ReactNode[]; 
+    className?: string;
+}
+
+export interface DefaultActionNodeObj {
+    copyNode: ReactNode;
+    likeNode: ReactNode;
+    dislikeNode: ReactNode;
+    resetNode: ReactNode;
+    deleteNode: ReactNode;
+}
+
+export interface RenderActionProps {
+    message?: Message;
+    defaultActions?: ReactNode | ReactNode[];
+    className: string;
+    defaultActionsObj?: DefaultActionNodeObj; /* Supported in  2.69.0*/
+};
+
+export interface FullChatBoxNodes {
+    avatar?: ReactNode;
+    title?: ReactNode; 
+    content?: ReactNode; 
+    action?: ReactNode
+}
+
+export interface RenderFullChatBoxProps {
+    message?: Message;
+    role?: Metadata;
+    defaultNodes?: FullChatBoxNodes;
+    className: string;
+}
+
+export interface ChatBoxRenderConfig {
     /* Custom rendering title */
-    renderChatBoxTitle?: (props: {role?: Metadata, defaultTitle?: ReactNode}) => ReactNode;
-    /* Custom rendering avatr */
-    renderChatBoxAvatar?: (props: { role?: Metadata, defaultAvatar?: ReactNode}) => ReactNode;
+    renderChatBoxTitle?: (props: RenderTitleProps) => ReactNode;
+     /* Custom rendering avatar */
+    renderChatBoxAvatar?: (props: RenderAvatarProps) => ReactNode;
     /* Custom rendering content */
-    renderChatBoxContent?: (props: {message?: Message, role?: Metadata, defaultContent?: ReactNode | ReactNode[], className?: string}) => ReactNode;
-    /* Custom rendering message action bar */
-    renderChatBoxAction?: (props: {message?: Message, defaultActions?: ReactNode | ReactNode[], className: string}) => ReactNode;
-    /* Fully customized rendering of the entire chat box */
-    renderFullChatBox?: (props: {message?: Message, role?: Metadata, defaultNodes?: FullChatBoxNodes, className: string}) => ReactNode;
+    renderChatBoxContent?: (props: RenderContentProps) => ReactNode;
+   /* Custom rendering content */
+    renderChatBoxAction?: (props: RenderActionProps) => ReactNode;
+   /* Fully customized rendering of the entire chat box */
+    renderFullChatBox?: (props: RenderFullChatBoxProps) => ReactNode
 }
 ```
 
@@ -1172,10 +1220,41 @@ export interface RenderInputAreaProps {
     onSend?: (content?: string, attachment?: FileItem[]) => void;
     /* If you customize the clear context button, it needs to be called when you click to clear the context */
     onClear?: (e?: any) => void;
+    /* DetailProps supported since 2.69.0 */
+    detailProps: {
+        /* clear context button */
+        clearContextNode?: ReactNode;
+        /* upload button */
+        uploadNOde?: ReactNode;
+        /* text input area */
+        inputNode?: ReactNode;
+        /* send button */
+        sendNode?: ReactNode;
+        /* The processing function that triggers the focus of the text input box after clicking */
+        onClick?: (e?: MouseEvent) => void;
+    }
 }
 ```
 
-Example:
+Example of `detailProps`
+
+```jsx
+function CustomInputRender(props) {
+     const { detailProps } = props;
+    const { clearContextNode, uploadNode, inputNode, sendNode, onClick } = detailProps;
+   
+    return <div style={{margin: '8px 16px', display: 'flex', flexDirection:'row',
+      alignItems: 'flex-end', borderRadius: 16,padding: 10, border: '1px solid var(--semi-color-border)'}}
+      onClick={onClick} 
+    >
+        {uploadNode}
+        {inputNode}
+        {sendNode}
+    </div>
+}
+```
+
+Other Example:
 
 ```jsx live=true noInline=true dir="column"
 import React, {useState, useCallback} from 'react';

+ 86 - 7
content/plus/chat/index.md

@@ -546,17 +546,65 @@ render(DefaultChat);
 通过 `chatBoxRenderConfig` 传入自定义渲染配置, chatBoxRenderConfig 类型如下
 
 ```ts
-interface ChatBoxRenderConfig {
+export interface RenderTitleProps {
+    message?: Message;
+    role?: Metadata;
+    defaultTitle?: ReactNode
+}
+
+export interface RenderAvatarProps {
+    message?: Message; /* Supported in  2.69.0*/
+    role?: Metadata, 
+    defaultAvatar?: ReactNode
+}
+
+export interface RenderContentProps {
+    message?: Message;
+    role?: Metadata;
+    defaultContent?: ReactNode | ReactNode[]; 
+    className?: string;
+}
+
+export interface DefaultActionNodeObj {
+    copyNode: ReactNode;
+    likeNode: ReactNode;
+    dislikeNode: ReactNode;
+    resetNode: ReactNode;
+    deleteNode: ReactNode;
+}
+
+export interface RenderActionProps {
+    message?: Message;
+    defaultActions?: ReactNode | ReactNode[];
+    className: string;
+    defaultActionsObj?: DefaultActionNodeObj; /* Supported in  2.69.0*/
+};
+
+export interface FullChatBoxNodes {
+    avatar?: ReactNode;
+    title?: ReactNode; 
+    content?: ReactNode; 
+    action?: ReactNode
+}
+
+export interface RenderFullChatBoxProps {
+    message?: Message;
+    role?: Metadata;
+    defaultNodes?: FullChatBoxNodes;
+    className: string;
+}
+
+export interface ChatBoxRenderConfig {
     /* 自定义渲染标题 */
-    renderChatBoxTitle?: (props: {role?: Metadata, defaultTitle?: ReactNode}) => ReactNode;
+    renderChatBoxTitle?: (props: RenderTitleProps) => ReactNode;
     /* 自定义渲染头像 */
-    renderChatBoxAvatar?: (props: { role?: Metadata, defaultAvatar?: ReactNode}) => ReactNode;
+    renderChatBoxAvatar?: (props: RenderAvatarProps) => ReactNode;
     /* 自定义渲染内容区域 */
-    renderChatBoxContent?: (props: {message?: Message, role?: Metadata, defaultContent?: ReactNode | ReactNode[], className?: string}) => ReactNode;
+    renderChatBoxContent?: (props: RenderContentProps) => ReactNode;
     /* 自定义渲染消息操作栏 */
-    renderChatBoxAction?: (props: {message?: Message, defaultActions?: ReactNode | ReactNode[], className: string}) => ReactNode;
+    renderChatBoxAction?: (props: RenderActionProps) => ReactNode;
     /* 完全自定义渲染整个聊天框 */
-    renderFullChatBox?: (props: {message?: Message, role?: Metadata, defaultNodes?: FullChatBoxNodes, className: string}) => ReactNode;
+    renderFullChatBox?: (props: RenderFullChatBoxProps) => ReactNode
 }
 ```
 
@@ -1175,10 +1223,41 @@ export interface RenderInputAreaProps {
     onSend?: (content?: string, attachment?: FileItem[]) => void;
     /* 如果自定义清除上下文按钮,点击清除上下文时需调用 */
     onClear?: (e?: any) => void;
+    /* detailProps 自 2.69.0 版本开始支持 */
+    detailProps: {
+        /* 清除上下文按钮 */
+        clearContextNode?: ReactNode;
+        /* 上传按钮 */
+        uploadNOde?: ReactNode;
+        /* 文本输入框 */
+        inputNode?: ReactNode;
+        /* 发送按钮 */
+        sendNode?: ReactNode;
+        /* 点击触发聚焦文本输入框的处理函数*/
+        onClick?: (e?: MouseEvent) => void;
+    }
+}
+```
+
+`detailProps` 的使用示例如下
+
+```jsx
+function CustomInputRender(props) {
+     const { detailProps } = props;
+    const { clearContextNode, uploadNode, inputNode, sendNode, onClick } = detailProps;
+   
+    return <div style={{margin: '8px 16px', display: 'flex', flexDirection:'row',
+      alignItems: 'flex-end', borderRadius: 16,padding: 10, border: '1px solid var(--semi-color-border)'}}
+      onClick={onClick} 
+    >
+        {uploadNode}
+        {inputNode}
+        {sendNode}
+    </div>
 }
 ```
 
-使用示例如下
+其他使用示例如下
 
 ```jsx live=true noInline=true dir="column"
 import React, {useState, useCallback} from 'react';

+ 51 - 0
packages/semi-ui/chat/_story/chat.stories.jsx

@@ -255,6 +255,57 @@ export const CustomRenderInputArea = () => {
     )
 }
 
+function CustomInputRender2(props) {
+    const { defaultNode, onClear, onSend, detailProps } = props;
+    const { clearContextNode, uploadNode, inputNode, sendNode, onClick } = detailProps;
+   
+    return <div style={{margin: '8px 16px', display: 'flex', flexDirection:'row',
+      alignItems: 'flex-end', borderRadius: 16,padding: 10, border: '1px solid var(--semi-color-border)'}}
+      onClick={onClick} 
+    >
+        {uploadNode}
+        {inputNode}
+        {sendNode}
+    </div>
+}
+
+export const CustomRenderInputArea2 = () => {
+    const [message, setMessage] = useState(initMessage.slice(0, 1));
+
+    const onChatsChange = useCallback((chats) => {
+        setMessage(chats);
+    }, []);
+
+    const onMessageSend = useCallback((content, attachment) => {
+        const newAssistantMessage = {
+            role: 'assistant',
+            id: getUuidv4(),
+            content: `This is a mock response`
+        }
+        setMessage((message) => ([...message, newAssistantMessage]));
+    }, []);
+
+    const renderInputArea = useCallback((props) => {
+        return (<CustomInputRender2 {...props} />)
+    }, []);     
+
+    return (
+        <div
+            style={{ height: 600}}
+        >
+            <Chat 
+                style={commonOuterStyle}
+                chats={message}
+                roleConfig={roleInfo}
+                onChatsChange={onChatsChange}
+                onMessageSend={onMessageSend}
+                renderInputArea={renderInputArea}
+                uploadProps={uploadProps}
+            />
+        </div>
+    )
+}
+
 export const CustomRenderAvatar = (props) => {
     const customRenderAvatar = useCallback((props)=> {
         const { role, defaultAvatar } = props;

+ 26 - 8
packages/semi-ui/chat/chatBox/chatBoxAction.tsx

@@ -1,6 +1,6 @@
 import React, { PureComponent, ReactNode } from 'react';
 import PropTypes from 'prop-types';
-import type { ChatBoxProps, Message } from '../interface';
+import type { ChatBoxProps, DefaultActionNodeObj, RenderActionProps } from '../interface';
 import { IconThumbUpStroked, 
     IconDeleteStroked, 
     IconCopyStroked, 
@@ -19,7 +19,7 @@ const { PREFIX_CHAT_BOX_ACTION } = cssClasses;
 const { ROLE, MESSAGE_STATUS } = strings;
 
 interface ChatBoxActionProps extends ChatBoxProps {
-    customRenderFunc?: (props: { message?: Message; defaultActions?: ReactNode | ReactNode[]; className: string }) => ReactNode
+    customRenderFunc?: (props: RenderActionProps) => ReactNode
 }
 
 interface ChatBoxActionState {
@@ -229,15 +229,33 @@ class ChatBoxAction extends BaseComponent<ChatBoxActionProps, ChatBoxActionState
         const { customRenderFunc } = this.props;
         if (customRenderFunc) {
             const actionNodes = [];
-            complete && actionNodes.push(this.copyNode());
-            showFeedback && actionNodes.push(this.likeNode());
-            showFeedback && actionNodes.push(this.dislikeNode());
-            showReset && actionNodes.push(this.resetNode());
-            actionNodes.push(this.deleteNode());
+            const actionNodeObj = {} as DefaultActionNodeObj;
+            if (complete) {
+                const copyNode = this.copyNode();
+                actionNodes.push(copyNode);
+                actionNodeObj.copyNode = copyNode;
+            }
+            if (showFeedback) {
+                const likeNode = this.likeNode();
+                actionNodes.push(likeNode);
+                actionNodeObj.likeNode = likeNode;
+                const dislikeNode = this.dislikeNode();
+                actionNodes.push(dislikeNode);
+                actionNodeObj.dislikeNode = dislikeNode;
+            }
+            if (showReset) {
+                const resetNode = this.resetNode();
+                actionNodes.push(resetNode);
+                actionNodeObj.resetNode = resetNode;
+            }
+            const deleteNode = this.deleteNode();
+            actionNodes.push(deleteNode);
+            actionNodeObj.deleteNode = deleteNode;
             return customRenderFunc({
                 message,
                 defaultActions: actionNodes,
-                className: wrapCls
+                className: wrapCls,
+                defaultActionsObj: actionNodeObj
             });
         }
         return <div className={wrapCls} ref={this.containerRef}>

+ 6 - 4
packages/semi-ui/chat/chatBox/chatBoxAvatar.tsx

@@ -1,6 +1,6 @@
 import React, { useMemo, ReactNode, ReactElement } from 'react';
 import Avatar from '../../avatar';
-import { Metadata } from '../interface';
+import { Message, Metadata, RenderAvatarProps } from '../interface';
 import { cssClasses } from '@douyinfe/semi-foundation/chat/constants';
 import cls from 'classnames';
 
@@ -10,11 +10,12 @@ interface ChatBoxAvatarProps {
     children?: string;
     role?: Metadata;
     continueSend?: boolean;
-    customRenderFunc?: (props: {role?: Metadata; defaultAvatar?: ReactNode}) => ReactNode
+    message?: Message;
+    customRenderFunc?: (props: RenderAvatarProps) => ReactNode
 }
 
 const ChatBoxAvatar = React.memo((props: ChatBoxAvatarProps) => {
-    const { role, customRenderFunc, continueSend } = props;
+    const { role, customRenderFunc, continueSend, message } = props;
 
     const node = useMemo(() => {
         const { avatar, color } = role;
@@ -32,7 +33,8 @@ const ChatBoxAvatar = React.memo((props: ChatBoxAvatarProps) => {
     if (customRenderFunc && typeof customRenderFunc === 'function') {
         return customRenderFunc({
             role, 
-            defaultAvatar: node
+            defaultAvatar: node,
+            message: message
         }) as ReactElement;
     }
     return node;

+ 3 - 3
packages/semi-ui/chat/chatBox/chatBoxContent.tsx

@@ -1,6 +1,6 @@
 import React, { ReactElement, ReactNode, useMemo } from 'react';
 import cls from 'classnames';
-import { Message, Metadata } from '../interface';
+import { Message, Metadata, RenderContentProps } from '../interface';
 import MarkdownRender from '../../markdownRender';
 import { cssClasses, strings } from '@douyinfe/semi-foundation/chat/constants';
 import { MDXProps } from 'mdx/types';
@@ -16,7 +16,7 @@ interface ChatBoxContentProps {
     children?: string;
     role?: Metadata;
     message?: Message;
-    customRenderFunc?: (props: {message?: Message; role?: Metadata; defaultContent?: ReactNode | ReactNode[]; className?: string}) => ReactNode
+    customRenderFunc?: (props: RenderContentProps) => ReactNode
 }
 
 const ChatBoxContent = (props: ChatBoxContentProps) => {
@@ -55,7 +55,7 @@ const ChatBoxContent = (props: ChatBoxContentProps) => {
                     components={markdownComponents as any}
                 />;
             } else if (Array.isArray(content)) {
-                realContent = content.map((item, index)=> {
+                realContent = content.map((item, index) => {
                     if (item.type === 'text') {
                         return <MarkdownRender
                             key={`index`}

+ 2 - 2
packages/semi-ui/chat/chatBox/chatBoxTitle.tsx

@@ -1,5 +1,5 @@
 import React, { useMemo, ReactNode, ReactElement } from 'react';
-import { Message, Metadata } from '../interface';
+import { Message, Metadata, RenderTitleProps } from '../interface';
 import { cssClasses } from '@douyinfe/semi-foundation/chat/constants';
 const { PREFIX_CHAT_BOX } = cssClasses;
 
@@ -7,7 +7,7 @@ interface ChatBoxTitleProps {
     children?: ReactNode | undefined | any;
     role?: Metadata;
     message?: Message;
-    customRenderFunc?: (props: {role?: Metadata; message: Message; defaultTitle?: ReactNode}) => ReactNode
+    customRenderFunc?: (props: RenderTitleProps) => ReactNode
 }
 
 const ChatBoxTitle = React.memo((props: ChatBoxTitleProps) => {

+ 3 - 2
packages/semi-ui/chat/chatBox/index.tsx

@@ -30,7 +30,7 @@ const ChatBox = React.memo((props: ChatBoxProps) => {
 
     const continueSend = useMemo(() => {
         return message?.role === previousMessage?.role;
-    }, [message.role, previousMessage])
+    }, [message.role, previousMessage]);
 
     const info = useMemo(() => {
         let info = {};
@@ -44,9 +44,10 @@ const ChatBox = React.memo((props: ChatBoxProps) => {
         return (<ChatBoxAvatar
             continueSend={continueSend}
             role={info} 
+            message={message}
             customRenderFunc={renderChatBoxAvatar}
         />);
-    }, [info, renderChatBoxAvatar]);
+    }, [info, message, renderChatBoxAvatar]);
 
     const titleNode = useMemo(() => {
         return (<ChatBoxTitle 

+ 1 - 1
packages/semi-ui/chat/index.tsx

@@ -228,7 +228,7 @@ class Chat extends BaseComponent<ChatProps, ChatState> {
                     if (oldChats.length === 0 || newLastChat.id !== oldLastChat.id) {
                         shouldScroll = true;
                     }
-                } else if (newChats.length === oldChats.length &&
+                } else if (newChats.length === oldChats.length && newChats.length && 
                     (newLastChat.status !== 'complete' || newLastChat.status !== oldLastChat.status)
                 ) {
                     shouldScroll = true;

+ 15 - 4
packages/semi-ui/chat/inputBox/index.tsx

@@ -141,17 +141,21 @@ class InputBox extends BaseComponent<InputBoxProps, InputBoxState> {
 
     render() {
         const { onClearContext, renderInputArea, onSend, style, className, showClearContext } = this.props;
+        const clearNode = this.renderClearButton();
+        const uploadNode = this.renderUploadButton();
+        const inputNode = this.renderInputArea();
+        const sendNode = this.renderSendButton();
         const nodes = (
             <div className={cls(PREFIX_INPUT_BOX, { [className]: className })} style={style}>
                 <div 
                     className={`${PREFIX_INPUT_BOX}-inner`}
                     onClick={this.onClick}
                 >
-                    {showClearContext && this.renderClearButton()}
+                    {showClearContext && clearNode}
                     <div className={`${PREFIX_INPUT_BOX}-container`}>
-                        {this.renderUploadButton()}
-                        {this.renderInputArea()}
-                        {this.renderSendButton()}
+                        {uploadNode}
+                        {inputNode}
+                        {sendNode}
                     </div>
                 </div>
             </div>
@@ -161,6 +165,13 @@ class InputBox extends BaseComponent<InputBoxProps, InputBoxState> {
                 defaultNode: nodes, 
                 onClear: onClearContext,
                 onSend: onSend,
+                detailProps: {
+                    clearContextNode: clearNode,
+                    uploadNode: uploadNode,
+                    inputNode: inputNode,
+                    sendNode: sendNode,
+                    onClick: this.onClick,
+                }
             });
         }
         return nodes; 

+ 55 - 7
packages/semi-ui/chat/interface.ts

@@ -19,7 +19,7 @@ export interface CommonChatsProps {
     onMessageCopy?: (message?: Message) => void;
     chatBoxRenderConfig?: ChatBoxRenderConfig;
     customMarkDownComponents?: MDXProps['components'];
-    renderDivider?: (message?: Message) => ReactNode;
+    renderDivider?: (message?: Message) => ReactNode
 }
 
 export interface ChatProps extends CommonChatsProps {
@@ -52,15 +52,63 @@ export interface ChatProps extends CommonChatsProps {
 export interface RenderInputAreaProps {
     defaultNode?: ReactNode;
     onSend?: (content?: string, attachment?: FileItem[]) => void;
-    onClear?: (e?: any) => void
+    onClear?: (e?: any) => void;
+    detailProps?: {
+        clearContextNode?: ReactNode;
+        uploadNode?: ReactNode;
+        inputNode?: ReactNode;
+        sendNode?: ReactNode;
+        onClick?: (e?: MouseEvent) => void
+    }
+}
+
+export interface RenderTitleProps {
+    message?: Message;
+    role?: Metadata;
+    defaultTitle?: ReactNode
+}
+
+export interface RenderAvatarProps {
+    message?: Message;
+    role?: Metadata; 
+    defaultAvatar?: ReactNode
+}
+
+export interface RenderContentProps {
+    message?: Message;
+    role?: Metadata;
+    defaultContent?: ReactNode | ReactNode[]; 
+    className?: string
+}
+
+export interface DefaultActionNodeObj {
+    copyNode: ReactNode;
+    likeNode: ReactNode;
+    dislikeNode: ReactNode;
+    resetNode: ReactNode;
+    deleteNode: ReactNode
+}
+
+export interface RenderActionProps {
+    message?: Message;
+    defaultActions?: ReactNode | ReactNode[];
+    className: string;
+    defaultActionsObj?: DefaultActionNodeObj
+}
+
+export interface RenderFullChatBoxProps {
+    message?: Message;
+    role?: Metadata;
+    defaultNodes?: FullChatBoxNodes;
+    className: string
 }
 
 export interface ChatBoxRenderConfig {
-    renderChatBoxTitle?: (props: {role?: Metadata; defaultTitle?: ReactNode}) => ReactNode;
-    renderChatBoxAvatar?: (props: { role?: Metadata; defaultAvatar?: ReactNode}) => ReactNode;
-    renderChatBoxContent?: (props: {message?: Message; role?: Metadata; defaultContent?: ReactNode | ReactNode[]; className?: string}) => ReactNode;
-    renderChatBoxAction?: (props: {message?: Message; defaultActions?: ReactNode | ReactNode[]; className: string}) => ReactNode;
-    renderFullChatBox?: (props: {message?: Message; role?: Metadata; defaultNodes?: FullChatBoxNodes; className: string}) => ReactNode
+    renderChatBoxTitle?: (props: RenderTitleProps) => ReactNode;
+    renderChatBoxAvatar?: (props: RenderAvatarProps) => ReactNode;
+    renderChatBoxContent?: (props: RenderContentProps) => ReactNode;
+    renderChatBoxAction?: (props: RenderActionProps) => ReactNode;
+    renderFullChatBox?: (props: RenderFullChatBoxProps) => ReactNode
 }
 
 export interface FullChatBoxNodes {

+ 92 - 28
yarn.lock

@@ -1585,11 +1585,25 @@
     "@douyinfe/semi-animation-styled" "2.65.0"
     classnames "^2.2.6"
 
+"@douyinfe/[email protected]":
+  version "2.68.4"
+  resolved "https://registry.yarnpkg.com/@douyinfe/semi-animation-react/-/semi-animation-react-2.68.4.tgz#38cef5e5f307b49c918d61ab22da2617af36403d"
+  integrity sha512-d6Y1/EIgg/sNoRnJu24KLvXA5hRYh7jLZk9kKjergaRCiujmY+zf5Gg1m7NAVH9gpyWKJkXqZwexZWHPlK1nkg==
+  dependencies:
+    "@douyinfe/semi-animation" "2.68.4"
+    "@douyinfe/semi-animation-styled" "2.68.4"
+    classnames "^2.2.6"
+
 "@douyinfe/[email protected]":
   version "2.65.0"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-animation-styled/-/semi-animation-styled-2.65.0.tgz#8c56047a5704a45b05cc9809a2a126cc24526ea1"
   integrity sha512-YFF8Ptcz/jwS0phm28XZV7ROqMQ233sjVR0Uy33FImCITr6EAPe5wcCeEmzVZoYS7x3tUFR30SF+0hSO01rQUg==
 
+"@douyinfe/[email protected]":
+  version "2.68.4"
+  resolved "https://registry.yarnpkg.com/@douyinfe/semi-animation-styled/-/semi-animation-styled-2.68.4.tgz#4f3c4cd89df7a8fcf299ae463f549467dd77811a"
+  integrity sha512-5mb1lYlDBBumJNNt9MpXIZ9qnA112O9Wc/+izP8OSDbPBBojg9goOH09IBJBLQSVAUEcfReaTbzDynWNDYaLSg==
+
 "@douyinfe/[email protected]":
   version "2.65.0"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-animation/-/semi-animation-2.65.0.tgz#f544a6b420c3e948c09836019e6b63f1382cd12c"
@@ -1597,6 +1611,13 @@
   dependencies:
     bezier-easing "^2.1.0"
 
+"@douyinfe/[email protected]":
+  version "2.68.4"
+  resolved "https://registry.yarnpkg.com/@douyinfe/semi-animation/-/semi-animation-2.68.4.tgz#5fea38d5c57d9a030adb937dcf174f4980e99d73"
+  integrity sha512-ZnrTIOuQnIjn60NqQ6tS2Z54CPuoBrQpocjR5xMUOdn36pP95jkdJlXEAknPi1gbuuVhhThpVWxjKb947l7b8g==
+  dependencies:
+    bezier-easing "^2.1.0"
+
 "@douyinfe/[email protected]":
   version "2.65.0"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-foundation/-/semi-foundation-2.65.0.tgz#20466a9b4baacdde2249930fb709ba035c5a7bea"
@@ -1616,6 +1637,25 @@
     remark-gfm "^4.0.0"
     scroll-into-view-if-needed "^2.2.24"
 
+"@douyinfe/[email protected]":
+  version "2.68.4"
+  resolved "https://registry.yarnpkg.com/@douyinfe/semi-foundation/-/semi-foundation-2.68.4.tgz#f607b70a192bfece0292bc2557a533d6fda221dd"
+  integrity sha512-fzHKaF4nmYe/brhUlc/o8Ck+FVd+MakhwS7WveLTLoQYMXTHpC2mXoWHniCh5R6rHwgcNopADj1QYOo1um9aWQ==
+  dependencies:
+    "@douyinfe/semi-animation" "2.68.4"
+    "@mdx-js/mdx" "^3.0.1"
+    async-validator "^3.5.0"
+    classnames "^2.2.6"
+    date-fns "^2.29.3"
+    date-fns-tz "^1.3.8"
+    fast-copy "^3.0.1 "
+    lodash "^4.17.21"
+    lottie-web "^5.12.2"
+    memoize-one "^5.2.1"
+    prismjs "^1.29.0"
+    remark-gfm "^4.0.0"
+    scroll-into-view-if-needed "^2.2.24"
+
 "@douyinfe/[email protected]", "@douyinfe/semi-icons@latest":
   version "2.65.0"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-icons/-/semi-icons-2.65.0.tgz#af39cbd5431ebccedcf7d9ce689646e54bebc432"
@@ -1623,11 +1663,23 @@
   dependencies:
     classnames "^2.2.6"
 
+"@douyinfe/[email protected]", "@douyinfe/semi-icons@^2.0.0":
+  version "2.68.4"
+  resolved "https://registry.yarnpkg.com/@douyinfe/semi-icons/-/semi-icons-2.68.4.tgz#2ec408c08051daf1cf905b24193501a559d80273"
+  integrity sha512-4M1NeLjyeF7iyHD/7shnrqcSFowVYPYazgk3413j/qs59lGNRP2U0PdQ1pg1vX+5rP13AY2jEOVG3UQfC4nw7g==
+  dependencies:
+    classnames "^2.2.6"
+
 "@douyinfe/[email protected]":
   version "2.65.0"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-illustrations/-/semi-illustrations-2.65.0.tgz#9916c540c91222a1d9f48cd34a941d28b8a05d2f"
   integrity sha512-1IhOztyBYiSu8WrcvN+oWWtcJTC9+x6zbnYtufx4ToISs5UO1te1PQofABpkDzIJYFtW9yYLxg4uoL4wGjqYMA==
 
+"@douyinfe/[email protected]":
+  version "2.68.4"
+  resolved "https://registry.yarnpkg.com/@douyinfe/semi-illustrations/-/semi-illustrations-2.68.4.tgz#90cad6193c4b23d12e951372a2ef5a4db3974bc5"
+  integrity sha512-hy8nd1CzyxjV1Q8+AIbq9TQAuoU95wHWVgduscp+yxXKjGQTNkXppdWoqvQ76Ib+nORBk7eXltlt5cx2xPox1g==
+
 "@douyinfe/[email protected]":
   version "2.23.2"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-scss-compile/-/semi-scss-compile-2.23.2.tgz#30884bb194ee9ae1e81877985e5663c3297c1ced"
@@ -1699,6 +1751,38 @@
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-theme-default/-/semi-theme-default-2.61.0.tgz#a7e9bf9534721c12af1d0eeb5d5a2de615896a23"
   integrity sha512-obn/DOw4vZyKFAlWvZxHTpBLAK9FO9kygTSm2GROgvi+UDB2PPU6l20cuUCsdGUNWJRSqYlTTVZ1tNYIyFZ5Sg==
 
+"@douyinfe/[email protected]":
+  version "2.68.4"
+  resolved "https://registry.yarnpkg.com/@douyinfe/semi-theme-default/-/semi-theme-default-2.68.4.tgz#dc36d5d786b41a16fa7bf683f0617aa5eef5522c"
+  integrity sha512-RhsDxeOHBfgWa/HOWtR8EDD6xIcHSqNvRnxypvDgNTvIgfMThzKHaMMvaNsSnfzeXARazKUrYmneAumx2rP8wQ==
+
+"@douyinfe/semi-ui@^2.0.0":
+  version "2.68.4"
+  resolved "https://registry.yarnpkg.com/@douyinfe/semi-ui/-/semi-ui-2.68.4.tgz#9281cde5434012a7c8f7b1cd0569d4e766b7505d"
+  integrity sha512-LvRss8L0gY+wkc9eVUbT/O8qawokNMbCsvJ5ayt2Mch+FHrt+C9hPXBH1sLcTzLruTn6Ae8m3VRf4gpFS3EHgQ==
+  dependencies:
+    "@dnd-kit/core" "^6.0.8"
+    "@dnd-kit/sortable" "^7.0.2"
+    "@dnd-kit/utilities" "^3.2.1"
+    "@douyinfe/semi-animation" "2.68.4"
+    "@douyinfe/semi-animation-react" "2.68.4"
+    "@douyinfe/semi-foundation" "2.68.4"
+    "@douyinfe/semi-icons" "2.68.4"
+    "@douyinfe/semi-illustrations" "2.68.4"
+    "@douyinfe/semi-theme-default" "2.68.4"
+    async-validator "^3.5.0"
+    classnames "^2.2.6"
+    copy-text-to-clipboard "^2.1.1"
+    date-fns "^2.29.3"
+    date-fns-tz "^1.3.8"
+    fast-copy "^3.0.1 "
+    lodash "^4.17.21"
+    prop-types "^15.7.2"
+    react-resizable "^3.0.5"
+    react-window "^1.8.2"
+    scroll-into-view-if-needed "^2.2.24"
+    utility-types "^3.10.0"
+
 "@douyinfe/semi-ui@latest":
   version "2.65.0"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-ui/-/semi-ui-2.65.0.tgz#295eb0dd8e9e961adb4ddd7c7bbce3468d1b7430"
@@ -11831,6 +11915,11 @@ eslint-plugin-react@^7.20.6, eslint-plugin-react@^7.24.0:
     string.prototype.matchall "^4.0.11"
     string.prototype.repeat "^1.0.0"
 
+eslint-plugin-semi-design@^2.33.0:
+  version "2.68.4"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-semi-design/-/eslint-plugin-semi-design-2.68.4.tgz#70d96e8ae0b93b383ebc83a0695bbbbd3498241e"
+  integrity sha512-j9tv4Pm+Fx4930Jq4T+c/nrjl/rtPBjWdQrHTaYx6Kuv17LKSE5ej/hJWbtkSKhRq57/tbrhCE66HiXfH0XEeg==
+
 eslint-rule-composer@^0.3.0:
   version "0.3.0"
   resolved "https://registry.yarnpkg.com/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz#79320c927b0c5c0d3d3d2b76c8b4a488f25bbaf9"
@@ -25031,7 +25120,7 @@ string-similarity@^1.2.2:
     lodash.map "^4.6.0"
     lodash.maxby "^4.6.0"
 
-"string-width-cjs@npm:string-width@^4.2.0":
+"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3:
   version "4.2.3"
   resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
   integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@@ -25049,15 +25138,6 @@ string-width@^1.0.1, string-width@^1.0.2:
     is-fullwidth-code-point "^1.0.0"
     strip-ansi "^3.0.0"
 
-"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3:
-  version "4.2.3"
-  resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
-  integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
-  dependencies:
-    emoji-regex "^8.0.0"
-    is-fullwidth-code-point "^3.0.0"
-    strip-ansi "^6.0.1"
-
 string-width@^2.0.0, string-width@^2.1.0:
   version "2.1.1"
   resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
@@ -25201,7 +25281,7 @@ stringify-object@^3.3.0:
     is-obj "^1.0.1"
     is-regexp "^1.0.0"
 
-"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
+"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
   version "6.0.1"
   resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
   integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
@@ -25229,13 +25309,6 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0:
   dependencies:
     ansi-regex "^4.1.0"
 
-strip-ansi@^6.0.0, strip-ansi@^6.0.1:
-  version "6.0.1"
-  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
-  integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
-  dependencies:
-    ansi-regex "^5.0.1"
-
 strip-ansi@^7.0.1:
   version "7.1.0"
   resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
@@ -27817,7 +27890,7 @@ worker-farm@^1.7.0:
   dependencies:
     errno "~0.1.7"
 
-"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
+"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
   version "7.0.0"
   resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
   integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
@@ -27852,15 +27925,6 @@ wrap-ansi@^6.2.0:
     string-width "^4.1.0"
     strip-ansi "^6.0.0"
 
-wrap-ansi@^7.0.0:
-  version "7.0.0"
-  resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
-  integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
-  dependencies:
-    ansi-styles "^4.0.0"
-    string-width "^4.1.0"
-    strip-ansi "^6.0.0"
-
 wrap-ansi@^8.1.0:
   version "8.1.0"
   resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"