Przeglądaj źródła

Merge branch 'release'

zhangyumei.0319 1 rok temu
rodzic
commit
8b647c52c5
94 zmienionych plików z 7735 dodań i 320 usunięć
  1. 6 2
      content/input/form/index-en-US.md
  2. 1 0
      content/input/form/index.md
  3. 1 1
      content/input/pincode/index-en-US.md
  4. 16 23
      content/input/timepicker/index-en-US.md
  5. 56 63
      content/input/timepicker/index.md
  6. 7 4
      content/input/upload/index-en-US.md
  7. 26 26
      content/input/upload/index.md
  8. 1 0
      content/order.js
  9. 1612 0
      content/plus/chat/index-en-US.md
  10. 1616 0
      content/plus/chat/index.md
  11. 1 1
      content/plus/codehighlight/index-en-US.md
  12. 1 1
      content/plus/lottie/index-en-US.md
  13. 1 1
      content/plus/markdownrender/index-en-US.md
  14. 1 1
      content/show/scrolllist/index-en-US.md
  15. 1 1
      content/show/sidesheet/index-en-US.md
  16. 1 1
      content/show/sidesheet/index.md
  17. 13 3
      content/start/changelog/index-en-US.md
  18. 11 0
      content/start/changelog/index.md
  19. 2 1
      content/start/overview/index-en-US.md
  20. 2 1
      content/start/overview/index.md
  21. 1 1
      lerna.json
  22. 1 1
      package.json
  23. 3 3
      packages/semi-animation-react/package.json
  24. 1 1
      packages/semi-animation-styled/package.json
  25. 1 1
      packages/semi-animation/package.json
  26. 1 1
      packages/semi-eslint-plugin/package.json
  27. 598 0
      packages/semi-foundation/chat/chat.scss
  28. 64 0
      packages/semi-foundation/chat/chatBoxActionFoundation.ts
  29. 68 0
      packages/semi-foundation/chat/constants.ts
  30. 306 0
      packages/semi-foundation/chat/foundation.ts
  31. 98 0
      packages/semi-foundation/chat/inputboxFoundation.ts
  32. 22 0
      packages/semi-foundation/chat/rtl.scss
  33. 125 0
      packages/semi-foundation/chat/variables.scss
  34. 5 0
      packages/semi-foundation/input/textareaFoundation.ts
  35. 2 2
      packages/semi-foundation/package.json
  36. 4 2
      packages/semi-foundation/upload/foundation.ts
  37. 1 1
      packages/semi-icons-lab/package.json
  38. 1 1
      packages/semi-icons/package.json
  39. 1 1
      packages/semi-illustrations/package.json
  40. 2 2
      packages/semi-next/package.json
  41. 1 1
      packages/semi-rspack/package.json
  42. 1 1
      packages/semi-scss-compile/package.json
  43. 1 1
      packages/semi-theme-default/package.json
  44. 828 0
      packages/semi-ui/chat/_story/chat.stories.jsx
  45. 90 0
      packages/semi-ui/chat/_story/chat.stories.tsx
  46. 141 0
      packages/semi-ui/chat/_story/constant.js
  47. 97 0
      packages/semi-ui/chat/attachment.tsx
  48. 253 0
      packages/semi-ui/chat/chatBox/chatBoxAction.tsx
  49. 42 0
      packages/semi-ui/chat/chatBox/chatBoxAvatar.tsx
  50. 88 0
      packages/semi-ui/chat/chatBox/chatBoxContent.tsx
  51. 31 0
      packages/semi-ui/chat/chatBox/chatBoxTitle.tsx
  52. 54 0
      packages/semi-ui/chat/chatBox/code.tsx
  53. 118 0
      packages/semi-ui/chat/chatBox/index.tsx
  54. 58 0
      packages/semi-ui/chat/chatContent.tsx
  55. 53 0
      packages/semi-ui/chat/hint.tsx
  56. 382 0
      packages/semi-ui/chat/index.tsx
  57. 170 0
      packages/semi-ui/chat/inputBox/index.tsx
  58. 126 0
      packages/semi-ui/chat/interface.ts
  59. 2 1
      packages/semi-ui/form/arrayField.tsx
  60. 10 0
      packages/semi-ui/form/baseForm.tsx
  61. 4 0
      packages/semi-ui/form/interface.ts
  62. 3 1
      packages/semi-ui/index.ts
  63. 8 2
      packages/semi-ui/input/textarea.tsx
  64. 9 0
      packages/semi-ui/locale/interface.ts
  65. 9 0
      packages/semi-ui/locale/source/ar.ts
  66. 9 0
      packages/semi-ui/locale/source/de.ts
  67. 9 0
      packages/semi-ui/locale/source/en_GB.ts
  68. 9 0
      packages/semi-ui/locale/source/en_US.ts
  69. 9 0
      packages/semi-ui/locale/source/es.ts
  70. 9 0
      packages/semi-ui/locale/source/fr.ts
  71. 9 0
      packages/semi-ui/locale/source/id_ID.ts
  72. 9 0
      packages/semi-ui/locale/source/it.ts
  73. 9 0
      packages/semi-ui/locale/source/ja_JP.ts
  74. 9 0
      packages/semi-ui/locale/source/ko_KR.ts
  75. 9 0
      packages/semi-ui/locale/source/ms_MY.ts
  76. 9 0
      packages/semi-ui/locale/source/nl_NL.ts
  77. 9 0
      packages/semi-ui/locale/source/pl_PL.ts
  78. 9 0
      packages/semi-ui/locale/source/pt_BR.ts
  79. 9 0
      packages/semi-ui/locale/source/ro.ts
  80. 9 0
      packages/semi-ui/locale/source/ru_RU.ts
  81. 9 0
      packages/semi-ui/locale/source/sv_SE.ts
  82. 9 0
      packages/semi-ui/locale/source/th_TH.ts
  83. 9 0
      packages/semi-ui/locale/source/tr_TR.ts
  84. 9 0
      packages/semi-ui/locale/source/vi_VN.ts
  85. 9 0
      packages/semi-ui/locale/source/zh_CN.ts
  86. 9 0
      packages/semi-ui/locale/source/zh_TW.ts
  87. 0 1
      packages/semi-ui/markdownRender/_story/markdownRender.stories.jsx
  88. 10 6
      packages/semi-ui/package.json
  89. 1 1
      packages/semi-ui/upload/index.tsx
  90. 1 1
      packages/semi-webpack/package.json
  91. 148 148
      sitemap.xml
  92. 7 0
      src/images/docIcons/doc-chat.svg
  93. 26 0
      src/styles/docDemo.scss
  94. 93 9
      yarn.lock

+ 6 - 2
content/input/form/index-en-US.md

@@ -2059,7 +2059,7 @@ render(WithFieldDemo2);
 | component         | For declaring fields, not used at the same time as render, props.children                                                                                                                                                                                                                                           | ReactNode                                       |
 | component         | For declaring fields, not used at the same time as render, props.children                                                                                                                                                                                                                                           | ReactNode                                       |
 | className         | Classname for form tag                                                                                                                                                                                                                                                                                              | string                                          |
 | className         | Classname for form tag                                                                                                                                                                                                                                                                                              | string                                          |
 | disabled          | If true, all fields inside the form structure will automatically inherit the disabled attribute                                                                                                                                                                                                                     | boolean                                         | false      |
 | disabled          | If true, all fields inside the form structure will automatically inherit the disabled attribute                                                                                                                                                                                                                     | boolean                                         | false      |
-| extraTextPosition | The extraTextPosition property applied to each Field uniformly controls the display position of extraText. Middle (the vertical direction is displayed in the order of Label, extraText, and Field), bottom (the vertical direction is displayed in the order of Label, Field, and extraText) <br/>**since v1.9.0** | string                                          | 'bottom'   |
+| extraTextPosition | The extraTextPosition property applied to each Field uniformly controls the display position of extraText. Middle (the vertical direction is displayed in the order of Label, extraText, and Field), bottom (the vertical direction is displayed in the order of Label, Field, and extraText) | string                                          | 'bottom'   |
 | getFormApi        | This function will be executed once when the form is mounted and returns formApi. <br/>formApi can be used to modify the internal state of the form (value, touched, error)                                                                                                                                         | function (formApi: object)                      |            |
 | getFormApi        | This function will be executed once when the form is mounted and returns formApi. <br/>formApi can be used to modify the internal state of the form (value, touched, error)                                                                                                                                         | function (formApi: object)                      |            |
 | initValues        | Used to uniformly set the initial value of the form <br/>(will be consumed only once when form is mount)                                                                                                                                                                                                            | object                                          |            |
 | initValues        | Used to uniformly set the initial value of the form <br/>(will be consumed only once when form is mount)                                                                                                                                                                                                            | object                                          |            |
 | layout            | The layout of fields, optional `horizontal` or `vertical`                                                                                                                                                                                                                                                           | string                                          | 'vertical' |
 | layout            | The layout of fields, optional `horizontal` or `vertical`                                                                                                                                                                                                                                                           | string                                          | 'vertical' |
@@ -2073,7 +2073,11 @@ render(WithFieldDemo2);
 | onSubmit          | Callback invoked after clicked on submit button or executed `formApi.submit()`, <br/>and all validation pass.                                                                                                                                                                                                        | function (values: object, e: event)                       |            |
 | onSubmit          | Callback invoked after clicked on submit button or executed `formApi.submit()`, <br/>and all validation pass.                                                                                                                                                                                                        | function (values: object, e: event)                       |            |
 | onSubmitFail      | Callback invoked after clicked on submit button or executed `formApi.submit()`,<br/> but validate failed.                                                                                                                                                                                                            | function (error: object, values: object, e: event)               |            |
 | onSubmitFail      | Callback invoked after clicked on submit button or executed `formApi.submit()`,<br/> but validate failed.                                                                                                                                                                                                            | function (error: object, values: object, e: event)               |            |
 | render            | For declaring fields, not used at the same time as component, props.children                                                                                                                                                                                                                                        | function                                        |
 | render            | For declaring fields, not used at the same time as component, props.children                                                                                                                                                                                                                                        | function                                        |
-| showValidateIcon  | Whether the verification information block in the field automatically adds the corresponding status icon display <br/>**since v1.0.0**                                                                                                                                                                              | boolean                                         | true       |
+| showValidateIcon  | Whether the verification information block in the field automatically adds the corresponding status icon display                                                                                                                                                                               | boolean                                         | true       |
+| style             | inline style of form element                                                                          | object                                        |
+| stopValidateWithError | Apply stopValidateWithError to each Field uniformly. For usage instructions, see the API of the same name in Field props (available after v2.42)                                                                            | boolean                             | false     |
+| stopPropagation | Whether to prevent submit or reset events from bubbling. This is used in nested Form scenarios to prevent events from propagating outwards when the inner Form submits or resets, triggering events in the outer Form. The default is `{ reset: false, submit: false }`(available after v2.63)                                                                              | object                             |      |
+| trigger  | Apply the trigger uniformly to each Field to control the timing of verification. For detailed instructions, see the API of the same name in Field props.(available after v2.42)                                               | string\|array                            |  'change'  |
 | validateFields    | Form-level custom validate functions are called at submit or formApi.validate(). <br/>Supported synchronous / asynchronous function                                                                                                                                                                                 | function (values)                               |            |
 | validateFields    | Form-level custom validate functions are called at submit or formApi.validate(). <br/>Supported synchronous / asynchronous function                                                                                                                                                                                 | function (values)                               |            |
 | wrapperCol        | Uniformly apply the layout on each Field, with [Col component](/en-US/basic/grid#Col), <br/>set `span`, `span` values, such as {span: 20, offset: 4}                                                                                                                                     | object                                          |
 | wrapperCol        | Uniformly apply the layout on each Field, with [Col component](/en-US/basic/grid#Col), <br/>set `span`, `span` values, such as {span: 20, offset: 4}                                                                                                                                     | object                                          |
 
 

+ 1 - 0
content/input/form/index.md

@@ -2073,6 +2073,7 @@ render(WithFieldDemo2);
 | showValidateIcon  | Field 内的校验信息区块否自动添加对应状态的 icon 展示                                                                                                                         | boolean                                       | true       |
 | showValidateIcon  | Field 内的校验信息区块否自动添加对应状态的 icon 展示                                                                                                                         | boolean                                       | true       |
 | style             | 可将内联样式传入 form 标签                                                                                                                                                   | object                                        |
 | style             | 可将内联样式传入 form 标签                                                                                                                                                   | object                                        |
 | stopValidateWithError | 统一应用在每个 Field 的 stopValidateWithError,使用说明见 Field props中同名 API (v2.42后提供)                                                                            | boolean                             | false     |
 | stopValidateWithError | 统一应用在每个 Field 的 stopValidateWithError,使用说明见 Field props中同名 API (v2.42后提供)                                                                            | boolean                             | false     |
+| stopPropagation | 是否阻止 submit或reset事件冒泡,用于嵌套 Form 场景下,内部 Form submit或reset时阻止事件往外传播,触发外部Form的事件。默认为 `{ reset: false, submit: false }`(v2.63后提供)                                                                            | object                             |      |
 | trigger    |  统一应用在每个 Field 的 trigger,使用说明详见 Field props中同名 API(v2.42后提供)                                                        | string\|array                            |  'change'  |
 | trigger    |  统一应用在每个 Field 的 trigger,使用说明详见 Field props中同名 API(v2.42后提供)                                                        | string\|array                            |  'change'  |
 | validateFields    | Form 级别的自定义校验函数,submit 时或 formApi.validate 时会被调用(配置Form级别校验器后,Field级别校验器在submit或formApi.validate()时不会再被触发)。支持同步校验、异步校验                                                                                   | function(values)                              |            |
 | validateFields    | Form 级别的自定义校验函数,submit 时或 formApi.validate 时会被调用(配置Form级别校验器后,Field级别校验器在submit或formApi.validate()时不会再被触发)。支持同步校验、异步校验                                                                                   | function(values)                              |            |
 | wrapperCol        | 统一应用在每个 Field 上的布局,同[Col 组件](/zh-CN/basic/grid#Col),设置`span`、`offset`值,如{span: 20, offset: 4}                                 | object                                        |
 | wrapperCol        | 统一应用在每个 Field 上的布局,同[Col 组件](/zh-CN/basic/grid#Col),设置`span`、`offset`值,如{span: 20, offset: 4}                                 | object                                        |

+ 1 - 1
content/input/pincode/index-en-US.md

@@ -3,7 +3,7 @@ localeCode: en-US
 order: 31
 order: 31
 category: Input
 category: Input
 title: PinCode
 title: PinCode
-icon: doc-input
+icon: doc-pincode
 width: 60%
 width: 60%
 brief: For easy and intuitive verification code entry
 brief: For easy and intuitive verification code entry
 ---
 ---

+ 16 - 23
content/input/timepicker/index-en-US.md

@@ -203,8 +203,6 @@ function Demo() {
 
 
 ### Custom Trigger
 ### Custom Trigger
 
 
-**Version:** >=0.34.0
-
 By default we use the `Input` component as the trigger for the `DatePicker` component. You can customize this trigger by passing the `triggerRender` method.
 By default we use the `Input` component as the trigger for the `DatePicker` component. You can customize this trigger by passing the `triggerRender` method.
 
 
 ```jsx live=true hideInDSM
 ```jsx live=true hideInDSM
@@ -216,18 +214,6 @@ import { IconChevronDown, IconClose } from '@douyinfe/semi-icons';
 function Demo() {
 function Demo() {
     const formatToken = 'HH:mm:ss';
     const formatToken = 'HH:mm:ss';
     const [time, setTime] = useState(new Date());
     const [time, setTime] = useState(new Date());
-    const triggerIcon = useMemo(() => {
-        return time ? (
-            <IconClose
-                onClick={e => {
-                    e && e.stopPropagation();
-                    setTime();
-                }}
-            />
-        ) : (
-            <IconChevronDown />
-        );
-    }, [time]);
 
 
     return (
     return (
         <TimePicker
         <TimePicker
@@ -235,9 +221,16 @@ function Demo() {
             format={formatToken}
             format={formatToken}
             onChange={time => setTime(time)}
             onChange={time => setTime(time)}
             triggerRender={({ placeholder }) => (
             triggerRender={({ placeholder }) => (
-                <Button theme={'light'} icon={triggerIcon} iconPosition={'right'}>
+                <Tag
+                    color='cyan'
+                    size='large'
+                    shape='circle'
+                    style={{ padding: 12, paddingRight: 16, fontSize: 14 }}
+                    theme={'light'}
+                    prefixIcon={<IconTimePicker />}
+                >
                     {time ? dateFns.format(time, formatToken) : placeholder}
                     {time ? dateFns.format(time, formatToken) : placeholder}
-                </Button>
+                </Tag>
             )}
             )}
         />
         />
     );
     );
@@ -299,13 +292,13 @@ function Demo(props = {}) {
 
 
 | Parameters | Instructions                                                                                                                                                                                                                                  | Type | Default | Version |
 | Parameters | Instructions                                                                                                                                                                                                                                  | Type | Default | Version |
 | --- |-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| --- | --- | --- |
 | --- |-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| --- | --- | --- |
-| autoAdjustOverflow | Whether the floating layer automatically adjusts its direction when it is blocked                                                                                                                                                             | boolean | true | **0.34.0** |
+| autoAdjustOverflow | Whether the floating layer automatically adjusts its direction when it is blocked                                                                                                                                                             | boolean | true |  |
 | autoFocus | Automatic access to focus                                                                                                                                                                                                                     | boolean | false |
 | autoFocus | Automatic access to focus                                                                                                                                                                                                                     | boolean | false |
 | borderless        | borderless mode  >=2.33.0                                                                                                                                                                                                                     | boolean                         |           |
 | borderless        | borderless mode  >=2.33.0                                                                                                                                                                                                                     | boolean                         |           |
 | className | Outer style name                                                                                                                                                                                                                              | string |  |
 | className | Outer style name                                                                                                                                                                                                                              | string |  |
 | clearIcon | Can be used to customize the clear button, valid when showClear is true                                                                                                                                                                       | ReactNode |  |  **2.25.0**|
 | clearIcon | Can be used to customize the clear button, valid when showClear is true                                                                                                                                                                       | ReactNode |  |  **2.25.0**|
 | clearText | Clear button prompt copy                                                                                                                                                                                                                      | string | Clear |
 | clearText | Clear button prompt copy                                                                                                                                                                                                                      | string | Clear |
-| defaultOpen | Whether the panel is open by default                                                                                                                                                                                                          | boolean |  | **0.19.0** |
+| defaultOpen | Whether the panel is open by default                                                                                                                                                                                                          | boolean |  |  |
 | defaultValue | Default time                                                                                                                                                                                                                                  | Date\|timeStamp\|string (array when type = "timeRange") |  |
 | defaultValue | Default time                                                                                                                                                                                                                                  | Date\|timeStamp\|string (array when type = "timeRange") |  |
 | disabled | Disable all operations                                                                                                                                                                                                                        | boolean | false |
 | disabled | Disable all operations                                                                                                                                                                                                                        | boolean | false |
 | disabledHours | Prohibited selection of partial hour options                                                                                                                                                                                                  | () => number [] |  |
 | disabledHours | Prohibited selection of partial hour options                                                                                                                                                                                                  | () => number [] |  |
@@ -331,19 +324,19 @@ function Demo(props = {}) {
 | prefix | Prefix content                                                                                                                                                                                                                                | string\|ReactNode |  |  |
 | prefix | Prefix content                                                                                                                                                                                                                                | string\|ReactNode |  |  |
 | preventScroll | Indicates whether the browser should scroll the document to display the newly focused element, acting on the focus method inside the component, excluding the component passed in by the user                                                 | boolean |  |  |
 | preventScroll | Indicates whether the browser should scroll the document to display the newly focused element, acting on the focus method inside the component, excluding the component passed in by the user                                                 | boolean |  |  |
 | rangeSeparator | time range delimiter                                                                                                                                                                                                                          | string | "~" |
 | rangeSeparator | time range delimiter                                                                                                                                                                                                                          | string | "~" |
-| scrollItemProps | The props passed through to ScrollItem. The optional values are the same as [ScrollList#API](/zh-CN/show/scrolllist#ScrollItem)                                                                                                               | object |  | **0.31.0** |
+| scrollItemProps | The props passed through to ScrollItem. The optional values are the same as [ScrollList#API](/zh-CN/show/scrolllist#ScrollItem)                                                                                                               | object |  | |
 | secondStep | Second option interval                                                                                                                                                                                                                        | number | 1 |
 | secondStep | Second option interval                                                                                                                                                                                                                        | number | 1 |
-| showClear | Whether to show the clear button                                                                                                                                                                                                              | boolean | true | **0.35.0**|
+| showClear | Whether to show the clear button                                                                                                                                                                                                              | boolean | true | |
 | stopPropagation | Whether to prevent click events on the popup layer from bubbling | boolean | true | **2.49.0** |
 | stopPropagation | Whether to prevent click events on the popup layer from bubbling | boolean | true | **2.49.0** |
 | size  | Size of input box, one of 'default', 'small' and 'large'                                                                                                                                                                                      | string                                                                   | 'default'                                                              |                    |
 | size  | Size of input box, one of 'default', 'small' and 'large'                                                                                                                                                                                      | string                                                                   | 'default'                                                              |                    |
-| triggerRender | Custom trigger rendering method                                                                                                                                                                                                               | ({ placeholder: string }) => ReactNode |  | **0.34.0** |
+| triggerRender | Custom trigger rendering method                                                                                                                                                                                                               | ({ placeholder: string }) => ReactNode |  |  |
 | type | type                                                                                                                                                                                                                                          | "time"\|"timeRange" | "time" |
 | type | type                                                                                                                                                                                                                                          | "time"\|"timeRange" | "time" |
 | use12Hours | Using a 12-hour system, `format` default to `h: mm: ssa` when true                                                                                                                                                                            | boolean | false |
 | use12Hours | Using a 12-hour system, `format` default to `h: mm: ssa` when true                                                                                                                                                                            | boolean | false |
 | value | Current time                                                                                                                                                                                                                                  | Date\|timeStamp\|string (array when type = "timeRange") |  |
 | value | Current time                                                                                                                                                                                                                                  | Date\|timeStamp\|string (array when type = "timeRange") |  |
-| onBlur | Callback when focus is lost                                                                                                                                                                                                                   | (e: domEvent) => void | () => {} | **1.0.0** |
+| onBlur | Callback when focus is lost                                                                                                                                                                                                                   | (e: domEvent) => void | () => {} | |
 | onChange | A callback in time.                                                                                                                                                                                                                           | (time: Date\|Date[], timeString: string\|string[]) => void |  |
 | onChange | A callback in time.                                                                                                                                                                                                                           | (time: Date\|Date[], timeString: string\|string[]) => void |  |
 | onChangeWithDateFirst | Set the order of parameter in `onChange`, `true`: (Date, string); `false`: (string, Date)                                                                                                                                                     | boolean | true | **2.4.0** |
 | onChangeWithDateFirst | Set the order of parameter in `onChange`, `true`: (Date, string); `false`: (string, Date)                                                                                                                                                     | boolean | true | **2.4.0** |
-| onFocus | Callback when focus is obtained                                                                                                                                                                                                               | (e: domEvent) => void | () => {} | **1.0.0** |
+| onFocus | Callback when focus is obtained                                                                                                                                                                                                               | (e: domEvent) => void | () => {} |  |
 | onOpenChange | A callback when the panel is on / off                                                                                                                                                                                                         | (isOpen: boolean) => void |  |
 | onOpenChange | A callback when the panel is on / off                                                                                                                                                                                                         | (isOpen: boolean) => void |  |
 
 
 ## Methods
 ## Methods

+ 56 - 63
content/input/timepicker/index.md

@@ -197,8 +197,6 @@ function Demo() {
 
 
 ### 自定义触发器
 ### 自定义触发器
 
 
-**版本:**>=0.34.0
-
 默认情况下我们使用 `Input` 组件作为 `TimePicker` 组件的触发器,通过传递 `triggerRender` 方法你可以自定义这个触发器。
 默认情况下我们使用 `Input` 组件作为 `TimePicker` 组件的触发器,通过传递 `triggerRender` 方法你可以自定义这个触发器。
 
 
 ```jsx live=true hideInDSM
 ```jsx live=true hideInDSM
@@ -210,18 +208,6 @@ import { IconChevronDown, IconClose } from '@douyinfe/semi-icons';
 function Demo() {
 function Demo() {
     const formatToken = 'HH:mm:ss';
     const formatToken = 'HH:mm:ss';
     const [time, setTime] = useState(new Date());
     const [time, setTime] = useState(new Date());
-    const triggerIcon = useMemo(() => {
-        return time ? (
-            <IconClose
-                onClick={e => {
-                    e && e.stopPropagation();
-                    setTime();
-                }}
-            />
-        ) : (
-            <IconChevronDown />
-        );
-    }, [time]);
 
 
     return (
     return (
         <TimePicker
         <TimePicker
@@ -229,9 +215,16 @@ function Demo() {
             format={formatToken}
             format={formatToken}
             onChange={time => setTime(time)}
             onChange={time => setTime(time)}
             triggerRender={({ placeholder }) => (
             triggerRender={({ placeholder }) => (
-                <Button theme={'light'} icon={triggerIcon} iconPosition={'right'}>
+                <Tag
+                    color='cyan'
+                    size='large'
+                    shape='circle'
+                    style={{ padding: 12, paddingRight: 16, fontSize: 14 }}
+                    theme={'light'}
+                    prefixIcon={<IconTimePicker />}
+                >
                     {time ? dateFns.format(time, formatToken) : placeholder}
                     {time ? dateFns.format(time, formatToken) : placeholder}
-                </Button>
+                </Tag>
             )}
             )}
         />
         />
     );
     );
@@ -288,53 +281,53 @@ function Demo(props = {}) {
 
 
 ## API 参考
 ## API 参考
 
 
-| 参数                | 说明                                                   | 类型                                                                              | 默认值                                                            | 版本               |
-| ------------------- | ------------------------------------------------------ | --------------------------------------------------------------------------------- | ----------------------------------------------------------------- | ------------------ |
-| autoAdjustOverflow  | 浮层被遮挡时是否自动调整方向                           | boolean                                                                           | true                                                              | **0.34.0** |
-| autoFocus           | 自动获取焦点                                           | boolean                                                                           | false                                                             |                    |
-| borderless        | 无边框模式  >=2.33.0                                                                                                                                     | boolean                         |           |
-| className           | 外层样式名                                             | string                                                                            |                                                                   |                    |
-| clearIcon | 可用于自定义清除按钮, showClear为true时有效 | ReactNode |  |**2.25.0**  |
-| defaultOpen         | 面板是否默认打开                                       | boolean                                                                           |                                                                   | **0.19.0**         |
-| defaultValue        | 默认时间                                               | Date\|timeStamp\|String(type="timeRange"时为数组)                               |                                                                   |                    |
-| disabled            | 禁用全部操作                                           | boolean                                                                           | false                                                             |                    |
-| disabledHours       | 禁止选择部分小时选项                                   | Function(): number[]                                                              |                                                                   |                    |
-| disabledMinutes     | 禁止选择部分分钟选项                                   | Function(selectedHour: number): number[]                                          |                                                                   |                    |
-| disabledSeconds     | 禁止选择部分秒选项                                     | Function(selectedHour: number, selectedMinute: number): number[]                  |                                                                   |                    |
-| dropdownMargin      | 浮层算溢出时的增加的冗余值,详见[issue#549](https://github.com/DouyinFE/semi-design/issues/549),作用同 Tooltip margin      | object\|number  |  | **2.25.0**
-| focusOnOpen         | 挂载时是否打开面板并focus输入框                         | boolean                                                                            | false                                                     |                    |
-| format              | 展示的时间格式                                         | string                                                                            | "HH:mm:ss"                                                        |                    |
-| getPopupContainer   | 指定容器,浮层将会渲染至该元素内,自定义需要设置 `position: relative`  这会改变浮层 DOM 树位置,但不会改变视图渲染位置。                      | Function(): HTMLElement                                                           | () => document.body                                               |                    |
-| hideDisabledOptions | 隐藏禁止选择的选项                                     | boolean                                                                           | false                                                             |                    |
-| hourStep            | 小时选项间隔                                           | number                                                                            | 1                                                                 |                    |
-| inputReadOnly       | 设置输入框为只读(避免在移动设备上打开虚拟键盘)       | boolean                                                                           | false                                                             |                    |
-| insetLabel          | 前缀标签,优先级低于 `prefix`                          | string\|ReactNode                                                                 |                                                                   |                    |
-| minuteStep          | 分钟选项间隔                                           | number                                                                            | 1                                                                 |                    |
-| motion | 是否展示弹出层动画 | boolean | true |  |
-| open                | 面板是否打开的受控属性                                 | boolean                                                                           |                                                                   |                    |
-| panelFooter         | 面板底部 addon                                         | ReactNode\|ReactNode[]\|string                                                                 | 无                                                                |                    |
-| panelHeader         | 面板头部 addon                                         | ReactNode\|ReactNode[]\|string                                                                 | 无                                                                |                    |
-| placeholder         | 没有值的时候显示的内容                                 | string                                                                            | "请选择时间"                                                      |                    |
-| popupClassName      | 弹出层类名                                             | string                                                                            | ''                                                                |                    |
-| popupStyle          | 弹出层样式对象                                         | object                                                                            | -                                                                 |                    |
-| position            | 浮层位置                                               | string                                                                            | type="timeRange"时默认为"bottom",type="time"时默认为"bottomLeft" |                    |
-| prefix              | 前缀内容                                               | string\|ReactNode                                                                 |                                                                   |                    |
-| preventScroll | 指示浏览器是否应滚动文档以显示新聚焦的元素,作用于组件内的 focus 方法 | boolean |  |  |
-| rangeSeparator      | 时间范围分隔符                                         | string                                                                            | " ~ "                                                             |                    |
-| scrollItemProps     | 透传给 scrollItem 的属性,可选值同[ScrollList#API](/zh-CN/show/scrolllist#ScrollItem)                                                | object                                                           | | **0.31.0**         |
-| secondStep          | 秒选项间隔                                             | number                                                                            | 1                                                                 |                    |
-| showClear           | 是否展示清除按钮 **v>=0.35.0**                         | boolean                                                                           | true                                                              |                    |
-| stopPropagation | 是否阻止弹出层上的点击事件冒泡                                                                                      | boolean | true | **2.49.0**   |
-| size                | 输入框的大小,可选 'default','small','large'          | string                                                                   | 'default'                                                              |                    |
-| triggerRender       | 自定义触发器渲染方法                                   | ({ placeholder: string }) => ReactNode                                            | -                                                                 | **0.34.0**  |
-| type                | 类型                                                   | "time"\|"timeRange"                                                               | "time"                                                            |                    |
-| use12Hours          | 使用 12 小时制,为 true 时 `format` 默认为 `h:mm:ss a` | boolean                                                                           | false                                                             |                    |
-| value               | 当前时间                                               | Date\|timeStamp\|String(type="timeRange"时为数组)                               |                                                                   |                    |
-| onBlur              | 失去焦点时的回调                                       | (e: domEvent) => void                                                             | () => {}                                                          | **1.0.0**          |
-| onChange            | 时间发生变化的回调                                     | Function(time: Date, timeString: string): void (type="timeRange"时入参皆为数组) | 无                                                                |                    |
-| onChangeWithDateFirst | 设置为 `true` 时 onChange 的入参顺序为 (Date, string), `false` 时为 (string, Date) | boolean | true | **2.4.0** |
-| onFocus             | 获得焦点时的回调                                       | (e: domEvent) => void                                                             | () => {}                                                          | **1.0.0**          |
-| onOpenChange        | 面板打开/关闭时的回调                                  | Function(isOpen: boolean): void                                                   | 无                                                                |                    |
+| 参数                  | 说明                                                                                                                     | 类型                                                                              | 默认值                                                            | 版本       |
+| --------------------- | ------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------- | ----------------------------------------------------------------- | ---------- |
+| autoAdjustOverflow    | 浮层被遮挡时是否自动调整方向                                                                                             | boolean                                                                           | true                                                              |            |
+| autoFocus             | 自动获取焦点                                                                                                             | boolean                                                                           | false                                                             |            |
+| borderless            | 无边框模式  >=2.33.0                                                                                                     | boolean                                                                           |                                                                   |
+| className             | 外层样式名                                                                                                               | string                                                                            |                                                                   |            |
+| clearIcon             | 可用于自定义清除按钮, showClear为true时有效                                                                              | ReactNode                                                                         |                                                                   | **2.25.0** |
+| defaultOpen           | 面板是否默认打开                                                                                                         | boolean                                                                           |                                                                   |            |
+| defaultValue          | 默认时间                                                                                                                 | Date\|timeStamp\|String(type="timeRange"时为数组)                               |                                                                   |            |
+| disabled              | 禁用全部操作                                                                                                             | boolean                                                                           | false                                                             |            |
+| disabledHours         | 禁止选择部分小时选项                                                                                                     | Function(): number[]                                                              |                                                                   |            |
+| disabledMinutes       | 禁止选择部分分钟选项                                                                                                     | Function(selectedHour: number): number[]                                          |                                                                   |            |
+| disabledSeconds       | 禁止选择部分秒选项                                                                                                       | Function(selectedHour: number, selectedMinute: number): number[]                  |                                                                   |            |
+| dropdownMargin        | 浮层算溢出时的增加的冗余值,详见[issue#549](https://github.com/DouyinFE/semi-design/issues/549),作用同 Tooltip margin   | object\|number                                                                    |                                                                   | **2.25.0** |
+| focusOnOpen           | 挂载时是否打开面板并focus输入框                                                                                          | boolean                                                                           | false                                                             |            |
+| format                | 展示的时间格式                                                                                                           | string                                                                            | "HH:mm:ss"                                                        |            |
+| getPopupContainer     | 指定容器,浮层将会渲染至该元素内,自定义需要设置 `position: relative`  这会改变浮层 DOM 树位置,但不会改变视图渲染位置。 | Function(): HTMLElement                                                           | () => document.body                                               |            |
+| hideDisabledOptions   | 隐藏禁止选择的选项                                                                                                       | boolean                                                                           | false                                                             |            |
+| hourStep              | 小时选项间隔                                                                                                             | number                                                                            | 1                                                                 |            |
+| inputReadOnly         | 设置输入框为只读(避免在移动设备上打开虚拟键盘)                                                                         | boolean                                                                           | false                                                             |            |
+| insetLabel            | 前缀标签,优先级低于 `prefix`                                                                                            | string\|ReactNode                                                                 |                                                                   |            |
+| minuteStep            | 分钟选项间隔                                                                                                             | number                                                                            | 1                                                                 |            |
+| motion                | 是否展示弹出层动画                                                                                                       | boolean                                                                           | true                                                              |            |
+| open                  | 面板是否打开的受控属性                                                                                                   | boolean                                                                           |                                                                   |            |
+| panelFooter           | 面板底部 addon                                                                                                           | ReactNode\|ReactNode[]\|string                                                    | 无                                                                |            |
+| panelHeader           | 面板头部 addon                                                                                                           | ReactNode\|ReactNode[]\|string                                                    | 无                                                                |            |
+| placeholder           | 没有值的时候显示的内容                                                                                                   | string                                                                            | "请选择时间"                                                      |            |
+| popupClassName        | 弹出层类名                                                                                                               | string                                                                            | ''                                                                |            |
+| popupStyle            | 弹出层样式对象                                                                                                           | object                                                                            | -                                                                 |            |
+| position              | 浮层位置                                                                                                                 | string                                                                            | type="timeRange"时默认为"bottom",type="time"时默认为"bottomLeft" |            |
+| prefix                | 前缀内容                                                                                                                 | string\|ReactNode                                                                 |                                                                   |            |
+| preventScroll         | 指示浏览器是否应滚动文档以显示新聚焦的元素,作用于组件内的 focus 方法                                                    | boolean                                                                           |                                                                   |            |
+| rangeSeparator        | 时间范围分隔符                                                                                                           | string                                                                            | " ~ "                                                             |            |
+| scrollItemProps       | 透传给 scrollItem 的属性,可选值同[ScrollList#API](/zh-CN/show/scrolllist#ScrollItem)                                    | object                                                                            |                                                                   |            |
+| secondStep            | 秒选项间隔                                                                                                               | number                                                                            | 1                                                                 |            |
+| showClear             | 是否展示清除按钮                                                                                                         | boolean                                                                           | true                                                              |            |
+| stopPropagation       | 是否阻止弹出层上的点击事件冒泡                                                                                           | boolean                                                                           | true                                                              | **2.49.0** |
+| size                  | 输入框的大小,可选 'default','small','large'                                                                           | string                                                                            | 'default'                                                         |            |
+| triggerRender         | 自定义触发器渲染方法                                                                                                     | ({ placeholder: string }) => ReactNode                                            | -                                                                 |            |
+| type                  | 类型                                                                                                                     | "time"\|"timeRange"                                                               | "time"                                                            |            |
+| use12Hours            | 使用 12 小时制,为 true 时 `format` 默认为 `h:mm:ss a`                                                                   | boolean                                                                           | false                                                             |            |
+| value                 | 当前时间                                                                                                                 | Date\|timeStamp\|String(type="timeRange"时为数组)                               |                                                                   |            |
+| onBlur                | 失去焦点时的回调                                                                                                         | (e: domEvent) => void                                                             | () => {}                                                          |            |
+| onChange              | 时间发生变化的回调                                                                                                       | Function(time: Date, timeString: string): void (type="timeRange"时入参皆为数组) | 无                                                                |            |
+| onChangeWithDateFirst | 设置为 `true` 时 onChange 的入参顺序为 (Date, string), `false` 时为 (string, Date)                                       | boolean                                                                           | true                                                              | **2.4.0**  |
+| onFocus               | 获得焦点时的回调                                                                                                         | (e: domEvent) => void                                                             | () => {}                                                          |            |
+| onOpenChange          | 面板打开/关闭时的回调                                                                                                    | Function(isOpen: boolean): void                                                   | 无                                                                |            |
 
 
 ## Methods
 ## Methods
 
 

+ 7 - 4
content/input/upload/index-en-US.md

@@ -1181,10 +1181,13 @@ afterUpload is triggered when the upload is completed (xhr.onload) and no error
 ```ts
 ```ts
 // afterUploadResult:
 // afterUploadResult:
 {
 {
-     status?:'success' |'uploadFail' |'validateFail' |'validating' |'uploading' |'wait',
-     validateMessage?: React.ReactNode | string, // file validation information
-     autoRemove: boolean, // Whether to remove the file from the fileList, the default is false
-     name: string,
+    status?:'success' |'uploadFail' |'validateFail' |'validating' |'uploading' |'wait',
+    validateMessage?: React.ReactNode | string, // file validation information
+    autoRemove?: boolean, // Whether to remove the file from the fileList, the default is false
+    name?: string;
+    // The URL for previewing image file, usually the storage address returned by the Server after receiving response, supported since v2.63.
+    // Previous versions can also manually update the controlled properties in the fileList through onChange callback.
+    url?: string; // support after v2.63
 }
 }
 ```
 ```
 
 

+ 26 - 26
content/input/upload/index.md

@@ -630,7 +630,8 @@ import { IconUpload } from '@douyinfe/semi-icons';
 
 
 ### 图片墙
 ### 图片墙
 
 
-设置 `listType = 'picture'`,用户可以上传图片并在列表中显示缩略图
+设置 `listType = 'picture'`,用户可以上传图片并在列表中显示缩略图  
+如果通过 defaultFileList 或 fileList 设置已上传的文件列表时,会自动读取对象数组中的 url 属性用于展示图片
 
 
 ```jsx live=true width=48%
 ```jsx live=true width=48%
 import React from 'react';
 import React from 'react';
@@ -784,6 +785,7 @@ import { IconPlus, IconEyeOpened } from '@douyinfe/semi-icons';
 
 
 ### 图片墙设置宽高
 ### 图片墙设置宽高
 通过设置 picHeight, picWidth ( v2.42 后提供),可以统一设置图片墙元素的宽高
 通过设置 picHeight, picWidth ( v2.42 后提供),可以统一设置图片墙元素的宽高
+如果同时使用 `renderThumbnail` return Image 组件来实现点击放大预览,你需要同时指定 Image 组件的 width 和 height
 
 
 ```jsx live=true dir="column"
 ```jsx live=true dir="column"
 import React from 'react';
 import React from 'react';
@@ -813,6 +815,7 @@ import { IconPlus } from '@douyinfe/semi-icons';
                 defaultFileList={defaultFileList}
                 defaultFileList={defaultFileList}
                 picHeight={110}
                 picHeight={110}
                 picWidth={200}
                 picWidth={200}
+                renderThumbnail={(file) => (<Image src={file.url} width={200} height={110} />)}
             >
             >
                 <IconPlus size="extra-large" style={{ margin: 4 }} />
                 <IconPlus size="extra-large" style={{ margin: 4 }} />
                 点击添加图片
                 点击添加图片
@@ -822,8 +825,6 @@ import { IconPlus } from '@douyinfe/semi-icons';
 };
 };
 ```
 ```
 
 
-
-
 设置 `hotSpotLocation` 自定义点击热区的顺序,默认在照片墙列表结尾
 设置 `hotSpotLocation` 自定义点击热区的顺序,默认在照片墙列表结尾
 
 
 ```jsx live=true width=48%
 ```jsx live=true width=48%
@@ -1186,15 +1187,21 @@ class AsyncBeforeUploadDemo extends React.Component {
 
 
 可以通过 `afterUpload` 钩子,对文件状态,校验信息,文件名进行更新。  
 可以通过 `afterUpload` 钩子,对文件状态,校验信息,文件名进行更新。  
 `({ response: any, file: FileItem, fileList: Array<FileItem> }) => afterUploadResult`  
 `({ response: any, file: FileItem, fileList: Array<FileItem> }) => afterUploadResult`  
-afterUpload 在上传完成后(xhr.onload)且没有发生错误的情况下触发,需返回一个 Object 对象(不支持异步返回),具体结构如下
+`afterUpload` 在上传完成后(`xhr.onload`)且没有发生错误的情况下触发,需返回一个 Object 对象(不支持异步返回),具体结构如下
 
 
 ```ts
 ```ts
 // afterUploadResult:
 // afterUploadResult:
 {
 {
-    status?: 'success' | 'uploadFail' | 'validateFail' | 'validating' | 'uploading' | 'wait',
-    validateMessage?: React.ReactNode | string, // 文件的校验信息
-    autoRemove: boolean, // 是否从fileList中移除该文件,默认为false
-    name: string,
+    status?: 'success' | 'uploadFail' | 'validateFail' | 'validating' | 'uploading' | 'wait';
+    // 文件的校验信息
+    validateMessage?: React.ReactNode | string;
+    // 是否从fileList中移除该文件,默认为false
+    autoRemove?: boolean;
+    // 文件的名称
+    name?: string;
+    // 预览文件的url,一般为当次上传请求中 Server 接收到文件后返回的存储地址,v2.63后支持传入。
+    // 之前的版本也可以通过 onChange 中结合 status 手动更新受控 fileList 中的属性实现
+    url?: string 
 }
 }
 ```
 ```
 
 
@@ -1203,14 +1210,8 @@ import React from 'react';
 import { Upload, Button } from '@douyinfe/semi-ui';
 import { Upload, Button } from '@douyinfe/semi-ui';
 import { IconUpload } from '@douyinfe/semi-icons';
 import { IconUpload } from '@douyinfe/semi-icons';
 
 
-class ValidateDemo extends React.Component {
-    constructor(props) {
-        super(props);
-        this.state = {};
-        this.count = 0;
-    }
-
-    afterUpload({ response, file }) {
+() => {
+    const afterUpload = ({ response, file }) => {
         // 可以根据业务接口返回,决定当次上传是否成功
         // 可以根据业务接口返回,决定当次上传是否成功
         if (response.status_code === 200) {
         if (response.status_code === 200) {
             return {
             return {
@@ -1218,21 +1219,20 @@ class ValidateDemo extends React.Component {
                 status: 'uploadFail',
                 status: 'uploadFail',
                 validateMessage: '内容不合法',
                 validateMessage: '内容不合法',
                 name: 'RenameByServer.jpg',
                 name: 'RenameByServer.jpg',
+                url: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/edit-bag.jpeg'
             };
             };
         } else {
         } else {
             return {};
             return {};
         }
         }
-    }
+    };
 
 
-    render() {
-        return (
-            <Upload action="https://api.semi.design/upload" afterUpload={this.afterUpload}>
-                <Button icon={<IconUpload />} theme="light">
-                    点击上传
-                </Button>
-            </Upload>
-        );
-    }
+    return (
+        <Upload action="https://api.semi.design/upload" afterUpload={afterUpload}>
+            <Button icon={<IconUpload />} theme="light">
+                点击上传
+            </Button>
+        </Upload>
+    )
 }
 }
 ```
 ```
 
 

+ 1 - 0
content/order.js

@@ -80,6 +80,7 @@ const order = [
     'toast',
     'toast',
     'configprovider',
     'configprovider',
     'locale',
     'locale',
+    'chat',
 ];
 ];
 let { exec } = require('child_process');
 let { exec } = require('child_process');
 let fs = require('fs');
 let fs = require('fs');

+ 1612 - 0
content/plus/chat/index-en-US.md

@@ -0,0 +1,1612 @@
+---
+localeCode: en-US
+order: 82
+category: Plus
+title:  Chat
+icon: doc-chat
+dir: column
+brief: Used to quickly build conversation content
+---
+
+## When to use
+
+The Chat component can be used in scenarios such as regular conversations or AI conversations.
+
+The rendering of the conversation content is based on the MarkdownRender component, which supports Markdown and MDX. It allows for common rich text features such as images, tables, links, bold text, code blocks, and more. More complex and customized document writing and display requirements can be achieved using JSX.
+
+## Demos
+
+### How to import
+
+Chat is supported starting from version v2.63.0.
+```jsx
+import { Chat } from '@douyinfe/semi-ui';
+```
+
+### Basic usage
+
+By setting `chats`, `onChatsChange`, and `onMessageSend`, you can achieve basic conversation display and interaction.
+
+Conversations involve multiple participants and multiple rounds of interaction. Role information, including names and avatars, can be passed through the `roleConfig` parameter. For detailed parameter information, refer to [RoleConfig](#RoleConfig).
+
+The prompt text of the upload button can be set through `uploadTipProps`. For details, please refer to [Tooltip](/zh-CN/tooltip#API%20%E5%8F%82%E8%80%83).
+
+Dialogue is a scene involving multiple parties and multiple rounds of interaction. Role information (including name, avatar, etc.) can be passed in through `roleConfig`, and the specific parameter details are [RoleConfig](#roleConfig).
+
+Use the `align` attribute to set the alignment of the dialog, supporting left and right alignment (`leftRight`, default) and left alignment (`leftAlign`).
+
+```jsx live=true noInline=true dir="column"
+import React, {useState, useCallback} from 'react';
+import { Chat, Radio } from '@douyinfe/semi-ui';
+
+const defaultMessage = [
+    {
+        role: 'system',
+        id: '1',
+        createAt: 1715676751919,
+        content: "Hello, I'm your AI assistant.",   
+    },
+    {
+        role: 'user',
+        id: '2',
+        createAt: 1715676751919,
+        content: "Give an example of using Semi Design’s Button component",
+    },
+    {
+        role: 'assistant',
+        id: '3',
+        createAt: 1715676751919,
+        content: "The following is an example of using Semi code:\n\`\`\`jsx \nimport React from 'react';\nimport { Button } from '@douyinfe/semi-ui';\n\nconst MyComponent = () => {\n  return (\n    <Button>Click me</Button>\n );\n};\nexport default MyComponent;\n\`\`\`\n",
+    }
+];
+
+const roleInfo = {
+    user:  {
+        name: 'User',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/docs-icon.png'
+    },
+    assistant: {
+        name: 'Assistant',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png'
+    },
+    system: {
+        name: 'System',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png'
+    }
+}
+
+const commonOuterStyle = {
+    border: '1px solid var(--semi-color-border)',
+    borderRadius: '16px',
+    margin: '8px 16px',
+    height: 550,
+}
+
+let id = 0;
+function getId() {
+    return `id-${id++}`
+}
+
+const uploadProps = { action: 'https://api.semi.design/upload' }
+const uploadTipProps = { content: 'Customize upload button prompt information' }
+
+function DefaultChat() {
+    const [message, setMessage] = useState(defaultMessage);
+    const [mode, setMode] = useState('bubble');
+    const [align, setAlign] = useState('leftRight');
+
+    const onAlignChange = useCallback((e) => {
+        setAlign(e.target.value);
+    }, []);
+
+    const onModeChange = useCallback((e) => {
+        setMode(e.target.value);
+    }, []); 
+
+    const onMessageSend = useCallback((content, attachment) => {
+        const newAssistantMessage = {
+            role: 'assistant',
+            id: getId(),
+            createAt: Date.now(),
+            content: "This is a mock response",
+        }
+        setTimeout(() => { 
+            setMessage((message) => ([ ...message, newAssistantMessage])); 
+        }, 200);
+    }, []);
+
+    const onChatsChange = useCallback((chats) => {
+        setMessage(chats);
+    }, []);
+
+    const onMessageReset = useCallback((e) => {
+        setTimeout(() => {
+            setMessage((message) => {
+                const lastMessage = message[message.length - 1];
+                const newLastMessage = {
+                    ...lastMessage,
+                    status: 'complete',
+                    content: 'This is a mock reset message.',
+                }
+                return [...message.slice(0, -1), newLastMessage]
+            })
+        }, 200);
+    })
+
+    return (
+        <>
+            <span style={{ display: 'flex', flexDirection: 'column', rowGap: '8px'}}>
+                <span style={{ display: 'flex', alignItems: 'center', columnGap: '10px'}}>
+                    Mode
+                    <RadioGroup onChange={onModeChange} value={mode} type={"button"}>
+                        <Radio value={'bubble'}>bubble</Radio>
+                        <Radio value={'noBubble'}>noBubble</Radio>
+                        <Radio value={'userBubble'}>userBubble</Radio>
+                    </RadioGroup>
+                </span>
+                <span style={{ display: 'flex', alignItems: 'center', columnGap: '10px'}}>
+                    Chat align
+                    <RadioGroup onChange={onAlignChange} value={align} type={"button"}>
+                        <Radio value={'leftRight'}>leftRight</Radio>
+                        <Radio value={'leftAlign'}>leftAlign</Radio>
+                    </RadioGroup>
+                </span>
+            </span>
+            <Chat 
+                key={align + mode}
+                align={align}
+                mode={mode}
+                uploadProps={uploadProps}
+                style={commonOuterStyle}
+                chats={message}
+                roleConfig={roleInfo}
+                onChatsChange={onChatsChange}
+                onMessageSend={onMessageSend}
+                onMessageReset={onMessageReset}
+                uploadTipProps={uploadTipProps}
+            />
+        </>
+    )
+}
+
+render(DefaultChat);
+```
+
+### Chat status
+
+The chats type is `Message[]`, where each `Message` contains various information about the conversation, such as role, content, attachment, status, unique identifier (id), creation time (createAt), and more. For detailed information, please refer to [Message](#message). The conversation style may vary depending on the different status values.
+
+``` jsx live=true noInline=true dir="column"
+import React, {useState, useCallback} from 'react';
+import { Chat } from '@douyinfe/semi-ui';
+
+const defaultMessage = [
+    {
+        role: 'assistant',
+        id: '1',
+        createAt: 1715676751919,
+        content: "Success response",   
+    },
+    {
+        id: 'loading',
+        role: 'assistant',
+        status: 'loading'
+    },
+    {
+        role: 'assistant',
+        id: 'error',
+        content: 'Error response',
+        status: 'error'
+    }
+];
+
+const roleInfo = {
+    user:  {
+        name: 'User',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/docs-icon.png'
+    },
+    assistant: {
+        name: 'Assistant',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png'
+    },
+    system: {
+        name: 'System',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png'
+    }
+}
+
+const commonOuterStyle = {
+    border: '1px solid var(--semi-color-border)',
+    borderRadius: '16px',
+    height: 400,
+}
+
+let id = 0;
+function getId() { return `id-${id++}` }
+const uploadProps = { action: 'https://api.semi.design/upload' }
+
+function MessageStatus() {
+    const [message, setMessage] = useState(defaultMessage);
+
+    const onMessageSend = useCallback((content, attachment) => {
+        const newAssistantMessage = {
+            role: 'assistant',
+            id: getId(),
+            createAt: Date.now(),
+            content: "This is a mock response",
+        }
+        setTimeout(() => { 
+            setMessage((message) => ([ ...message, newAssistantMessage])); 
+        }, 200);
+    }, []);
+
+    const onChatsChange = useCallback((chats) => {
+        setMessage(chats);
+    }, []);
+
+    return (
+        <Chat 
+            style={commonOuterStyle}
+            chats={message}
+            roleConfig={roleInfo}
+            onChatsChange={onChatsChange}
+            onMessageSend={onMessageSend}
+            uploadProps={uploadProps}
+        />
+    )
+}
+
+render(MessageStatus);
+```
+
+### Dynamic update chats
+
+For the case of receiving Server-Sent Event data from the backend, the obtained data can be used to update the `chats`, and the conversation content will be updated in real-time.
+
+The `showStopGenerate` parameter can be used to determine whether to display the stop generation button, with a default value of `false`. The logic for stopping the generation can be handled in the `onStopGenerator` function.
+
+```jsx live=true noInline=true dir="column"
+import React, {useState, useCallback} from 'react';
+import { Chat } from '@douyinfe/semi-ui';
+
+const defaultMessage = [
+    {
+        role: 'system',
+        id: '1',
+        createAt: 1715676751919,
+        content: "Hello, I'm your AI assistant.",   
+    },
+    {
+        role: 'user',
+        id: '2',
+        createAt: 1715676751919,
+        content: "介绍一下 Semi design"
+    },
+    {
+        role: 'assistant',
+        id: '3',
+        createAt: 1715676751919,
+        content: `
+Semi Design is a design system designed, developed and maintained by Douyin's front-end team and MED product design team. As a comprehensive, easy-to-use, high-quality modern application UI solution, Semi Design is extracted from the complex scenarios of ByteDance's various business lines. It has currently supported nearly a thousand platform products and served more than 100,000 internal and external users.[[1]](https://semi.design/zh-CN/start/introduction)。
+
+Semi Design features include:
+
+1. Simple and modern design.
+2. Provide theme solutions, which can be customized in depth.
+3. Provide two sets of light and dark color modes, easy to switch.
+4. Internationalization, covering 20+ languages ​​such as Simplified/Traditional Chinese, English, Japanese, Korean, Portuguese, etc. The date and time component provides global time zone support, and all components can automatically adapt to the Arabic RTL layout.
+5. Use Foundation and Adapter cross-framework technical solutions to facilitate expansion.
+
+---
+Learn more:
+1. [Introduction - Semi Design](https://semi.design/zh-CN/start/introduction)
+2. [Getting Started - Semi Design](https://semi.design/zh-CN/start/getting-started)
+3. [The evolution of Semi D2C design draft to code - Zhihu](https://zhuanlan.zhihu.com/p/667189184)
+`,
+    }
+];
+
+const roleInfo = {
+    user:  {
+        name: 'User',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/docs-icon.png'
+    },
+    assistant: {
+        name: 'Assistant',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png'
+    },
+    system: {
+        name: 'System',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png'
+    }
+}
+
+const commonOuterStyle = {
+    border: '1px solid var(--semi-color-border)',
+    borderRadius: '16px',
+    height: 600,
+}
+
+let id = 0;
+function getId() {
+    return `id-${id++}`
+}
+const uploadProps = { action: 'https://api.semi.design/upload' }
+
+function DynamicUpdateChat() {
+    const [message, setMessage] = useState(defaultMessage);
+    const intervalId = useRef();
+    const onMessageSend = useCallback((content, attachment) => {
+        setMessage((message) => {
+            return [
+                ...message,
+                {
+                    role: 'assistant',
+                    status: 'loading',
+                    createAt: Date.now(),
+                    id: getId()
+                }
+            ]
+        }); 
+        generateMockResponse(content);
+    }, []);
+
+    const onChatsChange = useCallback((chats) => {
+        setMessage(chats);
+    }, []);
+
+    const generateMockResponse = useCallback((content) => {
+        const id = setInterval(() => {
+            setMessage((message) => {
+                const lastMessage = message[message.length - 1];
+                let newMessage = {...lastMessage};
+                if (lastMessage.status === 'loading') {
+                    newMessage = {
+                        ...newMessage,
+                        content:  `mock Response for ${content} \n`,
+                        status: 'incomplete'
+                    }
+                } else if (lastMessage.status === 'incomplete') {
+                    if (lastMessage.content.length > 200) {
+                        clearInterval(id);
+                        intervalId.current = null
+                        newMessage = {
+                            ...newMessage,
+                            content: `${lastMessage.content} mock stream message`,
+                            status: 'complete'
+                        }
+                    } else {
+                        newMessage = {
+                            ...newMessage,
+                            content: `${lastMessage.content} mock stream message`
+                        }
+                    }  
+                }
+                return [ ...message.slice(0, -1), newMessage ]
+            })
+        }, 400);
+        intervalId.current = id;
+    }, []);
+
+    const onStopGenerator = useCallback(() => {
+        if (intervalId.current) {
+            clearInterval(intervalId.current);
+            setMessage((message) => {
+                const lastMessage = message[message.length - 1];
+                if (lastMessage.status && lastMessage.status !== 'complete') {
+                    const lastMessage = message[message.length - 1];
+                    let newMessage = {...lastMessage};
+                    newMessage.status = 'complete';
+                    return [
+                        ...message.slice(0, -1),
+                        newMessage
+                    ]
+                } else {
+                    return message;
+                }
+            })
+        }
+    }, [intervalId]);
+
+    return (
+        <Chat 
+            chats={message}
+            showStopGenerate={true}
+            style={commonOuterStyle}
+            onStopGenerator={onStopGenerator}
+            roleConfig={roleInfo}
+            onChatsChange={onChatsChange}
+            onMessageSend={onMessageSend}
+            uploadProps={uploadProps}
+        />
+    )
+}
+
+render(DynamicUpdateChat);
+```
+
+### Clear context
+
+Displaying the clear context button in the input box can be enabled through `showClearContext`, which defaults to `false`.
+The context can also be cleared by calling the `clearContext` method through ref.
+
+```jsx live=true noInline=true dir="column"
+import React, {useState, useCallback} from 'react';
+import { Chat, Radio } from '@douyinfe/semi-ui';
+
+const defaultMessage = [
+    {
+        role: 'system',
+        id: '1',
+        createAt: 1715676751919,
+        content: "Hello, I'm your AI assistant.",   
+    },
+    {
+        role: 'user',
+        id: '2',
+        createAt: 1715676751919,
+        content: "Introduce semi design", 
+    },
+    {
+        role: 'assistant',
+        id: '3',
+        createAt: 1715676751919,
+        content: 'Semi Design is a design system designed, developed and maintained by the Douyin front-end team and MED product design team.',
+    }
+];
+
+const roleInfo = {
+    user:  {
+        name: 'User',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/docs-icon.png'
+    },
+    assistant: {
+        name: 'Assistant',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png'
+    },
+    system: {
+        name: 'System',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png'
+    }
+}
+
+const commonOuterStyle = {
+    border: '1px solid var(--semi-color-border)',
+    borderRadius: '16px',
+    margin: '8px 16px',
+    height: 550,
+}
+
+let id = 0;
+function getId() {
+    return `id-${id++}`
+}
+
+const uploadProps = { action: 'https://api.semi.design/upload' }
+const uploadTipProps = { content: 'Customize upload button prompt information' }
+
+function DefaultChat() {
+    const [message, setMessage] = useState(defaultMessage);
+
+    const onMessageSend = useCallback((content, attachment) => {
+        const newAssistantMessage = {
+            role: 'assistant',
+            id: getId(),
+            createAt: Date.now(),
+            content: "This is a mock response message.",
+        }
+        setTimeout(() => { 
+            setMessage((message) => ([ ...message, newAssistantMessage])); 
+        }, 200);
+    }, []);
+
+    const onChatsChange = useCallback((chats) => {
+        setMessage(chats);
+    }, []);
+
+    const onMessageReset = useCallback((e) => {
+        setTimeout(() => {
+            setMessage((message) => {
+                const lastMessage = message[message.length - 1];
+                const newLastMessage = {
+                    ...lastMessage,
+                    status: 'complete',
+                    content: 'This is a mock reset message.',
+                }
+                return [...message.slice(0, -1), newLastMessage]
+            })
+        }, 200);
+    })
+
+    return (
+        <>
+            <Chat
+                uploadProps={uploadProps}
+                style={commonOuterStyle}
+                chats={message}
+                roleConfig={roleInfo}
+                onChatsChange={onChatsChange}
+                onMessageSend={onMessageSend}
+                onMessageReset={onMessageReset}
+                uploadTipProps={uploadTipProps}
+                showClearContext
+            />
+        </>
+    )
+}
+
+render(DefaultChat);
+```
+
+### Custom rendering dialog box
+
+Pass in custom rendering configuration through `chatBoxRenderConfig`, the chatBoxRenderConfig type is as follows
+
+```ts
+interface ChatBoxRenderConfig {
+    /* Custom rendering title */
+    renderChatBoxTitle?: (props: {role?: Metadata, defaultTitle?: ReactNode}) => ReactNode;
+    /* Custom rendering avatr */
+    renderChatBoxAvatar?: (props: { role?: Metadata, defaultAvatar?: ReactNode}) => 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;
+}
+```
+
+Custom render avatar and Title through `renderChatBoxAvatar` and `renderChatBoxTitle`。
+
+```jsx live=true noInline=true dir="column"
+
+import React, {useState, useCallback} from 'react';
+import { Chat, Avatar, Tag } from '@douyinfe/semi-ui';
+
+const defaultMessage = [
+    {
+        role: 'system',
+        id: '1',
+        createAt: 1715676751919,
+        content: "Hello, I'm your AI assistant.",   
+    },
+    {
+        role: 'user',
+        id: '2',
+        createAt: 1715676751919,
+        content: [
+            {
+                type: 'text',
+                text: 'What\'s in this picture?'
+            },
+            {
+                type: 'image_url',
+                image_url: {
+                    url: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/edit-bag.jpeg'
+                }
+            }
+        ], 
+    },
+    {
+        role: 'assistant',
+        id: '3',
+        createAt: 1715676751919,
+        content: 'The picture shows a yellow backpack decorated with cartoon images'
+    },
+
+];
+
+const roleInfo = {
+    user:  {
+        name: 'User',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/docs-icon.png'
+    },
+    assistant: {
+        name: 'Assistant',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png'
+    },
+    system: {
+        name: 'System',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png'
+    }
+}
+
+const commonOuterStyle = {
+    border: '1px solid var(--semi-color-border)',
+    borderRadius: '16px',
+    height: 400,
+}
+
+let id = 0;
+function getId() { return `id-${id++}`; }
+const uploadProps = { action: 'https://api.semi.design/upload' }
+
+function CustomRender() {
+    const [title, setTitle] = useState('null');
+    const [avatar, setAvatar] = useState('null');
+    const [message, setMessage] = useState(defaultMessage);
+
+    const onChatsChange = useCallback((chats) => {
+        setMessage(chats);
+    }, []);
+
+    const customRenderAvatar = useMemo(()=> {
+        switch(avatar) {
+            case 'custom': return (props) => {
+                    const { role, defaultAvatar } = props;
+                    return <Avatar size="extra-small" shape="square" style={{ flexShrink: '0'}}>{role.name}</Avatar >
+                }
+            case 'null': return () => null
+            case 'default': return undefined;
+        }
+    }, [avatar]);
+
+    const customRenderTitle = useMemo(()=> {
+        switch(title) {
+            case 'custom': return (props) => {
+                    const { role, defaultTitle, message } = props;
+                    const date = new Date(message.createAt);
+                    const hours = ('0' + date.getHours()).slice(-2);
+                    const minutes = ('0' + date.getMinutes()).slice(-2);
+                    const formatTime = `${hours}:${minutes}`;
+                    return (<span className="title" >
+                        {role.name}
+                        <span className={'time'}>{formatTime}</span>
+                    </span>)
+            }
+            case 'null': return () => null
+            case 'default': return undefined;
+        }
+    }, [title]);;
+
+    const onAvatarChange = useCallback((e) => { setAvatar(e.target.value) }, []);
+    const onTitleChange = useCallback((e) => { setTitle(e.target.value) }, []);
+
+     const onMessageSend = useCallback((content, attachment) => {
+        const newAssistantMessage = {
+            role: 'assistant',
+            id: getId(),
+            content: `This is a mock response`
+        }
+        setTimeout(() => { 
+            setMessage((message) => ([ ...message, newAssistantMessage])); 
+        }, 200);
+    }, []);
+
+    return (
+        <>
+            <span style={{ display: 'flex', flexDirection: 'column', rowGap: 8, marginBottom: 5}}>
+                <span style={{ display: 'flex', alignItems: 'center', columnGap: 10}}>
+                    Avatar Render Mode
+                    <RadioGroup onChange={onAvatarChange} value={avatar} type="button">
+                    <Radio value={'default'}>default</Radio>
+                    <Radio value={'null'}>null</Radio>
+                    <Radio value={'custom'}>custom</Radio>
+                </RadioGroup>
+                </span>
+                <span style={{ display: 'flex', alignItems: 'center', columnGap: 10}}>
+                    Title Render mode
+                    <RadioGroup onChange={onTitleChange} value={title} type="button">
+                    <Radio value={'default'}>default</Radio>
+                    <Radio value={'null'}>null</Radio>
+                    <Radio value={'custom'}>custom</Radio>
+                </RadioGroup>
+                </span>
+            </span>
+            <Chat
+                chatBoxRenderConfig={{
+                    renderChatBoxTitle: customRenderTitle,
+                    renderChatBoxAvatar: customRenderAvatar
+                }} 
+                key={`${avatar}${title}`}
+                style={commonOuterStyle}
+                className={'component-chat-demo-custom-render'}
+                chats={message}
+                onChatsChange={onChatsChange}
+                onMessageSend={onMessageSend}
+                roleConfig={roleInfo}
+                uploadProps={uploadProps}
+            />
+        </>
+    );
+}
+
+render(CustomRender);
+```
+
+When hovering over a conversation, the conversation action area will be displayed. You can customize the rendering of the action area using `renderChatBoxAction`.
+
+```jsx live=true noInline=true dir="column"
+import React, {useState, useCallback} from 'react';
+import { Chat, Dropdown } from '@douyinfe/semi-ui';
+import { IconForward } from '@douyinfe/semi-icons';
+
+const defaultMessage = [
+    {
+        role: 'system',
+        id: '1',
+        createAt: 1715676751919,
+        content: "Hello, I'm your AI assistant.",   
+    },
+    {
+        role: 'user',
+        id: '2',
+        createAt: 1715676751919,
+        content: "Introduce Semi design", 
+    },
+    {
+        role: 'assistant',
+        id: '3',
+        createAt: 1715676751919,
+        content: 'Semi Design is a design system designed, developed, and maintained by the front-end team at Douyin and the MED product design team.',
+    }
+];
+
+const roleInfo = {
+    user:  {
+        name: 'User',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/docs-icon.png'
+    },
+    assistant: {
+        name: 'Assistant',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png'
+    },
+    system: {
+        name: 'System',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png'
+    }
+}
+
+const commonOuterStyle = {
+    border: '1px solid var(--semi-color-border)',
+    borderRadius: '16px',
+    height: 400,
+}
+
+let id = 0;
+function getId() { return `id-${id++}`; }
+const uploadProps = { action: 'https://api.semi.design/upload' }
+
+const CustomActions = React.memo((props) => {
+    const { role, message, defaultActions, className } = props;
+    const myRef = useRef();
+    const getContainer = useCallback(() => {
+        if (myRef.current) {
+            const element = myRef.current;
+            let parentElement = element.parentElement;
+            while (parentElement) {
+                if (parentElement.classList.contains('semi-chat-chatBox-wrap')) {
+                    return parentElement;
+                }
+                parentElement = parentElement.parentElement;
+            }
+        }
+    }, [myRef]);
+
+    return <span 
+        className={className}
+        ref={myRef}
+    >
+        {defaultActions}
+        {<Dropdown
+            key="dropdown"
+            render={
+                <Dropdown.Menu >
+                    <Dropdown.Item icon={<IconForward />}>Share</Dropdown.Item>
+                </Dropdown.Menu>
+            }
+            trigger="click"
+            position="top"
+            getPopupContainer={getContainer}
+        >
+            <Button 
+                className='semi-chat-chatBox-action-btn'
+                icon={<IconMoreStroked/>}
+                theme='borderless'
+                type='tertiary'
+            />
+        </Dropdown>}
+    </span>
+});
+
+function CustomRender() {
+    const [message, setMessage] = useState(defaultMessage);
+
+    const customRenderAction = useCallback((props) => {
+        return <CustomActions {...props} />
+    }, []);
+
+    const onChatsChange = useCallback((chats) => {
+        setMessage(chats);
+    }, []);
+
+    const onMessageSend = useCallback((content, attachment) => {
+        const newAssistantMessage = {
+            role: 'assistant',
+            id: getId(),
+            content: `This is a mock response`
+        }
+        setTimeout(() => { 
+            setMessage((message) => ([ ...message, newAssistantMessage])); 
+        }, 200);
+    }, []);
+
+    return (
+        <Chat
+            chatBoxRenderConfig={{ 
+                renderChatBoxAction: customRenderAction 
+            }}
+            style={commonOuterStyle}
+            chats={message}
+            onChatsChange={onChatsChange}
+            onMessageSend={onMessageSend}
+            roleConfig={roleInfo}
+            uploadProps={uploadProps}
+        />
+    );
+}
+
+render(CustomRender);
+```
+
+You can customize the content of the action area using `renderChatBoxContent`.
+
+```jsx live=true noInline=true dir="column"
+import React, { useState, useCallback, useRef} from 'react';
+import { Chat, MarkdownRender } from '@douyinfe/semi-ui';
+
+const defaultMessage = [
+        {
+        role: 'assistant',
+        id: '1',
+        createAt: 1715676751919,
+        content: "Semi Design is a design system designed, developed and maintained by Douyin's front-end team and MED product design team. As a comprehensive, easy-to-use, high-quality modern application UI solution, it is extracted from the complex scenarios of ByteDance's various business lines, supports nearly a thousand platform products, and serves 100,000+ internal and external users.",
+        source: [
+            {
+                avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png',
+                url: '/en-US/start/introduction',
+                title: 'semi Design',
+                subTitle: 'Semi design website',
+                content: 'Semi Design is a design system designed, developed and maintained by Douyin\'s front-end team and MED product design team.'
+            },
+            {
+                avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png',
+                url: '/dsm/landing',
+                subTitle: 'Semi DSM website',
+                title: 'Semi Design System',
+                content: 'From Semi Design to Any Design, quickly define your design system and apply it in design drafts and code'
+            },
+            {
+                avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png',
+                url: '/code/en-US/start/introduction',
+                subTitle: 'Semi D2C website',
+                title: 'Design to Code',
+                content: 'Semi Design to Code, or Semi D2C for short, is a new performance improvement tool launched by the Douyin front-end Semi Design team.'
+            },
+        ]
+    }];
+
+const roleInfo = {
+    user:  {
+        name: 'User',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/docs-icon.png'
+    },
+    assistant: {
+        name: 'Assistant',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png'
+    },
+    system: {
+        name: 'System',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png'
+    }
+}
+
+const commonOuterStyle = {
+    border: '1px solid var(--semi-color-border)',
+    borderRadius: '16px',
+    height: 500,
+}
+
+let id = 0;
+function getId() { return `id-${id++}` }
+const uploadProps = { action: 'https://api.semi.design/upload' }
+
+const SourceCard = (props) => {
+    const [open, setOpen] = useState(true);
+    const [show, setShow] = useState(false);
+    const { source } = props;
+    const spanRef = useRef();
+    const onOpen = useCallback(() => {
+        setOpen(false);
+        setShow(true);
+    }, []);
+
+    const onClose = useCallback(() => {
+        setOpen(true);
+        setTimeout(() => {
+            setShow(false);
+        }, 350)
+    }, []);
+
+    return (<div style={{ 
+            transition: open ? 'height 0.4s ease, width 0.4s ease': 'height 0.4s ease',
+            height: open ? '30px' : '200px',
+            width: open ? '190px': '100%', 
+            background: 'var(--semi-color-tertiary-light-hover)', 
+            borderRadius: 16,
+            boxSizing: 'border-box',
+            marginBottom: 10,
+        }}
+        >
+        <span
+            ref={spanRef} 
+            style={{
+                display: !open ? 'none' : 'flex',
+                width: 'fit-content',
+                columnGap: 10,
+                background: 'var(--semi-color-tertiary-light-hover)', 
+                borderRadius: '16px',
+                padding: '5px 10px',
+                point: 'cursor',
+                fontSize: 14,
+                color: 'var(--semi-color-text-1)',
+            }}
+            onClick={onOpen} 
+        >
+            <span> Got {source.length} sources </span>
+            <AvatarGroup size="extra-extra-small" >
+                {source.map((s, index) => (<Avatar key={index} src={s.avatar}></Avatar>))}        
+            </AvatarGroup>
+        </span>
+        <span 
+            style={{
+                height: '100%',
+                boxSizing: 'border-box',
+                display: !open ? 'flex' : 'none',
+                flexDirection: 'column',
+                background: 'var(--semi-color-tertiary-light-hover)', borderRadius: '16px', padding: 12, boxSize: 'border-box'
+            }}
+            onClick={onClose}
+            >
+            <span style={{display: 'flex', alignItems: 'center', justifyContent: 'space-between',
+                    padding: '5px 10px', columnGap: 10, color: 'var(--semi-color-text-1)'
+            }}>
+                <span style={{fontSize: 14, fontWeight: 500}}>Source</span>
+                <IconChevronUp />
+            </span>
+            <span style={{display: 'flex', flexWrap: 'wrap', gap: 10,  overflow: 'scroll', padding: '5px 10px'}}>
+                {source.map(s => (
+                    <span style={{ 
+                        display: 'flex', 
+                        flexDirection: 'column', 
+                        rowGap: 5, 
+                        flexBasis: 150, 
+                        flexGrow: 1,
+                        border: "1px solid var(--semi-color-border)",
+                        borderRadius: 12,
+                        padding: 12,
+                        fontSize: 12
+                    }}>
+                        <span style={{display: 'flex', columnGap: 5, alignItems: 'center', }}>
+                            <Avatar style={{width: 16, height: 16, flexShrink: 0 }} shape="square" src={s.avatar} />
+                            <span style={{ color: 'var(--semi-color-text-2)', textOverflow: 'ellipsis'}}>{s.title}</span>
+                        </span>
+                        <span style={{
+                            color: 'var(--semi-color-primary)',
+                            fontSize: 12,
+                        }}
+                        >{s.subTitle}</span>
+                        <span style={{
+                            display: '-webkit-box',
+                            "-webkit-box-orient": 'vertical',
+                            WebkitLineClamp: '3', 
+                            textOverflow: 'ellipsis', 
+                            overflow: 'hidden',
+                            color: 'var(--semi-color-text-2)',
+                        }}>{s.content}</span>
+                    </span>))}
+                </span>
+            </span>
+        </div>
+    )
+}
+
+function CustomRender() {
+    const [message, setMessage] = useState(defaultMessage);
+
+    const onChatsChange = useCallback((chats) => {
+        setMessage(chats);
+    }, []);
+
+     const onMessageSend = useCallback((content, attachment) => {
+        const newAssistantMessage = {
+            role: 'assistant',
+            id: getId(),
+            content: `This is a mock response`
+        }
+        setTimeout(() => { 
+            setMessage((message) => ([ ...message, newAssistantMessage])); 
+        }, 200);
+    }, []);
+
+    const renderContent = useCallback((props) => {
+        const { role, message, defaultNode, className } = props;
+        return <div className={className}>
+            {message.source && <SourceCard source={message.source} />}
+            <MarkdownRender raw={message.content}/>
+        </div>
+    }, []);
+
+    return (
+        <Chat
+            style={commonOuterStyle}
+            chats={message}
+            roleConfig={roleInfo}
+            chatBoxRenderConfig={{ renderChatBoxContent: renderContent }}
+            onChatsChange={onChatsChange}
+            onMessageSend={onMessageSend}
+            uploadProps={uploadProps}
+        />
+    );
+}
+
+render(CustomRender);
+```
+
+Use `renderFullChatBox` to custom render the entire chat box
+
+```jsx live=true noInline=true dir="column"
+import React, {useState, useCallback} from 'react';
+import { Chat, Avatar } from '@douyinfe/semi-ui';
+
+const defaultMessage = [
+    {
+        role: 'system',
+        id: '1',
+        createAt: 1715676751919,
+        content: "Hello, I'm your AI assistant.",   
+    },
+    {
+        role: 'user',
+        id: '2',
+        createAt: 1715676751919,
+        content: "Introduce Semi", 
+    },
+    {
+        role: 'assistant',
+        id: '3',
+        createAt: 1715676751919,
+        content: 'Semi Design is a design system designed, developed, and maintained by the front-end team at Douyin and the MED product design team.',
+    }
+];
+
+const roleInfo = {
+    user:  {
+        name: 'User',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/docs-icon.png'
+    },
+    assistant: {
+        name: 'Assistant',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png'
+    },
+    system: {
+        name: 'System',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png'
+    }
+}
+
+const commonOuterStyle = {
+    border: '1px solid var(--semi-color-border)',
+    borderRadius: '16px',
+    height: 400,
+}
+
+let id = 0;
+function getId() { return `id-${id++}`; }
+const uploadProps = { action: 'https://api.semi.design/upload' }
+
+const titleStyle = { display:' flex', alignItems: 'center', justifyContent: 'center', columnGap: '10px', padding: '5px 0px', width: 'fit-content' };
+
+function CustomFullRender() {
+    const [message, setMessage] = useState(defaultMessage);
+
+    const customRenderChatBox = useCallback((props) => {
+        const { role, message, defaultNodes, className } = props;
+        let titleNode = null;
+        if (message.role !== 'user') {
+            titleNode = (<span style={titleStyle}>
+                <Avatar size="extra-small" shape="square" src={role.avatar} />
+                {defaultNodes.title}
+            </span>)
+        }
+        return <div className={className}>
+            <div style={{ display: 'flex', flexDirection: 'column', rowGap: 4, alignItems: message.role === 'user' ? 'end' : ''}}>
+                {titleNode}
+                <div style={{ width: 'fit-content'}}>
+                    {defaultNodes.content}
+                </div>
+                {defaultNodes.action}
+            </div>
+        </div>
+    }, []);
+
+    const onChatsChange = useCallback((chats) => {
+        setMessage(chats)
+    } ,[]);
+
+     const onMessageSend = useCallback((content, attachment) => {
+        const newAssistantMessage = {
+            role: 'assistant',
+            id: getId(),
+            content: `This is a mock response`
+        }
+        setTimeout(() => { 
+            setMessage((message) => ([ ...message, newAssistantMessage])); 
+        }, 200);
+    }, []);
+    
+    return ( <Chat
+        chatBoxRenderConfig={{ renderFullChatBox: customRenderChatBox }}
+        style={commonOuterStyle} 
+        chats={message}
+        onChatsChange={onChatsChange}
+        onMessageSend={onMessageSend}
+        roleConfig={roleInfo}
+        uploadProps={uploadProps}
+    />);
+}
+
+render(CustomFullRender)
+```
+
+### Custom render InputArea
+
+The rendering input box can be customized through `renderInputArea`, the parameters are as follows
+
+``` ts
+export interface RenderInputAreaProps {
+    /* Default node */
+    defaultNode?: ReactNode;
+    /* If you customize the input box, you need to call it when sending a message. */
+    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;
+}
+```
+
+Example:
+
+```jsx live=true noInline=true dir="column"
+import React, {useState, useCallback} from 'react';
+import { Form, Chat } from '@douyinfe/semi-ui';
+
+const defaultMessage = [
+    {
+        role: 'system',
+        id: '1',
+        createAt: 1715676751919,
+        content: "Hello, I'm your AI assistant.",   
+    },
+];
+
+const roleInfo = {
+    user:  {
+        name: 'User',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/docs-icon.png'
+    },
+    assistant: {
+        name: 'Assistant',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png'
+    },
+    system: {
+        name: 'System',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png'
+    }
+}
+
+const commonOuterStyle = {
+    border: '1px solid var(--semi-color-border)',
+    borderRadius: '16px',
+    height: 500,
+};
+
+let id = 0;
+function getId() {
+    return `id-${id++}`
+}
+const uploadProps = { action: 'https://api.semi.design/upload' }
+
+const inputStyle = {   
+    display: 'flex', 
+    flexDirection: 'column', 
+    border: '1px solid var(--semi-color-border)',
+    margin: '8px 16px',
+    borderRadius: 8,
+    padding: 8
+}
+
+function CustomInputRender(props) {
+    const { defaultNode, onClear, onSend } = props;
+    const api = useRef();
+    const onSubmit = useCallback(() => {
+        if (api.current) {
+            const values = api.current.getValues();
+            if ((values.name && values.name.length !== 0) || (values.file && values.file.length !== 0)) {
+                onSend(values.name, values.file);
+                api.current.reset();
+            } 
+        }
+    }, []);
+
+    return (<div style={inputStyle}>
+        <Form
+            getFormApi={formApi => api.current = formApi}
+        >
+            <strong>Information Chart</strong>
+            <Form.Input
+                field="name"
+                label="Name"
+                style={{ width: 250 }}
+                trigger='blur'
+            />
+            <Form.Upload
+                field='file'
+                label='File'
+                action='https://api.semi.design/upload'
+            >
+                <Button icon={<IconUpload />} theme="light">
+                    Upload
+                </Button>
+            </Form.Upload>
+        </Form>
+        <Button style={{ width: 'fit-content' }} onClick={onSubmit}>Submit</Button>
+    </div>);
+}
+
+function CustomRenderInputArea() {
+    const [message, setMessage] = useState(defaultMessage);
+
+    const onChatsChange = useCallback((chats) => {
+        setMessage(chats);
+    }, []);
+
+    const onMessageSend = useCallback((content, attachment) => {
+        const newAssistantMessage = {
+            role: 'assistant',
+            id: getId(),
+            content: `This is a mock response`
+        } 
+        setTimeout(() => { 
+            setMessage((message) => ([ ...message, newAssistantMessage])); 
+        }, 200);
+    }, []);
+
+    const renderInputArea = useCallback((props) => {
+        return (<CustomInputRender {...props} />)
+    }, []);    
+
+    return (
+        <Chat
+            renderInputArea={renderInputArea}
+            style={commonOuterStyle}
+            chats={message}
+            roleConfig={roleInfo}
+            onChatsChange={onChatsChange}
+            onMessageSend={onMessageSend}
+            uploadProps={uploadProps}
+        />
+    )
+}
+render(CustomRenderInputArea);
+```
+
+### Hint
+
+The prompt area content can be set through `hints`. After clicking the prompt content, the prompt content will become the new user input content and trigger the `onHintClick` callback.
+
+```jsx live=true noInline=true dir="column"
+import React, {useState, useCallback} from 'react';
+import { Chat } from '@douyinfe/semi-ui';
+
+const defaultMessage = [
+    {
+        role: 'assistant',
+        id: '1',
+        createAt: 1715676751919,
+        content: 'Semi Design is a design system designed, developed, and maintained by the front-end team at Douyin and the MED product design team.',
+    }
+];
+
+const hintsExample = [
+    "Tell me more",
+    "What are the components of Semi Design?",
+    "What are the addresses of Semi Design’s official website and github warehouse?",
+]
+
+const roleInfo = {
+    user:  {
+        name: 'User',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/docs-icon.png'
+    },
+    assistant: {
+        name: 'Assistant',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png'
+    },
+    system: {
+        name: 'System',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png'
+    }
+}
+
+const commonOuterStyle = {
+    border: '1px solid var(--semi-color-border)',
+    borderRadius: '16px',
+    height: 400,
+};
+
+let id = 0;
+function getId() {
+    return `id-${id++}`
+}
+const uploadProps = { action: 'https://api.semi.design/upload' }
+
+function DefaultChat() {
+    const [message, setMessage] = useState(defaultMessage);
+    const [hints, setHints] = useState(hintsExample);
+
+    const onHintClick = useCallback(() => {
+        setHints([]);
+    }, [])
+
+    const onMessageSend = useCallback((content, attachment) => {
+        const newAssistantMessage = {
+            role: 'assistant',
+            id: getId(),
+            createAt: Date.now(),
+            content: "This is a mock response",
+        }
+        setTimeout(() => { 
+            setMessage((message) => ([ ...message, newAssistantMessage])); 
+        }, 200);
+    }, []);
+
+    const onChatsChange = useCallback((chats) => {
+        setMessage(chats);
+    }, []);
+
+    onClear = useCallback(() => {
+        setHints([]);
+    }, [])
+
+    return (
+        <Chat 
+            hints={hints}
+            onHintClick={onHintClick}
+            style={commonOuterStyle}
+            chats={message}
+            roleConfig={roleInfo}
+            onChatsChange={onChatsChange}
+            onMessageSend={onMessageSend}
+            onClear={onClear}
+            uploadProps={uploadProps}
+        />
+    )
+}
+
+render(DefaultChat);
+```
+
+### Custom render Hint
+
+Customize the content of the prompt area through `renderHintBox`, the parameters are as follows
+
+```ts
+type renderHintBox = (props: {content: string; index: number,onHintClick: () => void}) => React.ReactNode;
+```
+
+Example:
+
+```jsx live=true noInline=true dir="column"
+import React, {useState, useCallback} from 'react';
+import { Chat } from '@douyinfe/semi-ui';
+
+const defaultMessage = [
+    {
+        role: 'assistant',
+        id: '1',
+        createAt: 1715676751919,
+        content: 'Semi Design is a design system designed, developed, and maintained by the front-end team at Douyin and the MED product design team.',
+    }
+];
+
+const hintsExample = [
+    "Tell me more",
+    "What are the components of Semi Design?",
+    "What are the addresses of Semi Design’s official website and github warehouse?",
+]
+
+const roleInfo = {
+    user:  {
+        name: 'User',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/docs-icon.png'
+    },
+    assistant: {
+        name: 'Assistant',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png'
+    },
+    system: {
+        name: 'System',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png'
+    }
+}
+
+const commonOuterStyle = {
+    border: '1px solid var(--semi-color-border)',
+    borderRadius: '16px',
+    height: 400,
+};
+
+let id = 0;
+function getId() {
+    return `id-${id++}`
+}
+const uploadProps = { action: 'https://api.semi.design/upload' }
+
+function DefaultChat() {
+    const [message, setMessage] = useState(defaultMessage);
+    const [hints, setHints] = useState(hintsExample);
+
+    const onHintClick = useCallback(() => {
+        setHints([]);
+    }, [])
+
+    const onMessageSend = useCallback((content, attachment) => {
+        const newAssistantMessage = {
+            role: 'assistant',
+            id: getId(),
+            createAt: Date.now(),
+            content: "This is a mock reply message",
+        }
+        setTimeout(() => { 
+            setMessage((message) => ([ ...message, newAssistantMessage])); 
+        }, 200);
+        setHints([]);
+    }, []);
+
+    const onChatsChange = useCallback((chats) => {
+        setMessage(chats);
+    }, []);
+
+    const commonHintStyle = useMemo(() => ({
+        border: '1px solid var(--semi-color-border)',
+        padding: '10px',
+        borderRadius: '10px',
+        color: 'var( --semi-color-text-1)',
+        display: 'flex',
+        justifyContent: 'space-between',
+        alignItems: 'center',
+        cursor: 'pointer',
+        fontSize: '14px'
+    }), []);
+    
+    const renderHintBox = useCallback((props) => {
+        const { content, onHintClick, index } = props;
+        return <div style={commonHintStyle} onClick={onHintClick} key={index}>
+            {content}
+            <IconArrowRight style={{ marginLeft: 10 }}>click me</IconArrowRight>
+        </div>
+    }, []);
+
+    onClear = useCallback(() => {
+        setHints([]);
+    }, [])
+
+    return (
+        <Chat 
+            renderHintBox={renderHintBox}
+            hints={hints}
+            onHintClick={onHintClick}
+            style={commonOuterStyle}
+            chats={message}
+            roleConfig={roleInfo}
+            onChatsChange={onChatsChange}
+            onMessageSend={onMessageSend}
+            onClear={onClear}
+            uploadProps={uploadProps}
+        />
+    )
+}
+
+render(DefaultChat);
+```
+
+### API
+
+| PROPERTIES | INSTRUCTIONS | TYPE | DEFAULT |
+|------|--------|-------|-------|
+| align | Dialog alignment, supports `leftRight`,`leftAlign` | string | `leftRight` |
+| bottomSlot | bottom slot for chat | React.ReactNode | - |
+| chatBoxRenderConfig | chatBox rendering configuration | ChatBoxRenderConfig | - |
+| chats | Controlled conversation list | Message | - |
+| className | Custom class name | string | - |
+| customMarkDownComponents | custom markdown render, transparently passed to MarkdownRender for conversation content rendering | MDXProps\['components'\]| - |
+| hints | prompt information | string | - |
+| hintCls | hint style | string | - |
+| hintStyle | hint style | CSSProperties | - |
+| 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` |
+| mode | Conversation mode, support `bubble` \| `noBubble` \| `userBubble`  | string | `bubble` |
+| roleConfig | Role information configuration, see[RoleConfig](#RoleConfig) | RoleConfig | - |
+| renderHintBox | Custom rendering prompt information | (props: {content: string; index: number,onHintClick: () => void}) => React.ReactNode| - |
+| onChatsChange | Triggered when the conversation list changes | (chats: Message[]) => void | - |
+| onClear | Triggered when context message is cleared | () => void | - |
+| onHintClick | Triggered when the prompt message is clicked | (hint: string) => void | - |
+| onInputChange | Triggered when input area information changes | (props: { value?: string, attachment?: FileItem[] }) => void; | - |
+| onMessageBadFeedback | Triggered when the message is negatively fed back | (message: Message) => void | - |
+| onMessageCopy | Triggered when copying a message | (message: Message) => void | - |
+| onMessageDelete | Triggered when a message is deleted | (message: Message) => void | - |
+| onMessageGoodFeedback | Triggered when the message is fed back positively | (message: Message) => void | - |
+| onMessageReset | Triggered when message is reset | (message: Message) => void | - |
+| onMessageSend | Triggered when sending a message | (content: string, attachment?: FileItem[]) => void | - |
+| onStopGenerator | Fires when the stop generation button is clicked | (message: Message) => void | - |
+| placeholder | Input box placeholder | string | - |
+| renderInputArea | Custom rendering input box | (props: RenderInputAreaProps) => React.ReactNode | - |
+| 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 | - |
+
+
+#### RoleConfig
+
+| PROPERTIES | INSTRUCTIONS | TYPE | DEFAULT |
+|------|--------|-------|-------|
+| user | User information | Metadata | - |
+| assistant | Assistant information | Metadata | - |
+| system | System information | Metadata | - |
+
+#### Metadata
+
+| PROPERTIES | INSTRUCTIONS | TYPE | DEFAULT |
+|------|--------|-------|-------|
+| name | name | string | - |
+| avatar | avatar | string | - |
+| color | Avatar background color, same as the color parameter of Avatar component, support `amber`、 `blue`、 `cyan`、 `green`、 `grey`、 `indigo`、 `light-blue`、 `light-green`、 `lime`、 `orange`、 `pink`、 `purple`、 `red`、 `teal`、 `violet`、 `yellow` | string | `grey` |
+
+#### Message
+
+| PROPERTIES | INSTRUCTIONS | TYPE | DEFAULT |
+|------|--------|-------|-------|
+| role | role  | string | - |
+| name | name  | string | - |
+| id | Uniquely identifies  | string\| number | - |
+| content | all content | string| Content[] | - |
+| parentId | parent Uniquely identifies | string | - |
+| createAt | creation time | number | -|
+| status | Information status, `loading` \| `incomplete` \| `complete` \| `error` | string | complete |
+
+
+#### Content
+
+| PROPERTIES | INSTRUCTIONS | TYPE | DEFAULT |
+|------|--------|-------|-------|
+| type | type,  suport `text` \| `image_url` \| `file_url`  | string | - |
+| text | Content data when type is `text` | string | - |
+| image_url | Content data when type is `image_url` | { url: string } | - |
+| file_url | Content data when type is `file_url` | { url: string; name: string; size: string; type: string } | - |
+
+#### Methods
+
+| METHOD  | INSTRUCTIONS   |
+|------|--------|
+| resetMessage | Reset message |
+| scrollToBottom(animation: boolean) | Scroll to the bottom, if animation is true, there will be animation, otherwise there will be no animation. |
+| clearContext | clear context|
+| sendMessage(content: string, attachment: FileItem[]) | send message with content and attachment |
+
+## Design Token
+
+<DesignToken/>

+ 1616 - 0
content/plus/chat/index.md

@@ -0,0 +1,1616 @@
+---
+localeCode: zh-CN
+order: 82
+category: Plus
+title:  Chat 对话
+icon: doc-chat
+dir: column
+brief: 用于快速搭建对话内容
+---
+
+## 使用场景
+
+Chat 组件可用于普通会话,AI 会话等场景。
+
+对话内容渲染基于 MarkdownRender 组件,支持 Markdown 和 MDX,可实现图片,表格,链接,加粗,代码区等常用富文本功能。也可通过 JSX 实现更加复杂定制化的文档撰写与展示需求。
+
+
+## 代码演示
+
+### 如何引入
+
+Chat 从 v2.63.0 版本开始支持。
+
+```jsx
+import { Chat } from '@douyinfe/semi-ui';
+```
+
+### 基本用法
+
+通过设置 `chats` 和 `onChatsChange`,`onMessageSend` 实现基础对话显示和交互。
+
+附件支持通过点击上传按钮,输入框粘贴,拖拽文件至 Chat 区域上传。通过 `uploadProps` 设置上传参数,详情参考 [Upload](/zh-CN/input/upload#API%20%E5%8F%82%E8%80%83)。
+
+上传按钮的提示文案可通过 `uploadTipProps` 设置,详情参考 [Tooltip](/zh-CN/tooltip#API%20%E5%8F%82%E8%80%83)。
+
+对话是多方参与,多轮交互的场景。可通过 `roleConfig` 传入角色信息(包括名称,头像等),具体参数细节 [RoleConfig](#roleConfig)。
+
+使用 `align` 属性可以设置对话的对齐方式,支持左右对齐(`leftRight`, 默认)和左对齐(`leftAlign`)。
+
+```jsx live=true noInline=true dir="column"
+import React, {useState, useCallback} from 'react';
+import { Chat, Radio } from '@douyinfe/semi-ui';
+
+const defaultMessage = [
+    {
+        role: 'system',
+        id: '1',
+        createAt: 1715676751919,
+        content: "Hello, I'm your AI assistant.",   
+    },
+    {
+        role: 'user',
+        id: '2',
+        createAt: 1715676751919,
+        content: "给一个 Semi Design 的 Button 组件的使用示例",
+    },
+    {
+        role: 'assistant',
+        id: '3',
+        createAt: 1715676751919,
+        content: "以下是一个 Semi 代码的使用示例:\n\`\`\`jsx \nimport React from 'react';\nimport { Button } from '@douyinfe/semi-ui';\n\nconst MyComponent = () => {\n  return (\n    <Button>Click me</Button>\n );\n};\nexport default MyComponent;\n\`\`\`\n",
+    }
+];
+
+const roleInfo = {
+    user:  {
+        name: 'User',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/docs-icon.png'
+    },
+    assistant: {
+        name: 'Assistant',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png'
+    },
+    system: {
+        name: 'System',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png'
+    }
+}
+
+const commonOuterStyle = {
+    border: '1px solid var(--semi-color-border)',
+    borderRadius: '16px',
+    margin: '8px 16px',
+    height: 550,
+}
+
+let id = 0;
+function getId() {
+    return `id-${id++}`
+}
+
+const uploadProps = { action: 'https://api.semi.design/upload' }
+const uploadTipProps = { content: '自定义上传按钮提示信息' }
+
+function DefaultChat() {
+    const [message, setMessage] = useState(defaultMessage);
+    const [mode, setMode] = useState('bubble');
+    const [align, setAlign] = useState('leftRight');
+
+    const onAlignChange = useCallback((e) => {
+        setAlign(e.target.value);
+    }, []);
+
+    const onModeChange = useCallback((e) => {
+        setMode(e.target.value);
+    }, []); 
+
+    const onMessageSend = useCallback((content, attachment) => {
+        const newAssistantMessage = {
+            role: 'assistant',
+            id: getId(),
+            createAt: Date.now(),
+            content: "这是一条 mock 回复信息",
+        }
+        setTimeout(() => { 
+            setMessage((message) => ([ ...message, newAssistantMessage])); 
+        }, 200);
+    }, []);
+
+    const onChatsChange = useCallback((chats) => {
+        setMessage(chats);
+    }, []);
+
+    const onMessageReset = useCallback((e) => {
+        setTimeout(() => {
+            setMessage((message) => {
+                const lastMessage = message[message.length - 1];
+                const newLastMessage = {
+                    ...lastMessage,
+                    status: 'complete',
+                    content: 'This is a mock reset message.',
+                }
+                return [...message.slice(0, -1), newLastMessage]
+            })
+        }, 200);
+    })
+
+    return (
+        <>
+            <span style={{ display: 'flex', flexDirection: 'column', rowGap: '8px'}}>
+                <span style={{ display: 'flex', alignItems: 'center', columnGap: '10px'}}>
+                    模式
+                    <RadioGroup onChange={onModeChange} value={mode} type={"button"}>
+                        <Radio value={'bubble'}>气泡</Radio>
+                        <Radio value={'noBubble'}>非气泡</Radio>
+                        <Radio value={'userBubble'}>用户会话气泡</Radio>
+                    </RadioGroup>
+                </span>
+                <span style={{ display: 'flex', alignItems: 'center', columnGap: '10px'}}>
+                    会话对齐方式
+                    <RadioGroup onChange={onAlignChange} value={align} type={"button"}>
+                        <Radio value={'leftRight'}>左右分布</Radio>
+                        <Radio value={'leftAlign'}>左对齐</Radio>
+                    </RadioGroup>
+                </span>
+            </span>
+            <Chat 
+                key={align + mode}
+                align={align}
+                mode={mode}
+                uploadProps={uploadProps}
+                style={commonOuterStyle}
+                chats={message}
+                roleConfig={roleInfo}
+                onChatsChange={onChatsChange}
+                onMessageSend={onMessageSend}
+                onMessageReset={onMessageReset}
+                uploadTipProps={uploadTipProps}
+            />
+        </>
+    )
+}
+
+render(DefaultChat);
+```
+
+### 消息状态
+
+chats 类型为 `Message[]`, `Message` 包含对话的各种信息,如角色(role)、内容(content)、附件(attachment)、状态(status)
+、唯一标识(id)、创建时间(createAt)等,具体见 [Message](#Message)。其中 status 不同,会话样式不同。
+
+``` jsx live=true noInline=true dir="column"
+import React, {useState, useCallback} from 'react';
+import { Chat } from '@douyinfe/semi-ui';
+
+const defaultMessage = [
+    {
+        role: 'assistant',
+        id: '1',
+        createAt: 1715676751919,
+        content: "请求成功",   
+    },
+    {
+        id: 'loading',
+        role: 'assistant',
+        status: 'loading'
+    },
+    {
+        role: 'assistant',
+        id: 'error',
+        content: '请求错误',
+        status: 'error'
+    }
+];
+
+const roleInfo = {
+    user:  {
+        name: 'User',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/docs-icon.png'
+    },
+    assistant: {
+        name: 'Assistant',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png'
+    },
+    system: {
+        name: 'System',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png'
+    }
+}
+
+const commonOuterStyle = {
+    border: '1px solid var(--semi-color-border)',
+    borderRadius: '16px',
+    height: 400,
+}
+
+let id = 0;
+function getId() { return `id-${id++}` }
+const uploadProps = { action: 'https://api.semi.design/upload' }
+
+function MessageStatus() {
+    const [message, setMessage] = useState(defaultMessage);
+
+    const onMessageSend = useCallback((content, attachment) => {
+        const newAssistantMessage = {
+            role: 'assistant',
+            id: getId(),
+            createAt: Date.now(),
+            content: "这是一条 mock 回复信息",
+        }
+        setTimeout(() => { 
+            setMessage((message) => ([ ...message, newAssistantMessage])); 
+        }, 200);
+    }, []);
+
+    const onChatsChange = useCallback((chats) => {
+        setMessage(chats);
+    }, []);
+
+    return (
+        <Chat 
+            style={commonOuterStyle}
+            chats={message}
+            roleConfig={roleInfo}
+            onChatsChange={onChatsChange}
+            onMessageSend={onMessageSend}
+            uploadProps={uploadProps}
+        />
+    )
+}
+
+render(MessageStatus);
+```
+
+### 动态更新数据
+
+对于后台返回 Serve Side Event 数据情况,可将获取到的数据用于更新 `chats`,对话内容将实时更新。
+
+`showStopGenerate` 参数可用于设置是否展示停止生成按钮,默认为 `false`。 可以在 `onStopGenerator` 中处理停止生成逻辑。
+
+```jsx live=true noInline=true dir="column"
+import React, {useState, useCallback} from 'react';
+import { Chat } from '@douyinfe/semi-ui';
+
+const defaultMessage = [
+    {
+        role: 'system',
+        id: '1',
+        createAt: 1715676751919,
+        content: "Hello, I'm your AI assistant.",   
+    },
+    {
+        role: 'user',
+        id: '2',
+        createAt: 1715676751919,
+        content: "介绍一下 Semi design"
+    },
+    {
+        role: 'assistant',
+        id: '3',
+        createAt: 1715676751919,
+        content: `
+Semi Design 是由抖音前端团队和MED产品设计团队设计、开发并维护的设计系统。作为一个全面、易用、优质的现代应用UI解决方案,Semi Design从字节跳动各业务线的复杂场景中提炼而来,目前已经支撑了近千个平台产品,服务了内外部超过10万用户[[1]](https://semi.design/zh-CN/start/introduction)。
+
+Semi Design的特点包括:
+
+1. 设计简洁、现代化。
+2. 提供主题方案,可深度样式定制。
+3. 提供明暗色两套模式,切换方便。
+4. 国际化,覆盖了简/繁体中文、英语、日语、韩语、葡萄牙语等20+种语言,日期时间组件提供全球时区支持,全部组件可自动适配阿拉伯文RTL布局。
+5. 采用 Foundation 和 Adapter 跨框架技术方案,方便扩展。
+
+---
+Learn more:
+1. [Introduction 介绍 - Semi Design](https://semi.design/zh-CN/start/introduction)
+2. [Getting Started 快速开始 - Semi Design](https://semi.design/zh-CN/start/getting-started)
+3. [Semi D2C 设计稿转代码的演进之路 - 知乎](https://zhuanlan.zhihu.com/p/667189184)
+`,
+    }
+];
+
+const roleInfo = {
+    user:  {
+        name: 'User',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/docs-icon.png'
+    },
+    assistant: {
+        name: 'Assistant',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png'
+    },
+    system: {
+        name: 'System',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png'
+    }
+}
+
+const commonOuterStyle = {
+    border: '1px solid var(--semi-color-border)',
+    borderRadius: '16px',
+    height: 600,
+}
+
+let id = 0;
+function getId() {
+    return `id-${id++}`
+}
+const uploadProps = { action: 'https://api.semi.design/upload' }
+
+function DynamicUpdateChat() {
+    const [message, setMessage] = useState(defaultMessage);
+    const intervalId = useRef();
+    const onMessageSend = useCallback((content, attachment) => {
+        setMessage((message) => {
+            return [
+                ...message,
+                {
+                    role: 'assistant',
+                    status: 'loading',
+                    createAt: Date.now(),
+                    id: getId()
+                }
+            ]
+        }); 
+        generateMockResponse(content);
+    }, []);
+
+    const onChatsChange = useCallback((chats) => {
+        setMessage(chats);
+    }, []);
+
+    const generateMockResponse = useCallback((content) => {
+        const id = setInterval(() => {
+            setMessage((message) => {
+                const lastMessage = message[message.length - 1];
+                let newMessage = {...lastMessage};
+                if (lastMessage.status === 'loading') {
+                    newMessage = {
+                        ...newMessage,
+                        content:  `mock Response for ${content} \n`,
+                        status: 'incomplete'
+                    }
+                } else if (lastMessage.status === 'incomplete') {
+                    if (lastMessage.content.length > 200) {
+                        clearInterval(id);
+                        intervalId.current = null
+                        newMessage = {
+                            ...newMessage,
+                            content: `${lastMessage.content} mock stream message`,
+                            status: 'complete'
+                        }
+                    } else {
+                        newMessage = {
+                            ...newMessage,
+                            content: `${lastMessage.content} mock stream message`
+                        }
+                    }  
+                }
+                return [ ...message.slice(0, -1), newMessage ]
+            })
+        }, 400);
+        intervalId.current = id;
+    }, []);
+
+    const onStopGenerator = useCallback(() => {
+        if (intervalId.current) {
+            clearInterval(intervalId.current);
+            setMessage((message) => {
+                const lastMessage = message[message.length - 1];
+                if (lastMessage.status && lastMessage.status !== 'complete') {
+                    const lastMessage = message[message.length - 1];
+                    let newMessage = {...lastMessage};
+                    newMessage.status = 'complete';
+                    return [
+                        ...message.slice(0, -1),
+                        newMessage
+                    ]
+                } else {
+                    return message;
+                }
+            })
+        }
+    }, [intervalId]);
+
+    return (
+        <Chat 
+            chats={message}
+            showStopGenerate={true}
+            style={commonOuterStyle}
+            onStopGenerator={onStopGenerator}
+            roleConfig={roleInfo}
+            onChatsChange={onChatsChange}
+            onMessageSend={onMessageSend}
+            uploadProps={uploadProps}
+        />
+    )
+}
+
+render(DynamicUpdateChat);
+```
+
+### 清除上下文
+
+通过 `showClearContext` 可以开启在输入框中显示清除上下文按钮,默认为 `false`。
+也可以通过 ref 调用 `clearContext` 方法清除上下文。
+
+```jsx live=true noInline=true dir="column"
+import React, {useState, useCallback} from 'react';
+import { Chat, Radio } from '@douyinfe/semi-ui';
+
+const defaultMessage = [
+    {
+        role: 'system',
+        id: '1',
+        createAt: 1715676751919,
+        content: "Hello, I'm your AI assistant.",   
+    },
+    {
+        role: 'user',
+        id: '2',
+        createAt: 1715676751919,
+        content: "介绍一下 semi design", 
+    },
+    {
+        role: 'assistant',
+        id: '3',
+        createAt: 1715676751919,
+        content: 'Semi Design 是由抖音前端团队和MED产品设计团队设计、开发并维护的设计系统',
+    }
+];
+
+const roleInfo = {
+    user:  {
+        name: 'User',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/docs-icon.png'
+    },
+    assistant: {
+        name: 'Assistant',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png'
+    },
+    system: {
+        name: 'System',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png'
+    }
+}
+
+const commonOuterStyle = {
+    border: '1px solid var(--semi-color-border)',
+    borderRadius: '16px',
+    margin: '8px 16px',
+    height: 550,
+}
+
+let id = 0;
+function getId() {
+    return `id-${id++}`
+}
+
+const uploadProps = { action: 'https://api.semi.design/upload' }
+const uploadTipProps = { content: '自定义上传按钮提示信息' }
+
+function DefaultChat() {
+    const [message, setMessage] = useState(defaultMessage);
+
+    const onMessageSend = useCallback((content, attachment) => {
+        const newAssistantMessage = {
+            role: 'assistant',
+            id: getId(),
+            createAt: Date.now(),
+            content: "这是一条 mock 回复信息",
+        }
+        setTimeout(() => { 
+            setMessage((message) => ([ ...message, newAssistantMessage])); 
+        }, 200);
+    }, []);
+
+    const onChatsChange = useCallback((chats) => {
+        setMessage(chats);
+    }, []);
+
+    const onMessageReset = useCallback((e) => {
+        setTimeout(() => {
+            setMessage((message) => {
+                const lastMessage = message[message.length - 1];
+                const newLastMessage = {
+                    ...lastMessage,
+                    status: 'complete',
+                    content: 'This is a mock reset message.',
+                }
+                return [...message.slice(0, -1), newLastMessage]
+            })
+        }, 200);
+    })
+
+    return (
+        <>
+            <Chat
+                uploadProps={uploadProps}
+                style={commonOuterStyle}
+                chats={message}
+                roleConfig={roleInfo}
+                onChatsChange={onChatsChange}
+                onMessageSend={onMessageSend}
+                onMessageReset={onMessageReset}
+                uploadTipProps={uploadTipProps}
+                showClearContext
+            />
+        </>
+    )
+}
+
+render(DefaultChat);
+```
+
+### 自定义渲染会话框
+
+通过 `chatBoxRenderConfig` 传入自定义渲染配置, chatBoxRenderConfig 类型如下
+
+```ts
+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;
+}
+```
+
+自定义渲染头像和标题,可通过 `renderChatBoxAvatar` 和 `renderChatBoxTitle` 实现。
+
+```jsx live=true noInline=true dir="column"
+
+import React, {useState, useCallback} from 'react';
+import { Chat, Avatar, Tag } from '@douyinfe/semi-ui';
+
+const defaultMessage = [
+    {
+        role: 'system',
+        id: '1',
+        createAt: 1715676751919,
+        content: "Hello, I'm your AI assistant.",   
+    },
+    {
+        role: 'user',
+        id: '2',
+        createAt: 1715676751919,
+        content: [
+            {
+                type: 'text',
+                text: '这张图片里有什么?'
+            },
+            {
+                type: 'image_url',
+                image_url: {
+                    url: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/edit-bag.jpeg'
+                }
+            }
+        ], 
+    },
+    {
+        role: 'assistant',
+        id: '3',
+        createAt: 1715676751919,
+        content: '图片中是一个有卡通画像装饰的黄色背包。'
+    },
+
+];
+
+const roleInfo = {
+    user:  {
+        name: 'User',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/docs-icon.png'
+    },
+    assistant: {
+        name: 'Assistant',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png'
+    },
+    system: {
+        name: 'System',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png'
+    }
+}
+
+const commonOuterStyle = {
+    border: '1px solid var(--semi-color-border)',
+    borderRadius: '16px',
+    height: 400,
+}
+
+let id = 0;
+function getId() { return `id-${id++}`; }
+const uploadProps = { action: 'https://api.semi.design/upload' }
+
+function CustomRender() {
+    const [title, setTitle] = useState('null');
+    const [avatar, setAvatar] = useState('null');
+    const [message, setMessage] = useState(defaultMessage);
+
+    const onChatsChange = useCallback((chats) => {
+        setMessage(chats);
+    }, []);
+
+    const customRenderAvatar = useMemo(()=> {
+        switch(avatar) {
+            case 'custom': return (props) => {
+                    const { role, defaultAvatar } = props;
+                    return <Avatar size="extra-small" shape="square" style={{ flexShrink: '0'}}>{role.name}</Avatar >
+                }
+            case 'null': return () => null
+            case 'default': return undefined;
+        }
+    }, [avatar]);
+
+    const customRenderTitle = useMemo(()=> {
+        switch(title) {
+            case 'custom': return (props) => {
+                    const { role, defaultTitle, message } = props;
+                    const date = new Date(message.createAt);
+                    const hours = ('0' + date.getHours()).slice(-2);
+                    const minutes = ('0' + date.getMinutes()).slice(-2);
+                    const formatTime = `${hours}:${minutes}`;
+                    return (<span className="title" >
+                        {role.name}
+                        <span className={'time'}>{formatTime}</span>
+                    </span>)
+            }
+            case 'null': return () => null
+            case 'default': return undefined;
+        }
+    }, [title]);;
+
+    const onAvatarChange = useCallback((e) => { setAvatar(e.target.value) }, []);
+    const onTitleChange = useCallback((e) => { setTitle(e.target.value) }, []);
+
+     const onMessageSend = useCallback((content, attachment) => {
+        const newAssistantMessage = {
+            role: 'assistant',
+            id: getId(),
+            content: `This is a mock response`
+        }
+        setTimeout(() => { 
+            setMessage((message) => ([ ...message, newAssistantMessage])); 
+        }, 200);
+    }, []);
+
+    return (
+        <>
+            <span style={{ display: 'flex', flexDirection: 'column', rowGap: 8, marginBottom: 5}}>
+                <span style={{ display: 'flex', alignItems: 'center', columnGap: 10}}>
+                    头像渲染模式
+                    <RadioGroup onChange={onAvatarChange} value={avatar} type="button">
+                    <Radio value={'default'}>默认头像</Radio>
+                    <Radio value={'null'}>无头像</Radio>
+                    <Radio value={'custom'}>自定义头像</Radio>
+                </RadioGroup>
+                </span>
+                <span style={{ display: 'flex', alignItems: 'center', columnGap: 10}}>
+                    标题渲染模式
+                    <RadioGroup onChange={onTitleChange} value={title} type="button">
+                    <Radio value={'default'}>默认标题</Radio>
+                    <Radio value={'null'}>无标题</Radio>
+                    <Radio value={'custom'}>自定义标题</Radio>
+                </RadioGroup>
+                </span>
+            </span>
+            <Chat
+                chatBoxRenderConfig={{
+                    renderChatBoxTitle: customRenderTitle,
+                    renderChatBoxAvatar: customRenderAvatar
+                }} 
+                key={`${avatar}${title}`}
+                style={commonOuterStyle}
+                className={'component-chat-demo-custom-render'}
+                chats={message}
+                onChatsChange={onChatsChange}
+                onMessageSend={onMessageSend}
+                roleConfig={roleInfo}
+                uploadProps={uploadProps}
+            />
+        </>
+    );
+}
+
+render(CustomRender);
+```
+
+鼠标移动到会话上,即可显示会话操作区,通过 `renderChatBoxAction` 自定义渲染操作区
+
+```jsx live=true noInline=true dir="column"
+import React, {useState, useCallback} from 'react';
+import { Chat, Dropdown } from '@douyinfe/semi-ui';
+import { IconForward } from '@douyinfe/semi-icons';
+
+const defaultMessage = [
+    {
+        role: 'system',
+        id: '1',
+        createAt: 1715676751919,
+        content: "Hello, I'm your AI assistant.",   
+    },
+    {
+        role: 'user',
+        id: '2',
+        createAt: 1715676751919,
+        content: "介绍一下 semi design", 
+    },
+    {
+        role: 'assistant',
+        id: '3',
+        createAt: 1715676751919,
+        content: 'Semi Design 是由抖音前端团队和MED产品设计团队设计、开发并维护的设计系统',
+    }
+];
+
+const roleInfo = {
+    user:  {
+        name: 'User',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/docs-icon.png'
+    },
+    assistant: {
+        name: 'Assistant',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png'
+    },
+    system: {
+        name: 'System',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png'
+    }
+}
+
+const commonOuterStyle = {
+    border: '1px solid var(--semi-color-border)',
+    borderRadius: '16px',
+    height: 400,
+}
+
+let id = 0;
+function getId() { return `id-${id++}`; }
+const uploadProps = { action: 'https://api.semi.design/upload' }
+
+const CustomActions = React.memo((props) => {
+    const { role, message, defaultActions, className } = props;
+    const myRef = useRef();
+    const getContainer = useCallback(() => {
+        if (myRef.current) {
+            const element = myRef.current;
+            let parentElement = element.parentElement;
+            while (parentElement) {
+                if (parentElement.classList.contains('semi-chat-chatBox-wrap')) {
+                    return parentElement;
+                }
+                parentElement = parentElement.parentElement;
+            }
+        }
+    }, [myRef]);
+
+    return <span 
+        className={className}
+        ref={myRef}
+    >
+        {defaultActions}
+        {<Dropdown
+            key="dropdown"
+            render={
+                <Dropdown.Menu >
+                    <Dropdown.Item icon={<IconForward />}>分享</Dropdown.Item>
+                </Dropdown.Menu>
+            }
+            trigger="click"
+            position="top"
+            getPopupContainer={getContainer}
+        >
+            <Button 
+                className='semi-chat-chatBox-action-btn'
+                icon={<IconMoreStroked/>}
+                theme='borderless'
+                type='tertiary'
+            />
+        </Dropdown>}
+    </span>
+});
+
+function CustomRender() {
+    const [message, setMessage] = useState(defaultMessage);
+
+    const customRenderAction = useCallback((props) => {
+        return <CustomActions {...props} />
+    }, []);
+
+    const onChatsChange = useCallback((chats) => {
+        setMessage(chats);
+    }, []);
+
+    const onMessageSend = useCallback((content, attachment) => {
+        const newAssistantMessage = {
+            role: 'assistant',
+            id: getId(),
+            content: `This is a mock response`
+        }
+        setTimeout(() => { 
+            setMessage((message) => ([ ...message, newAssistantMessage])); 
+        }, 200);
+    }, []);
+
+    return (
+        <Chat
+            chatBoxRenderConfig={{ 
+                renderChatBoxAction: customRenderAction 
+            }}
+            style={commonOuterStyle}
+            chats={message}
+            onChatsChange={onChatsChange}
+            onMessageSend={onMessageSend}
+            roleConfig={roleInfo}
+            uploadProps={uploadProps}
+        />
+    );
+}
+
+render(CustomRender);
+```
+
+通过 `renderChatBoxContent` 自定义操作区域
+
+```jsx live=true noInline=true dir="column"
+import React, { useState, useCallback, useRef} from 'react';
+import { Chat, MarkdownRender } from '@douyinfe/semi-ui';
+
+const defaultMessage = [
+        {
+        role: 'assistant',
+        id: '3',
+        createAt: 1715676751919,
+        content: "Semi Design 是由抖音前端团队,MED 产品设计团队设计、开发并维护的设计系统。它作为全面、易用、优质的现代应用 UI 解决方案,从字节跳动各业务线的复杂场景提炼而来,支撑近千计平台产品,服务内外部 10 万+ 用户。",
+        source: [
+            {
+                avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png',
+                url: '/zh-CN/start/introduction',
+                title: 'semi Design',
+                subTitle: 'Semi design website',
+                content: 'Semi Design 是由抖音前端团队,MED 产品设计团队设计、开发并维护的设计系统。'
+            },
+            {
+                avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png',
+                url: '/dsm/landing',
+                subTitle: 'Semi DSM website',
+                title: 'Semi 设计系统',
+                content: '从 Semi Design,到 Any Design 快速定义你的设计系统,并应用在设计稿和代码中'
+            },
+            {
+                avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png',
+                url: '/code/zh-CN/start/introduction',
+                subTitle: 'Semi D2C website',
+                title: '设计稿转代码',
+                content: 'Semi 设计稿转代码(Semi Design to Code,或简称 Semi D2C),是由抖音前端 Semi Design 团队推出的全新的提效工具'
+            },
+        ]
+    }];
+
+const roleInfo = {
+    user:  {
+        name: 'User',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/docs-icon.png'
+    },
+    assistant: {
+        name: 'Assistant',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png'
+    },
+    system: {
+        name: 'System',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png'
+    }
+}
+
+const commonOuterStyle = {
+    border: '1px solid var(--semi-color-border)',
+    borderRadius: '16px',
+    height: 500,
+}
+
+let id = 0;
+function getId() { return `id-${id++}` }
+const uploadProps = { action: 'https://api.semi.design/upload' }
+
+const SourceCard = (props) => {
+    const [open, setOpen] = useState(true);
+    const [show, setShow] = useState(false);
+    const { source } = props;
+    const spanRef = useRef();
+    const onOpen = useCallback(() => {
+        setOpen(false);
+        setShow(true);
+    }, []);
+
+    const onClose = useCallback(() => {
+        setOpen(true);
+        setTimeout(() => {
+            setShow(false);
+        }, 350)
+    }, []);
+
+    return (<div style={{ 
+            transition: open ? 'height 0.4s ease, width 0.4s ease': 'height 0.4s ease',
+            height: open ? '30px' : '200px',
+            width: open ? '190px': '100%', 
+            background: 'var(--semi-color-tertiary-light-hover)', 
+            borderRadius: 16,
+            boxSizing: 'border-box',
+            marginBottom: 10,
+        }}
+        >
+        <span
+            ref={spanRef} 
+            style={{
+                display: !open ? 'none' : 'flex',
+                width: 'fit-content',
+                columnGap: 10,
+                background: 'var(--semi-color-tertiary-light-hover)', 
+                borderRadius: '16px',
+                padding: '5px 10px',
+                point: 'cursor',
+                fontSize: 14,
+                color: 'var(--semi-color-text-1)',
+            }}
+            onClick={onOpen} 
+        >
+            <span>基于{source.length}个搜索来源</span>
+            <AvatarGroup size="extra-extra-small" >
+                {source.map((s, index) => (<Avatar key={index} src={s.avatar}></Avatar>))}        
+            </AvatarGroup>
+        </span>
+        <span 
+            style={{
+                height: '100%',
+                boxSizing: 'border-box',
+                display: !open ? 'flex' : 'none',
+                flexDirection: 'column',
+                background: 'var(--semi-color-tertiary-light-hover)', borderRadius: '16px', padding: 12, boxSize: 'border-box'
+            }}
+            onClick={onClose}
+            >
+            <span style={{display: 'flex', alignItems: 'center', justifyContent: 'space-between',
+                    padding: '5px 10px', columnGap: 10, color: 'var(--semi-color-text-1)'
+            }}>
+                <span style={{fontSize: 14, fontWeight: 500}}>Source</span>
+                <IconChevronUp />
+            </span>
+            <span style={{display: 'flex', flexWrap: 'wrap', gap: 10,  overflow: 'scroll', padding: '5px 10px'}}>
+                {source.map(s => (
+                    <span style={{ 
+                        display: 'flex', 
+                        flexDirection: 'column', 
+                        rowGap: 5, 
+                        flexBasis: 150, 
+                        flexGrow: 1,
+                        border: "1px solid var(--semi-color-border)",
+                        borderRadius: 12,
+                        padding: 12,
+                        fontSize: 12
+                    }}>
+                        <span style={{display: 'flex', columnGap: 5, alignItems: 'center', }}>
+                            <Avatar style={{width: 16, height: 16, flexShrink: 0 }} shape="square" src={s.avatar} />
+                            <span style={{ color: 'var(--semi-color-text-2)', textOverflow: 'ellipsis'}}>{s.title}</span>
+                        </span>
+                        <span style={{
+                            color: 'var(--semi-color-primary)',
+                            fontSize: 12,
+                        }}
+                        >{s.subTitle}</span>
+                        <span style={{
+                            display: '-webkit-box',
+                            "-webkit-box-orient": 'vertical',
+                            WebkitLineClamp: '3', 
+                            textOverflow: 'ellipsis', 
+                            overflow: 'hidden',
+                            color: 'var(--semi-color-text-2)',
+                        }}>{s.content}</span>
+                    </span>))}
+                </span>
+            </span>
+        </div>
+    )
+}
+
+function CustomRender() {
+    const [message, setMessage] = useState(defaultMessage);
+
+    const onChatsChange = useCallback((chats) => {
+        setMessage(chats);
+    }, []);
+
+     const onMessageSend = useCallback((content, attachment) => {
+        const newAssistantMessage = {
+            role: 'assistant',
+            id: getId(),
+            content: `This is a mock response`
+        }
+        setTimeout(() => { 
+            setMessage((message) => ([ ...message, newAssistantMessage])); 
+        }, 200);
+    }, []);
+
+    const renderContent = useCallback((props) => {
+        const { role, message, defaultNode, className } = props;
+        return <div className={className}>
+            {message.source && <SourceCard source={message.source} />}
+            <MarkdownRender raw={message.content}/>
+        </div>
+    }, []);
+
+    return (
+        <Chat
+            style={commonOuterStyle}
+            chats={message}
+            roleConfig={roleInfo}
+            chatBoxRenderConfig={{ renderChatBoxContent: renderContent }}
+            onChatsChange={onChatsChange}
+            onMessageSend={onMessageSend}
+            uploadProps={uploadProps}
+        />
+    );
+}
+
+render(CustomRender);
+```
+
+使用 `renderFullChatBox` 自定义渲染整个会话框
+
+```jsx live=true noInline=true dir="column"
+import React, {useState, useCallback} from 'react';
+import { Chat, Avatar } from '@douyinfe/semi-ui';
+
+const defaultMessage = [
+    {
+        role: 'system',
+        id: '1',
+        createAt: 1715676751919,
+        content: "Hello, I'm your AI assistant.",   
+    },
+    {
+        role: 'user',
+        id: '2',
+        createAt: 1715676751919,
+        content: "介绍一下 semi design", 
+    },
+    {
+        role: 'assistant',
+        id: '3',
+        createAt: 1715676751919,
+        content: 'Semi Design 是由抖音前端团队和MED产品设计团队设计、开发并维护的设计系统',
+    }
+];
+
+const roleInfo = {
+    user:  {
+        name: 'User',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/docs-icon.png'
+    },
+    assistant: {
+        name: 'Assistant',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png'
+    },
+    system: {
+        name: 'System',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png'
+    }
+}
+
+const commonOuterStyle = {
+    border: '1px solid var(--semi-color-border)',
+    borderRadius: '16px',
+    height: 400,
+}
+
+let id = 0;
+function getId() { return `id-${id++}`; }
+const uploadProps = { action: 'https://api.semi.design/upload' }
+
+const titleStyle = { display:' flex', alignItems: 'center', justifyContent: 'center', columnGap: '10px', padding: '5px 0px', width: 'fit-content' };
+
+function CustomFullRender() {
+    const [message, setMessage] = useState(defaultMessage);
+
+    const customRenderChatBox = useCallback((props) => {
+        const { role, message, defaultNodes, className } = props;
+        let titleNode = null;
+        if (message.role !== 'user') {
+            titleNode = (<span style={titleStyle}>
+                <Avatar size="extra-small" shape="square" src={role.avatar} />
+                {defaultNodes.title}
+            </span>)
+        }
+        return <div className={className}>
+            <div style={{ display: 'flex', flexDirection: 'column', rowGap: 4, alignItems: message.role === 'user' ? 'end' : ''}}>
+                {titleNode}
+                <div style={{ width: 'fit-content'}}>
+                    {defaultNodes.content}
+                </div>
+                {defaultNodes.action}
+            </div>
+        </div>
+    }, []);
+
+    const onChatsChange = useCallback((chats) => {
+        setMessage(chats)
+    } ,[]);
+
+     const onMessageSend = useCallback((content, attachment) => {
+        const newAssistantMessage = {
+            role: 'assistant',
+            id: getId(),
+            content: `This is a mock response`
+        }
+        setTimeout(() => { 
+            setMessage((message) => ([ ...message, newAssistantMessage])); 
+        }, 200);
+    }, []);
+    
+    return ( <Chat
+        chatBoxRenderConfig={{ renderFullChatBox: customRenderChatBox }}
+        style={commonOuterStyle} 
+        chats={message}
+        onChatsChange={onChatsChange}
+        onMessageSend={onMessageSend}
+        roleConfig={roleInfo}
+        uploadProps={uploadProps}
+    />);
+}
+
+render(CustomFullRender)
+```
+
+### 自定义渲染输入框
+
+可通过 `renderInputArea` 自定义渲染输入框,参数如下
+
+``` ts
+export interface RenderInputAreaProps {
+    /* 默认节点 */
+    defaultNode?: ReactNode;
+    /* 如果自定义输入框,发送消息时需调用 */
+    onSend?: (content?: string, attachment?: FileItem[]) => void;
+    /* 如果自定义清除上下文按钮,点击清除上下文时需调用 */
+    onClear?: (e?: any) => void;
+}
+```
+
+使用示例如下
+
+```jsx live=true noInline=true dir="column"
+import React, {useState, useCallback} from 'react';
+import { Form, Chat } from '@douyinfe/semi-ui';
+
+const defaultMessage = [
+    {
+        role: 'system',
+        id: '1',
+        createAt: 1715676751919,
+        content: "Hello, I'm your AI assistant.",   
+    },
+];
+
+const roleInfo = {
+    user:  {
+        name: 'User',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/docs-icon.png'
+    },
+    assistant: {
+        name: 'Assistant',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png'
+    },
+    system: {
+        name: 'System',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png'
+    }
+}
+
+const commonOuterStyle = {
+    border: '1px solid var(--semi-color-border)',
+    borderRadius: '16px',
+    height: 500,
+};
+
+let id = 0;
+function getId() {
+    return `id-${id++}`
+}
+const uploadProps = { action: 'https://api.semi.design/upload' }
+
+const inputStyle = {   
+    display: 'flex', 
+    flexDirection: 'column', 
+    border: '1px solid var(--semi-color-border)',
+    margin: '8px 16px',
+    borderRadius: 8,
+    padding: 8
+}
+
+function CustomInputRender(props) {
+    const { defaultNode, onClear, onSend } = props;
+    const api = useRef();
+    const onSubmit = useCallback(() => {
+        if (api.current) {
+            const values = api.current.getValues();
+            if ((values.name && values.name.length !== 0) || (values.file && values.file.length !== 0)) {
+                onSend(values.name, values.file);
+                api.current.reset();
+            } 
+        }
+    }, []);
+
+    return (<div style={inputStyle}>
+        <Form
+            getFormApi={formApi => api.current = formApi}
+        >
+            <strong>输入信息</strong>
+            <Form.Input
+                field="name"
+                label="名称(Input)"
+                style={{ width: 250 }}
+                trigger='blur'
+            />
+            <Form.Upload
+                field='file'
+                label='文档'
+                action='https://api.semi.design/upload'
+            >
+                <Button icon={<IconUpload />} theme="light">
+                    点击上传
+                </Button>
+            </Form.Upload>
+        </Form>
+        <Button style={{ width: 'fit-content' }} onClick={onSubmit}>提交</Button>
+    </div>);
+}
+
+function CustomRenderInputArea() {
+    const [message, setMessage] = useState(defaultMessage);
+
+    const onChatsChange = useCallback((chats) => {
+        setMessage(chats);
+    }, []);
+
+    const onMessageSend = useCallback((content, attachment) => {
+        const newAssistantMessage = {
+            role: 'assistant',
+            id: getId(),
+            content: `This is a mock response`
+        } 
+        setTimeout(() => { 
+            setMessage((message) => ([ ...message, newAssistantMessage])); 
+        }, 200);
+    }, []);
+
+    const renderInputArea = useCallback((props) => {
+        return (<CustomInputRender {...props} />)
+    }, []);    
+
+    return (
+        <Chat
+            renderInputArea={renderInputArea}
+            style={commonOuterStyle}
+            chats={message}
+            roleConfig={roleInfo}
+            onChatsChange={onChatsChange}
+            onMessageSend={onMessageSend}
+            uploadProps={uploadProps}
+        />
+    )
+}
+render(CustomRenderInputArea);
+```
+
+### 提示信息
+
+通过 `hints` 可设置提示区域内容, 点击提示内容后,提示内容将成为新的用户输入内容,并触发 `onHintClick` 回调。
+
+```jsx live=true noInline=true dir="column"
+import React, {useState, useCallback} from 'react';
+import { Chat } from '@douyinfe/semi-ui';
+
+const defaultMessage = [
+    {
+        role: 'assistant',
+        id: '1',
+        createAt: 1715676751919,
+        content: 'Semi Design 是由抖音前端团队和MED产品设计团队设计、开发并维护的设计系统,你可以向我提问任何关于 Semi 的问题。',
+    }
+];
+
+const hintsExample = [
+    "告诉我更多",
+    "Semi Design 的组件有哪些?",
+    "我能够通过 DSM 定制自己的主题吗?",
+]
+
+const roleInfo = {
+    user:  {
+        name: 'User',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/docs-icon.png'
+    },
+    assistant: {
+        name: 'Assistant',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png'
+    },
+    system: {
+        name: 'System',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png'
+    }
+}
+
+const commonOuterStyle = {
+    border: '1px solid var(--semi-color-border)',
+    borderRadius: '16px',
+    height: 400,
+};
+
+let id = 0;
+function getId() {
+    return `id-${id++}`
+}
+const uploadProps = { action: 'https://api.semi.design/upload' }
+
+function DefaultChat() {
+    const [message, setMessage] = useState(defaultMessage);
+    const [hints, setHints] = useState(hintsExample);
+
+    const onHintClick = useCallback(() => {
+        setHints([]);
+    }, [])
+
+    const onMessageSend = useCallback((content, attachment) => {
+        const newAssistantMessage = {
+            role: 'assistant',
+            id: getId(),
+            createAt: Date.now(),
+            content: "这是一条 mock 回复信息",
+        }
+        setTimeout(() => { 
+            setMessage((message) => ([ ...message, newAssistantMessage])); 
+        }, 200);
+    }, []);
+
+    const onChatsChange = useCallback((chats) => {
+        setMessage(chats);
+    }, []);
+
+    onClear = useCallback(() => {
+        setHints([]);
+    }, [])
+
+    return (
+        <Chat 
+            hints={hints}
+            onHintClick={onHintClick}
+            style={commonOuterStyle}
+            chats={message}
+            roleConfig={roleInfo}
+            onChatsChange={onChatsChange}
+            onMessageSend={onMessageSend}
+            onClear={onClear}
+            uploadProps={uploadProps}
+        />
+    )
+}
+
+render(DefaultChat);
+```
+
+### 自定义提示信息渲染
+
+通过 `renderHintBox` 自定义提示区域内容, 参数如下
+
+```ts
+type renderHintBox = (props: {content: string; index: number,onHintClick: () => void}) => React.ReactNode;
+```
+
+使用示例如下:
+
+```jsx live=true noInline=true dir="column"
+import React, {useState, useCallback} from 'react';
+import { Chat } from '@douyinfe/semi-ui';
+
+const defaultMessage = [
+    {
+        role: 'assistant',
+        id: '1',
+        createAt: 1715676751919,
+        content: 'Semi Design 是由抖音前端团队和MED产品设计团队设计、开发并维护的设计系统,你可以向我提问任何关于 Semi 的问题。',
+    }
+];
+
+const hintsExample = [
+    "告诉我更多",
+    "Semi Design 的组件有哪些?",
+    "我能够通过 DSM 定制自己的主题吗?",
+]
+
+const roleInfo = {
+    user:  {
+        name: 'User',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/docs-icon.png'
+    },
+    assistant: {
+        name: 'Assistant',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png'
+    },
+    system: {
+        name: 'System',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png'
+    }
+}
+
+const commonOuterStyle = {
+    border: '1px solid var(--semi-color-border)',
+    borderRadius: '16px',
+    height: 400,
+};
+
+let id = 0;
+function getId() {
+    return `id-${id++}`
+}
+const uploadProps = { action: 'https://api.semi.design/upload' }
+
+function DefaultChat() {
+    const [message, setMessage] = useState(defaultMessage);
+    const [hints, setHints] = useState(hintsExample);
+
+    const onHintClick = useCallback(() => {
+        setHints([]);
+    }, [])
+
+    const onMessageSend = useCallback((content, attachment) => {
+        const newAssistantMessage = {
+            role: 'assistant',
+            id: getId(),
+            createAt: Date.now(),
+            content: "这是一条 mock 回复信息",
+        }
+        setTimeout(() => { 
+            setMessage((message) => ([ ...message, newAssistantMessage])); 
+        }, 200);
+        setHints([]);
+    }, []);
+
+    const onChatsChange = useCallback((chats) => {
+        setMessage(chats);
+    }, []);
+
+    const commonHintStyle = useMemo(() => ({
+        border: '1px solid var(--semi-color-border)',
+        padding: '10px',
+        borderRadius: '10px',
+        color: 'var( --semi-color-text-1)',
+        display: 'flex',
+        justifyContent: 'space-between',
+        alignItems: 'center',
+        cursor: 'pointer',
+        fontSize: '14px'
+    }), []);
+    
+    const renderHintBox = useCallback((props) => {
+        const { content, onHintClick, index } = props;
+        return <div style={commonHintStyle} onClick={onHintClick} key={index}>
+            {content}
+            <IconArrowRight style={{ marginLeft: 10 }}>click me</IconArrowRight>
+        </div>
+    }, []);
+
+    onClear = useCallback(() => {
+        setHints([]);
+    }, [])
+
+    return (
+        <Chat 
+            renderHintBox={renderHintBox}
+            hints={hints}
+            onHintClick={onHintClick}
+            style={commonOuterStyle}
+            chats={message}
+            roleConfig={roleInfo}
+            onChatsChange={onChatsChange}
+            onMessageSend={onMessageSend}
+            onClear={onClear}
+            uploadProps={uploadProps}
+        />
+    )
+}
+
+render(DefaultChat);
+```
+
+### API
+
+| 属性  | 说明   | 类型   | 默认值 |
+|------|--------|-------|-------|
+| align | 对话对齐方式,支持 `leftRight`、`leftAlign` | string | `leftRight` |
+| bottomSlot | 底部插槽 | React.ReactNode | - |
+| chatBoxRenderConfig | chatBox 渲染配置 | ChatBoxRenderConfig | - |
+| chats | 受控对话列表 | Message | - |
+| className | 自定义类名 | string | - |
+| customMarkDownComponents | 自定义 markdown render, 透传给对话内容渲染的 MarkdownRender | MDXProps\['components'\]| - |
+| hints | 提示信息 | string | - |
+| hintCls | 提示区最外层样式类名 | string | - |
+| hintStyle | 提示区最外层样式 | CSSProperties | - |
+| inputBoxStyle | 输入框样式 | CSSProperties | - |
+| inputBoxCls | 输入框类名 | string | - |
+| sendHotKey | 发送输入内容的键盘快捷键,支持 `enter` \| `shift+enter`。前者在单独按下 enter 将发送输入框中的消息, shift 和 enter 按键同时按下时,仅换行,不发送。后者相反 | string | `enter` |
+| mode | 对话模式,支持 `bubble` \| `noBubble` \| `userBubble`  | string | `bubble` |
+| roleConfig | 角色信息配置,具体见[RoleConfig](#RoleConfig) | RoleConfig | - |
+| renderHintBox | 自定义渲染提示信息 | (props: {content: string; index: number,onHintClick: () => void}) => React.ReactNode| - |
+| onChatsChange | 对话列表变化时触发 | (chats: Message[]) => void | - |
+| onClear | 清除上下文消息时候触发 | () => void | - |
+| onHintClick | 点击提示信息时触发 | (hint: string) => void | - |
+| onInputChange | 输入区域信息变化时触发 | (props: { value?: string, attachment?: FileItem[] }) => void; | - |
+| onMessageBadFeedback | 消息负向反馈时触发 | (message: Message) => void | - |
+| onMessageCopy | 复制消息时触发 | (message: Message) => void | - |
+| onMessageDelete | 删除消息时触发 | (message: Message) => void | - |
+| onMessageGoodFeedback | 消息正向反馈时触发 | (message: Message) => void | - |
+| onMessageReset | 重置消息时触发 | (message: Message) => void | - |
+| onMessageSend | 发送消息时触发 | (content: string, attachment?: FileItem[]) => void | - |
+| onStopGenerator | 点击停止生成按钮时触发 | (message: Message) => void | - |
+| placeholder | 输入框占位符 | string | - |
+| renderInputArea | 自定义渲染输入框 | (props: RenderInputAreaProps) => React.ReactNode | - |
+| showClearContext | 是否展示清除上下文按钮| boolean | false |
+| showStopGenerate | 是否展示停止生成按钮| boolean | false |
+| topSlot | 顶部插槽 | React.ReactNode | - |
+| uploadProps | 上传组件属性, 详情参考 [Upload](/zh-CN/input/upload#API%20%E5%8F%82%E8%80%83) | UploadProps | - |
+| uploadTipProps | 上传组件提示属性, 详情参考 [Tooltip](/zh-CN/show/tooltip#API%20%E5%8F%82%E8%80%83) | TooltipProps | - |
+
+
+#### RoleConfig
+
+| 属性  | 说明   | 类型   | 默认值 |
+|------|--------|-------|-------|
+| user | 用户信息 | Metadata | - |
+| assistant | 助手信息 | Metadata | - |
+| system | 系统信息 | Metadata | - |
+
+#### Metadata
+
+| 属性  | 说明   | 类型   | 默认值 |
+|------|--------|-------|-------|
+| name | 名称 | string | - |
+| avatar | 头像 | string | - |
+| color | 头像背景色,同 Avatar 组件的 color 参数, 支持 `amber`、 `blue`、 `cyan`、 `green`、 `grey`、 `indigo`、 `light-blue`、 `light-green`、 `lime`、 `orange`、 `pink`、 `purple`、 `red`、 `teal`、 `violet`、 `yellow` | string | `grey` |
+
+#### Message
+
+| 属性  | 说明   | 类型   | 默认值 |
+|------|--------|-------|-------|
+| role | 角色  | string | - |
+| name | 名称  | string | - |
+| id | 唯一标识  | string\| number | - |
+| content | 文本内容 | string| Content[] | - |
+| parentId | 父节点id | string | - |
+| createAt | 创建时间 | number | -|
+| status | 消息状态,可选值为 `loading` \| `incomplete` \| `complete` \| `error` | string | complete |
+
+
+#### Content
+
+| 属性  | 说明   | 类型   | 默认值 |
+|------|--------|-------|-------|
+| type | 类型, 可选值`text` \| `image_url` \| `file_url`  | string | - |
+| text | 当类型为 `text` 时的内容数据 | string | - |
+| image_url | 当类型为 `image_url` 时的内容数据 | { url: string } | - |
+| file_url | 当类型为 `file_url` 时的内容数据 | { url: string; name: string; size: string; type: string } | - |
+
+#### Methods
+
+| 方法  | 说明   |
+|------|--------|
+| resetMessage | 重置消息 |
+| scrollToBottom(animation: boolean) | 滚动到最底部, animation 为 true,则有动画,反之无动画 |
+| clearContext | 清除上下文|
+| sendMessage(content: string, attachment: FileItem[]) |发送消息 |
+
+## 设计变量
+
+<DesignToken/>
+

+ 1 - 1
content/plus/codehighlight/index-en-US.md

@@ -3,7 +3,7 @@ localeCode: en-US
 order: 0
 order: 0
 category: Plus
 category: Plus
 title:  CodeHighlight
 title:  CodeHighlight
-icon: doc-configprovider
+icon: doc-codehighlight
 dir: column
 dir: column
 brief: Highlight code blocks in the page according to syntax
 brief: Highlight code blocks in the page according to syntax
 ---
 ---

+ 1 - 1
content/plus/lottie/index-en-US.md

@@ -3,7 +3,7 @@ localeCode: en-US
 order: 23
 order: 23
 category: Plus
 category: Plus
 title: Lottie Animation
 title: Lottie Animation
-icon: doc-configprovider
+icon: doc-lottie
 dir: column
 dir: column
 brief: Display Lottie animation on the web page
 brief: Display Lottie animation on the web page
 ---
 ---

+ 1 - 1
content/plus/markdownrender/index-en-US.md

@@ -3,7 +3,7 @@ localeCode: en-US
 order: 22
 order: 22
 category: Plus
 category: Plus
 title:  Markdown Render
 title:  Markdown Render
-icon: doc-configprovider
+icon: doc-markdown
 dir: column
 dir: column
 brief: Instantly render Markdown and MDX in web pages
 brief: Instantly render Markdown and MDX in web pages
 ---
 ---

+ 1 - 1
content/show/scrolllist/index-en-US.md

@@ -18,7 +18,7 @@ import { ScrollList, ScrollItem } from '@douyinfe/semi-ui';
 ```
 ```
 ### Basic Usage
 ### Basic Usage
 
 
-The scrolling list provides a scrolling selection mode similar to the IOS operating system, while supporting scrolling to the specified window location selection and click selection.
+The scrolling list provides a scrolling selection mode similar to the iOS operating system, while supporting scrolling to the specified window location selection and click selection.
 
 
 ```jsx live=true
 ```jsx live=true
 import React from 'react';
 import React from 'react';

+ 1 - 1
content/show/sidesheet/index-en-US.md

@@ -254,7 +254,7 @@ class Demo extends React.Component {
                             initValue={'all'}
                             initValue={'all'}
                         >
                         >
                             <Radio value="all">All</Radio>
                             <Radio value="all">All</Radio>
-                            <Radio value="ios">IOS</Radio>
+                            <Radio value="ios">iOS</Radio>
                             <Radio value="android">Android</Radio>
                             <Radio value="android">Android</Radio>
                             <Radio value="web">Web</Radio>
                             <Radio value="web">Web</Radio>
                         </RadioGroup>
                         </RadioGroup>

+ 1 - 1
content/show/sidesheet/index.md

@@ -256,7 +256,7 @@ class Demo extends React.Component {
                         />
                         />
                         <RadioGroup field="type" label="目标操作系统" direction="horizontal" initValue={'all'}>
                         <RadioGroup field="type" label="目标操作系统" direction="horizontal" initValue={'all'}>
                             <Radio value="all">全平台</Radio>
                             <Radio value="all">全平台</Radio>
-                            <Radio value="ios">IOS</Radio>
+                            <Radio value="ios">iOS</Radio>
                             <Radio value="android">Android</Radio>
                             <Radio value="android">Android</Radio>
                             <Radio value="web">Web</Radio>
                             <Radio value="web">Web</Radio>
                         </RadioGroup>
                         </RadioGroup>

+ 13 - 3
content/start/changelog/index-en-US.md

@@ -16,6 +16,16 @@ Version:Major.Minor.Patch (follow the **Semver** specification)
 
 
 ---
 ---
 
 
+#### 🎉 2.63.0-beta.0 (2024-07-22)
+- 【New Component】
+    - Added `Chat` component for rendering conversation list [#2248](https://github.com/DouyinFE/semi-design/pull/2248)
+- 【Feat】
+    - Form adds stopPropagation to prevent the issue of submit and reset events triggering in multiple levels of containers at the same time in nested Form scenarios [#2355](https://github.com/DouyinFE/semi-design/issues/2355)
+    - Upload support afterUpload return url modification preview link [#2346](https://github.com/DouyinFE/semi-design/pull/2346)
+- 【Fix】
+    - Fixed Form ArrayField addWithInitValue without scope isolation for imported parameter cloning  [#2351](https://github.com/DouyinFE/semi-design/issues/2351)
+    - Fixed the problem that the width and height are constant when using renderThumbnail with the Image component in Upload  [#2343](https://github.com/DouyinFE/semi-design/issues/2343)
+
 #### 🎉 2.62.1 (2024-07-16)
 #### 🎉 2.62.1 (2024-07-16)
 - 【Fix】
 - 【Fix】
   - Fixed the issue that when TreeSelect enables showFilteredOnly and the search box is in the trigger, the treeSelect panel does not display correctly when it is opened again after searching [#2345](https://github.com/DouyinFE/semi-design/pull/2345)
   - Fixed the issue that when TreeSelect enables showFilteredOnly and the search box is in the trigger, the treeSelect panel does not display correctly when it is opened again after searching [#2345](https://github.com/DouyinFE/semi-design/pull/2345)
@@ -32,9 +42,9 @@ Version:Major.Minor.Patch (follow the **Semver** specification)
 
 
 #### 🎉 2.62.0-beta.0 (2024-07-05)
 #### 🎉 2.62.0-beta.0 (2024-07-05)
 - 【New Component】
 - 【New Component】
-    - Added new verification code input component pinCode for quickly and conveniently entering verification codes  [#2130 ](https://github.com/DouyinFE/semi-design/issues/2130)
-    - Added Lottie component for convenient rendering of Lottie animations
-    - Added CodeHighlight code highlighting component, used to highlight code displayed in web pages
+    - Added new verification code input component `pinCode` for quickly and conveniently entering verification codes  [#2130 ](https://github.com/DouyinFE/semi-design/issues/2130)
+    - Added `Lottie` component for convenient rendering of Lottie animations
+    - Added `CodeHighlight` code highlighting component, used to highlight code displayed in web pages
 - 【Feat】
 - 【Feat】
     - TreeSelect, Cascader supports closing the popup layer through the esc key
     - TreeSelect, Cascader supports closing the popup layer through the esc key
 - 【Style】
 - 【Style】

+ 11 - 0
content/start/changelog/index.md

@@ -13,6 +13,17 @@ Semi 版本号遵循 **Semver** 规范(主版本号-次版本号-修订版本
 -   修订版本号(patch):仅会进行 bugfix,发布时间不限
 -   修订版本号(patch):仅会进行 bugfix,发布时间不限
 -   不同版本间的详细关系,可查阅 [FAQ](/zh-CN/start/faq)
 -   不同版本间的详细关系,可查阅 [FAQ](/zh-CN/start/faq)
 
 
+
+#### 🎉 2.63.0-beta.0 (2024-07-22)
+- 【New Component】
+    - 新增 Chat 组件用于渲染对话列表 [#2248](https://github.com/DouyinFE/semi-design/pull/2248)
+- 【Fix】
+    - 修复 Form ArrayField addWithInitValue 时未对入参 clone做作用域隔离的问题   [#2351](https://github.com/DouyinFE/semi-design/issues/2351)
+    - 修复 Upload 使用 renderThumbnail 搭配 Image 组件使用时,宽高度恒定的问题  [#2343](https://github.com/DouyinFE/semi-design/issues/2343)
+- 【Feat】
+    - Form 新增 stopPropagation 可用于阻止嵌套Form场景下,submit 、reset事件同时在多级容器触发的问题 [#2355](https://github.com/DouyinFE/semi-design/issues/2355)
+    - Upload 支持 afterUpload 中 return url 修改预览链接 [#2346](https://github.com/DouyinFE/semi-design/pull/2346)
+
 #### 🎉 2.62.1 (2024-07-16)
 #### 🎉 2.62.1 (2024-07-16)
 - 【Fix】
 - 【Fix】
   - 修复 TreeSelect 启用 showFilteredOnly 并且搜索框在 trigger 中的 treeSelect 面板,在搜索后再次打开显示不正确问题 [#2345](https://github.com/DouyinFE/semi-design/pull/2345)
   - 修复 TreeSelect 启用 showFilteredOnly 并且搜索框在 trigger 中的 treeSelect 面板,在搜索后再次打开显示不正确问题 [#2345](https://github.com/DouyinFE/semi-design/pull/2345)

+ 2 - 1
content/start/overview/index-en-US.md

@@ -22,7 +22,8 @@ Typography
 ```overview
 ```overview
 CodeHighlight,
 CodeHighlight,
 Markdown,
 Markdown,
-Lottie
+Lottie,
+Chat
 ```
 ```
 
 
 
 

+ 2 - 1
content/start/overview/index.md

@@ -23,7 +23,8 @@ Typography 版式
 ```overview
 ```overview
 CodeHighlight 代码高亮,
 CodeHighlight 代码高亮,
 Markdown 渲染器,
 Markdown 渲染器,
-Lottie 动画
+Lottie 动画,
+Chat 聊天
 ```
 ```
 
 
 ## 输入类
 ## 输入类

+ 1 - 1
lerna.json

@@ -1,5 +1,5 @@
 {
 {
     "useWorkspaces": true,
     "useWorkspaces": true,
     "npmClient": "yarn",
     "npmClient": "yarn",
-    "version": "2.62.1"
+    "version": "2.63.0-beta.0"
 }
 }

+ 1 - 1
package.json

@@ -50,7 +50,7 @@
         "@douyinfe/semi-site-banner": "^0.1.5",
         "@douyinfe/semi-site-banner": "^0.1.5",
         "@douyinfe/semi-site-doc-style": "0.0.4",
         "@douyinfe/semi-site-doc-style": "0.0.4",
         "@douyinfe/semi-site-header": "^0.0.29",
         "@douyinfe/semi-site-header": "^0.0.29",
-        "@douyinfe/semi-site-markdown-blocks": "^0.0.17",
+        "@douyinfe/semi-site-markdown-blocks": "^0.0.18",
         "@mdx-js/mdx": "1.6.22",
         "@mdx-js/mdx": "1.6.22",
         "@mdx-js/react": "^1.6.22",
         "@mdx-js/react": "^1.6.22",
         "@storybook/react-webpack5": "^7.0.7",
         "@storybook/react-webpack5": "^7.0.7",

+ 3 - 3
packages/semi-animation-react/package.json

@@ -1,6 +1,6 @@
 {
 {
     "name": "@douyinfe/semi-animation-react",
     "name": "@douyinfe/semi-animation-react",
-    "version": "2.62.1",
+    "version": "2.63.0-beta.0",
     "description": "motion library for semi-ui-react",
     "description": "motion library for semi-ui-react",
     "keywords": [
     "keywords": [
         "motion",
         "motion",
@@ -25,8 +25,8 @@
         "prepublishOnly": "npm run build:lib"
         "prepublishOnly": "npm run build:lib"
     },
     },
     "dependencies": {
     "dependencies": {
-        "@douyinfe/semi-animation": "2.62.1",
-        "@douyinfe/semi-animation-styled": "2.62.1",
+        "@douyinfe/semi-animation": "2.63.0-beta.0",
+        "@douyinfe/semi-animation-styled": "2.63.0-beta.0",
         "classnames": "^2.2.6"
         "classnames": "^2.2.6"
     },
     },
     "devDependencies": {
     "devDependencies": {

+ 1 - 1
packages/semi-animation-styled/package.json

@@ -1,6 +1,6 @@
 {
 {
     "name": "@douyinfe/semi-animation-styled",
     "name": "@douyinfe/semi-animation-styled",
-    "version": "2.62.1",
+    "version": "2.63.0-beta.0",
     "description": "semi styled animation",
     "description": "semi styled animation",
     "keywords": [
     "keywords": [
         "semi",
         "semi",

+ 1 - 1
packages/semi-animation/package.json

@@ -1,6 +1,6 @@
 {
 {
     "name": "@douyinfe/semi-animation",
     "name": "@douyinfe/semi-animation",
-    "version": "2.62.1",
+    "version": "2.63.0-beta.0",
     "description": "animation base library for semi-ui",
     "description": "animation base library for semi-ui",
     "keywords": [
     "keywords": [
         "animation",
         "animation",

+ 1 - 1
packages/semi-eslint-plugin/package.json

@@ -1,6 +1,6 @@
 {
 {
     "name": "eslint-plugin-semi-design",
     "name": "eslint-plugin-semi-design",
-    "version": "2.62.1",
+    "version": "2.63.0-beta.0",
     "description": "semi ui eslint plugin",
     "description": "semi ui eslint plugin",
     "keywords": [
     "keywords": [
         "semi",
         "semi",

+ 598 - 0
packages/semi-foundation/chat/chat.scss

@@ -0,0 +1,598 @@
+@import './variables.scss';
+
+$module: #{$prefix}-chat;
+
+
+@mixin loading-circle-common() {
+    border-radius: 50%;
+    height: $width-chat_chatBox_loading;
+    width: $width-chat_chatBox_loading;
+    background-color: $color-chat_chatBox_loading-bg;
+}
+
+.#{$module} {
+    padding-top: $spacing-chat_paddingY;
+    padding-bottom: $spacing-chat_paddingY;
+    display: flex;
+    flex-direction: column;
+    height: 100%;
+    max-width: $width-chat_max;
+    position: relative;
+    overflow: hidden;
+
+    &-inner {
+        display: flex;
+        flex-direction: column;
+        height: 100%;
+    }
+
+    &-dropArea {
+        position: absolute;
+        top: 0;
+        left: 0;
+        right: 0;
+        bottom: 0;
+        background: $color-chat_dropArea-bg;
+        z-index: $z-chat_dropArea;
+        border: $width-chat_dropArea-border dotted $color-chat_dropArea-border;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        border-radius: $radius-chat_dropArea;
+
+        &-text {
+            font-size: $font-chat_dropArea_text;
+        }
+    }
+
+    &-content {
+        overflow: hidden;
+        flex: 1 1;
+        position: relative;
+    }
+
+    &-toast {
+        position: absolute;
+        top: 0;
+        left: 50%;
+        transform: translateX(-50%);
+    }
+
+    &-container {
+        padding-left: $spacing-chat_container-paddingX;
+        padding-right: $spacing-chat_container-paddingX;
+        height: 100%;
+        overflow: scroll;
+
+        &-scroll-hidden {
+            &::-webkit-scrollbar {
+                display: none;
+            }
+        }
+
+    }
+
+    &-action {
+        position: relative;
+        z-index: $z-chat_action;
+
+        &-content.#{$prefix}-button {
+            position: absolute;
+            bottom: $spacing-chat_action_content-bottom;
+            left: 50%;
+            transform: translateX(-50%);
+            display: flex;
+            justify-content: center;
+            align-items: center;
+            background: $color-chat_action_content-bg;
+            border: $width-chat_action_content-border solid $color-chat_action_content-border;
+        }
+
+        &-content.#{$prefix}-button-light:not(.#{$prefix}-button-disabled):hover {
+            background: $color-chat_action_content-bg-hover;
+            border: $width-chat_action_content-border solid $color-chat_action_content-border;
+        }
+
+        &-backBottom.#{$prefix}-button {
+            width: $width-chat_backBottom_wrapper;
+            height: $width-chat_backBottom_wrapper;
+            border-radius: 50%;
+        }
+
+        &-stop.#{$prefix}-button {
+            user-select: none;
+            height: $height-chat_action_stop;
+            border-radius: calc($height-chat_action_stop / 2);
+        }
+       
+    }
+
+    &-divider {
+        color: $color-chat_divider;
+        font-size: $font-chat_divider-fontSize;
+        margin-top: $spacing-chat_divider-marginY;
+        margin-bottom: $spacing-chat_divider-marginY;
+        font-weight: $font-chat_divider-fontWeight;  
+    }
+
+    &-chatBox {
+        display: flex;
+        flex-direction: row;
+        margin-top: $spacing-chat_chatBox-marginY;
+        margin-Bottom: $spacing-chat_chatBox-marginY;
+        column-gap: $spacing-chat_chatBox-columnGap;
+
+        &:hover {
+            .#{$module}-chatBox-action:not(.#{$module}-chatBox-action-hidden) {
+                visibility: visible;
+            }
+        }
+
+        &-right {
+            flex-direction: row-reverse;
+
+            .#{$module}-chatBox-wrap {
+                align-items: end;
+            }
+        }
+
+        &-avatar {
+            flex-shrink: 0;
+
+            &-hidden {
+                visibility: hidden;
+            }
+        }
+
+        &-title {
+            line-height: $font-chat_chatBox_title-lineHeight;
+            font-size: $font-chat_chatBox_title-fontSize;
+            color: $color-chat_chatBox_title;
+            font-weight: $font-chat_chatBox_title-fontWeight;
+            text-overflow: ellipsis;
+        }
+
+        &-action {
+            visibility: hidden;
+            display: flex;
+            align-items: center;
+            position: relative;
+            column-gap: $spacing-chat_chatBox_action-columnGap;
+            margin-left: $spacing-chat_chatBox_action-marginX;
+            margin-right: $spacing-chat_chatBox_action-marginX;
+
+            &-btn {
+                &.#{$prefix}-button {
+                    height: fit-content;
+                }
+                
+                &.#{$prefix}-button.#{$prefix}-button-with-icon-only {
+                    padding: $spacing-chat_chatBox_action_btn-padding;
+                }
+            }
+
+            &-icon-flip {
+                transform: scaleY(-1);
+            }
+
+            &-show {
+                visibility: visible;
+            }
+
+            &-delete-wrap {
+                display: inline-flex;
+            }
+
+            &.#{$module}-chatBox-action-hidden, &:hover.#{$module}-chatBox-action-hidden {
+                visibility: hidden;
+            }
+
+            .#{$prefix}-button-borderless:not(.#{$prefix}-button-disabled):hover {
+                background-color: $color-chat_chatBox_action-bg-hover;
+            }
+
+            .#{$prefix}-button-tertiary.#{$prefix}-button-borderless {
+                color: $color-chat_chatBox_action_icon;
+
+                &:hover {
+                    color: $color-chat_chatBox_action-icon-hover;
+                }
+            }
+        }
+
+    
+        &-wrap {
+            display: flex;
+            flex-direction: column;
+            align-items: start;
+            position: relative;
+            row-gap: $spacing-chat_chatBox_wrap;
+        }
+
+
+        &-content {
+
+            &-bubble, &-userBubble {
+                padding: $spacing-chat_chatBox_content-paddingY $spacing-chat_chatBox_content-paddingX;
+                border-radius: $radius-chat_chatBox_content;
+                background-color: $color-chat_chatBox_content_bg;
+            }
+
+            code {
+                white-space: pre-wrap;
+            }
+
+            .#{$prefix}-typography { 
+                color: $color-chat_chatBox_content_text;
+            }
+
+            .#{$module}-attachment-file {
+                background: $color-chat_chatBox_other_attachment_file-bg;
+            }
+
+            .#{$module}-attachment-file, .#{$module}-attachment-img  {
+                margin-top: $spacing-chat_chatBox_content_attachment-marginY;
+                margin-bottom: $spacing-chat_chatBox_content_attachment-marginY;
+            }
+        
+            &-user {
+                background: $color-chat_chatBox_content_user-bg;
+                color: $color-chat_chatBox_content_user-text;
+
+                .#{$module}-attachment-file {
+                    background: $color-chat_chatBox_user_attachment_file-bg;
+                }
+
+                .#{$prefix}-typography,  .#{$prefix}-typography code { 
+                    color: $color-chat_chatBox_content_user-text;
+                }
+
+                .#{$prefix}-markdownRender ul, .#{$prefix}-markdownRender li {
+                    color: $color-chat_chatBox_content_user-text;
+                }
+
+                .#{$prefix}-typography a {
+                    &, &:visited, &:hover {
+                        color: $color-chat_chatBox_content_user-text;
+                    }  
+                }
+
+            }
+
+            &-error {
+                background: $color-chat_chatBox_content_error-bg;
+                .#{$prefix}-typography { 
+                    color: $color-chat_chatBox_content_error-text;
+                }
+            }
+
+            &-loading {
+                display: flex;
+                align-items: baseline;
+
+                &-item {
+                    @include loading-circle-common();
+                    margin: $spacing-chat_chatBox_loading-item-marginY $spacing-chat_chatBox_loading-item-marginX;
+
+                    overflow: visible;
+                    position: relative;
+
+                    animation: #{$module}-loading-flashing .8s infinite alternate;
+                    animation-delay: -0.2s;
+                    animation-timing-function: ease;
+
+
+                    &::before {
+                        content: '';
+                        @include loading-circle-common();
+
+                        position: absolute;
+                        top: 0;
+                        left: -$spacing-chat_chatBox_loading_item-gap;
+
+                        animation: #{$module}-loading-flashing .8s infinite alternate;
+                        animation-timing-function: ease;
+                        animation-delay: -0.4s;  
+                    }
+
+                    &::after {
+                        content: '';
+                        @include loading-circle-common();
+                        position: absolute;
+                        top: 0;
+                        left: $spacing-chat_chatBox_loading_item-gap;
+                        
+                        animation: #{$module}-loading-flashing .8s infinite alternate;
+                        animation-delay: 0s;
+                        animation-timing-function: ease;
+                    }
+                }
+            }
+
+            pre {
+                background-color: transparent;
+            }
+
+            &-code {
+                border-radius: $radius-chat_chatBox_content_code;
+                overflow: hidden;
+
+                & .#{$prefix}-codeHighlight pre {
+                    word-break: break-all;
+                    white-space: pre-wrap;
+                }
+
+                &-topSlot {
+                    display: flex;
+                    justify-content: space-between;
+                    background-color: $color-chat_chatBox_code_topSlot-bg;
+                    align-items: center;
+                    padding: $spacing-chat_chatBox_content_code_topSlot-paddingX $spacing-chat_chatBox_content_code_topSlot-paddingY;
+                    color: $color-chat_chatBox_code_topSlot;
+                    font-size: $font-chat_chatBox_code_topSlot;
+                    
+                    &-copy {
+                        min-width: $width-chat_chatBox_content_code_topSlot_copy;
+                        display: flex;
+                        justify-content: flex-end;
+
+                        &-wrapper {
+                            display: flex;
+                            align-items: center;
+                            column-gap: $spacing-chat_chatBox_content_code_topSlot_copy-columnGap;
+                            cursor: pointer;
+                            background: transparent;
+                            border: none;
+                            color: $color-chat_chatBox_code_topSlot;
+                            line-height: $font-chat_chatBox_code_topSlot-lineHeight;
+                            padding: $spacing-chat_chatBox_content_code_topSlot_copy-padding;
+                            border-radius: $radius-chat_chatBox_content_code_topSlot_copy; 
+                        }
+                          
+                    }
+        
+                    &-toCopy {
+                        &:hover {
+                            background: $color-chat_chatBox_code_topSlot_toCopy-bg-hover;
+                        }
+                    }
+                
+                }  
+                
+                .semi-codeHighlight-defaultTheme pre[class*=language-] {
+                    margin: 0px;
+                    background: $color-chat_chatBox_code_content;
+                } 
+            }
+        }
+    }
+
+    &-inputBox {
+        padding-left: $spacing-chat_inputBox-paddingX;
+        padding-right: $spacing-chat_inputBox-paddingX;
+        padding-top: $spacing-chat_inputBox-paddingTop;
+        padding-bottom: $spacing-chat_inputBox-paddingBottom;
+
+        &-clearButton.#{$prefix}-button {
+            border-radius: 50%;
+            width: $width-chat_inputBottom_clearButton;
+            height: $width-chat_inputBottom_clearButton;
+            margin-top: $spacing-chat_inputBox-marginY;
+            margin-bottom: $spacing-chat_inputBox-marginY;
+
+            .#{$prefix}-icon {
+                font-size: $font-chat_inputBottom_clearButton_icon-fontSize;
+            }
+
+            &.#{$prefix}-button-primary.#{$prefix}-button-borderless {
+                color: $color-chat_inputBottom_clearButton_icon;
+            } 
+
+        }
+
+        &-upload {
+            .#{$prefix}-upload-file-list {
+                display: none;
+            }
+        }
+
+        &-uploadButton.#{$prefix}-button {
+            width: $width-chat_inputBottom_uploadButton;
+            height: $width-chat_inputBottom_uploadButton;
+            &.#{$prefix}-button-primary.#{$prefix}-button-borderless {
+                color: $color-chat_inputBottom_uploadButton_icon;
+            }  
+        }
+
+        &-sendButton.#{$prefix}-button{
+            width: $width-chat_inputBottom_sendButton;
+            height: $width-chat_inputBottom_sendButton;
+            &-icon {
+                transform: rotate(45deg);
+            }
+            
+            &.#{$prefix}-button-disabled.#{$prefix}-button-borderless {
+                color: $color-chat_inputBottom_sendButton_icon-disable;
+            }
+        }
+
+        &-inner {
+            display: flex;
+            flex-direction: row; 
+            align-items: flex-end;
+            column-gap: $spacing-chat_inputBox_inner-columnGap;
+        }
+
+        &-container {
+            display: flex;
+            flex-direction: row; 
+            flex-grow: 1;
+            border-radius: $radius-chat_inputBox_container;
+            padding: $spacing-chat_inputBox_container-padding;
+            border: $width-chat_inputBox_container-border solid $color-chat_inputBox_container-border;
+            align-items: end;
+        }
+            
+        &-inputArea {
+            flex-grow: 1;
+            display: flex;
+            flex-direction: column;
+        }
+        
+        &-textarea {
+            flex-grow: 1;
+
+            &.#{$prefix}-input-textarea-wrapper {
+                &, &:hover, &:active {
+                    border: none;
+                    background-color: transparent;
+                }
+            }
+        }    
+    }
+
+    &-attachment {
+        display: flex;
+        flex-direction: row;
+        flex-wrap: wrap;
+        column-gap: $spacing-chat_attachment-columnGap;
+        row-gap: $spacing-chat_attachment-RowGap;
+
+        &-item {
+            position: relative;
+
+            &:hover {
+                .#{$module}-inputBox-attachment-clear {
+                    visibility: visible;
+                }
+            }
+        }
+
+        &-img {
+            border-radius: $radius-chat_attachment_img;
+            vertical-align: top;
+        }
+
+        a {
+            text-decoration: none;
+            color: inherit;
+        }
+
+        &-clear {
+            position: absolute;
+            top: -1 * $spacing-chat_attachment_clear-top;
+            right: -1 * $spacing-chat_attachment_clear-right;
+            color: $color-chat_attachment_clear_icon;
+        }
+
+        &-process.#{$prefix}-progress-circle {
+            position: absolute;
+            top: 50%;
+            left: 50%;
+            transform: translate(-50%, -50%);
+        }
+
+        &-file {
+            display: flex;
+            flex-direction: row;
+            align-items: center;
+            height: $width-chat_attachment_file;
+            column-gap: $spacing-chat_attachment_file-columnGap;
+            padding: $spacing-chat_attachment_file-padding;
+            border-radius: $radius-chat_attachment_file;
+            background: $color-chat_attachment_file-bg;
+            text-decoration: none;
+    
+            &-icon {
+                color: $color-chat_attachment_file_icon;
+            }
+
+            &-info {
+                display: flex;
+                flex-direction: column;
+            }
+
+            &-title {
+                font-size: $font-chat_attachment_file_title-fontSize;
+                color: $color-chat_attachment_file_title;
+                max-width: $width-chat_attachment_file_title;
+                text-overflow: ellipsis;
+                overflow: hidden;
+            }
+
+            &-metadata {
+                font-size: $font-chat_attachment_file_metadata-fontSize;
+                color: $color-chat_attachment_file_metadata_text;
+            }
+
+            &-type {
+                text-transform: uppercase;
+            }
+        }
+
+    }
+
+    .#{$prefix}-typography a.#{$module}-attachment-file {
+        display: flex;
+
+        .#{$module}-attachment-file-title {
+            color: $color-chat_attachment_file_title;
+        }
+        
+    }
+
+    &-hints {
+        display: flex;
+        flex-direction: column;
+        row-gap: $spacing-chat_hint-rowGap;
+        margin-top: $spacing-chat_hint-marginY; 
+        margin-bottom: $spacing-chat_hint-marginY;
+        margin-left: $spacing-chat_hint-marginLeft;
+    }
+
+    &-hint {
+        &-item {
+            cursor: pointer;
+            display: flex;
+            flex-direction: row;
+            column-gap: $spacing-chat_hint_item-columnGap;
+            width: fit-content;
+            // justify-content: space-between;
+            background: $color-chat_hint_item-bg;
+            align-items: center;
+            border: $width-chat_hint_item-border solid $color-chat_hint_item-border;
+            padding: $spacing-chat_hint_item-marginY $spacing-chat_hint_item-marginX;
+            border-radius: $radius-chat_hint_item;
+
+            &:hover {
+                background-color: $color-chat_hint_item-bg-hover;
+            }
+        }
+
+        &-content {
+            font-size: $font-chat_hint_content-fontSize;
+            color: $color-chat_hint_content_text;
+        }
+
+        &-icon {
+            // font-size: $font-chat_hint_icon;
+            color: $color-chat_hint_icon;
+        }
+       
+    }
+}
+
+@keyframes #{$module}-loading-flashing {
+    0% {
+        opacity: 1;;
+    }
+    50% {
+        opacity: 0.1;
+    }
+    to {
+        opacity: 1;
+    }
+}
+
+
+@import './rtl.scss';

+ 64 - 0
packages/semi-foundation/chat/chatBoxActionFoundation.ts

@@ -0,0 +1,64 @@
+import BaseFoundation, { DefaultAdapter } from "../base/foundation";
+
+export interface ChatBoxActionAdapter<P = Record<string, any>, S = Record<string, any>> extends DefaultAdapter<P, S> {
+    notifyDeleteMessage: () => void;
+    notifyMessageCopy: () => void;
+    copyToClipboardAndToast: () => void;
+    notifyLikeMessage: () => void;
+    notifyDislikeMessage: () => void;
+    notifyResetMessage: () => void;
+    setVisible: (visible: boolean) => void;
+    setShowAction: (showAction: boolean) => void;
+    registerClickOutsideHandler(...args: any[]): void;
+    unregisterClickOutsideHandler(...args: any[]): void
+}
+
+export default class ChatBoxActionFoundation <P = Record<string, any>, S = Record<string, any>> extends BaseFoundation<ChatBoxActionAdapter<P, S>, P, S> {
+    constructor(adapter: ChatBoxActionAdapter<P, S>) {
+        super({ ...adapter });
+    }
+
+    showDeletePopup = () => {
+        this._adapter.setVisible(true);
+        this._adapter.setShowAction(true);
+        this._adapter.registerClickOutsideHandler(this.hideDeletePopup);
+    }
+
+    hideDeletePopup = () => {
+        /** visible 控制 popConfirm 的显隐
+         * showAction 控制在 popConfirm 显示时候,保证操作区显示
+         * 需要有时间间隔,用 visible 直接控制的话,在 popconfirm 通过取消按钮关闭时会导致操作区显示闪动
+        */ 
+        this._adapter.setVisible(false);
+        setTimeout(() => {
+            this._adapter.setShowAction(false);
+        }, 150);
+        this._adapter.unregisterClickOutsideHandler();
+    }
+
+    destroy = () => {
+        this._adapter.unregisterClickOutsideHandler();
+    }
+
+    deleteMessage = () => {
+        this._adapter.notifyDeleteMessage();
+    }
+
+    copyMessage = () => {
+        this._adapter.notifyMessageCopy();
+        this._adapter.copyToClipboardAndToast(); 
+    }
+
+    likeMessage = () => {
+        this._adapter.notifyLikeMessage();
+    }
+
+    dislikeMessage = () => {
+        this._adapter.notifyDislikeMessage();
+    }
+
+    resetMessage = () => {
+        this._adapter.notifyResetMessage();
+    }
+
+}

+ 68 - 0
packages/semi-foundation/chat/constants.ts

@@ -0,0 +1,68 @@
+import {
+    BASE_CLASS_PREFIX
+} from '../base/constants';
+
+const cssClasses = {
+    PREFIX: `${BASE_CLASS_PREFIX}-chat`,
+    PREFIX_DIVIDER: `${BASE_CLASS_PREFIX}-chat-divider`,
+    PREFIX_CHAT_BOX: `${BASE_CLASS_PREFIX}-chat-chatBox`,
+    PREFIX_CHAT_BOX_ACTION: `${BASE_CLASS_PREFIX}-chat-chatBox-action`,
+    PREFIX_INPUT_BOX: `${BASE_CLASS_PREFIX}-chat-inputBox`,
+    PREFIX_ATTACHMENT: `${BASE_CLASS_PREFIX}-chat-attachment`,
+    PREFIX_HINT: `${BASE_CLASS_PREFIX}-chat-hint`,
+};
+
+const ROLE = {
+    USER: 'user',
+    ASSISTANT: 'assistant',
+    SYSTEM: 'system',
+    DIVIDER: 'divider',
+};
+
+const CHAT_ALIGN = {
+    LEFT_RIGHT: 'leftRight',
+    LEFT_ALIGN: 'leftAlign',
+};
+
+const MESSAGE_STATUS = {
+    LOADING: 'loading',
+    INCOMPLETE: 'incomplete',
+    COMPLETE: 'complete',
+    ERROR: 'error'
+};
+
+const PIC_SUFFIX_ARRAY = ['png', 'jpg', 'jpeg', 'gif', 'bmp', 'webp'];
+
+const PIC_PREFIX = 'image/';
+
+const SCROLL_ANIMATION_TIME = 300;
+const SHOW_SCROLL_GAP = 100;
+
+const MODE = {
+    BUBBLE: 'bubble',
+    NO_BUBBLE: 'noBubble',
+    USER_BUBBLE: 'userBubble'
+};
+
+const SEND_HOT_KEY = {
+    ENTER: 'enter',
+    SHIFT_PLUS_ENTER: 'shift+enter'
+};
+
+const strings = {
+    ROLE,
+    CHAT_ALIGN,
+    MESSAGE_STATUS,
+    PIC_SUFFIX_ARRAY,
+    PIC_PREFIX,
+    SCROLL_ANIMATION_TIME,
+    SHOW_SCROLL_GAP,
+    MODE,
+    SEND_HOT_KEY,
+};
+
+
+export {
+    cssClasses,
+    strings,
+};

+ 306 - 0
packages/semi-foundation/chat/foundation.ts

@@ -0,0 +1,306 @@
+import BaseFoundation, { DefaultAdapter } from "../base/foundation";
+import { strings } from "./constants";
+import { Animation } from '@douyinfe/semi-animation';
+import { debounce } from "lodash";
+import { getUuidv4 } from "../utils/uuid";
+import { handlePrevent } from "../utils/a11y";
+
+const { PIC_PREFIX, PIC_SUFFIX_ARRAY, ROLE, 
+    SCROLL_ANIMATION_TIME, SHOW_SCROLL_GAP
+} = strings;
+
+export interface Content {
+    type: 'text' | 'image_url' | 'file_url';
+    text?: string;
+    image_url?: { 
+        url: string;
+        [x: string]: any
+    };
+    file_url?: {
+        url: string;
+        name: string;
+        size: string;
+        type: string;
+        [x: string]: any
+    }
+}
+
+export interface Message {
+    role?: string;
+    name?: string;
+    id?: string;
+    content?: string | Content[];
+    parentId?: string;
+    createAt?: number;
+    status?: 'loading' | 'incomplete' | 'complete' | 'error';
+    [x: string]: any
+}
+
+export interface ChatAdapter<P = Record<string, any>, S = Record<string, any>> extends DefaultAdapter<P, S> {
+    getContainerRef: () => React.RefObject<HTMLDivElement>;
+    setWheelScroll: (flag: boolean) => void;
+    notifyChatsChange: (chats: Message[]) => void;
+    notifyLikeMessage: (message: Message) => void;
+    notifyDislikeMessage: (message: Message) => void;
+    notifyCopyMessage: (message: Message) => void;
+    notifyClearContext: () => void;
+    notifyMessageSend: (content: string, attachment: any[]) => void;
+    notifyInputChange: (props: { inputValue: string; attachment: any[]}) => void;
+    setBackBottomVisible: (visible: boolean) => void;
+    registerWheelEvent: () => void;
+    unRegisterWheelEvent: () => void;
+    notifyStopGenerate: (e: any) => void;
+    notifyHintClick: (hint: string) => void;
+    setUploadAreaVisible: (visible: boolean) => void;
+    manualUpload: (e: any) => void;
+    getDropAreaElement: () => HTMLDivElement
+}
+
+
+export default class ChatFoundation <P = Record<string, any>, S = Record<string, any>> extends BaseFoundation<ChatAdapter<P, S>, P, S> {
+
+    animation: any;
+
+    constructor(adapter: ChatAdapter<P, S>) {
+        super({ ...adapter });
+    }
+
+    init = () => {
+        this.scrollToBottomImmediately();
+        this._adapter.registerWheelEvent();
+    }
+
+    destroy = () => {
+        this.animation && this.animation.destroy();
+        this._adapter.unRegisterWheelEvent();
+    }
+
+    stopGenerate = (e: any) => {
+        this._adapter.notifyStopGenerate(e);
+    }
+
+    scrollToBottomImmediately = () => {
+        const containerRef = this._adapter.getContainerRef();
+        const element = containerRef?.current;
+        if (element) {
+            element.scrollTop = element.scrollHeight;
+        } 
+    }
+
+    scrollToBottomWithAnimation = () => {
+        const duration = SCROLL_ANIMATION_TIME;
+        const containerRef = this._adapter.getContainerRef();
+        const element = containerRef?.current;
+        if (!element) {
+            return;
+        }
+        const from = element.scrollTop;
+        const to = element.scrollHeight;
+        this.animation = new Animation(
+            {
+                from: { scrollTop: from },
+                to: { scrollTop: to },
+            },
+            {
+                duration,
+                easing: 'easeInOutCubic'
+            }
+        );
+    
+        this.animation.on('frame', ({ scrollTop }: { scrollTop: number }) => {
+            element.scrollTop = scrollTop;
+        });
+    
+        this.animation.start();
+    }
+
+    containerScroll = (e: any) => {
+        if (e.target !== e.currentTarget) {
+            return;
+        }
+        e.persist();
+        const update = () => {
+            this.getScroll(e.target);
+        };
+        requestAnimationFrame(update);
+    }
+
+    getScroll = debounce((target: any) => {
+        const scrollHeight = target.scrollHeight;
+        const clientHeight = target.clientHeight;
+        const scrollTop = target.scrollTop;
+        const { backBottomVisible } = this.getStates();
+        if (scrollHeight - scrollTop - clientHeight <= SHOW_SCROLL_GAP) {
+            if (backBottomVisible) {
+                this._adapter.setBackBottomVisible(false);
+            }
+        } else {
+            if (!backBottomVisible) {
+                this._adapter.setBackBottomVisible(true);
+            }
+        }
+        return scroll;
+    }, 100)
+
+    clearContext = (e: any) => {
+        const { chats } = this.getStates();
+        if (chats[chats.length - 1].role === ROLE.DIVIDER) {
+            return;
+        }
+        const dividerMessage = {
+            role: ROLE.DIVIDER,
+            id: getUuidv4(),
+            createAt: Date.now(),
+        };
+        const newChats = [...chats, dividerMessage];
+        this._adapter.notifyChatsChange(newChats);
+        this._adapter.notifyClearContext();
+    } 
+
+    onMessageSend = (input: string, attachment: any[]) => {
+        let content;
+        if (Boolean(attachment) && attachment.length === 0) {
+            content = input;
+        } else {
+            content = [];
+            input && content.push({ type: 'text', text: input });
+            (attachment ?? []).map(item => {
+                const { fileInstance, name = '', url, size } = item;
+                const suffix = name.split('.').pop();
+                const isImg = fileInstance?.type?.startsWith(PIC_PREFIX) || PIC_SUFFIX_ARRAY.includes(suffix);
+                if (isImg) {
+                    content.push({ 
+                        type: 'image_url', 
+                        image_url: { url: url } 
+                    });
+                } else {
+                    content.push({ 
+                        type: 'file_url', 
+                        file_url: {
+                            url: url,
+                            name: name,
+                            size: size,
+                            type: fileInstance?.type
+                        }
+                    });
+                }
+            });
+        }
+        if (content) {
+            const newMessage = {
+                role: ROLE.USER,
+                id: getUuidv4(),
+                createAt: Date.now(),
+                content,
+            };
+            this._adapter.notifyChatsChange([...this.getStates().chats, newMessage]);
+        }
+        this._adapter.setWheelScroll(false);
+        this._adapter.registerWheelEvent();
+        this._adapter.notifyMessageSend(input, attachment);
+    }
+
+    onHintClick = (hint: string) => {
+        const { chats } = this.getStates();
+        const newMessage = {
+            role: ROLE.USER,
+            id: getUuidv4(),
+            createAt: Date.now(),
+            content: hint,
+        };
+        const newChats = [...chats, newMessage];
+        this._adapter.notifyChatsChange(newChats);
+        this._adapter.notifyHintClick(hint);
+    }
+
+    onInputChange = (props: { inputValue: string; attachment: any[]}) => {
+        this._adapter.notifyInputChange(props as any);
+    }
+
+    deleteMessage = (message: Message) => {
+        const { onMessageDelete, onChatsChange } = this.getProps();
+        const { chats } = this.getStates();
+        onMessageDelete?.(message);
+        const newChats = chats.filter(item => item.id !== message.id);
+        onChatsChange?.(newChats);
+    }
+
+    likeMessage = (message: Message) => {
+        const { chats } = this.getStates();
+        this._adapter.notifyLikeMessage(message);
+        const index = chats.findIndex(item => item.id === message.id);
+        const newChat = {
+            ...chats[index],
+            like: !chats[index].like,
+            dislike: false,
+        };
+        const newChats = [...chats];
+        newChats.splice(index, 1, newChat);
+        this._adapter.notifyChatsChange(newChats);
+    }
+  
+    dislikeMessage = (message: Message) => {
+        const { chats } = this.getStates();
+        this._adapter.notifyDislikeMessage(message);
+        const index = chats.findIndex(item => item.id === message.id);
+        const newChat = {
+            ...chats[index],
+            like: false,
+            dislike: !chats[index].dislike,
+        };
+        const newChats = [...chats];
+        newChats.splice(index, 1, newChat);
+        this._adapter.notifyChatsChange(newChats);
+    }
+  
+    resetMessage = (message: Message) => {
+        const { chats } = this.getStates();
+        const lastMessage = chats[chats.length - 1];
+        const newLastChat = {
+            ...lastMessage,
+            status: 'loading',
+            content: '',
+            id: getUuidv4(),
+            createAt: Date.now(),
+        };
+        const newChats = chats.slice(0, -1).concat(newLastChat);
+        this._adapter.notifyChatsChange(newChats);
+        const { onMessageReset } = this.getProps();
+        onMessageReset?.(message);
+    }
+
+    handleDragOver = (e: any) => {
+        this._adapter.setUploadAreaVisible(true);
+    }
+
+    handleContainerDragOver = (e: any) => {
+        handlePrevent(e);
+    }
+
+    handleContainerDrop = (e) => {
+        this._adapter.setUploadAreaVisible(false);
+        this._adapter.manualUpload(e?.dataTransfer?.files);
+        // 禁用默认实现,防止文件被打开
+        //Disable the default implementation, preventing files from being opened
+        handlePrevent(e);
+    }
+    
+    handleContainerDragLeave = (e: any) => {
+        handlePrevent(e);
+        // 鼠标移动至 container 的子元素,则不做任何操作
+        // If the mouse moves to the child element of container, no operation will be performed.
+        const dropAreaElement = this._adapter.getDropAreaElement();
+        if (dropAreaElement !== e.target && dropAreaElement.contains(e.target)) {
+            return;
+        }
+        /**
+         * 延迟隐藏 container ,防止父元素的 mouseOver 被触发,导致 container 无法隐藏
+         * Delay hiding of the container to prevent the parent element's mouseOver from being triggered, 
+         * causing the container to be unable to be hidden.
+        */
+        setTimeout(() => {
+            this._adapter.setUploadAreaVisible(false);
+        });
+    }
+}
+

+ 98 - 0
packages/semi-foundation/chat/inputboxFoundation.ts

@@ -0,0 +1,98 @@
+import { handlePrevent } from "../utils/a11y";
+import BaseFoundation, { DefaultAdapter } from "../base/foundation";
+import { strings } from './constants';
+
+const { SEND_HOT_KEY } = strings;
+
+export interface InputBoxAdapter<P = Record<string, any>, S = Record<string, any>> extends DefaultAdapter<P, S> {
+    notifyInputChange: (props: { inputValue: string; attachment: any[]}) => void;
+    setInputValue: (value: string) => void;
+    setAttachment: (attachment: any[]) => void;
+    notifySend: (content: string, attachment: any[]) => void
+}
+
+export default class InputBoxFoundation <P = Record<string, any>, S = Record<string, any>> extends BaseFoundation<InputBoxAdapter<P, S>, P, S> {
+    constructor(adapter: InputBoxAdapter<P, S>) {
+        super({ ...adapter });
+    }
+
+    onInputAreaChange = (value: string) => {
+        const attachment = this.getState('attachment');
+        this._adapter.setInputValue(value);
+        this._adapter.notifyInputChange({ inputValue: value, attachment });
+    }
+
+    onAttachmentAdd = (props: any) => {
+        const { fileList } = props;
+        const { uploadProps } = this.getProps();
+        const { onChange } = uploadProps;
+        if (onChange) {
+            onChange(props);
+        }
+        const { content } = this.getStates();
+        let newFileList = [...fileList];
+        this._adapter.setAttachment(newFileList);
+        this._adapter.notifyInputChange({
+            inputValue: content,
+            attachment: newFileList
+        });
+    }
+    
+    onAttachmentDelete = (props: any) => {
+        const { content, attachment } = this.getStates();
+        const newAttachMent = attachment.filter(item => item.uid !== props.uid);
+        this._adapter.setAttachment(newAttachMent);
+        this._adapter.notifyInputChange({
+            inputValue: content,
+            attachment: newAttachMent
+        });
+    }
+    
+    onSend = (e: any) => {
+        if (this.getDisableSend()) {
+            return; 
+        }
+        const { content, attachment } = this.getStates();
+        this._adapter.setInputValue('');
+        this._adapter.setAttachment([]);
+        this._adapter.notifySend(content, attachment);
+    }
+
+    getDisableSend = () => {
+        const { content, attachment } = this.getStates();
+        const { disableSend: disableSendInProps } = this.getProps();
+        const disabledSend = disableSendInProps || (content.length === 0 && attachment.length === 0);
+        return disabledSend;
+    }
+
+    onEnterPress = (e: any) => {
+        const { sendHotKey } = this.getProps();
+        if (sendHotKey === SEND_HOT_KEY.SHIFT_PLUS_ENTER && e.shiftKey === false) {
+            return ;
+        } else if (sendHotKey === SEND_HOT_KEY.ENTER && e.shiftKey === true) {
+            return ;
+        }
+        handlePrevent(e);
+        this.onSend(e);
+    };
+
+    onPaste = (e: any) => {
+        const items = e.clipboardData?.items;
+        const { manualUpload } = this.getProps();
+        let files = [];
+        if (items) {
+            for (const it of items) {
+                const file = it.getAsFile();
+                file && files.push(it.getAsFile());
+            }
+            if (files.length) {
+                // 文件上传,则需要阻止默认粘贴行为
+                // File upload, you need to prevent the default paste behavior
+                manualUpload(files);
+                e.preventDefault();
+                e.stopPropagation();
+            }
+        }
+    }
+
+}

+ 22 - 0
packages/semi-foundation/chat/rtl.scss

@@ -0,0 +1,22 @@
+
+$module: #{$prefix}-chat;
+
+.#{$prefix}-rtl,
+.#{$prefix}-portal-rtl {
+    .#{$module} {
+        direction: rtl;
+
+        &-hint-icon {
+            transform: scaleX(-1);
+        }
+
+        &-inputBox-sendButton-icon {
+            transform: rotate(225deg);
+        }
+
+        &-chatBox-action-icon-redo {
+            transform: scaleX(-1);
+        } 
+    }
+   
+}

+ 125 - 0
packages/semi-foundation/chat/variables.scss

@@ -0,0 +1,125 @@
+// radius
+$radius-chat_chatBox_content: var(--semi-border-radius-large);  // 聊天框内容圆角
+$radius-chat_inputBox_container: 16px; // 输入框容器圆角
+$radius-chat_attachment_img: var(--semi-border-radius-medium); // 附件图片圆角
+$radius-chat_attachment_file: var(--semi-border-radius-medium); // 附件文件圆角
+$radius-chat_hint_item: var(--semi-border-radius-large); // 提示条圆角
+$radius-chat_chatBox_content_code: var(--semi-border-radius-large); // 代码块圆角
+$radius-chat_chatBox_content_code_topSlot_copy: var(--semi-border-radius-large); // 代码块顶部复制按钮圆角
+$radius-chat_dropArea: 16px; // 拖拽上传区域圆角
+
+//color
+$color-chat_action_content-bg: var(--semi-color-bg-0); // 返回按钮/停止生成内容按钮背景颜色
+$color-chat_action_content-border: var(--semi-color-border); // 返回按钮/停止生成按钮描边颜色
+$color-chat_divider: var(--semi-color-text-2); // 分割线颜色
+$color-chat_chatBox_title: var(--semi-color-text-0); //聊天框标题颜色
+$color-chat_chatBox_action_icon: var(--semi-color-text-2); // 聊天框操作区域按钮图标颜色
+$color-chat_chatBox_action_icon-hover: var(--semi-color-text-0); // 聊天框操作区域按钮图标hover颜色
+$color-chat_chatBox_action-bg-hover: transparent; // 聊天框操作区域按钮hover背景颜色
+$color-chat_chatBox_content_text: var(--semi-color-text-0); // 聊天框内容文字颜色
+$color-chat_chatBox_content_bg: var(--semi-color-fill-0); // 聊天框内容背景颜色
+$color-chat_chatBox_content_user-bg: var(--semi-color-primary); // 聊天框内容用户背景颜色
+$color-chat_chatBox_content_user-text: var(--semi-color-white); // 聊天框内容用户文字颜色
+$color-chat_chatBox_content_error-bg: var(--semi-color-danger-hover); // 聊天框内容错误背景颜色
+$color-chat_chatBox_content_error-text: var(--semi-color-white); // 聊天框内容错误文字颜色
+$color-chat_inputBottom_clearButton_icon: var(--semi-color-text-2); //清空按钮图标颜色
+$color-chat_inputBottom_uploadButton_icon: var(--semi-color-text-0); // 上传按钮图标颜色
+$color-chat_inputBottom_sendButton_icon-disable: var(--semi-color-primary-disabled); // 发送按钮禁用态图标颜色
+$color-chat_inputBox_container-border: var(--semi-color-border);  // 输入框容器边框颜色
+$color-chat_attachment_clear_icon: var(--semi-color-text-2);  // 附件清除图标颜色
+$color-chat_attachment_file-bg: var(--semi-color-fill-0); // 附件文件背景颜色
+$color-chat_chatBox_user_attachment_file-bg: var(--semi-color-bg-0); // 用户聊天框附件文件背景颜色
+$color-chat_chatBox_other_attachment_file-bg: var(--semi-color-fill-2); // 聊天框附件文件背景颜色
+$color-chat_attachment_file_icon: var(--semi-color-text-2); // 附件文件图标颜色
+$color-chat_attachment_file_title: var(--semi-color-text-0); // 附件文件标题颜色
+$color-chat_attachment_file_metadata_text: var(--semi-color-text-2); // 附件文件元数据文字颜色
+$color-chat_hint_item-border: var(--semi-color-border); // 提示条边框颜色
+$color-chat_hint_item-bg: transparent; // 提示条背景颜色
+$color-chat_hint_item-bg-hover: var(--semi-color-fill-0); // 提示条hover背景颜色
+$color-chat_hint_content_text: var(--semi-color-text-1); // 提示条文字颜色
+$color-chat_hint_icon: var(--semi-color-text-2); // 提示条图标颜色
+$color-chat_chatBox_loading-bg: var(--semi-color-text-0); // 聊天内容加载图标圆圈颜色
+$color-chat_chatBox_code_topSlot: rgba(var(--semi-white), 1);  // 代码块顶部字体颜色
+$color-chat_chatBox_code_topSlot-bg: rgba(var(--semi-grey-4), 1); //代码块顶部背景色
+$color-chat_chatBox_code_topSlot_toCopy-bg-hover: rgba(var(--semi-grey-5), 1); // 代码块顶部复制按钮hover背景色
+$color-chat_chatBox_code_content: var(--semi-color-bg-0); // 代码块内容背景色
+$color-chat_action_content-bg-hover: var(--semi-color-tertiary-light-hover); // 返回按钮/停止生成按钮hover背景颜色
+$color-chat_dropArea-bg: rgba(var(--semi-grey-2), 0.9); // 拖拽区域文字颜色
+$color-chat_dropArea-border: var(--semi-color-border); // 拖拽区域边框颜色
+
+// spacing
+$spacing-chat_paddingY: 12px; // chat组件上下内边距
+$spacing-chat_container-paddingX: 16px;  // 消息框水平内边距
+$spacing-chat_action_content-bottom: 0; // 返回按钮/停止生成按钮底部边距
+$spacing-chat_chatBox-marginY: 8px; // 聊天框上下外边距
+$spacing-chat_chatBox-columnGap: 12px; // 聊天框内容列间距
+$spacing-chat_chatBox_action-columnGap: 10px; // 聊天框操作区域按钮列间距
+$spacing-chat_chatBox_action-marginX: 10px; // 聊天框操作区域左右外边距
+$spacing-chat_chatBox_action_btn-padding: 0;  // 聊天框操作区域按钮内边距
+$spacing-chat_chatBox_content-paddingY: 8px;  // 聊天框内容上下内边距
+$spacing-chat_chatBox_content-paddingX: 12px;  // 聊天框内容左右内边距
+$spacing-chat_inputBox-paddingTop: 8px; // 输入框顶部内边距
+$spacing-chat_inputBox-paddingBottom: 8px; // 输入框底部内边距
+$spacing-chat_inputBox-paddingX: 16px; // 输入框左右内边距
+$spacing-chat_inputBox_container-padding: 11px; // 输入框容器内边距
+$spacing-chat_inputBox_inner-columnGap: 4px; // 输入框容器列间距
+// $spacing-chat_inputBox_textarea-marginX: 5px; // 输入框textArea左右内边距
+$spacing-chat_inputBox-marginY: 4px;
+$spacing-chat_attachment-columnGap: 10px; // 附件列间距
+$spacing-chat_attachment-RowGap: 5px; // 附件行间距
+$spacing-chat_attachment_clear-top: 8px;  // 附件清除图标顶部间距
+$spacing-chat_attachment_clear-right: 8px;  // 附件清除图标右内边距
+$spacing-chat_attachment_file-columnGap: 5px; // 文件附件列间距
+$spacing-chat_attachment_file-padding: 5px;  // 文件附件内边距
+$spacing-chat_chatBox_loading_item-gap: 15px; // 聊天内容加载图标间距 
+$spacing-chat_divider-marginY: 12px; // 分割线上下外边距
+$spacing-chat_chatBox_content_attachment-marginY: 4px; // 聊天框内容文件/图片上下外间距
+$spacing-chat_chatBox_content_code_topSlot-paddingX: 5px; // 聊天框代码块顶部上下内边距
+$spacing-chat_chatBox_content_code_topSlot-paddingY: 8px; // 聊天框代码块顶部左右内边距
+$spacing-chat_chatBox_content_code_topSlot_copy-columnGap: 5px; // 聊天框代码块顶部复制按钮列间距: 
+$spacing-chat_chatBox_content_code_topSlot_copy-padding: 5px; // 聊天框代码块顶部复制按钮列间距: 
+$spacing-chat_chatBox_wrap: 8px; // 聊天框外层间距
+$spacing-chat_hint-rowGap: 10px; // 提示条行间距
+$spacing-chat_hint-marginY: 12px; // 提示条容器上下外边距
+$spacing-chat_hint-marginLeft: 34px; // 提示条容器左外边距    
+$spacing-chat_hint_item-marginY: 8px; // 提示条上下外边距
+$spacing-chat_hint_item-marginX: 12px; // 提示条左右外边距
+$spacing-chat_hint_item-columnGap: 20px; // 提示条内容列间距
+$spacing-chat_chatBox_loading-item-marginX: 18px; // 聊天内容加载图标中心圆圈左右外边距
+$spacing-chat_chatBox_loading-item-marginY: 6px; // 聊天内容加载图标中心圆圈上下外边距
+
+// width
+$width-chat_backBottom_wrapper: 42px; // 返回按钮宽度
+$width-chat_action_content-border: 1px; // 返回按钮/停止生成按钮描边宽度
+$width-chat_inputBottom_clearButton: 48px; // 清空按钮宽度
+$width-chat_inputBottom_uploadButton: 32px; // 上传按钮宽度
+$width-chat_inputBottom_sendButton: 32px; // 发送按钮宽度
+$width-chat_inputBox_container-border: 1px; // 输入框容器边框宽度
+$width-chat_attachment_file: 50px; // 附件文件宽度
+$width-chat_hint_item-border: 1px; // 提示条边框宽度
+$width-chat_chatBox_loading: 8px; // 加载中单个圆圈图标宽度
+$width-chat_attachment_file_title: 90px; // 附件文件标题最大宽度
+$width-chat_max: 800px; // chat组件最大宽度
+$width-chat_dropArea-border: 5px; // 拖拽上传边框宽度
+$width-chat_chatBox_content_code_topSlot_copy: 150px; // 聊天框代码块顶部复制按钮最小宽度 
+// height
+$height-chat_action_stop: 42px; //停止生成按钮高度
+
+//font
+$font-chat_divider-fontWeight: $font-weight-regular; // 分割线字重
+$font-chat_divider-fontSize: $font-size-small; // 分割线字体大小
+$font-chat_chatBox_title-lineHeight: 20px; //聊天框标题行高
+$font-chat_chatBox_title-fontSize: $font-size-header-6; // 聊天框标题字体大小
+$font-chat_chatBox_title-fontWeight: $font-weight-regular; // 聊天框标题字重
+$font-chat_inputBottom_clearButton_icon-fontSize: 30px; // 输入区清空上下文按钮图标大小
+$font-chat_attachment_file_title-fontSize: $font-size-header-6; // 附件文件标题字体大小
+$font-chat_attachment_file_metadata-fontSize: $font-size-regular; // 附件文件元数据字体大小
+$font-chat_hint_content-fontSize: $font-size-regular; // 提示条文字大小
+// $font-chat_hint_icon: 20px; // 提示条图标大小
+$font-chat_chatBox_code_topSlot: 12px; // 代码块顶部字体大小
+$font-chat_chatBox_code_topSlot-lineHeight: 16px; //代码块顶部区域字体行高
+$font-chat_dropArea_text: 48px; // 拖拽上传区域文字大小
+
+//z-index
+$z-chat_dropArea: 10; // 拖拽上传区域z-index
+$z-chat_action: 1; // 返回按钮/停止生成按钮z-index

+ 5 - 0
packages/semi-foundation/input/textareaFoundation.ts

@@ -171,6 +171,11 @@ export default class TextAreaFoundation extends BaseFoundation<TextAreaAdapter>
     }
     }
 
 
     handleKeyDown(e: any) {
     handleKeyDown(e: any) {
+        const { disabledEnterStartNewLine } = this.getProps();
+        if (disabledEnterStartNewLine && e.key === 'Enter' && !e.shiftKey) {
+            // Prevent default line wrapping behavior
+            e.preventDefault(); 
+        }
         this._adapter.notifyKeyDown(e);
         this._adapter.notifyKeyDown(e);
         if (e.keyCode === 13) {
         if (e.keyCode === 13) {
             this._adapter.notifyPressEnter(e);
             this._adapter.notifyPressEnter(e);

+ 2 - 2
packages/semi-foundation/package.json

@@ -1,13 +1,13 @@
 {
 {
     "name": "@douyinfe/semi-foundation",
     "name": "@douyinfe/semi-foundation",
-    "version": "2.62.1",
+    "version": "2.63.0-beta.0",
     "description": "",
     "description": "",
     "scripts": {
     "scripts": {
         "build:lib": "node ./scripts/compileLib.js",
         "build:lib": "node ./scripts/compileLib.js",
         "prepublishOnly": "npm run build:lib"
         "prepublishOnly": "npm run build:lib"
     },
     },
     "dependencies": {
     "dependencies": {
-        "@douyinfe/semi-animation": "2.62.1",
+        "@douyinfe/semi-animation": "2.63.0-beta.0",
         "@mdx-js/mdx": "^3.0.1",
         "@mdx-js/mdx": "^3.0.1",
         "async-validator": "^3.5.0",
         "async-validator": "^3.5.0",
         "classnames": "^2.2.6",
         "classnames": "^2.2.6",

+ 4 - 2
packages/semi-foundation/upload/foundation.ts

@@ -60,7 +60,8 @@ export interface AfterUploadResult {
     autoRemove?: boolean;
     autoRemove?: boolean;
     status?: string;
     status?: string;
     validateMessage?: unknown;
     validateMessage?: unknown;
-    name?: string
+    name?: string;
+    url?: string
 }
 }
 
 
 export interface UploadAdapter<P = Record<string, any>, S = Record<string, any>> extends DefaultAdapter<P, S> {
 export interface UploadAdapter<P = Record<string, any>, S = Record<string, any>> extends DefaultAdapter<P, S> {
@@ -646,7 +647,7 @@ class UploadFoundation<P = Record<string, any>, S = Record<string, any>> extends
         e ? (newFileList[index].event = e) : null;
         e ? (newFileList[index].event = e) : null;
 
 
         if (afterUpload && typeof afterUpload === 'function') {
         if (afterUpload && typeof afterUpload === 'function') {
-            const { autoRemove, status, validateMessage, name } =
+            const { autoRemove, status, validateMessage, name, url } =
                 this._adapter.notifyAfterUpload({
                 this._adapter.notifyAfterUpload({
                     response: body,
                     response: body,
                     file: newFileList[index],
                     file: newFileList[index],
@@ -655,6 +656,7 @@ class UploadFoundation<P = Record<string, any>, S = Record<string, any>> extends
             status ? (newFileList[index].status = status) : null;
             status ? (newFileList[index].status = status) : null;
             validateMessage ? (newFileList[index].validateMessage = validateMessage) : null;
             validateMessage ? (newFileList[index].validateMessage = validateMessage) : null;
             name ? (newFileList[index].name = name) : null;
             name ? (newFileList[index].name = name) : null;
+            url ? (newFileList[index].url = url) : null;
             autoRemove ? newFileList.splice(index, 1) : null;
             autoRemove ? newFileList.splice(index, 1) : null;
         }
         }
         this._adapter.notifySuccess(body, fileInstance, newFileList);
         this._adapter.notifySuccess(body, fileInstance, newFileList);

+ 1 - 1
packages/semi-icons-lab/package.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "@douyinfe/semi-icons-lab",
   "name": "@douyinfe/semi-icons-lab",
-  "version": "2.62.1",
+  "version": "2.63.0-beta.0",
   "description": "semi icons lab",
   "description": "semi icons lab",
   "keywords": [
   "keywords": [
     "semi",
     "semi",

+ 1 - 1
packages/semi-icons/package.json

@@ -1,6 +1,6 @@
 {
 {
     "name": "@douyinfe/semi-icons",
     "name": "@douyinfe/semi-icons",
-    "version": "2.62.1",
+    "version": "2.63.0-beta.0",
     "description": "semi icons",
     "description": "semi icons",
     "keywords": [
     "keywords": [
         "semi",
         "semi",

+ 1 - 1
packages/semi-illustrations/package.json

@@ -1,6 +1,6 @@
 {
 {
     "name": "@douyinfe/semi-illustrations",
     "name": "@douyinfe/semi-illustrations",
-    "version": "2.62.1",
+    "version": "2.63.0-beta.0",
     "description": "semi illustrations",
     "description": "semi illustrations",
     "keywords": [
     "keywords": [
         "semi",
         "semi",

+ 2 - 2
packages/semi-next/package.json

@@ -1,6 +1,6 @@
 {
 {
     "name": "@douyinfe/semi-next",
     "name": "@douyinfe/semi-next",
-    "version": "2.62.1",
+    "version": "2.63.0-beta.0",
     "description": "Plugin that support Semi Design in Next.js",
     "description": "Plugin that support Semi Design in Next.js",
     "author": "伍浩威 <[email protected]>",
     "author": "伍浩威 <[email protected]>",
     "homepage": "",
     "homepage": "",
@@ -22,7 +22,7 @@
         "typescript": "^4"
         "typescript": "^4"
     },
     },
     "dependencies": {
     "dependencies": {
-        "@douyinfe/semi-webpack-plugin": "2.62.1"
+        "@douyinfe/semi-webpack-plugin": "2.63.0-beta.0"
     },
     },
     "gitHead": "eb34a4f25f002bb4cbcfa51f3df93bed868c831a"
     "gitHead": "eb34a4f25f002bb4cbcfa51f3df93bed868c831a"
 }
 }

+ 1 - 1
packages/semi-rspack/package.json

@@ -1,6 +1,6 @@
 {
 {
     "name": "@douyinfe/semi-rspack-plugin",
     "name": "@douyinfe/semi-rspack-plugin",
-    "version": "2.62.1",
+    "version": "2.63.0-beta.0",
     "description": "",
     "description": "",
     "homepage": "",
     "homepage": "",
     "license": "MIT",
     "license": "MIT",

+ 1 - 1
packages/semi-scss-compile/package.json

@@ -1,6 +1,6 @@
 {
 {
     "name": "@douyinfe/semi-scss-compile",
     "name": "@douyinfe/semi-scss-compile",
-    "version": "2.62.1",
+    "version": "2.63.0-beta.0",
     "description": "compile semi scss to css",
     "description": "compile semi scss to css",
     "author": "[email protected]",
     "author": "[email protected]",
     "license": "MIT",
     "license": "MIT",

+ 1 - 1
packages/semi-theme-default/package.json

@@ -1,6 +1,6 @@
 {
 {
     "name": "@douyinfe/semi-theme-default",
     "name": "@douyinfe/semi-theme-default",
-    "version": "2.62.1",
+    "version": "2.63.0-beta.0",
     "description": "semi-theme-default",
     "description": "semi-theme-default",
     "keywords": [
     "keywords": [
         "semi-theme",
         "semi-theme",

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

@@ -0,0 +1,828 @@
+import { getUuidv4 } from '@douyinfe/semi-foundation/utils/uuid';
+import Chat from '../index';
+import React, { useState, useCallback, useRef, useEffect, useMemo } from 'react';
+import { Form, Button, Avatar, Dropdown, Radio, RadioGroup, Switch, Collapsible, AvatarGroup} 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 } from './constant';
+
+export default {
+    title: 'Chat',
+    parameters: {
+      chromatic: { disableSnapshot: true },
+    }
+}
+
+const uploadProps = { action: 'https://api.semi.design/upload' }
+
+export const _Chat = () => {
+    const [message, setMessage] = useState(initMessage);
+    const [hints, setHints] = useState(hintsExample);
+    const [mode, setMode] = useState('bubble');
+    const [align, setAlign] = useState('leftRight');
+    const [sendHotKey, setSendHotKey] = useState('enter');
+    const [key, setKey] = useState(1);
+    const [showClearContext, setShowClearContext] = useState(false);
+
+    const onClear = useCallback((clearMessage) => {
+       console.log('onClear');
+    }, []);
+
+    const onMessageSend = useCallback((content, attachment) => {
+        const newAssistantMessage = {
+            role: 'assistant',
+            id: getUuidv4(),
+            content: "这是一条 mock 回复信息",
+        }
+        setMessage((message) => {
+            return [
+                ...message,
+                newAssistantMessage
+            ]
+        })
+    }, []);
+
+    const onMessageDelete = useCallback((message) => {
+       console.log('message delete', message);
+    }, []);
+
+    const onChatsChange = useCallback((chats) => {
+        console.log('onChatsChange', chats);
+        setMessage(chats);
+    }, []);
+
+    const onMessageGoodFeedback = useCallback((message) => {
+        console.log('message good feedback', message);
+    }, []);
+
+    const onMessageBadFeedback = useCallback((message) => {
+        console.log('message bad feedback', message);
+    }, []);
+
+    const onMessageReset = useCallback((message) => {
+        console.log('message reset', message);
+    }, []);
+
+    const onInputChange = useCallback((props) => {
+        console.log('onInputChange', props);
+    }, []);
+
+    const onHintClick = useCallback((hint) => {
+        setHints([]);
+    }, []);
+
+    const onModeChange = useCallback((e) => {
+        setMode(e.target.value);
+        setKey((key) => key + 1);
+    }, []); 
+
+    const onAlignChange = useCallback((e) => {
+        setAlign(e.target.value);
+        setKey((key) => key + 1);
+    }, []);
+
+    const onSwitchChange = useCallback(() => {
+        setShowClearContext((showClearContext) => !showClearContext);
+    }, [])
+
+    const onSendHotKeyChange = useCallback((e) => {
+        setSendHotKey(e.target.value);
+    }, []);
+
+    return (
+        <>
+            <div style={{margin: 10, display: 'flex', flexDirection: 'column', rowGap: 5}}>
+                <span style={{ display: 'flex', alignItems: 'center', columnGap: '10px'}}>
+                    展示清除上下文按钮:
+                    <Switch checked={showClearContext} onChange={onSwitchChange}/>
+                </span>
+                <span style={{ display: 'flex', alignItems: 'center', columnGap: '10px'}}>
+                    模式:
+                    <RadioGroup onChange={onModeChange} value={mode} type="button">
+                        <Radio value={'bubble'}>气泡</Radio>
+                        <Radio value={'noBubble'}>非气泡</Radio>
+                        <Radio value={'userBubble'}>用户会话气泡</Radio>
+                    </RadioGroup>
+                </span>
+                <span style={{ display: 'flex', alignItems: 'center', columnGap: '10px'}}>
+                    布局:
+                    <RadioGroup onChange={onAlignChange} value={align} type="button">
+                        <Radio value={'leftRight'}>左右分布</Radio>
+                        <Radio value={'leftAlign'}>全左</Radio>
+                    </RadioGroup>
+                </span>
+                <span style={{ display: 'flex', alignItems: 'center', columnGap: '10px'}}>
+                    按键发送策略:
+                    <RadioGroup onChange={onSendHotKeyChange} value={sendHotKey} type="button">
+                        <Radio value={'enter'}>enter</Radio>
+                        <Radio value={'shift+enter'}>shift+enter</Radio>
+                    </RadioGroup>
+                </span>
+            </div>
+            <div style={{ height: 650}}>
+                <Chat
+                    key={key}
+                    style={commonOuterStyle}
+                    chats={message}
+                    hints={hints}
+                    roleConfig={roleInfo}
+                    onClear={onClear}
+                    onMessageSend={onMessageSend}
+                    onMessageDelete={onMessageDelete}
+                    onMessageGoodFeedback={onMessageGoodFeedback}
+                    onMessageBadFeedback={onMessageBadFeedback}
+                    onChatsChange={onChatsChange}
+                    onMessageReset={onMessageReset}
+                    onInputChange={onInputChange}
+                    onHintClick={onHintClick}
+                    uploadProps={uploadProps}
+                    uploadTipProps={{
+                        content: '自定义输入提示'
+                    }}
+                    mode={mode} 
+                    align={align}
+                    sendHotKey={sendHotKey} 
+                    showClearContext={showClearContext}
+                />
+            </div>
+        </>
+    )
+}
+
+export const Attachment = () => {
+    const [message, setMessage] = useState(infoWithAttachment);
+
+    return (
+        <div
+            style={{ height: 600}}
+        >
+            <Chat 
+                placeholder={'不处理输入信息,仅用于展示附件'}
+                style={commonOuterStyle}
+                chats={message}
+                roleConfig={roleInfo}
+                uploadProps={uploadProps}
+            />
+        </div>
+    )
+}
+
+function CustomInputRender(props) {
+    const { defaultNode, onClear, onSend } = props;
+    const api = useRef();
+    const onSubmit = useCallback(() => {
+        if (api.current) {
+            const values = api.current.getValues();
+            if ((values.name && values.name.length !== 0) || (values.file && values.file.length !== 0)) {
+                onSend(values.name, values.file);
+                api.current.reset();
+            } 
+        }
+    }, []);
+
+    return (<div style={{   
+        display: 'flex', 
+        flexDirection: 'column', 
+        border: '1px solid var(--semi-color-border)',
+        margin: '8px 16px',
+        borderRadius: 8,
+        padding: 8
+    }}>
+        <Form
+            getFormApi={formApi => api.current = formApi}
+        >
+            <strong>输入信息</strong>
+            <Form.Input
+                field="name"
+                label="名称(Input)"
+                style={{ width: 250 }}
+                trigger='blur'
+            />
+            <Form.Upload
+                field='file'
+                label='文档'
+                action='https://api.semi.design/upload'
+            >
+                <Button icon={<IconUpload />} theme="light">
+                    点击上传
+                </Button>
+            </Form.Upload>
+        </Form>
+        <Button style={{ width: 'fit-content' }} onClick={onSubmit}>提交</Button>
+    </div>);
+}
+
+export const CustomRenderInputArea = () => {
+    const [message, setMessage] = useState(initMessage.slice(0, 1));
+
+    const onChatsChange = useCallback((chats) => {
+        setMessage(chats);
+    }, []);
+
+    const onMessageSend = useCallback((content, attachment) => {
+        const newUserMessage = {
+            role: 'user',
+            id: getUuidv4(),
+            content: content,
+            attachment: attachment
+        }
+        const newAssistantMessage = {
+            role: 'assistant',
+            id: getUuidv4(),
+            content: `This is a mock response`
+        }
+        setMessage((message) => ([...message, newUserMessage, newAssistantMessage]));
+    }, []);
+
+    const renderInputArea = useCallback((props) => {
+        return (<CustomInputRender {...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;
+        return <Avatar size="extra-small" shape="square" style={{ flexShrink: '0'}}>{role.name}</Avatar >
+    }, []);
+
+    const customRenderTitle =  useCallback((props)=> null, []);
+
+    return (<div
+        style={{ height: 600 }}
+    >
+        <Chat 
+            style={commonOuterStyle}
+            chats={initMessage.slice(0, 4)}
+            roleConfig={roleInfo}
+            chatBoxRenderConfig={{
+                renderChatBoxTitle: customRenderTitle,
+                renderChatBoxAvatar: customRenderAvatar
+            }}
+            uploadProps={uploadProps}
+        />
+    </div>);
+}
+
+export const CustomRenderTitle = (props) => {
+    const customRenderTitle = useCallback((props) => {
+        const { role, message, defaultTitle } = props;
+        if (message.role === 'user') {
+            return null;
+        }
+        return <span style={{ display:' flex', alignItems: 'center', justifyContent: 'center', columnGap: '10px'}}>
+            <Avatar size="extra-small" shape="square" src={role.avatar} />
+            {defaultTitle}
+        </span>
+    }, []);
+
+    const customRenderAvatar = useCallback((props)=> null, []);
+
+    return (<div
+        style={{ height: 600}}
+    >
+        <Chat
+            placeholder={"不处理输入信息,仅用于展示自定义头像和标题"}
+            style={commonOuterStyle}
+            chats={simpleInitMessage}
+            roleConfig={roleInfo}
+            chatBoxRenderConfig={{
+                renderChatBoxTitle: customRenderTitle,
+                renderChatBoxAvatar: customRenderAvatar
+            }}
+            uploadProps={uploadProps}
+        />
+    </div>);
+}
+
+export const CustomFullChatBox = () => {
+    const customRenderChatBox = useCallback((props) => {
+        const { role, message, defaultNodes, className } = props;
+        const date = new Date(message.createAt);
+        const year = date.getFullYear();
+        const month = ('0' + (date.getMonth() + 1)).slice(-2);
+        const day = ('0' + date.getDate()).slice(-2);
+        const hours = ('0' + date.getHours()).slice(-2);
+        const minutes = ('0' + date.getMinutes()).slice(-2);
+        const seconds = ('0' + date.getSeconds()).slice(-2);
+        const formattedDate = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
+
+        return <div className={className}>
+            <div style={{ display: 'flex', flexDirection: 'column', rowGap: 5, alignItems: message.role === 'user' ? 'end' : ''}}>
+                <span style={{color: 'var(--semi-color-text-2', fontSize: '12px'}}>{formattedDate}</span>
+                <div style={{ width: 'fit-content'}}>
+                    {defaultNodes.content}
+                </div>
+                {defaultNodes.action}
+            </div>
+        </div>
+    }, []);
+
+    return (<div
+        style={{ height: 600}}
+    >
+        <Chat
+            style={commonOuterStyle} 
+            chats={simpleInitMessage}
+            roleConfig={roleInfo}
+            chatBoxRenderConfig={{
+                renderFullChatBox: customRenderChatBox
+            }}
+            uploadProps={uploadProps}
+        />
+    </div>);
+}
+
+const CustomActions = React.memo((props) => {
+    const { role, message, defaultActions, className } = props;
+    const myRef = useRef();
+    const getContainer = useCallback(() => {
+        if (myRef.current) {
+            const element = myRef.current;
+            let parentElement = element.parentElement;
+            while (parentElement) {
+                if (parentElement.classList.contains('semi-chat-chatBox-wrap')) {
+                    return parentElement;
+                }
+                parentElement = parentElement.parentElement;
+            }
+        }
+    }, [myRef]);
+    return <span 
+        className={className}
+        ref={myRef}
+    >
+        {defaultActions.map((item, index)=> {
+            return <span key={index}>{item}</span>
+        })}
+        {<Dropdown
+            key="dropdown"
+            render={
+                <Dropdown.Menu >
+                    <Dropdown.Item icon={<IconForward />}>分享</Dropdown.Item>
+                </Dropdown.Menu>
+            }
+            trigger="click"
+            position="top"
+            getPopupContainer={getContainer}
+        >
+            <Button 
+                className='semi-chat-chatBox-action-btn'
+                icon={<IconMoreStroked/>}
+                theme='borderless'
+                type='tertiary'
+            />
+        </Dropdown>}
+    </span>
+});
+
+export const CustomRenderAction = () => {
+    const [message, setMessage] = useState(simpleInitMessage);
+    const customRenderAction = useCallback((props) => {
+        return <CustomActions {...props} />
+    }, []);
+
+    const onChatsChange = useCallback((chats) => {
+        setMessage(chats);
+    }, []);
+
+    return (<div
+        style={{ height: 600}}
+    >
+        <Chat 
+            chats={message}
+            onChatsChange={onChatsChange}
+            style={commonOuterStyle}
+            roleConfig={roleInfo}
+            chatBoxRenderConfig={{
+                renderChatBoxAction: customRenderAction
+            }}
+            uploadProps={uploadProps}
+        />
+    </div>);
+}
+
+export const CustomRenderContent = () => {
+    const renderContent = useCallback((props) => {
+        const { role, message, defaultNode, className } = props;
+        return <div className={className}>
+            <span>---custom render content---</span>
+            <MarkdownRender raw={message?.content}/>
+        </div>
+    }, []);
+
+    return (<div
+        style={{ height: 600}}
+    >
+        <Chat 
+            style={commonOuterStyle}
+            chats={simpleInitMessage}
+            roleConfig={roleInfo}
+            chatBoxRenderConfig={{
+                renderChatBoxContent: renderContent
+            }}
+            uploadProps={uploadProps}
+        />
+    </div>);
+}
+
+// const Card = (source) => {
+//     return (<span className="demo-card">
+//         <span className="demo-card-title"></span>
+//         <span className="demo-card-link"></span>
+//         <span className="demo-card-content"></span>
+//     </span>)
+// }
+
+const SourceCard = (props) => {
+    const [open, setOpen] = useState(true);
+    const [show, setShow] = useState(false);
+    const spanRef = useRef();
+    const onOpen = useCallback(() => {
+        setOpen(false);
+        setShow(true);
+    }, []);
+
+    const onClose = useCallback(() => {
+        setOpen(true);
+        setTimeout(() => {
+            setShow(false);
+        }, 350)
+    }, []);
+
+    return (<div style={{ 
+            transition: open ? 'height 0.4s ease, width 0.4s ease': 'height 0.4s ease',
+            height: open ? '30px' : '184px',
+            width: open ? '237px': '100%', 
+            background: 'var(--semi-color-tertiary-light-hover)', 
+            borderRadius: 16,
+            boxSizing: 'border-box',
+        }}
+        >
+        <span
+            ref={spanRef} 
+            style={{
+                display: !open ? 'none' : 'flex',
+                width: 'fit-content',
+                columnGap: 10,
+                background: 'var(--semi-color-tertiary-light-hover)', 
+                borderRadius: '16px',
+                padding: '5px 10px',
+                point: 'cursor',
+                fontSize: 14,
+                color: 'var(--semi-color-text-1)',
+            }}
+            onClick={onOpen} 
+        >
+            <span>基于{props.sources.length}个搜索来源</span>
+            <AvatarGroup size="extra-extra-small" >
+                {props.sources.map((s, index) => (<Avatar key={index} src={s.avatar}></Avatar>))}        
+            </AvatarGroup>
+        </span>
+        <span 
+            style={{
+                height: '100%',
+                boxSizing: 'border-box',
+                display: !open ? 'flex' : 'none',
+                flexDirection: 'column',
+                background: 'var(--semi-color-tertiary-light-hover)', borderRadius: '16px', padding: 12, boxSize: 'border-box'
+            }}
+            onClick={onClose}
+            >
+            <span style={{display: 'flex', alignItems: 'center', justifyContent: 'space-between',
+                    padding: '5px 10px', columnGap: 10, color: 'var(--semi-color-text-1)'
+            }}>
+                <span style={{fontSize: 14, fontWeight: 500}}>Source</span>
+                <IconChevronUp />
+            </span>
+            <span style={{display: 'flex', flexWrap: 'wrap', gap: 10,  overflow: 'scroll', padding: '5px 10px'}}>
+                {props.sources.map(s => (
+                    <span style={{ 
+                        display: 'flex', 
+                        flexDirection: 'column', 
+                        rowGap: 5, 
+                        flexBasis: 150, 
+                        flexGrow: 1,
+                        border: "1px solid var(--semi-color-border)",
+                        borderRadius: 12,
+                        padding: 12,
+                        fontSize: 12
+                    }}>
+                        <span style={{display: 'flex', columnGap: 5, alignItems: 'center', }}>
+                            <Avatar style={{width: 16, height: 16, flexShrink: 0 }} shape="square" src={s.avatar} />
+                            <span style={{ color: 'var(--semi-color-text-2)', textOverflow: 'ellipsis'}}>{s.title}</span>
+                        </span>
+                        <span style={{
+                            color: 'var(--semi-color-primary)',
+                            fontSize: 12,
+                        }}
+                        >{s.subTitle}</span>
+                        <span style={{
+                            display: '-webkit-box',
+                            "-webkit-box-orient": 'vertical',
+                            WebkitLineClamp: '3', 
+                            textOverflow: 'ellipsis', 
+                            overflow: 'hidden',
+                            color: 'var(--semi-color-text-2)',
+                        }}>{s.content}</span>
+                    </span>))}
+                </span>
+            </span>
+        </div>
+    )
+}
+
+
+export const CustomRenderContentPlus = () => {
+    const chat = [
+        {
+        role: 'assistant',
+        id: '3',
+        createAt: 1715676751919,
+        content: "Semi Design 是由抖音前端团队,MED 产品设计团队设计、开发并维护的设计系统。它作为全面、易用、优质的现代应用 UI 解决方案,从字节跳动各业务线的复杂场景提炼而来,支撑近千计平台产品,服务内外部 10 万+ 用户。",
+        source: [
+            {
+                avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png',
+                url: '/zh-CN/start/introduction',
+                title: 'semi Design',
+                subTitle: 'Semi design website',
+                content: 'Semi Design 是由抖音前端团队,MED 产品设计团队设计、开发并维护的设计系统。'
+            },
+            {
+                avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png',
+                url: '/dsm/landing',
+                subTitle: 'Semi DSM website',
+                title: 'Semi 设计系统',
+                content: '从 Semi Design,到 Any Design 快速定义你的设计系统,并应用在设计稿和代码中'
+            },
+            {
+                avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png',
+                url: '/code/zh-CN/start/introduction',
+                subTitle: 'Semi D2C website',
+                title: '设计稿转代码',
+                content: 'Semi 设计稿转代码(Semi Design to Code,或简称 Semi D2C),是由抖音前端 Semi Design 团队推出的全新的提效工具'
+            },
+            {
+                avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png',
+                url: '/zh-CN/start/introduction',
+                title: 'semi Design',
+                subTitle: 'Semi design website',
+                content: 'Semi Design 是由抖音前端团队,MED 产品设计团队设计、开发并维护的设计系统。'
+            },
+            {
+                avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png',
+                url: '/dsm/landing',
+                subTitle: 'Semi DSM website',
+                title: 'Semi 设计系统',
+                content: '从 Semi Design,到 Any Design 快速定义你的设计系统,并应用在设计稿和代码中'
+            },
+            {
+                avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png',
+                url: '/code/zh-CN/start/introduction',
+                subTitle: 'Semi D2C website',
+                title: '设计稿转代码',
+                content: 'Semi 设计稿转代码(Semi Design to Code,或简称 Semi D2C),是由抖音前端 Semi Design 团队推出的全新的提效工具'
+            },
+        ]
+    }];
+
+    const renderContent = useCallback((props) => {
+        const { role, message, defaultNode, className } = props;
+        return <div className={className}>
+            <SourceCard sources={message?.source} />
+            <MarkdownRender raw={message?.content}/>
+        </div>
+    }, []);
+
+    return (<div
+        style={{ height: 600}}
+    >
+        <Chat 
+            style={commonOuterStyle}
+            chats={chat}
+            roleConfig={roleInfo}
+            chatBoxRenderConfig={{
+                renderChatBoxContent: renderContent
+            }}
+            uploadProps={uploadProps}
+        />
+        <div></div>
+    </div>);
+}
+
+export const LeftAlign =  () => {
+    return (<div
+        style={{ height: 600}}
+    >
+        <Chat
+            style={commonOuterStyle} 
+            chats={simpleInitMessage}
+            roleConfig={roleInfo}
+            align='leftAlign'
+            uploadProps={uploadProps}
+        />
+    </div>);
+}
+
+export const MessageStatus = () => {
+    const messages = [
+        initMessage[1],
+        {
+            ...initMessage[2],
+            content: '请求错误',
+            status: 'error'
+        },
+        {
+            id: 'loading',
+            role: 'assistant',
+            status: 'loading'
+        },
+    ]
+    return (<div
+        style={{ height: 600}}
+    >
+        <Chat
+            style={commonOuterStyle} 
+            chats={messages}
+            roleConfig={roleInfo}
+            uploadProps={uploadProps}
+        />
+    </div>);
+}
+
+export const MockResponseMessage = () => {
+    const [message, setMessage] = useState([ initMessage[0]]);
+    const intervalId = useRef();
+
+    const onChatsChange = useCallback((chats) => {
+        console.log('onChatsChange', chats);
+        setMessage(chats);
+    }, []);
+
+    const onMessageSend = useCallback((content, attachment) => {
+        setMessage((message) => {
+            return [
+                ...message,
+                {
+                    role: 'user',
+                    createAt: Date.now(),
+                    id: getUuidv4(),
+                    content: content,
+                    attachment: attachment,
+                },
+                {
+                    role: 'assistant',
+                    status: 'loading',
+                    createAt: Date.now(),
+                    id: getUuidv4()
+                }
+            ]
+        }); 
+        generateMockResponse(content);
+    },[])
+
+    const generateMockResponse = useCallback((content) => {
+        const id = setInterval(() => {
+            setMessage((message) => {
+                const lastMessage = message[message.length - 1];
+                let newMessage = {};
+                if (lastMessage.status === 'loading') {
+                    newMessage =  {
+                        role: 'assistant',
+                        id: getUuidv4(),
+                        content:  `mock Response for ${content} \n`,
+                        status: 'incomplete'
+                    }
+                } else if (lastMessage.status === 'incomplete') {
+                    if (lastMessage.content.length > 200) {
+                        clearInterval(id);
+                        intervalId.current = null
+                        newMessage = {
+                            role: 'assistant',
+                            id: getUuidv4(),
+                            content: `${lastMessage.content} mock stream message`,
+                            status: 'complete'
+                        }
+                    } else {
+                        newMessage =  {
+                            role: 'assistant',
+                            id: getUuidv4(),
+                            content: `${lastMessage.content} mock stream message`,
+                            status: 'incomplete'
+                        }
+                    }  
+                }
+                return [
+                    ...message.slice(0, -1),
+                    newMessage
+                ]
+            })
+        }, 400);
+        intervalId.current = id;
+    }, []);
+
+    const onStopGenerator = useCallback(() => {
+        if (intervalId.current) {
+            clearInterval(intervalId.current);
+            setMessage((message) => {
+                const lastMessage = message[message.length - 1];
+                if (lastMessage.status && lastMessage.status !== 'complete') {
+                    const lastMessage = message[message.length - 1];
+                    let newMessage = {...lastMessage};
+                    newMessage.status = 'complete';
+                    return [
+                        ...message.slice(0, -1),
+                        newMessage
+                    ]
+                } else {
+                    return message;
+                }
+            })
+        }
+    }, [intervalId]);
+
+    return (
+    <div
+        style={{ height: 300}}
+    >
+        <Chat 
+            style={commonOuterStyle}
+            chats={message}
+            roleConfig={roleInfo}
+            onChatsChange={onChatsChange}
+            onMessageSend={onMessageSend}
+            onStopGenerator={onStopGenerator}
+            showStopGenerate={true}
+            uploadProps={uploadProps}
+        />
+    </div>
+    );
+}
+
+export const CustomRenderHint = () => {
+    const [message, setMessage] = useState(initMessage.slice(0, 3));
+    const [hint, setHint] = useState(hintsExample);
+
+    const commonHintStyle = useMemo(() => ({
+        border: '1px solid var(--semi-color-border)',
+        padding: '10px',
+        borderRadius: '10px',
+        width: 'fit-content',
+        color: 'var( --semi-color-text-1)',
+        display: 'flex',
+        alignItems: 'center',
+        cursor: 'pointer',
+        fontSize: '14px'
+    }), []);
+    
+    const renderHintBox = useCallback((props) => {
+        const { content, onHintClick, index } = props;
+        return <div style={commonHintStyle} onClick={onHintClick} key={index}>
+            {content}
+            <IconArrowRight style={{ marginLeft: 10 }}>click me</IconArrowRight>
+        </div>
+    }, []);
+
+    const onChatsChange = useCallback((chats) => {
+        setMessage(chats);
+    }, []);
+
+    const onHintClick = useCallback((hint) => {
+        setHint([]);
+    }, []);
+
+    const onClear = useCallback(() => {
+        setHint([]);
+    }, []);
+
+    return <div
+        style={{ height: 600}}
+    >
+        <Chat 
+            style={commonOuterStyle}
+            chats={message}
+            onChatsChange={onChatsChange}
+            onHintClick={onHintClick}
+            hints={hint}
+            roleConfig={roleInfo}
+            renderHintBox={renderHintBox}
+            onClear={onClear}
+            uploadProps={uploadProps}
+        />
+    </div>
+}

+ 90 - 0
packages/semi-ui/chat/_story/chat.stories.tsx

@@ -0,0 +1,90 @@
+import React, { useState, useCallback, } from 'react';
+import { storiesOf } from '@storybook/react';
+import Chat from '@douyinfe/semi-ui/chat';
+import { getUuidv4 } from '@douyinfe/semi-foundation/utils/uuid';
+import { initMessage, roleInfo, commonOuterStyle, hintsExample } from './constant';
+
+
+const stories = storiesOf('Chat', module);
+
+stories.add('Chat 对话', () => {
+    const [message, setMessage] = useState(initMessage);
+    const [hints, setHints] = useState(hintsExample);
+
+    const onClear = useCallback(() => {
+       console.log('onClear');
+       setHints([]);
+    }, []);
+
+    const onMessageSend = useCallback((content, attachment) => {
+        const newUserMessage = {
+            role: 'user',
+            id: getUuidv4(),
+            content: content,
+            attachment: attachment,
+        }
+        const newAssistantMessage = {
+            role: 'assistant',
+            id: getUuidv4(),
+            content: "这是一条 mock 回复信息",
+        }
+        setMessage((message) => {
+            return [
+                ...message,
+                newUserMessage,
+                newAssistantMessage
+            ] as any
+        })
+    }, []);
+
+    const onMessageDelete = useCallback((message) => {
+       console.log('message delete', message);
+    }, []);
+
+    const onChatsChange = useCallback((chats) => {
+        console.log('onChatsChange', chats);
+        setMessage(chats);
+    }, []);
+
+    const onMessageGoodFeedback = useCallback((message) => {
+        console.log('message good feedback', message);
+    }, []);
+
+    const onMessageBadFeedback = useCallback((message) => {
+        console.log('message bad feedback', message);
+    }, []);
+
+    const onMessageReset = useCallback((message) => {
+        console.log('message reset', message);
+    }, []);
+
+    const onInputChange = useCallback((props) => {
+        console.log('onInputChange', props);
+    }, []);
+
+    const onHintClick = useCallback((hint) => {
+        setHints([]);
+    }, []);
+
+    return (
+        <div
+            style={{ height: 600}}
+        >
+            <Chat 
+                style={commonOuterStyle}
+                chats={message}
+                hints={hints}
+                roleConfig={roleInfo}
+                onClear={onClear}
+                onMessageSend={onMessageSend}
+                onMessageDelete={onMessageDelete}
+                onMessageGoodFeedback={onMessageGoodFeedback}
+                onMessageBadFeedback={onMessageBadFeedback}
+                onChatsChange={onChatsChange}
+                onMessageReset={onMessageReset}
+                onInputChange={onInputChange}
+                onHintClick={onHintClick}
+            />
+        </div>
+    )
+});

+ 141 - 0
packages/semi-ui/chat/_story/constant.js

@@ -0,0 +1,141 @@
+const semiCode = "以下是一个 \`Semi\` 代码的使用示例:\n```jsx \nimport React from 'react';\nimport { Button } from '@douyinfe/semi-ui';\nconst MyComponent = () => {\n  const handleClick = () => {\n  console.log('Button clicked');\n};\n  return (\n    <div>\n      <h1>Hello, Semi Design!</h1>\n      <Button onClick={handleClick}>Click me</Button>\n    </div>\n  );\n};\nexport default MyComponent;\n```";
+const semiInfo = `
+Semi Design 是由抖音前端团队和MED产品设计团队设计、开发并维护的设计系统。作为一个全面、易用、优质的现代应用UI解决方案,Semi Design从字节跳动各业务线的复杂场景中提炼而来,目前已经支撑了近千个平台产品,服务了内外部超过10万用户[[1]](https://semi.design/zh-CN/start/introduction)。
+
+Semi Design的愿景是成为企业应用前端不可或缺的一半,为企业应用前端提供坚实且优质的基础。设计系统的真正价值在于降低前端的搭建成本,同时提供优秀的设计和工程化标准,充分解放设计师与开发者的生产力,从而不断孵化出优秀的产品[[1]](https://semi.design/zh-CN/start/introduction)。
+
+Semi Design的特点包括:
+
+1. 设计:Semi Design通过提炼简洁轻量、现代化的设计风格,细致打磨原子组件的交互,并在字节跳动的海量业务场景下进行迭代,沉淀了一套优质的默认基础。它将保证Semi Design打造的企业应用产品具有连贯一致的"语言",并且质量优于陈旧系统的基线。此外,Semi Design还充分进行模块化解耦,并开放自定义能力,方便用户进行二次裁剪与定制,搭建适用于不同形态产品的前端资产[[1]](https://semi.design/zh-CN/start/introduction)。
+
+2. 主题化:Semi Design提供了强大的主题化方案,通过对数千个设计变量的分层和梳理,设计师和开发者可以在全局、乃至组件级别对表现层进行深度定制。这使得Semi Design可以轻松实现品牌一键定制,满足业务和品牌多样化的视觉需求。主题化方案还支持从线上到设计工具的实时同步,提高设计和研发的持续对齐效率,降低产研间的沟通成本[[1]](https://semi.design/zh-CN/start/introduction)。
+
+3. 深色模式:为了兼容更多用户群体在不同生产环境下的使用偏好,Semi Design的任意主题均自动支持深色模式,并能在应用运行时动态切换。此外,Semi Design还允许用户在应用内局部区域开启深色模式,以兼容SDK或插件型产品的使用场景。用户还可以通过进阶设置实现应用和系统主题的自动保持一致。为了提升开发体验,Semi Design还提供了将未规范化的存量旧工程一键兼容到Semi暗色模式的CLI工具,通过自动化的方式规避迁移成本[[1]](https://semi.design/zh-CN/start/introduction)。
+
+4. 国际化:Semi Design经过30+版本迭代,已具备完善的国际化特性。它覆盖了简/繁体中文、英语、日语、韩语、葡萄牙语等20+种语言,日期时间组件提供全球时区支持,全部组件可自动适配阿拉伯文RTL布局。同时,Semi Design也支持海外地区的开发者使用,对站点和文档进行了双语适配,以保证开发无障碍[[1]](https://semi.design/zh-CN/start/introduction)。
+
+5. 跨框架技术方案:Semi Design采用了一套跨前端框架技术方案,将每个组件的JavaScript拆分为Foundation和Adapter两部Semi Design 是由抖音前端团队和MED产品设计团队设计、开发并维护的设计系统。作为一个全面、易用、优质的现代应用UI解决方案,Semi Design从字节跳动各业务线的复杂场景中提炼而来,目前已经支撑了近千个平台产品,服务了内外部超过10万用户[[1]](https://semi.design/zh-CN/start/introduction)。
+
+---
+Learn more:
+1. [Introduction 介绍 - Semi Design](https://semi.design/zh-CN/start/introduction)
+2. [Getting Started 快速开始 - Semi Design](https://semi.design/zh-CN/start/getting-started)
+3. [Semi D2C 设计稿转代码的演进之路 - 知乎](https://zhuanlan.zhihu.com/p/667189184)
+`;
+
+const initMessage = [
+    {
+        role: 'system',
+        id: '1',
+        createAt: 1715676751919,
+        content: "Hello, I'm your AI assistant.",   
+    },
+    {
+        role: 'user',
+        id: '2',
+        createAt: 1715676751919,
+        content: "介绍一下 semi design",
+    },
+    {
+        role: 'assistant',
+        id: '3',
+        createAt: 1715676751919,
+        content: semiInfo,
+    },
+    {
+        role: 'user',
+        id: '4',
+        createAt: 1715676751919,
+        content: "Semi design Button 使用示例",
+    },
+    {
+        role: 'assistant',
+        id: '5',
+        createAt: 1715676751919,
+        content: semiCode
+    },
+];
+
+const infoWithAttachment = [
+    {
+        role: 'user',
+        id: '2',
+        createAt: 1715676751919,
+        content: [
+            {
+                type: 'text',
+                text: '用于查看附件的样式,不处理任何输入',
+            },
+            {
+                type: 'image_url',
+                image_url: {
+                    url: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/edit-bag.jpeg'
+                },
+            }
+        ],
+    },
+    {
+        role: 'assistant',
+        id: '3',
+        createAt: 1715676751919,
+        content: `用于查看附件的样式, 不处理任何输入\n\n![image](https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/edit-bag.jpeg)`,
+    },
+];
+
+
+const roleInfo = {
+    user: {
+        name: 'User Test',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/docs-icon.png'
+    },
+    assistant: {
+        name: 'Assistant Test',
+        avatar: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/other/logo.png'
+    },
+    system: {
+        name: 'System Test'
+    }
+};
+
+const commonOuterStyle = {
+    border: '1px solid var(--semi-color-border)',
+    borderRadius: '16px',
+    margin: '8px 16px',
+};
+
+const hintsExample = [
+    "告诉我更多",
+    "Semi Design 的组件有哪些?",
+    "Semi Design 官网及 github 仓库地址是?",
+];
+
+const simpleInitMessage = [
+    {
+        role: 'system',
+        id: '1',
+        createAt: 1715676751919,
+        content: "Hello, I'm your AI assistant.",   
+    },
+    {
+        role: 'user',
+        id: '2',
+        createAt: 1715676751919,
+        content: "介绍一下 semi design",
+    },
+    {
+        role: 'assistant',
+        id: '3',
+        createAt: 1715676751919,
+        content: "Semi Design 是由抖音前端团队和MED产品设计团队设计、开发并维护的设计系统。",
+    },
+];
+
+export {
+    initMessage,
+    roleInfo,
+    commonOuterStyle,
+    hintsExample,
+    infoWithAttachment,
+    simpleInitMessage,
+    semiCode
+};

+ 97 - 0
packages/semi-ui/chat/attachment.tsx

@@ -0,0 +1,97 @@
+import React from "react";
+import { FileItem } from '../upload/interface';
+import Image from '../image';
+import { IconBriefStroked, IconClear } from '@douyinfe/semi-icons';
+import { strings, cssClasses } from '@douyinfe/semi-foundation/chat/constants';
+import cls from 'classnames';
+import { Progress } from "../index";
+
+const { PREFIX_ATTACHMENT, } = cssClasses;
+const { PIC_SUFFIX_ARRAY, PIC_PREFIX } = strings;
+
+interface AttachmentProps {
+    className?: string;
+    attachment?: FileItem[];
+    onClear?: (item: FileItem) => void;
+    showClear?: boolean
+}
+
+interface FileProps {
+    url?: string;
+    name?: string;
+    size?: string;
+    type?: string;
+}
+
+export const FileAttachment = React.memo((props: FileProps) => {
+    const { url, name, size, type } = props;
+    return <a
+        href={url}
+        target="_blank"
+        className={`${PREFIX_ATTACHMENT}-file`} rel="noreferrer"
+    >
+        <IconBriefStroked size="extra-large" className={`${PREFIX_ATTACHMENT}-file-icon`}/>
+        <div className={`${PREFIX_ATTACHMENT}-file-info`}>
+            <span className={`${PREFIX_ATTACHMENT}-file-title`}>{name}</span>
+            <span className={`${PREFIX_ATTACHMENT}-file-metadata`}>
+                <span className={`${PREFIX_ATTACHMENT}-file-type`}>{type}</span>
+                {type ? ' · ' : ''}{size}
+            </span>
+        </div>
+    </a>
+})
+
+export const ImageAttachment = React.memo((props: {src: string}) => {
+    const { src } = props;
+    return <Image
+    className={`${PREFIX_ATTACHMENT}-img`}
+    width={60}
+    height={60}
+    src={src}
+/>
+})
+
+const Attachment = React.memo((props: AttachmentProps) => {
+    const { attachment, onClear, showClear = true, className } = props;
+
+    return (
+        <div 
+            className={cls(PREFIX_ATTACHMENT, { [className]: className })}
+        >
+            {
+                attachment.map(item => {
+                    const { percent, status } = item;
+                    const suffix = item?.name.split('.').pop();
+                    const isImg = item?.fileInstance?.type?.startsWith(PIC_PREFIX) || PIC_SUFFIX_ARRAY.includes(suffix);
+                    const realType = suffix ?? item?.fileInstance?.type?.split('/').pop();
+                    const showProcess = !(percent === 100 || typeof percent === 'undefined') && status === 'uploading';
+                    return <div 
+                        className={`${PREFIX_ATTACHMENT}-item`}
+                        key={item.uid}
+                    >
+                        {isImg ? (
+                            <ImageAttachment src={item.url} />
+                        ) : (
+                            <FileAttachment
+                                url={item.url}
+                                name={item.name}
+                                size={item.size}
+                                type={realType}
+                            />
+                        )}
+                        {showClear && <IconClear 
+                            size="large" 
+                            className={`${PREFIX_ATTACHMENT}-clear`}
+                            onClick={()=> {
+                                onClear && onClear(item);
+                            }}
+                        />}
+                       {showProcess && <Progress percent={percent}  type="circle" size="small"  width={30} className={`${PREFIX_ATTACHMENT}-process`} aria-label="upload progress" />}
+                    </div>;
+                })
+            }
+        </div>
+    );  
+});
+
+export default Attachment;

+ 253 - 0
packages/semi-ui/chat/chatBox/chatBoxAction.tsx

@@ -0,0 +1,253 @@
+import React, { PureComponent, ReactNode } from 'react';
+import PropTypes from 'prop-types';
+import type { ChatBoxProps, Message } from '../interface';
+import { IconThumbUpStroked, 
+    IconDeleteStroked, 
+    IconCopyStroked, 
+    IconLikeThumb, 
+    IconRedoStroked 
+} from '@douyinfe/semi-icons';
+import { BaseComponent, Button, Popconfirm } from '../../index';
+import copy from 'copy-text-to-clipboard';
+import { cssClasses, strings } from '@douyinfe/semi-foundation/chat/constants';
+import ChatBoxActionFoundation, { ChatBoxActionAdapter } from '@douyinfe/semi-foundation/chat/chatBoxActionFoundation';
+import LocaleConsumer from "../../locale/localeConsumer";
+import { Locale } from "../../locale/interface";
+import cls from 'classnames';
+
+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
+}
+
+interface ChatBoxActionState {
+    visible: boolean;
+    showAction: boolean
+}
+
+class ChatBoxAction extends BaseComponent<ChatBoxActionProps, ChatBoxActionState> {
+
+    static propTypes = {
+        role: PropTypes.object,
+        message: PropTypes.object,
+        showReset: PropTypes.bool,
+        onMessageBadFeedback: PropTypes.func,
+        onMessageGoodFeedback: PropTypes.func,
+        onMessageCopy: PropTypes.func,
+        onChatsChange: PropTypes.func,
+        onMessageDelete: PropTypes.func,
+        onMessageReset: PropTypes.func,
+        customRenderFunc: PropTypes.func,
+    }
+
+    copySuccessNode: ReactNode;
+    foundation: ChatBoxActionFoundation;
+    containerRef: React.RefObject<HTMLDivElement>;
+    popconfirmTriggerRef: React.RefObject<HTMLSpanElement>;
+    clickOutsideHandler: any;
+
+    constructor(props: ChatBoxProps) {
+        super(props);
+        this.foundation = new ChatBoxActionFoundation(this.adapter);
+        this.copySuccessNode = null;
+        this.state = {
+            visible: false,
+            showAction: false,
+        };
+        this.clickOutsideHandler = null;
+        this.containerRef = React.createRef<HTMLDivElement>();
+        this.popconfirmTriggerRef = React.createRef<HTMLSpanElement>();
+    }
+
+    componentDidMount(): void {
+        this.copySuccessNode = <LocaleConsumer<Locale["Chat"]> componentName="Chat" >
+            {(locale: Locale["Chat"]) => locale['copySuccess']}
+        </LocaleConsumer>;
+    }
+
+    componentWillUnmount(): void {
+        this.foundation.destroy();
+    }
+
+    get adapter(): ChatBoxActionAdapter<ChatBoxActionProps, ChatBoxActionState> {
+        return {
+            ...super.adapter,
+            notifyDeleteMessage: () => {
+                const { message, onMessageDelete } = this.props;
+                onMessageDelete?.(message);
+            },
+            notifyMessageCopy: () => {
+                const { message, onMessageCopy } = this.props;
+                onMessageCopy?.(message);
+            },
+            copyToClipboardAndToast: () => {
+                const { message = {}, toast } = this.props;
+                if (typeof message.content === 'string') {
+                    copy(message.content);
+                } else if (Array.isArray(message.content)) {
+                    const content = message.content?.map(item => item.text).join('');
+                    copy(content);
+                }
+                toast.success({
+                    content: this.copySuccessNode
+                });
+            },
+            notifyLikeMessage: () => {
+                const { message, onMessageGoodFeedback } = this.props;
+                onMessageGoodFeedback?.(message);
+            },
+            notifyDislikeMessage: () => {
+                const { message, onMessageBadFeedback } = this.props;
+                onMessageBadFeedback?.(message);
+            },
+            notifyResetMessage: () => {
+                const { message, onMessageReset } = this.props;
+                onMessageReset?.(message);
+            },
+            setVisible: (visible) => {
+                this.setState({ visible });
+            },
+            setShowAction: (showAction) => {
+                this.setState({ showAction });
+            },
+            registerClickOutsideHandler: (cb: () => void) => {
+                if (this.clickOutsideHandler) {
+                    this.adapter.unregisterClickOutsideHandler();
+                }
+                this.clickOutsideHandler = (e: React.MouseEvent): any => {
+                    let el = this.popconfirmTriggerRef && this.popconfirmTriggerRef.current;
+                    const target = e.target as Element;
+                    const path = (e as any).composedPath && (e as any).composedPath() || [target];
+                    if (
+                        el && !(el as any).contains(target) && 
+                        ! path.includes(el)
+                    ) {
+                        cb();
+                    }
+                };
+                window.addEventListener('mousedown', this.clickOutsideHandler);
+            },
+            unregisterClickOutsideHandler: () => {
+                if (this.clickOutsideHandler) {
+                    window.removeEventListener('mousedown', this.clickOutsideHandler);
+                    this.clickOutsideHandler = null;
+                }
+            },
+        };
+    }
+
+    copyNode = () => {
+        return <Button
+            key={'copy'}
+            theme='borderless'
+            icon={<IconCopyStroked />}
+            type='tertiary'
+            onClick={this.foundation.copyMessage}
+            className={`${PREFIX_CHAT_BOX_ACTION}-btn`}
+        />;
+    }
+
+    likeNode = () => {
+        const { message = {} } = this.props;
+        const { like } = message;
+        return <Button
+            key={'like'}
+            theme='borderless'
+            icon={like ? <IconLikeThumb /> : <IconThumbUpStroked /> }
+            type='tertiary'
+            className={`${PREFIX_CHAT_BOX_ACTION}-btn`}
+            onClick={this.foundation.likeMessage}
+        />;
+    }
+
+    dislikeNode = () => {
+        const { message = {} } = this.props;
+        const { dislike } = message;
+        return <Button
+            theme='borderless'
+            key={'dislike'}
+            icon={dislike ? <IconLikeThumb className={`${PREFIX_CHAT_BOX_ACTION}-icon-flip`} /> : <IconThumbUpStroked className={'semi-chat-chatBox-action-icon-flip'} />}
+            type='tertiary'
+            className={`${PREFIX_CHAT_BOX_ACTION}-btn`}
+            onClick={this.foundation.dislikeMessage}
+        />;
+    }
+
+    resetNode = () => {
+        return <Button
+            key={'reset'}
+            theme='borderless'
+            icon={<IconRedoStroked className={`${PREFIX_CHAT_BOX_ACTION}-icon-redo`}/>}
+            type='tertiary'
+            onClick={this.foundation.resetMessage}
+            className={`${PREFIX_CHAT_BOX_ACTION}-btn`}
+        />;
+    }
+
+    deleteNode = () => {
+        const deleteMessage = (<LocaleConsumer<Locale["Chat"]> componentName="Chat" >
+            {(locale: Locale["Chat"]) => locale['deleteConfirm']}
+        </LocaleConsumer>);
+        return (<Popconfirm
+            trigger="custom"
+            visible={this.state.visible}
+            key={'delete'}
+            title={deleteMessage}
+            onConfirm={this.foundation.deleteMessage}
+            onCancel={this.foundation.hideDeletePopup}
+            position='top'
+        >
+            <span 
+                ref={this.popconfirmTriggerRef}
+                className={`${PREFIX_CHAT_BOX_ACTION}-delete-wrap`}
+            >
+                <Button
+                    theme='borderless'
+                    icon={<IconDeleteStroked />}
+                    type='tertiary'
+                    className={`${PREFIX_CHAT_BOX_ACTION}-btn`}
+                    onClick={this.foundation.showDeletePopup}
+                />
+            </span>
+        </Popconfirm>);
+    }
+
+    render() {
+        const { message = {}, lastChat } = this.props;
+        const { showAction } = this.state;
+        const { role, status = MESSAGE_STATUS.COMPLETE } = message;
+        const complete = status === MESSAGE_STATUS.COMPLETE ;
+        const showFeedback = role !== ROLE.USER && complete;
+        const showReset = lastChat && role === ROLE.ASSISTANT;
+        const finished = status !== MESSAGE_STATUS.LOADING && status !== MESSAGE_STATUS.INCOMPLETE;
+        const wrapCls = cls(PREFIX_CHAT_BOX_ACTION, { 
+            [`${PREFIX_CHAT_BOX_ACTION}-show`]: showReset && finished || showAction,
+            [`${PREFIX_CHAT_BOX_ACTION}-hidden`]: !finished,
+        });
+        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());
+            return customRenderFunc({
+                message,
+                defaultActions: actionNodes,
+                className: wrapCls
+            });
+        }
+        return <div className={wrapCls} ref={this.containerRef}>
+            {complete && this.copyNode()}
+            {showFeedback && this.likeNode()}
+            {showFeedback && this.dislikeNode()}
+            {showReset && this.resetNode()}
+            {this.deleteNode()}
+        </div>;
+    }
+}
+
+export default ChatBoxAction;

+ 42 - 0
packages/semi-ui/chat/chatBox/chatBoxAvatar.tsx

@@ -0,0 +1,42 @@
+import React, { useMemo, ReactNode, ReactElement } from 'react';
+import Avatar from '../../avatar';
+import { Metadata } from '../interface';
+import { cssClasses } from '@douyinfe/semi-foundation/chat/constants';
+import cls from 'classnames';
+
+const { PREFIX_CHAT_BOX } = cssClasses;
+
+interface ChatBoxAvatarProps {
+    children?: string;
+    role?: Metadata;
+    continueSend?: boolean;
+    customRenderFunc?: (props: {role?: Metadata; defaultAvatar?: ReactNode}) => ReactNode
+}
+
+const ChatBoxAvatar = React.memo((props: ChatBoxAvatarProps) => {
+    const { role, customRenderFunc, continueSend } = props;
+
+    const node = useMemo(() => {
+        const { avatar, color } = role;
+        return (<Avatar
+            className={cls(`${PREFIX_CHAT_BOX}-avatar`,
+                {
+                    [`${PREFIX_CHAT_BOX}-avatar-hidden`]: continueSend
+                })}
+            src={avatar}
+            size="extra-small"
+        >
+        </Avatar>);
+    }, [role]);
+
+    if (customRenderFunc && typeof customRenderFunc === 'function') {
+        return customRenderFunc({
+            role, 
+            defaultAvatar: node
+        }) as ReactElement;
+    }
+    return node;
+});
+
+export default ChatBoxAvatar;
+

+ 88 - 0
packages/semi-ui/chat/chatBox/chatBoxContent.tsx

@@ -0,0 +1,88 @@
+import React, { ReactElement, ReactNode, useMemo } from 'react';
+import cls from 'classnames';
+import { Message, Metadata } from '../interface';
+import MarkdownRender from '../../markdownRender';
+import { cssClasses, strings } from '@douyinfe/semi-foundation/chat/constants';
+import { MDXProps } from 'mdx/types';
+import { FileAttachment, ImageAttachment } from '../attachment';
+import Code from './code';
+
+const { PREFIX_CHAT_BOX } = cssClasses;
+const { MESSAGE_STATUS, MODE, ROLE } = strings;
+
+interface ChatBoxContentProps {
+    mode?: 'bubble' | 'noBubble' | 'userBubble';
+    customMarkDownComponents?: MDXProps['components'];
+    children?: string;
+    role?: Metadata;
+    message?: Message;
+    customRenderFunc?: (props: {message?: Message; role?: Metadata; defaultContent?: ReactNode | ReactNode[]; className?: string}) => ReactNode
+}
+
+const ChatBoxContent = (props: ChatBoxContentProps) => {
+    const { message = {}, customRenderFunc, role: roleInfo, customMarkDownComponents, mode } = props;
+    const { content, role, status } = message;
+
+    const markdownComponents = useMemo(() => ({
+        'code': Code,
+        'SemiFile': FileAttachment,
+        'img': ImageAttachment,
+        ...customMarkDownComponents
+    }), [customMarkDownComponents]);
+
+    const wrapCls = useMemo(() => {
+        const isUser = role === ROLE.USER;
+        const bubble = mode === MODE.BUBBLE;
+        const userBubble = mode === MODE.USER_BUBBLE && isUser;
+        return cls(`${PREFIX_CHAT_BOX}-content`, {
+            [`${PREFIX_CHAT_BOX}-content-${mode}`]: bubble || userBubble,
+            [`${PREFIX_CHAT_BOX}-content-user`]: (bubble && isUser) || userBubble,
+            [`${PREFIX_CHAT_BOX}-content-error`]: status === MESSAGE_STATUS.ERROR && (bubble || userBubble)
+        });
+    }, [role, status]);
+
+    const node = useMemo(() => {
+        if (status === MESSAGE_STATUS.LOADING) {
+            return <span className={`${PREFIX_CHAT_BOX}-content-loading`} >
+                <span className={`${PREFIX_CHAT_BOX}-content-loading-item`} />
+            </span>;
+        } else {
+            let realContent = '';
+            if (typeof content === 'string') {
+                realContent = content;
+            } else if (Array.isArray(content)) {
+                realContent = content.map((item)=> {
+                    if (item.type === 'text') {
+                        return item.text;
+                    } else if (item.type === 'image_url') {
+                        return `![image](${item.image_url.url})`;
+                    } else if (item.type === 'file_url') {
+                        const { name, size, url, type } = item.file_url;
+                        const realType = name.split('.').pop() ?? type?.split('/').pop();
+                        return `<SemiFile url={'${url}'} name={'${name}'} size={'${size}'} type={'${realType}'}></SemiFile>`;
+                    }
+                    return '';
+                }).join('\n\n');
+            }
+            return (<>
+                <MarkdownRender
+                    raw={realContent}
+                    components={markdownComponents as any}
+                />
+            </>);
+        }
+    }, [status, content]);
+        
+    if (customRenderFunc) {
+        return customRenderFunc({ 
+            message, 
+            role: roleInfo,
+            defaultContent: node,
+            className: wrapCls,
+        }) as ReactElement;
+    } else {
+        return <div className={wrapCls}>{node}</div>; 
+    } 
+};
+
+export default ChatBoxContent;

+ 31 - 0
packages/semi-ui/chat/chatBox/chatBoxTitle.tsx

@@ -0,0 +1,31 @@
+import React, { useMemo, ReactNode, ReactElement } from 'react';
+import { Message, Metadata } from '../interface';
+import { cssClasses } from '@douyinfe/semi-foundation/chat/constants';
+const { PREFIX_CHAT_BOX } = cssClasses;
+
+interface ChatBoxTitleProps {
+    children?: ReactNode | undefined | any;
+    role?: Metadata;
+    message?: Message;
+    customRenderFunc?: (props: {role?: Metadata; message: Message; defaultTitle?: ReactNode}) => ReactNode
+}
+
+const ChatBoxTitle = React.memo((props: ChatBoxTitleProps) => {
+    const { role, message, customRenderFunc } = props;
+    const title = useMemo(() => {
+        return <span
+            className={`${PREFIX_CHAT_BOX}-title`}
+        >{role?.name}</span>;
+    }, [role]);
+
+    if (customRenderFunc && typeof customRenderFunc === 'function') {
+        return customRenderFunc({
+            role,
+            message,
+            defaultTitle: title
+        }) as ReactElement;
+    }
+    return title;
+});
+
+export default ChatBoxTitle;

+ 54 - 0
packages/semi-ui/chat/chatBox/code.tsx

@@ -0,0 +1,54 @@
+import React, { useCallback, useMemo, useState } from 'react';
+import { PropsWithChildren } from 'react';
+import { cssClasses } from '@douyinfe/semi-foundation/chat/constants';
+import copy from 'copy-text-to-clipboard';
+import { IconCopyStroked, IconTick } from '@douyinfe/semi-icons';
+import { nth } from 'lodash';
+import { code } from '../../markdownRender/components';
+// code's default height type is html/js/css, add jsx & tsx;
+import "prismjs/components/prism-jsx.js";
+import "prismjs/components/prism-tsx.js";
+import LocaleConsumer from "../../locale/localeConsumer";
+import { Locale } from "../../locale/interface";
+
+const { PREFIX_CHAT_BOX } = cssClasses;
+
+const Code = (props: PropsWithChildren<{ className: string }>) => {
+    const [copied, setCopied] = useState(false);
+    const language = useMemo(() => {
+        return nth(props.className?.split("-"), -1);
+    }, [props.className]);
+
+    const onCopyButtonClick = useCallback(() => {
+        copy(props.children as string);
+        setCopied(true);
+        setTimeout(() => {
+            setCopied(false);
+        }, 2000);
+    }, [props.children]);
+    
+    return language ? (<div className={`${PREFIX_CHAT_BOX}-content-code semi-always-dark`}>
+        <div className={`${PREFIX_CHAT_BOX}-content-code-topSlot`}>
+            <span className={`${PREFIX_CHAT_BOX}-content-code-topSlot-type`}>{language}</span>
+            <span className={`${PREFIX_CHAT_BOX}-content-code-topSlot-copy`}>
+                {copied ? (<span className={`${PREFIX_CHAT_BOX}-content-code-topSlot-copy-wrapper`}>
+                    <IconTick />
+                    <LocaleConsumer<Locale["Chat"]> componentName="Chat" >
+                        {(locale: Locale["Chat"]) => locale['copied']}
+                    </LocaleConsumer>
+                </span>) : (<button 
+                    className={`${PREFIX_CHAT_BOX}-content-code-topSlot-copy-wrapper ${PREFIX_CHAT_BOX}-content-code-topSlot-toCopy`} 
+                    onClick={onCopyButtonClick}
+                >
+                    <IconCopyStroked />
+                    <LocaleConsumer<Locale["Chat"]> componentName="Chat" >
+                        {(locale: Locale["Chat"]) => locale['copy']}
+                    </LocaleConsumer>
+                </button>)}
+            </span> 
+        </div>
+        {code(props)}
+    </div>) : (code(props));
+};
+
+export default Code;

+ 118 - 0
packages/semi-ui/chat/chatBox/index.tsx

@@ -0,0 +1,118 @@
+import React, { useMemo, useEffect, ReactElement } from 'react';
+import cls from 'classnames';
+import type { ChatBoxProps } from '../interface';
+import ChatBoxAvatar from './chatBoxAvatar';
+import ChatBoxTitle from './chatBoxTitle';
+import ChatBoxContent from './chatBoxContent';
+import ChatBoxAction from './chatBoxAction';
+import { cssClasses, strings } from '@douyinfe/semi-foundation/chat/constants';
+
+const { PREFIX_CHAT_BOX } = cssClasses;
+const { ROLE, CHAT_ALIGN } = strings;
+
+const ChatBox = React.memo((props: ChatBoxProps) => {
+    const { message, lastChat, align, toast, mode,
+        roleConfig, 
+        onMessageBadFeedback, 
+        onMessageGoodFeedback,
+        onMessageCopy, 
+        onChatsChange,
+        onMessageDelete,
+        onMessageReset,
+        chatBoxRenderConfig = {}, 
+        customMarkDownComponents,
+        previousMessage,
+    } = props;
+    const { renderChatBoxAvatar, renderChatBoxAction, 
+        renderChatBoxContent, renderChatBoxTitle,
+        renderFullChatBox
+    } = chatBoxRenderConfig;
+
+    const continueSend = useMemo(() => {
+        return message?.role === previousMessage?.role;
+    }, [message.role, previousMessage])
+
+    const info = useMemo(() => {
+        let info = {};
+        if (roleConfig) {
+            info = roleConfig[message.role] ?? {};
+        }
+        return info;
+    }, [message.role, roleConfig]);
+
+    const avatarNode = useMemo(() => {
+        return (<ChatBoxAvatar
+            continueSend={continueSend}
+            role={info} 
+            customRenderFunc={renderChatBoxAvatar}
+        />);
+    }, [info, renderChatBoxAvatar]);
+
+    const titleNode = useMemo(() => {
+        return (<ChatBoxTitle 
+            role={info} 
+            message={message}
+            customRenderFunc={renderChatBoxTitle}
+        />);
+    }, [info, message, renderChatBoxTitle]);
+
+    const contentNode = useMemo(() => {
+        return (<ChatBoxContent
+            mode={mode}
+            role={info}
+            message={message}
+            customMarkDownComponents={customMarkDownComponents}
+            customRenderFunc={renderChatBoxContent}
+        />);
+    }, [message, info, renderChatBoxContent]);
+
+    const actionNode = useMemo(() => {
+        return (<ChatBoxAction 
+            toast={toast}
+            role={info} 
+            message={message}
+            lastChat={lastChat}
+            onMessageBadFeedback={onMessageBadFeedback}
+            onMessageCopy={onMessageCopy}
+            onChatsChange={onChatsChange}
+            onMessageDelete={onMessageDelete}
+            onMessageGoodFeedback={onMessageGoodFeedback}
+            onMessageReset={onMessageReset}
+            customRenderFunc={renderChatBoxAction}
+        />);
+    }, [message, info, lastChat, onMessageBadFeedback, onMessageGoodFeedback, onMessageCopy, onChatsChange, onMessageDelete, onMessageReset, renderChatBoxAction]);
+
+    const containerCls = useMemo(() => cls(PREFIX_CHAT_BOX, {
+        [`${PREFIX_CHAT_BOX}-right`]: message.role === ROLE.USER && align === CHAT_ALIGN.LEFT_RIGHT,
+    }
+    ), [message.role, align]);
+
+    if (typeof renderFullChatBox !== 'function') {
+        return (<div
+            className={containerCls}
+        >
+            {avatarNode}
+            <div
+                className={`${PREFIX_CHAT_BOX}-wrap`}
+            >
+                {!continueSend && titleNode}
+                {contentNode}
+                {actionNode}
+            </div>
+        </div>);
+    } else {
+        return renderFullChatBox({
+            message,
+            role: info,
+            defaultNodes: {
+                avatar: avatarNode,
+                title: titleNode,
+                content: contentNode,
+                action: actionNode,
+            },
+            className: containerCls
+        }) as ReactElement;
+    }
+});
+
+export default ChatBox;

+ 58 - 0
packages/semi-ui/chat/chatContent.tsx

@@ -0,0 +1,58 @@
+import React from "react";
+import Divider from '../divider';
+import ChatBox from './chatBox';
+import type { CommonChatsProps } from "./interface";
+import { cssClasses, strings } from "@douyinfe/semi-foundation/chat/constants";
+import LocaleConsumer from "../locale/localeConsumer";
+import { Locale } from "../locale/interface";
+import { Toast } from '../index';
+
+const { PREFIX_DIVIDER, PREFIX } = cssClasses;
+const { ROLE } = strings;
+
+interface ChatContentProps extends CommonChatsProps {}
+
+const ChatContent = React.memo((props: ChatContentProps) => {
+    const { chats, onMessageBadFeedback, onMessageCopy, mode,
+        onChatsChange, onMessageDelete, onMessageGoodFeedback,
+        onMessageReset, roleConfig, chatBoxRenderConfig, align,
+        customMarkDownComponents,
+    } = props;
+
+    const [toast, contextHolder] = Toast.useToast();
+
+    return (
+        <>
+            {chats.map((item, index) => {
+                const lastMessage = index === chats.length - 1;
+                return item.role === ROLE.DIVIDER ? 
+                    <Divider key={item.id} className={PREFIX_DIVIDER}>
+                        <LocaleConsumer<Locale["Chat"]> componentName="Chat" >
+                            {(locale: Locale["Chat"]) => locale['clearContext']}
+                        </LocaleConsumer>
+                    </Divider> :
+                    <ChatBox
+                        previousMessage={index ? chats[index - 1] : undefined}
+                        toast={toast}
+                        align={align} 
+                        mode={mode}
+                        key={item.id} 
+                        message={item}
+                        roleConfig={roleConfig}
+                        onMessageBadFeedback={onMessageBadFeedback}
+                        onMessageCopy={onMessageCopy}
+                        onChatsChange={onChatsChange}
+                        onMessageDelete={onMessageDelete}
+                        onMessageGoodFeedback={onMessageGoodFeedback}
+                        onMessageReset={onMessageReset}
+                        lastChat={lastMessage}
+                        customMarkDownComponents={customMarkDownComponents}
+                        chatBoxRenderConfig={chatBoxRenderConfig}
+                    />;
+            })}
+            <div className={`${PREFIX}-toast`}>{contextHolder as any}</div>
+        </>
+    );  
+});
+
+export default ChatContent;

+ 53 - 0
packages/semi-ui/chat/hint.tsx

@@ -0,0 +1,53 @@
+import React from "react";
+import { IconArrowRight } from "@douyinfe/semi-icons";
+import cls from 'classnames';
+import { cssClasses } from '@douyinfe/semi-foundation/chat/constants';
+const { PREFIX_HINT } = cssClasses;
+
+interface HintProps {
+    className?: string;
+    style?: React.CSSProperties;
+    value?: string[];
+    onHintClick?: (item: string) => void;
+    renderHintBox?: (props: {content: string; index: number; onHintClick: () => void}) => React.ReactNode
+}
+
+const Hint = React.memo((props: HintProps) => {
+    const { value, onHintClick, renderHintBox, className, style } = props;
+    return (
+        <section 
+            className={cls(`${PREFIX_HINT}s`, {
+                [className]: !!className,
+            })}
+            style={style}
+        >
+            {value.map((item, index) => {
+                if (renderHintBox) {
+                    return renderHintBox({
+                        content: item, 
+                        index: index,
+                        onHintClick: () => {
+                            onHintClick?.(item);
+                        }
+                    });
+                }
+                return (
+                    <div 
+                        className={`${PREFIX_HINT}-item`}
+                        key={index} 
+                        onClick={() => {
+                            onHintClick?.(item);
+                        }}
+                    >
+                        <div className={`${PREFIX_HINT}-content`}>
+                            {item}
+                        </div>
+                        <IconArrowRight className={`${PREFIX_HINT}-icon`}/>
+                    </div>
+                );
+            })} 
+        </section> 
+    );
+});
+
+export default Hint;

+ 382 - 0
packages/semi-ui/chat/index.tsx

@@ -0,0 +1,382 @@
+import * as React from 'react';
+import BaseComponent from '../_base/baseComponent';
+import cls from "classnames";
+import PropTypes from 'prop-types';
+import type { ChatProps, ChatState, Message } from './interface';
+import InputBox from './inputBox';
+import "@douyinfe/semi-foundation/chat/chat.scss";
+import Hint from './hint';
+import { IconChevronDown, IconDisc } from '@douyinfe/semi-icons';
+import ChatContent from './chatContent';
+import { getDefaultPropsFromGlobalConfig } from '../_utils';
+import { cssClasses, strings } from '@douyinfe/semi-foundation/chat/constants';
+import ChatFoundation, { ChatAdapter } from '@douyinfe/semi-foundation/chat/foundation';
+import type { FileItem } from '../upload';
+import LocaleConsumer from "../locale/localeConsumer";
+import { Locale } from "../locale/interface";
+import { Button, Upload } from '../index';
+
+const prefixCls = cssClasses.PREFIX;
+const { CHAT_ALIGN, MODE, SEND_HOT_KEY } = strings;
+
+class Chat extends BaseComponent<ChatProps, ChatState> {
+
+    static __SemiComponentName__ = "Chat";
+  
+    containerRef: React.RefObject<HTMLDivElement>;
+    animation: any;
+    wheelEventHandler: any;
+    foundation: ChatFoundation;
+    uploadRef: React.RefObject<Upload>;
+    dropAreaRef: React.RefObject<HTMLDivElement>;
+
+    static propTypes = {
+        className: PropTypes.string,
+        style: PropTypes.object,
+        roleConfig: PropTypes.object,
+        chats: PropTypes.array,
+        hints: PropTypes.array,
+        renderHintBox: PropTypes.func,
+        onChatsChange: PropTypes.func,
+        align: PropTypes.string,
+        chatBoxRenderConfig: PropTypes.object,
+        customMarkDownComponents: PropTypes.object,
+        onClear: PropTypes.func,
+        onMessageDelete: PropTypes.func,
+        onMessageReset: PropTypes.func,
+        onMessageCopy: PropTypes.func,
+        onMessageGoodFeedback: PropTypes.func,
+        onMessageBadFeedback: PropTypes.func,
+        inputContentConvert: PropTypes.func,
+        onMessageSend: PropTypes.func,
+        InputBoxStyle: PropTypes.object,
+        inputBoxCls: PropTypes.string,
+        renderFullInputBox: PropTypes.func,
+        placeholder: PropTypes.string,
+        topSlot: PropTypes.node || PropTypes.array,
+        bottomSlot: PropTypes.node || PropTypes.array,
+        showStopGenerate: PropTypes.bool,
+        showClearContext: PropTypes.bool,
+        hintStyle: PropTypes.object,
+        hintCls: PropTypes.string,
+        uploadProps: PropTypes.object,
+        uploadTipProps: PropTypes.object,
+        mode: PropTypes.string,
+    };
+
+    static defaultProps = getDefaultPropsFromGlobalConfig(Chat.__SemiComponentName__, {
+        align: CHAT_ALIGN.LEFT_RIGHT,
+        showStopGenerate: false,
+        mode: MODE.BUBBLE,
+        showClearContext: false,
+        sendHotKey: SEND_HOT_KEY.ENTER,
+    })
+
+    constructor(props: ChatProps) {
+        super(props);
+
+        this.containerRef = React.createRef();
+        this.uploadRef = React.createRef();
+        this.dropAreaRef = React.createRef();
+        this.wheelEventHandler = null;
+        this.foundation = new ChatFoundation(this.adapter);
+
+        this.state = {
+            backBottomVisible: false,
+            chats: [],
+            cacheHints: [],
+            wheelScroll: false,
+            uploadAreaVisible: false,
+        };
+    }
+
+    get adapter(): ChatAdapter {
+        return {
+            ...super.adapter,
+            getContainerRef: () => this.containerRef,
+            setWheelScroll: (flag: boolean) => {
+                this.setState({
+                    wheelScroll: flag,
+                });
+            },
+            notifyChatsChange: (chats: Message[]) => {
+                const { onChatsChange } = this.props;
+                onChatsChange && onChatsChange(chats);
+            },
+            notifyLikeMessage: (message: Message) => {
+                const { onMessageGoodFeedback } = this.props;
+                onMessageGoodFeedback && onMessageGoodFeedback(message);
+            },
+            notifyDislikeMessage: (message: Message) => {
+                const { onMessageBadFeedback } = this.props;
+                onMessageBadFeedback && onMessageBadFeedback(message);
+            },
+            notifyCopyMessage: (message: Message) => {
+                const { onMessageCopy } = this.props;
+                onMessageCopy && onMessageCopy(message);
+            },
+            notifyClearContext: () => {
+                const { onClear } = this.props;
+                onClear && onClear();
+            },
+            notifyMessageSend: (content: string, attachment: any[]) => {
+                const { onMessageSend } = this.props;
+                onMessageSend && onMessageSend(content, attachment);
+            },
+            notifyInputChange: (props: { inputValue: string; attachment: any[]}) => {
+                const { onInputChange } = this.props;
+                onInputChange && onInputChange(props);
+            },
+            setBackBottomVisible: (visible: boolean) => {
+                this.setState((state) => {
+                    if (state.backBottomVisible !== visible) {
+                        return {
+                            backBottomVisible: visible,
+                        };
+                    }
+                    return null;
+                });
+            },
+            registerWheelEvent: () => {
+                this.adapter.unRegisterWheelEvent();
+                const containerElement = this.containerRef.current;
+                if (!containerElement) {
+                    return ;
+                }
+                this.wheelEventHandler = (e: any) => {
+                    if (e.target !== containerElement) {
+                        return;
+                    }
+                    this.adapter.setWheelScroll(true);
+                    this.adapter.unRegisterWheelEvent();
+                };
+        
+                containerElement.addEventListener('wheel', this.wheelEventHandler);
+            },
+            unRegisterWheelEvent: () => {
+                if (this.wheelEventHandler) {
+                    const containerElement = this.containerRef.current;
+                    if (!containerElement) {
+                        return ;
+                    } else {
+                        containerElement.removeEventListener('wheel', this.wheelEventHandler);
+                    }
+                    this.wheelEventHandler = null;
+                }
+            },
+            notifyStopGenerate: (e: MouseEvent) => {
+                const { onStopGenerator } = this.props;
+                onStopGenerator && onStopGenerator(e);
+            },
+            notifyHintClick: (hint: string) => {
+                const { onHintClick } = this.props;
+                onHintClick && onHintClick(hint);
+            },
+            setUploadAreaVisible: (visible: boolean) => {
+                this.setState({ uploadAreaVisible: visible });
+            },
+            manualUpload: (file: File[]) => {
+                const uploadComponent = this.uploadRef.current;
+                if (uploadComponent) {
+                    uploadComponent.insert(file);
+                }
+            },
+            getDropAreaElement: () => {
+                return this.dropAreaRef?.current;
+            } 
+        };
+    }
+
+    static getDerivedStateFromProps(nextProps: ChatProps, prevState: ChatState) {
+        const { chats, hints } = nextProps;
+        const newState = {} as any;
+        if (chats !== prevState.chats) {
+            newState.chats = chats;
+        }
+        if (hints !== prevState.cacheHints) {
+            newState.cacheHints = hints;
+        }
+        if (Object.keys(newState).length) {
+            return newState;
+        }
+        return null;
+    }
+
+    componentDidMount(): void {
+        this.foundation.init();    
+    }
+
+    componentDidUpdate(prevProps: Readonly<ChatProps>, prevState: Readonly<ChatState>, snapshot?: any): void {
+        const { chats: newChats, hints: newHints } = this.props;
+        const { chats: oldChats, cacheHints } = prevState;
+        const { wheelScroll } = this.state;
+        let shouldScroll = false;
+        if (newChats !== oldChats) {
+            const newLastChat = newChats[newChats.length - 1];
+            const oldLastChat = oldChats[oldChats.length - 1];
+            if (newChats.length > oldChats.length) {
+                if (newLastChat.id !== oldLastChat.id) {
+                    shouldScroll = true;
+                }
+            } else if (newChats.length === oldChats.length &&
+        (
+            newLastChat.status !== 'complete' ||
+          newLastChat.status !== oldLastChat.status
+        )
+            ) {
+                shouldScroll = true;
+            }
+        }
+        if (newHints !== cacheHints) {
+            if (newHints.length > cacheHints.length) {
+                shouldScroll = true;
+            }
+        }
+        if (!wheelScroll && shouldScroll) {
+            this.foundation.scrollToBottomImmediately();
+        }
+    }
+
+    componentWillUnmount(): void {
+        this.foundation.destroy();
+    }
+
+    resetMessage() {
+        this.foundation.resetMessage(null);
+    }
+
+    clearContext() {
+        this.foundation.clearContext(null);
+    }
+
+    scrollToBottom(animation: boolean) {
+        if (animation) {
+            this.foundation.scrollToBottomWithAnimation();
+        } else {
+            this.foundation.scrollToBottomImmediately();
+        }
+    }
+
+    sendMessage(content: string, attachment: FileItem[]) {
+        this.foundation.onMessageSend(content, attachment);
+    }
+
+    render() {
+        const { topSlot, bottomSlot, roleConfig, hints,
+            onChatsChange, onMessageCopy, renderInputArea,
+            chatBoxRenderConfig, align, renderHintBox,
+            style, className, showStopGenerate,
+            customMarkDownComponents, mode, showClearContext,
+            placeholder, inputBoxCls, inputBoxStyle,
+            hintStyle, hintCls, uploadProps, uploadTipProps,
+            sendHotKey,
+        } = this.props;
+        const { backBottomVisible, chats, wheelScroll, uploadAreaVisible } = this.state;
+        let showStopGenerateFlag = false;
+        const lastChat = chats.length > 0 && chats[chats.length - 1];
+        let disableSend = false;
+        if (lastChat && showStopGenerate) {
+            const lastChatOnGoing = lastChat.status && lastChat.status !== 'complete';
+            disableSend = lastChatOnGoing;
+            showStopGenerate && (showStopGenerateFlag = lastChatOnGoing);
+        }
+        return (
+            <div
+                className={cls(`${prefixCls}`, className)} 
+                style={style}
+                onDragOver={this.foundation.handleDragOver}
+            >
+                {uploadAreaVisible && <div
+                    ref={this.dropAreaRef} 
+                    className={`${prefixCls}-dropArea`}
+                    onDragOver={this.foundation.handleContainerDragOver}
+                    onDrop={this.foundation.handleContainerDrop}
+                    onDragLeave={this.foundation.handleContainerDragLeave}
+                >
+                    <span className={`${prefixCls}-dropArea-text`}>
+                        <LocaleConsumer<Locale["Chat"]> componentName="Chat" >
+                            {(locale: Locale["Chat"]) => locale['dropAreaText']}
+                        </LocaleConsumer>
+                    </span>  
+                </div>}
+                <div className={`${prefixCls}-inner`}>
+                    {/* top slot */}
+                    {topSlot}
+                    {/* chat area */}
+                    <div className={`${prefixCls}-content`}>
+                        <div 
+                            className={cls(`${prefixCls}-container`, {
+                                'semi-chat-container-scroll-hidden': !wheelScroll
+                            })}
+                            onScroll={this.foundation.containerScroll}
+                            ref={this.containerRef}
+                        >
+                            <ChatContent 
+                                align={align}
+                                mode={mode}
+                                chats={chats}  
+                                roleConfig={roleConfig}
+                                customMarkDownComponents={customMarkDownComponents}
+                                onMessageDelete={this.foundation.deleteMessage}
+                                onChatsChange={onChatsChange}
+                                onMessageBadFeedback={this.foundation.dislikeMessage}
+                                onMessageGoodFeedback={this.foundation.likeMessage}
+                                onMessageReset={this.foundation.resetMessage}
+                                onMessageCopy={onMessageCopy}
+                                chatBoxRenderConfig={chatBoxRenderConfig}
+                            />
+                            {/* hint area */}
+                            {!!hints?.length && <Hint 
+                                className={hintCls}
+                                style={hintStyle}
+                                value={hints} 
+                                onHintClick={this.foundation.onHintClick}
+                                renderHintBox={renderHintBox}
+                            />}
+                        </div>
+                    </div>
+                    {backBottomVisible && !showStopGenerateFlag && (<span className={`${prefixCls}-action`}>
+                        <Button
+                            className={`${prefixCls}-action-content ${prefixCls}-action-backBottom`} 
+                            icon={<IconChevronDown size="extra-large"/>}
+                            type="tertiary"
+                            onClick={this.foundation.scrollToBottomWithAnimation}
+                        />
+                    </span>)}
+                    {showStopGenerateFlag && (<span className={`${prefixCls}-action`}>
+                        <Button
+                            className={`${prefixCls}-action-content ${prefixCls}-action-stop`} 
+                            icon={<IconDisc size="extra-large" />}
+                            type="tertiary"
+                            onClick={this.foundation.stopGenerate} 
+                        >
+                            <LocaleConsumer<Locale["Chat"]> componentName="Chat" >
+                                {(locale: Locale["Chat"]) => locale['stop']}
+                            </LocaleConsumer>
+                        </Button>
+                    </span>)}
+                    {/* input area */}
+                    <InputBox
+                        showClearContext={showClearContext}
+                        uploadRef={this.uploadRef}
+                        manualUpload={this.adapter.manualUpload}
+                        style={inputBoxStyle}
+                        className={inputBoxCls}
+                        placeholder={placeholder}
+                        disableSend={disableSend}
+                        onClearContext={this.foundation.clearContext}
+                        onSend={this.foundation.onMessageSend}
+                        onInputChange={this.foundation.onInputChange}
+                        renderInputArea={renderInputArea}
+                        uploadProps={uploadProps}
+                        uploadTipProps={uploadTipProps}
+                        sendHotKey={sendHotKey}
+                    />
+                    {bottomSlot}
+                </div>
+            </div>
+        );
+    }
+}
+
+export default Chat;

+ 170 - 0
packages/semi-ui/chat/inputBox/index.tsx

@@ -0,0 +1,170 @@
+import React, { PureComponent } from 'react';
+import cls from 'classnames';
+import PropTypes from 'prop-types';
+import { FileItem } from '../../upload/interface';
+import type { InputBoxProps, InputBoxState } from '../interface';
+import { BaseComponent, Button, Upload, Tooltip, TextArea } from '../../index';
+import { IconDeleteStroked, IconChainStroked, IconArrowUp } from '@douyinfe/semi-icons';
+import { cssClasses, strings } from "@douyinfe/semi-foundation/chat/constants";
+import InputBoxFoundation, { InputBoxAdapter } from '@douyinfe/semi-foundation/chat/inputboxFoundation';
+import Attachment from '../attachment';
+
+const { PREFIX_INPUT_BOX } = cssClasses;
+const { SEND_HOT_KEY } = strings;
+const textAutoSize = { minRows: 1, maxRows: 5 };
+
+class InputBox extends BaseComponent<InputBoxProps, InputBoxState> {
+
+    inputAreaRef: React.RefObject<any>;
+    static propTypes = {
+        uploadProps: PropTypes.object,
+    };
+
+    static defaultProps = {
+        uploadProps: {}
+    };
+
+    constructor(props: InputBoxProps) {
+        super(props);
+        this.inputAreaRef = React.createRef();
+        this.foundation = new InputBoxFoundation(this.adapter);
+
+        this.state = {
+            content: '',
+            attachment: []
+        };
+    }
+
+    get adapter(): InputBoxAdapter<InputBoxProps, InputBoxState> {
+        return {
+            ...super.adapter,
+            notifyInputChange: (props: { inputValue: string; attachment: any[]}) => {
+                const { onInputChange } = this.props;
+                onInputChange && onInputChange(props);
+            },
+            setInputValue: (value) => {
+                this.setState({
+                    content: value
+                });
+            },
+            setAttachment: (attachment: any[]) => {
+                this.setState({
+                    attachment: attachment
+                });
+            },
+            notifySend: (content: string, attachment: FileItem[]) => {
+                const { onSend } = this.props;
+                onSend && onSend(content, attachment);
+            }
+        };
+    }
+
+    onClick = () => {
+        this.inputAreaRef.current?.focus();
+    }
+
+    renderUploadButton = () => {
+        const { uploadProps, uploadRef, uploadTipProps } = this.props;
+        const { attachment } = this.state;
+        const { className, onChange, renderFileItem, children, ...rest } = uploadProps;
+        const realUploadProps = {
+            ...rest,
+            className: cls(`${PREFIX_INPUT_BOX}-upload`, {
+                [className]: className
+            }),
+            onChange: this.foundation.onAttachmentAdd,
+        };
+        const uploadNode = <Upload
+            ref={uploadRef}
+            fileList={attachment}
+            {...realUploadProps}
+        >
+            {children ? children : <Button 
+                className={`${PREFIX_INPUT_BOX}-uploadButton`}
+                icon={<IconChainStroked size="extra-large" />}
+                theme='borderless'
+            />}
+        </Upload>;
+        return (uploadTipProps ? <Tooltip {...uploadTipProps}><span>{uploadNode}</span></Tooltip> : uploadNode);
+    }
+
+    renderInputArea = () => {
+        const { content, attachment } = this.state;
+        const { placeholder, sendHotKey } = this.props;
+        return (<div
+            className={`${PREFIX_INPUT_BOX}-inputArea`}
+        >
+            <TextArea
+                placeholder={placeholder}
+                onEnterPress={this.foundation.onEnterPress}
+                value={content}
+                onChange={this.foundation.onInputAreaChange}
+                ref={this.inputAreaRef}
+                className={`${PREFIX_INPUT_BOX}-textarea`}
+                autosize={textAutoSize} 
+                disabledEnterStartNewLine={sendHotKey === SEND_HOT_KEY.ENTER ? true : false}
+                onPaste={this.foundation.onPaste as any}
+            />
+            <Attachment 
+                attachment={attachment as any} 
+                onClear={this.foundation.onAttachmentDelete}
+            />
+        </div>);
+    }
+
+    renderClearButton = () => {
+        const { onClearContext } = this.props;
+        return (
+            <Button
+                className={`${PREFIX_INPUT_BOX}-clearButton`}
+                theme='borderless'
+                icon={<IconDeleteStroked />}
+                onClick={onClearContext}
+            />
+        );
+    }
+
+    renderSendButton = () => {
+        const disabledSend = this.foundation.getDisableSend();
+        return (
+            <Button
+                disabled={disabledSend}
+                theme='solid'
+                type='primary'
+                className={`${PREFIX_INPUT_BOX}-sendButton`}
+                // icon={<IconSend size="extra-large" className={`${PREFIX_INPUT_BOX}-sendButton-icon`} />}
+                icon={<IconArrowUp size="large" className={`${PREFIX_INPUT_BOX}-sendButton-icon`} />}
+                onClick={this.foundation.onSend}
+            />
+        );
+    }
+
+    render() {
+        const { onClearContext, renderInputArea, onSend, style, className, showClearContext } = this.props;
+        const nodes = (
+            <div className={cls(PREFIX_INPUT_BOX, { [className]: className })} style={style}>
+                <div 
+                    className={`${PREFIX_INPUT_BOX}-inner`}
+                    onClick={this.onClick}
+                >
+                    {showClearContext && this.renderClearButton()}
+                    <div className={`${PREFIX_INPUT_BOX}-container`}>
+                        {this.renderUploadButton()}
+                        {this.renderInputArea()}
+                        {this.renderSendButton()}
+                    </div>
+                </div>
+            </div>
+        );
+        if (renderInputArea) {
+            return renderInputArea({
+                defaultNode: nodes, 
+                onClear: onClearContext,
+                onSend: onSend,
+            });
+        }
+        return nodes; 
+    }
+}
+
+export default InputBox;

+ 126 - 0
packages/semi-ui/chat/interface.ts

@@ -0,0 +1,126 @@
+import React, { ReactNode } from 'react';
+import { MDXProps } from 'mdx/types';
+import { Upload } from '../index';
+import type { FileItem, UploadProps } from '../upload';
+import { Message } from '@douyinfe/semi-foundation/chat/foundation';
+import type { TooltipProps } from '../tooltip';
+
+export { Message };
+export interface CommonChatsProps {
+    align?: 'leftRight' | 'leftAlign';
+    mode?: 'bubble' | 'noBubble' | 'userBubble';
+    chats?: Message[];
+    roleConfig?: RoleConfig;
+    onMessageDelete?: (message?: Message) => void;
+    onChatsChange?: (chats?: Message[]) => void;
+    onMessageBadFeedback?: (message?: Message) => void;
+    onMessageGoodFeedback?: (message?: Message) => void;
+    onMessageReset?: (message?: Message) => void;
+    onMessageCopy?: (message?: Message) => void;
+    chatBoxRenderConfig?: ChatBoxRenderConfig;
+    customMarkDownComponents?: MDXProps['components']
+}
+
+export interface ChatProps extends CommonChatsProps {
+    style?: React.CSSProperties;
+    className?: string;
+    hints?: string[];
+    renderHintBox?: (props: {content: string; index: number;onHintClick: () => void}) => React.ReactNode;
+    onHintClick?: (hint: string) => void;
+    onChatsChange?: (chats?: Message[]) => void;
+    onStopGenerator?: (e?: MouseEvent) => void;
+    customMarkDownComponents?: MDXProps['components'];
+    onClear?: () => void;
+    onInputChange?: (props: { value?: string; attachment?: FileItem[] }) => void;
+    onMessageSend?: (content: string, attachment: FileItem[]) => void;
+    inputBoxStyle?: React.CSSProperties;
+    inputBoxCls?: string;
+    renderInputArea?: (props?: RenderInputAreaProps) => ReactNode;
+    placeholder?: string;
+    topSlot?: ReactNode | ReactNode[];
+    bottomSlot?: ReactNode | ReactNode[];
+    showStopGenerate?: boolean;
+    hintStyle?: React.CSSProperties;
+    hintCls?: string;
+    uploadProps?: UploadProps;
+    uploadTipProps?: TooltipProps;
+    showClearContext?: boolean;
+    sendHotKey?: 'enter' | 'shift+enter'
+}
+
+export interface RenderInputAreaProps {
+    defaultNode?: ReactNode;
+    onSend?: (content?: string, attachment?: FileItem[]) => void;
+    onClear?: (e?: any) => void
+}
+
+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
+}
+
+export interface FullChatBoxNodes {
+    avatar?: ReactNode;
+    title?: ReactNode; 
+    content?: ReactNode; 
+    action?: ReactNode
+}
+
+export interface RoleConfig {
+    user?: Metadata;
+    assistant?: Metadata;
+    system?: Metadata;
+    [x: string]: Metadata
+}
+
+export interface Metadata {
+    name?: string;
+    avatar?: string;
+    color?: string;
+    [x: string]: any
+}
+
+export interface ChatState {
+    chats?: Message[];
+    isLoading?: boolean;
+    backBottomVisible?: boolean;
+    scrollVisible?: boolean;
+    wheelScroll?: boolean;
+    cacheHints?: string[];
+    uploadAreaVisible?: boolean
+}
+
+export interface ChatBoxProps extends Omit<CommonChatsProps, "chats"> {
+    toast?: any;
+    style?: React.CSSProperties;
+    className?: string;
+    previousMessage?: Message;
+    message?: Message;
+    lastChat?: boolean;
+    customMarkDownComponents?: MDXProps['components']
+}
+
+export interface InputBoxProps {
+    showClearContext?: boolean;
+    sendHotKey?: 'enter' | 'shift+enter';
+    placeholder: string;
+    className?: string;
+    style?: React.CSSProperties;
+    disableSend?: boolean;
+    uploadRef?: React.RefObject<Upload>;
+    uploadTipProps?: TooltipProps;
+    uploadProps?: UploadProps;
+    manualUpload?: (file: File[]) => void;
+    renderInputArea?: (props: RenderInputAreaProps) => React.ReactNode;
+    onSend?: (content: string, attachment: FileItem[]) => void;
+    onClearContext?: (e: any) => void;
+    onInputChange?: (props: {inputValue: string; attachment: FileItem[]}) => void
+}
+
+export interface InputBoxState {
+    content: string;
+    attachment: FileItem[]
+}

+ 2 - 1
packages/semi-ui/form/arrayField.tsx

@@ -142,7 +142,8 @@ class ArrayFieldComponent extends Component<ArrayFieldProps, ArrayFieldState> {
         const updater = this.context;
         const updater = this.context;
         const { field } = this.props;
         const { field } = this.props;
         const newArrayFieldVal = updater.getValue(field) ? updater.getValue(field).slice() : [];
         const newArrayFieldVal = updater.getValue(field) ? updater.getValue(field).slice() : [];
-        newArrayFieldVal.push(rowVal);
+        const cloneRowVal = copy(rowVal);
+        newArrayFieldVal.push(cloneRowVal);
         updater.updateStateValue(field, newArrayFieldVal, {});
         updater.updateStateValue(field, newArrayFieldVal, {});
         updater.updateArrayField(field, { updateKey: new Date().valueOf() });
         updater.updateArrayField(field, { updateKey: new Date().valueOf() });
     }
     }

+ 10 - 0
packages/semi-ui/form/baseForm.tsx

@@ -75,6 +75,10 @@ class Form<Values extends Record<string, any> = any> extends BaseComponent<BaseF
         style: PropTypes.object,
         style: PropTypes.object,
         showValidateIcon: PropTypes.bool,
         showValidateIcon: PropTypes.bool,
         stopValidateWithError: PropTypes.bool,
         stopValidateWithError: PropTypes.bool,
+        stopPropagation: PropTypes.shape({
+            submit: PropTypes.bool,
+            reset: PropTypes.bool,
+        }),
         id: PropTypes.string,
         id: PropTypes.string,
         wrapperCol: PropTypes.object, // Control wrapperCol {span: number, offset: number} for all field child nodes
         wrapperCol: PropTypes.object, // Control wrapperCol {span: number, offset: number} for all field child nodes
         trigger: PropTypes.oneOfType([
         trigger: PropTypes.oneOfType([
@@ -240,11 +244,17 @@ class Form<Values extends Record<string, any> = any> extends BaseComponent<BaseF
 
 
     submit(e: React.FormEvent<HTMLFormElement>) {
     submit(e: React.FormEvent<HTMLFormElement>) {
         e.preventDefault();
         e.preventDefault();
+        if (this.props.stopPropagation && this.props.stopPropagation.submit) {
+            e.stopPropagation();
+        }
         this.foundation.submit(e);
         this.foundation.submit(e);
     }
     }
 
 
     reset(e: React.FormEvent<HTMLFormElement>) {
     reset(e: React.FormEvent<HTMLFormElement>) {
         e.preventDefault();
         e.preventDefault();
+        if (this.props.stopPropagation && this.props.stopPropagation.reset) {
+            e.stopPropagation();
+        }
         this.foundation.reset();
         this.foundation.reset();
     }
     }
 
 

+ 4 - 0
packages/semi-ui/form/interface.ts

@@ -128,5 +128,9 @@ export interface BaseFormProps <Values extends Record<string, any> = any> extend
     disabled?: boolean;
     disabled?: boolean;
     showValidateIcon?: boolean;
     showValidateIcon?: boolean;
     stopValidateWithError?: boolean;
     stopValidateWithError?: boolean;
+    stopPropagation?: {
+        submit?: boolean;
+        reset?: boolean
+    };
     trigger?: FieldValidateTriggerType
     trigger?: FieldValidateTriggerType
 }
 }

+ 3 - 1
packages/semi-ui/index.ts

@@ -108,4 +108,6 @@ export { default as PinCode } from "./pincode";
 
 
 export { default as MarkdownRender } from "./markdownRender";
 export { default as MarkdownRender } from "./markdownRender";
 export { default as CodeHighlight } from "./codeHighlight";
 export { default as CodeHighlight } from "./codeHighlight";
-export { default as Lottie } from "./lottie";
+export { default as Lottie } from "./lottie";
+
+export { default as Chat } from './chat';

+ 8 - 2
packages/semi-ui/input/textarea.tsx

@@ -56,7 +56,12 @@ export interface TextAreaProps extends Omit<React.TextareaHTMLAttributes<HTMLTex
     onPressEnter?: (e: React.KeyboardEvent<HTMLTextAreaElement>) => void;
     onPressEnter?: (e: React.KeyboardEvent<HTMLTextAreaElement>) => void;
     onResize?: (data: { height: number }) => void;
     onResize?: (data: { height: number }) => void;
     getValueLength?: (value: string) => number;
     getValueLength?: (value: string) => number;
-    forwardRef?: ((instance: HTMLTextAreaElement) => void) | React.MutableRefObject<HTMLTextAreaElement> | null
+    forwardRef?: ((instance: HTMLTextAreaElement) => void) | React.MutableRefObject<HTMLTextAreaElement> | null;
+    /* Inner params for TextArea, Chat use it, 。
+       Used to disable line breaks by pressing the enter key。
+       Press enter + shift at the same time can start new line.
+    */
+    disabledEnterStartNewLine?: boolean
 }
 }
 
 
 export interface TextAreaState {
 export interface TextAreaState {
@@ -85,6 +90,7 @@ class TextArea extends BaseComponent<TextAreaProps, TextAreaState> {
         onClear: PropTypes.func,
         onClear: PropTypes.func,
         onResize: PropTypes.func,
         onResize: PropTypes.func,
         getValueLength: PropTypes.func,
         getValueLength: PropTypes.func,
+        disabledEnterStartNewLine: PropTypes.bool,
         // TODO
         // TODO
         // resize: PropTypes.bool,
         // resize: PropTypes.bool,
     };
     };
@@ -284,7 +290,7 @@ class TextArea extends BaseComponent<TextAreaProps, TextAreaState> {
             [`${prefixCls}-textarea-showClear`]: showClear,
             [`${prefixCls}-textarea-showClear`]: showClear,
         });
         });
         const itemProps = {
         const itemProps = {
-            ...omit(rest, 'insetLabel', 'insetLabelId', 'getValueLength', 'onClear', 'showClear'),
+            ...omit(rest, 'insetLabel', 'insetLabelId', 'getValueLength', 'onClear', 'showClear', 'disabledEnterStartNewLine'),
             autoFocus: autoFocus || this.props['autofocus'],
             autoFocus: autoFocus || this.props['autofocus'],
             className: itemCls,
             className: itemCls,
             disabled,
             disabled,

+ 9 - 0
packages/semi-ui/locale/interface.ts

@@ -168,5 +168,14 @@ export interface Locale {
         downloadTip: string;
         downloadTip: string;
         adaptiveTip: string;
         adaptiveTip: string;
         originTip: string
         originTip: string
+    };
+    Chat: {
+        deleteConfirm: string;
+        clearContext: string;
+        copySuccess: string;
+        stop: string;
+        copy: string;
+        copied: string;
+        dropAreaText: string
     }
     }
 }
 }

+ 9 - 0
packages/semi-ui/locale/source/ar.ts

@@ -170,6 +170,15 @@ const local: Locale = {
         adaptiveTip: "التكيف مع الصفحة",
         adaptiveTip: "التكيف مع الصفحة",
         originTip: "الحجم الأصلي",
         originTip: "الحجم الأصلي",
     },
     },
+    Chat: {
+        deleteConfirm: 'هل ترغب في حذف هذه الجلسة؟',
+        clearContext: 'تم مسح السياق',
+        copySuccess: 'تم النسخ بنجاح',
+        stop: 'توقف',
+        copy: 'نسخ',
+        copied: 'نسخ',
+        dropAreaText: 'ضع الملف هنا',
+    },
 };
 };
 
 
 // [i18n-Arabic]
 // [i18n-Arabic]

+ 9 - 0
packages/semi-ui/locale/source/de.ts

@@ -170,6 +170,15 @@ const local: Locale = {
         adaptiveTip: 'An die Seite anpassen',
         adaptiveTip: 'An die Seite anpassen',
         originTip: 'Originalgröße',
         originTip: 'Originalgröße',
     },
     },
+    Chat: {
+        deleteConfirm: 'Möchten Sie diesen Chat wirklich löschen?',
+        clearContext: 'Der Kontext wurde gelöscht',
+        copySuccess: 'Erfolgreich kopiert',
+        stop: 'stoppen',
+        copy: 'Kopieren',
+        copied: 'Kopiert',
+        dropAreaText: 'Datei hier ablegen',
+    },
 };
 };
 
 
 // [i18n-German]
 // [i18n-German]

+ 9 - 0
packages/semi-ui/locale/source/en_GB.ts

@@ -170,6 +170,15 @@ const local: Locale = {
         adaptiveTip: 'Adapt to the page',
         adaptiveTip: 'Adapt to the page',
         originTip: 'Original size',
         originTip: 'Original size',
     },
     },
+    Chat: {
+        deleteConfirm: 'Are you sure you want to delete this session?',
+        clearContext: 'Context cleared',
+        copySuccess: 'Copy successful.',
+        stop: 'Stop',
+        copy: 'Copy',
+        copied: 'Copied',
+        dropAreaText: 'Put the file here',
+    }
 };
 };
 
 
 // [i18n-English(GB)]
 // [i18n-English(GB)]

+ 9 - 0
packages/semi-ui/locale/source/en_US.ts

@@ -170,6 +170,15 @@ const local: Locale = {
         adaptiveTip: 'Adapt to the page',
         adaptiveTip: 'Adapt to the page',
         originTip: 'Original size',
         originTip: 'Original size',
     },
     },
+    Chat: {
+        deleteConfirm: 'Are you sure you want to delete this session?',
+        clearContext: 'Context cleared',
+        copySuccess: 'Copy successful.',
+        stop: 'Stop',
+        copy: 'Copy',
+        copied: 'Copied',
+        dropAreaText: 'Put the file here',
+    }
 };
 };
 
 
 // [i18n-English(US)]
 // [i18n-English(US)]

+ 9 - 0
packages/semi-ui/locale/source/es.ts

@@ -175,6 +175,15 @@ const locale: Locale = {
         adaptiveTip: 'Adaptarse a la página',
         adaptiveTip: 'Adaptarse a la página',
         originTip: 'Tamaño original',
         originTip: 'Tamaño original',
     },
     },
+    Chat: {
+        deleteConfirm: '¿Estás seguro de que quieres eliminar esta conversación?',
+        clearContext: 'El contexto ha sido eliminado',
+        copySuccess: 'Copiado exitosamente',
+        stop: 'Detener',
+        copy: 'Copiar',
+        copied: 'Copiado',
+        dropAreaText: 'Coloca el archivo aquí',
+    },
 };
 };
 
 
 export default locale;
 export default locale;

+ 9 - 0
packages/semi-ui/locale/source/fr.ts

@@ -170,6 +170,15 @@ const local: Locale = {
         adaptiveTip: 'Adapter à la page',
         adaptiveTip: 'Adapter à la page',
         originTip: 'Taille d\'origine',
         originTip: 'Taille d\'origine',
     },
     },
+    Chat: {
+        deleteConfirm: 'Êtes-vous sûr de vouloir supprimer cette conversation ?',
+        clearContext: 'Le contexte a été effacé',
+        copySuccess: 'Copie réussie',
+        stop: 'Arrêter',
+        copy: 'Copier',
+        copied: 'Copié',
+        dropAreaText: 'Déposez le fichier ici',
+    },
 };
 };
 
 
 // [i18n-French]
 // [i18n-French]

+ 9 - 0
packages/semi-ui/locale/source/id_ID.ts

@@ -170,6 +170,15 @@ const local: Locale = {
         adaptiveTip: 'Beradaptasi dengan halaman',
         adaptiveTip: 'Beradaptasi dengan halaman',
         originTip: 'Ukuran asli',
         originTip: 'Ukuran asli',
     },
     },
+    Chat: {
+        deleteConfirm: 'Apakah Anda yakin ingin menghapus sesi ini??',
+        clearContext: 'Konteks telah dihapus',
+        copySuccess: 'Berhasil disalin',
+        stop: 'Berhenti',
+        copy: 'Salin',
+        copied: 'Disalin',
+        dropAreaText: 'Letakkan file di sini',
+    },
 };
 };
 
 
 // [i18n-Indonesia(ID)]
 // [i18n-Indonesia(ID)]

+ 9 - 0
packages/semi-ui/locale/source/it.ts

@@ -170,6 +170,15 @@ const local: Locale = {
         adaptiveTip: 'Adatta alla pagina',
         adaptiveTip: 'Adatta alla pagina',
         originTip: 'Formato originale',
         originTip: 'Formato originale',
     },
     },
+    Chat: {
+        deleteConfirm: 'Sei sicuro di voler eliminare questa conversazione?',
+        clearContext: 'Il contesto è stato eliminato',
+        copySuccess: 'Copiato con successo',
+        stop: 'Fermare',
+        copy: 'Copia',
+        copied: 'Copiato',
+        dropAreaText: 'Metti il file qui',
+    },
 };
 };
 
 
 // [i18n-Italian]
 // [i18n-Italian]

+ 9 - 0
packages/semi-ui/locale/source/ja_JP.ts

@@ -171,6 +171,15 @@ const local: Locale = {
         adaptiveTip: 'ページに適応',
         adaptiveTip: 'ページに適応',
         originTip: '元のサイズ',
         originTip: '元のサイズ',
     },
     },
+    Chat: {
+        deleteConfirm: 'このセッションを削除してもよろしいですか?',
+        clearContext: 'コンテキストを削除しました',
+        copySuccess: '正常にコピーされました',
+        stop: 'とめる',
+        copy: 'コピー',
+        copied: 'コピーしました',
+        dropAreaText: 'ファイルをここに置いてください',
+    },
 };
 };
 
 
 // [i18n-Japan]
 // [i18n-Japan]

+ 9 - 0
packages/semi-ui/locale/source/ko_KR.ts

@@ -171,6 +171,15 @@ const local: Locale = {
         adaptiveTip: '페이지에 맞게 조정',
         adaptiveTip: '페이지에 맞게 조정',
         originTip: '원래 크기',
         originTip: '원래 크기',
     },
     },
+    Chat: {
+        deleteConfirm: '이 대화를 삭제하시겠습니까?',
+        clearContext: '컨텍스트가 지워졌습니다',
+        copySuccess: '복사 성공',
+        stop: '중지',
+        copy: '복사',
+        copied: '복사했습니다',
+        dropAreaText: '파일을 여기에 놓으세요',
+    },
 };
 };
 
 
 // [i18n-Korea]
 // [i18n-Korea]

+ 9 - 0
packages/semi-ui/locale/source/ms_MY.ts

@@ -170,6 +170,15 @@ const local: Locale = {
         adaptiveTip: 'Menyesuaikan diri dengan halaman',
         adaptiveTip: 'Menyesuaikan diri dengan halaman',
         originTip: 'Saiz asal',
         originTip: 'Saiz asal',
     },
     },
+    Chat: {
+        deleteConfirm: 'Adakah anda pasti ingin memadam sesi ini?',
+        clearContext: 'Konteks telah dibersihkan',
+        copySuccess: 'Berjaya disalin',
+        stop: 'Berhenti',
+        copy: 'Samin',
+        copied: 'Disalin',
+        dropAreaText: 'Letakkan fail di sini',
+    },
 };
 };
 
 
 // [i18n-Malaysia(MY)]
 // [i18n-Malaysia(MY)]

+ 9 - 0
packages/semi-ui/locale/source/nl_NL.ts

@@ -177,6 +177,15 @@ const local: Locale = {
         adaptiveTip: 'Adaptieve weergave',
         adaptiveTip: 'Adaptieve weergave',
         originTip: 'Standaardweergave',
         originTip: 'Standaardweergave',
     },
     },
+    Chat: {
+        deleteConfirm: 'Weet u zeker dat u deze conversatie wilt verwijderen?',
+        clearContext: 'De context is gewist',
+        copySuccess: 'Succesvol gekopieerd',
+        stop: 'Stoppen',
+        copy: 'Kopiëren',
+        copied: 'Gekopieerd',
+        dropAreaText: 'Plaats het bestand hier',
+    },
 };
 };
 
 
 export default local;
 export default local;

+ 9 - 0
packages/semi-ui/locale/source/pl_PL.ts

@@ -178,6 +178,15 @@ const local: Locale = {
         adaptiveTip: 'Dostosowywanie ekranu',
         adaptiveTip: 'Dostosowywanie ekranu',
         originTip: 'Wyświetlacz domyślny',
         originTip: 'Wyświetlacz domyślny',
     },
     },
+    Chat: {
+        deleteConfirm: 'Czy na pewno chcesz usunąć tę rozmowę?',
+        clearContext: 'Kontekst został wyczyszczony',
+        copySuccess: 'Skopiowano pomyślnie',
+        stop: 'Zatrzymać',
+        copy: 'Kopiuj',
+        copied: 'Skopiowano',
+        dropAreaText: 'Umieść plik tutaj',
+    },
 };
 };
 
 
 export default local;
 export default local;

+ 9 - 0
packages/semi-ui/locale/source/pt_BR.ts

@@ -178,6 +178,15 @@ const local: Locale = {
         adaptiveTip: 'Adaptar à página',
         adaptiveTip: 'Adaptar à página',
         originTip: 'Tamanho original',
         originTip: 'Tamanho original',
     },
     },
+    Chat: {
+        deleteConfirm: 'Você tem certeza de que deseja excluir esta sessão?',
+        clearContext: 'Contexto limpo',
+        copySuccess: 'Copiado com sucesso',
+        stop: 'Parar',
+        copy: 'Copiar',
+        copied: 'Cópia bem sucedida',
+        dropAreaText: 'Coloque o arquivo aqui',
+    },
 };
 };
 
 
 // 葡萄牙语
 // 葡萄牙语

+ 9 - 0
packages/semi-ui/locale/source/ro.ts

@@ -170,6 +170,15 @@ const local: Locale = {
         adaptiveTip: 'Afișaj adaptabil',
         adaptiveTip: 'Afișaj adaptabil',
         originTip: 'Afișaj implicit',
         originTip: 'Afișaj implicit',
     },
     },
+    Chat: {
+        deleteConfirm: 'Sunteți sigur că doriți să ștergeți această conversație?',
+        clearContext: 'Contextul a fost șters',
+        copySuccess: 'Copiere reușită',
+        stop: 'Oprire',
+        copy: 'Copiază',
+        copied: 'Copiat',
+        dropAreaText: 'Puneți fișierul aici',
+    },
 };
 };
 
 
 // [i18n-Romanian] 罗马尼亚语
 // [i18n-Romanian] 罗马尼亚语

+ 9 - 0
packages/semi-ui/locale/source/ru_RU.ts

@@ -173,6 +173,15 @@ const local: Locale = {
         adaptiveTip: 'Адаптировать к странице',
         adaptiveTip: 'Адаптировать к странице',
         originTip: 'Исходный размер',
         originTip: 'Исходный размер',
     },
     },
+    Chat: {
+        deleteConfirm: 'Вы уверены, что хотите удалить эту сессию?',
+        clearContext: 'Контекст очищен',
+        copySuccess: 'Скопировано успешно',
+        stop: 'остановить',
+        copy: 'Копировать',
+        copied: 'Скопировано',
+        dropAreaText: 'Положите файл здесь',
+    },
 };
 };
 
 
 // [i18n-Russia] 俄罗斯语
 // [i18n-Russia] 俄罗斯语

+ 9 - 0
packages/semi-ui/locale/source/sv_SE.ts

@@ -175,6 +175,15 @@ const local: Locale = {
         adaptiveTip: 'Adaptiv visning',
         adaptiveTip: 'Adaptiv visning',
         originTip: 'Standardvisning',
         originTip: 'Standardvisning',
     },
     },
+    Chat: {
+        deleteConfirm: 'Är du säker på att du vill ta bort denna konversation?',
+        clearContext: 'Kontexten har rensats',
+        copySuccess: 'Kopiering lyckades',
+        stop: 'Stoppa',
+        copy: 'Kopiera',
+        copied: 'Kopierad',
+        dropAreaText: 'Placera filen här',   
+    }, 
 };
 };
 
 
 export default local;
 export default local;

+ 9 - 0
packages/semi-ui/locale/source/th_TH.ts

@@ -174,6 +174,15 @@ const local: Locale = {
         adaptiveTip: 'ปรับให้เข้ากับหน้า',
         adaptiveTip: 'ปรับให้เข้ากับหน้า',
         originTip: 'ขนาดเดิม',
         originTip: 'ขนาดเดิม',
     },
     },
+    Chat: {
+        deleteConfirm: 'คุณต้องการลบการสนทนานี้ใช่หรือไม่?',
+        clearContext: 'ล้างความเข้าใจเรียบร้อยแล้ว',
+        copySuccess: 'คัดลอกสำเร็จ',
+        stop: 'หยุด',
+        copy: 'สำเนา"',
+        copied: 'คัดลอกสำเร็จ',
+        dropAreaText: 'วางไฟล์ที่นี่',
+    },
 };
 };
 
 
 // [i18n-Thai]
 // [i18n-Thai]

+ 9 - 0
packages/semi-ui/locale/source/tr_TR.ts

@@ -170,6 +170,15 @@ const local: Locale = {
         adaptiveTip: 'Sayfaya uyarla',
         adaptiveTip: 'Sayfaya uyarla',
         originTip: 'Orijinal boyut',
         originTip: 'Orijinal boyut',
     },
     },
+    Chat: {
+        deleteConfirm: 'Bu sohbeti silmek istediğinize emin misiniz?',
+        clearContext: 'Bağlam temizlendi',
+        copySuccess: 'Başarıyla kopyalandı',
+        stop: 'Durmak',
+        copy: 'Kopyala',
+        copied: 'Kopyalama başarılı',
+        dropAreaText: 'Dosyayı buraya yerleştirin',
+    },
 };
 };
 
 
 // [i18n-Turkish] 
 // [i18n-Turkish] 

+ 9 - 0
packages/semi-ui/locale/source/vi_VN.ts

@@ -173,6 +173,15 @@ const local: Locale = {
         adaptiveTip: 'Thích ứng với trang',
         adaptiveTip: 'Thích ứng với trang',
         originTip: 'Kích thước ban đầu',
         originTip: 'Kích thước ban đầu',
     },
     },
+    Chat: {
+        deleteConfirm: 'Bạn có chắc muốn xóa phiên này không?',
+        clearContext: 'Ngữ cảnh đã được xóa',
+        copySuccess: 'Sao chép thành công',
+        stop: 'Dừng',
+        copy: 'Sao chép',
+        copied: 'Đã sao chép',
+        dropAreaText: 'Đặt tệp vào đây',
+    }, 
 };
 };
 
 
 // [i18n-Vietnam] 越南语
 // [i18n-Vietnam] 越南语

+ 9 - 0
packages/semi-ui/locale/source/zh_CN.ts

@@ -171,6 +171,15 @@ const local: Locale = {
         adaptiveTip: '适应页面',
         adaptiveTip: '适应页面',
         originTip: '原始尺寸',
         originTip: '原始尺寸',
     },
     },
+    Chat: {
+        deleteConfirm: '确认删除该会话吗?',
+        clearContext: '上下文已清除',
+        copySuccess: '复制成功',
+        stop: '停止',
+        copy: '复制',
+        copied: '复制成功',
+        dropAreaText: '将文件放到这里',
+    },
 };
 };
 
 
 // 中文
 // 中文

+ 9 - 0
packages/semi-ui/locale/source/zh_TW.ts

@@ -171,6 +171,15 @@ const local: Locale = {
         adaptiveTip: '適應頁面',
         adaptiveTip: '適應頁面',
         originTip: '原始尺寸',
         originTip: '原始尺寸',
     },
     },
+    Chat: {
+        deleteConfirm: '確認刪除該對話嗎?',
+        clearContext: '上下文已清除',
+        copySuccess: '複製成功',
+        stop: '停止',
+        copy: '複制',
+        copied: '複制成功',
+        dropAreaText: '將文件放到這裡',
+    },
 };
 };
 
 
 // 中文
 // 中文

+ 0 - 1
packages/semi-ui/markdownRender/_story/markdownRender.stories.jsx

@@ -5,7 +5,6 @@ export default {
 }
 }
 
 
 
 
-
 export const Basic = ()=>{
 export const Basic = ()=>{
     return <MarkdownRender raw={"# Two 🍰 is: {Math.PI * 2}"} components={semiComponents}/>
     return <MarkdownRender raw={"# Two 🍰 is: {Math.PI * 2}"} components={semiComponents}/>
 }
 }

+ 10 - 6
packages/semi-ui/package.json

@@ -1,6 +1,6 @@
 {
 {
     "name": "@douyinfe/semi-ui",
     "name": "@douyinfe/semi-ui",
-    "version": "2.62.1",
+    "version": "2.63.0-beta.0",
     "description": "A modern, comprehensive, flexible design system and UI library. Connect DesignOps & DevOps. Quickly build beautiful React apps. Maintained by Douyin-fe team.",
     "description": "A modern, comprehensive, flexible design system and UI library. Connect DesignOps & DevOps. Quickly build beautiful React apps. Maintained by Douyin-fe team.",
     "main": "lib/cjs/index.js",
     "main": "lib/cjs/index.js",
     "module": "lib/es/index.js",
     "module": "lib/es/index.js",
@@ -12,6 +12,10 @@
         "build:css": "node scripts/compileScss.js",
         "build:css": "node scripts/compileScss.js",
         "prepublishOnly": "npm run clean && npm run build:lib && npm run build:js && npm run build:css"
         "prepublishOnly": "npm run clean && npm run build:lib && npm run build:js && npm run build:css"
     },
     },
+    "exports": {
+        "require": "./lib/cjs/index.js",
+        "import": "./lib/es/index.js"
+    },
     "files": [
     "files": [
         "dist/*",
         "dist/*",
         "lib/*"
         "lib/*"
@@ -20,11 +24,11 @@
         "@dnd-kit/core": "^6.0.8",
         "@dnd-kit/core": "^6.0.8",
         "@dnd-kit/sortable": "^7.0.2",
         "@dnd-kit/sortable": "^7.0.2",
         "@dnd-kit/utilities": "^3.2.1",
         "@dnd-kit/utilities": "^3.2.1",
-        "@douyinfe/semi-animation": "2.62.1",
-        "@douyinfe/semi-animation-react": "2.62.1",
-        "@douyinfe/semi-foundation": "2.62.1",
-        "@douyinfe/semi-icons": "2.62.1",
-        "@douyinfe/semi-illustrations": "2.62.1",
+        "@douyinfe/semi-animation": "2.63.0-beta.0",
+        "@douyinfe/semi-animation-react": "2.63.0-beta.0",
+        "@douyinfe/semi-foundation": "2.63.0-beta.0",
+        "@douyinfe/semi-icons": "2.63.0-beta.0",
+        "@douyinfe/semi-illustrations": "2.63.0-beta.0",
         "@douyinfe/semi-theme-default": "2.61.0",
         "@douyinfe/semi-theme-default": "2.61.0",
         "async-validator": "^3.5.0",
         "async-validator": "^3.5.0",
         "classnames": "^2.2.6",
         "classnames": "^2.2.6",

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

@@ -384,7 +384,7 @@ class Upload extends BaseComponent<UploadProps, UploadState> {
      * @param index number
      * @param index number
      * @returns
      * @returns
      */
      */
-    insert = (files: Array<CustomFile>, index: number): void => {
+    insert = (files: Array<CustomFile>, index?: number): void => {
         return this.foundation.insertFileToList(files, index);
         return this.foundation.insertFileToList(files, index);
     };
     };
 
 

+ 1 - 1
packages/semi-webpack/package.json

@@ -1,6 +1,6 @@
 {
 {
     "name": "@douyinfe/semi-webpack-plugin",
     "name": "@douyinfe/semi-webpack-plugin",
-    "version": "2.62.1",
+    "version": "2.63.0-beta.0",
     "description": "",
     "description": "",
     "author": "伍浩威 <[email protected]>",
     "author": "伍浩威 <[email protected]>",
     "homepage": "",
     "homepage": "",

+ 148 - 148
sitemap.xml

@@ -2,22 +2,22 @@
 <urlset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd" xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
 <urlset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd" xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
     <url>
     <url>
         <loc>https://juejin.cn/post/7267418854124699702</loc>
         <loc>https://juejin.cn/post/7267418854124699702</loc>
-        <lastmod>2024-07-16T12:04:37.828Z</lastmod>
+        <lastmod>2024-07-22T09:10:01.817Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://medium.com/front-end-weekly/how-we-test-semi-design-component-libraries-64b854f63b65</loc>
         <loc>https://medium.com/front-end-weekly/how-we-test-semi-design-component-libraries-64b854f63b65</loc>
-        <lastmod>2024-07-16T12:04:38.069Z</lastmod>
+        <lastmod>2024-07-22T09:10:00.122Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://mp.weixin.qq.com/s/noHoWRuA25PgqFNcurhIUA</loc>
         <loc>https://mp.weixin.qq.com/s/noHoWRuA25PgqFNcurhIUA</loc>
-        <lastmod>2024-07-16T12:04:41.207Z</lastmod>
+        <lastmod>2024-07-22T09:10:02.531Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://mp.weixin.qq.com/s/O3js-SZDNPEOjGxh-aAkbw</loc>
         <loc>https://mp.weixin.qq.com/s/O3js-SZDNPEOjGxh-aAkbw</loc>
-        <lastmod>2024-07-16T12:04:41.188Z</lastmod>
+        <lastmod>2024-07-22T09:10:03.732Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
@@ -790,77 +790,77 @@
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/basic/divider</loc>
         <loc>https://semi.design/en-US/basic/divider</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/basic/grid</loc>
         <loc>https://semi.design/en-US/basic/grid</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/basic/icon</loc>
         <loc>https://semi.design/en-US/basic/icon</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/basic/layout</loc>
         <loc>https://semi.design/en-US/basic/layout</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/basic/space</loc>
         <loc>https://semi.design/en-US/basic/space</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/basic/tokens</loc>
         <loc>https://semi.design/en-US/basic/tokens</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/basic/typography</loc>
         <loc>https://semi.design/en-US/basic/typography</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/feedback/banner</loc>
         <loc>https://semi.design/en-US/feedback/banner</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/feedback/notification</loc>
         <loc>https://semi.design/en-US/feedback/notification</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/feedback/popconfirm</loc>
         <loc>https://semi.design/en-US/feedback/popconfirm</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/feedback/progress</loc>
         <loc>https://semi.design/en-US/feedback/progress</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/feedback/skeleton</loc>
         <loc>https://semi.design/en-US/feedback/skeleton</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/feedback/spin</loc>
         <loc>https://semi.design/en-US/feedback/spin</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/feedback/toast</loc>
         <loc>https://semi.design/en-US/feedback/toast</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/input/autocomplete</loc>
         <loc>https://semi.design/en-US/input/autocomplete</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
@@ -870,362 +870,362 @@
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/input/cascader</loc>
         <loc>https://semi.design/en-US/input/cascader</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/input/checkbox</loc>
         <loc>https://semi.design/en-US/input/checkbox</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/input/datepicker</loc>
         <loc>https://semi.design/en-US/input/datepicker</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/input/form</loc>
         <loc>https://semi.design/en-US/input/form</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/input/input</loc>
         <loc>https://semi.design/en-US/input/input</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/input/inputnumber</loc>
         <loc>https://semi.design/en-US/input/inputnumber</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/input/radio</loc>
         <loc>https://semi.design/en-US/input/radio</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/input/rating</loc>
         <loc>https://semi.design/en-US/input/rating</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/input/select</loc>
         <loc>https://semi.design/en-US/input/select</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/input/slider</loc>
         <loc>https://semi.design/en-US/input/slider</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/input/switch</loc>
         <loc>https://semi.design/en-US/input/switch</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/input/taginput</loc>
         <loc>https://semi.design/en-US/input/taginput</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/input/timepicker</loc>
         <loc>https://semi.design/en-US/input/timepicker</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/input/transfer</loc>
         <loc>https://semi.design/en-US/input/transfer</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/input/treeselect</loc>
         <loc>https://semi.design/en-US/input/treeselect</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/input/upload</loc>
         <loc>https://semi.design/en-US/input/upload</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/navigation/anchor</loc>
         <loc>https://semi.design/en-US/navigation/anchor</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/navigation/backtop</loc>
         <loc>https://semi.design/en-US/navigation/backtop</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/navigation/breadcrumb</loc>
         <loc>https://semi.design/en-US/navigation/breadcrumb</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/navigation/navigation</loc>
         <loc>https://semi.design/en-US/navigation/navigation</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/navigation/pagination</loc>
         <loc>https://semi.design/en-US/navigation/pagination</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/navigation/steps</loc>
         <loc>https://semi.design/en-US/navigation/steps</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/navigation/tabs</loc>
         <loc>https://semi.design/en-US/navigation/tabs</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/navigation/tree</loc>
         <loc>https://semi.design/en-US/navigation/tree</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/other/configprovider</loc>
         <loc>https://semi.design/en-US/other/configprovider</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/other/locale</loc>
         <loc>https://semi.design/en-US/other/locale</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/show/avatar</loc>
         <loc>https://semi.design/en-US/show/avatar</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/show/badge</loc>
         <loc>https://semi.design/en-US/show/badge</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/show/calendar</loc>
         <loc>https://semi.design/en-US/show/calendar</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/show/card</loc>
         <loc>https://semi.design/en-US/show/card</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/show/carousel</loc>
         <loc>https://semi.design/en-US/show/carousel</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/show/collapse</loc>
         <loc>https://semi.design/en-US/show/collapse</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/show/collapsible</loc>
         <loc>https://semi.design/en-US/show/collapsible</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/show/descriptions</loc>
         <loc>https://semi.design/en-US/show/descriptions</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/show/dropdown</loc>
         <loc>https://semi.design/en-US/show/dropdown</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/show/empty</loc>
         <loc>https://semi.design/en-US/show/empty</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/show/highlight</loc>
         <loc>https://semi.design/en-US/show/highlight</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/show/image</loc>
         <loc>https://semi.design/en-US/show/image</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/show/list</loc>
         <loc>https://semi.design/en-US/show/list</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/show/modal</loc>
         <loc>https://semi.design/en-US/show/modal</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/show/overflowlist</loc>
         <loc>https://semi.design/en-US/show/overflowlist</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/show/popover</loc>
         <loc>https://semi.design/en-US/show/popover</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/show/scrolllist</loc>
         <loc>https://semi.design/en-US/show/scrolllist</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/show/sidesheet</loc>
         <loc>https://semi.design/en-US/show/sidesheet</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/show/table</loc>
         <loc>https://semi.design/en-US/show/table</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/show/tag</loc>
         <loc>https://semi.design/en-US/show/tag</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/show/timeline</loc>
         <loc>https://semi.design/en-US/show/timeline</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/show/tooltip</loc>
         <loc>https://semi.design/en-US/show/tooltip</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/start/accessibility</loc>
         <loc>https://semi.design/en-US/start/accessibility</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/start/changelog</loc>
         <loc>https://semi.design/en-US/start/changelog</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/start/customize-theme</loc>
         <loc>https://semi.design/en-US/start/customize-theme</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/start/dark-mode</loc>
         <loc>https://semi.design/en-US/start/dark-mode</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/start/faq</loc>
         <loc>https://semi.design/en-US/start/faq</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/start/getting-started</loc>
         <loc>https://semi.design/en-US/start/getting-started</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/start/introduction</loc>
         <loc>https://semi.design/en-US/start/introduction</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/start/overview</loc>
         <loc>https://semi.design/en-US/start/overview</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/en-US/start/update-to-v2</loc>
         <loc>https://semi.design/en-US/start/update-to-v2</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/basic/divider</loc>
         <loc>https://semi.design/zh-CN/basic/divider</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/basic/grid</loc>
         <loc>https://semi.design/zh-CN/basic/grid</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/basic/icon</loc>
         <loc>https://semi.design/zh-CN/basic/icon</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/basic/layout</loc>
         <loc>https://semi.design/zh-CN/basic/layout</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/basic/space</loc>
         <loc>https://semi.design/zh-CN/basic/space</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/basic/tokens</loc>
         <loc>https://semi.design/zh-CN/basic/tokens</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/basic/typography</loc>
         <loc>https://semi.design/zh-CN/basic/typography</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/feedback/banner</loc>
         <loc>https://semi.design/zh-CN/feedback/banner</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/feedback/notification</loc>
         <loc>https://semi.design/zh-CN/feedback/notification</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/feedback/popconfirm</loc>
         <loc>https://semi.design/zh-CN/feedback/popconfirm</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/feedback/progress</loc>
         <loc>https://semi.design/zh-CN/feedback/progress</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/feedback/skeleton</loc>
         <loc>https://semi.design/zh-CN/feedback/skeleton</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/feedback/spin</loc>
         <loc>https://semi.design/zh-CN/feedback/spin</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/feedback/toast</loc>
         <loc>https://semi.design/zh-CN/feedback/toast</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/input/autocomplete</loc>
         <loc>https://semi.design/zh-CN/input/autocomplete</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
@@ -1235,287 +1235,287 @@
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/input/cascader</loc>
         <loc>https://semi.design/zh-CN/input/cascader</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/input/checkbox</loc>
         <loc>https://semi.design/zh-CN/input/checkbox</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/input/datepicker</loc>
         <loc>https://semi.design/zh-CN/input/datepicker</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/input/form</loc>
         <loc>https://semi.design/zh-CN/input/form</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/input/input</loc>
         <loc>https://semi.design/zh-CN/input/input</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/input/inputnumber</loc>
         <loc>https://semi.design/zh-CN/input/inputnumber</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/input/radio</loc>
         <loc>https://semi.design/zh-CN/input/radio</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/input/rating</loc>
         <loc>https://semi.design/zh-CN/input/rating</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/input/select</loc>
         <loc>https://semi.design/zh-CN/input/select</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/input/slider</loc>
         <loc>https://semi.design/zh-CN/input/slider</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/input/switch</loc>
         <loc>https://semi.design/zh-CN/input/switch</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/input/taginput</loc>
         <loc>https://semi.design/zh-CN/input/taginput</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/input/timepicker</loc>
         <loc>https://semi.design/zh-CN/input/timepicker</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/input/transfer</loc>
         <loc>https://semi.design/zh-CN/input/transfer</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/input/treeselect</loc>
         <loc>https://semi.design/zh-CN/input/treeselect</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/input/upload</loc>
         <loc>https://semi.design/zh-CN/input/upload</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/navigation/anchor</loc>
         <loc>https://semi.design/zh-CN/navigation/anchor</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/navigation/backtop</loc>
         <loc>https://semi.design/zh-CN/navigation/backtop</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/navigation/breadcrumb</loc>
         <loc>https://semi.design/zh-CN/navigation/breadcrumb</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/navigation/navigation</loc>
         <loc>https://semi.design/zh-CN/navigation/navigation</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/navigation/pagination</loc>
         <loc>https://semi.design/zh-CN/navigation/pagination</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/navigation/steps</loc>
         <loc>https://semi.design/zh-CN/navigation/steps</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/navigation/tabs</loc>
         <loc>https://semi.design/zh-CN/navigation/tabs</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/navigation/tree</loc>
         <loc>https://semi.design/zh-CN/navigation/tree</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/other/configprovider</loc>
         <loc>https://semi.design/zh-CN/other/configprovider</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/other/locale</loc>
         <loc>https://semi.design/zh-CN/other/locale</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/show/avatar</loc>
         <loc>https://semi.design/zh-CN/show/avatar</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/show/badge</loc>
         <loc>https://semi.design/zh-CN/show/badge</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/show/calendar</loc>
         <loc>https://semi.design/zh-CN/show/calendar</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/show/card</loc>
         <loc>https://semi.design/zh-CN/show/card</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/show/carousel</loc>
         <loc>https://semi.design/zh-CN/show/carousel</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/show/collapse</loc>
         <loc>https://semi.design/zh-CN/show/collapse</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/show/collapsible</loc>
         <loc>https://semi.design/zh-CN/show/collapsible</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/show/descriptions</loc>
         <loc>https://semi.design/zh-CN/show/descriptions</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/show/dropdown</loc>
         <loc>https://semi.design/zh-CN/show/dropdown</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/show/empty</loc>
         <loc>https://semi.design/zh-CN/show/empty</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/show/highlight</loc>
         <loc>https://semi.design/zh-CN/show/highlight</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/show/image</loc>
         <loc>https://semi.design/zh-CN/show/image</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/show/list</loc>
         <loc>https://semi.design/zh-CN/show/list</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/show/modal</loc>
         <loc>https://semi.design/zh-CN/show/modal</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/show/overflowlist</loc>
         <loc>https://semi.design/zh-CN/show/overflowlist</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/show/popover</loc>
         <loc>https://semi.design/zh-CN/show/popover</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/show/scrolllist</loc>
         <loc>https://semi.design/zh-CN/show/scrolllist</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/show/sidesheet</loc>
         <loc>https://semi.design/zh-CN/show/sidesheet</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/show/table</loc>
         <loc>https://semi.design/zh-CN/show/table</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/show/tag</loc>
         <loc>https://semi.design/zh-CN/show/tag</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/show/timeline</loc>
         <loc>https://semi.design/zh-CN/show/timeline</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/show/tooltip</loc>
         <loc>https://semi.design/zh-CN/show/tooltip</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/start/accessibility</loc>
         <loc>https://semi.design/zh-CN/start/accessibility</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/start/changelog</loc>
         <loc>https://semi.design/zh-CN/start/changelog</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/start/customize-theme</loc>
         <loc>https://semi.design/zh-CN/start/customize-theme</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/start/dark-mode</loc>
         <loc>https://semi.design/zh-CN/start/dark-mode</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/start/faq</loc>
         <loc>https://semi.design/zh-CN/start/faq</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/start/getting-started</loc>
         <loc>https://semi.design/zh-CN/start/getting-started</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/start/introduction</loc>
         <loc>https://semi.design/zh-CN/start/introduction</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/start/overview</loc>
         <loc>https://semi.design/zh-CN/start/overview</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
     <url>
     <url>
         <loc>https://semi.design/zh-CN/start/update-to-v2</loc>
         <loc>https://semi.design/zh-CN/start/update-to-v2</loc>
-        <lastmod>2024-07-16T12:01:27.000Z</lastmod>
+        <lastmod>2024-07-22T08:54:39.000Z</lastmod>
         <changefreq>weekly</changefreq>
         <changefreq>weekly</changefreq>
     </url>
     </url>
 </urlset>
 </urlset>

+ 7 - 0
src/images/docIcons/doc-chat.svg

@@ -0,0 +1,7 @@
+<svg width="22" height="20" viewBox="0 0 22 20" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M18 5C18 3.89543 17.1046 3 16 3H2C0.89543 3 0 3.89543 0 5V17.5858C0 18.4767 1.07714 18.9229 1.70711 18.2929L3.41421 16.5858C3.78929 16.2107 4.29799 16 4.82843 16H16C17.1046 16 18 15.1046 18 14V5Z" fill="#DFDFE7"/>
+<path d="M4 2C4 0.89543 4.89543 0 6 0H20C21.1046 0 22 0.895431 22 2V14.5858C22 15.4767 20.9229 15.9229 20.2929 15.2929L18.5858 13.5858C18.2107 13.2107 17.702 13 17.1716 13H6C4.89543 13 4 12.1046 4 11V2Z" fill="#49C4FD"/>
+<rect x="14" y="2.5" width="4" height="1.5" fill="#F4CE2D"/>
+<rect x="8" y="8.5" width="10" height="1.5" fill="#F9FCFF"/>
+<rect x="8" y="5.5" width="10" height="1.5" fill="#F9FCFF"/>
+</svg>

+ 26 - 0
src/styles/docDemo.scss

@@ -474,6 +474,32 @@ body > .component-list-demo-drag-item {
     will-change: unset!important;
     will-change: unset!important;
 }
 }
 
 
+.component-chat-demo-custom-render {
+    .time {
+        font-size: 12px;
+        color: var(--semi-color-text-2);
+        visibility: hidden;
+    }
+
+    .title {
+        display: flex;
+        align-items: 'center';
+        column-gap: 10px;
+    }
+
+    .semi-chat-chatBox:hover {
+        .time {
+            visibility: visible;
+        }
+    }
+
+    .semi-chat-chatBox-right {
+        .title {
+            flex-direction: row-reverse;
+        }
+    }
+}
+
 
 
 
 
 
 

+ 93 - 9
yarn.lock

@@ -1603,11 +1603,25 @@
     "@douyinfe/semi-animation-styled" "2.60.0"
     "@douyinfe/semi-animation-styled" "2.60.0"
     classnames "^2.2.6"
     classnames "^2.2.6"
 
 
+"@douyinfe/[email protected]":
+  version "2.62.1"
+  resolved "https://registry.yarnpkg.com/@douyinfe/semi-animation-react/-/semi-animation-react-2.62.1.tgz#60d6fb88b43401d0b939cd07f10ba715a2638e72"
+  integrity sha512-dRM5p22+skqLV1cKl+IMrUw22coKWqOj54+NgywsMtTOsC0D3XPI7NJ0lB4RzAAiGaAd16VxlW+I5Kuzooxqmw==
+  dependencies:
+    "@douyinfe/semi-animation" "2.62.1"
+    "@douyinfe/semi-animation-styled" "2.62.1"
+    classnames "^2.2.6"
+
 "@douyinfe/[email protected]":
 "@douyinfe/[email protected]":
   version "2.60.0"
   version "2.60.0"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-animation-styled/-/semi-animation-styled-2.60.0.tgz#0e935b68aaaed55671c59e92029eed5599d6d207"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-animation-styled/-/semi-animation-styled-2.60.0.tgz#0e935b68aaaed55671c59e92029eed5599d6d207"
   integrity sha512-S5Gun0LmDuIDCkGwLADjcWY4eOU64p7OQH30+yCII1GrS0CSGAPlMzJ0p0zOieGUilcvmyCtWRZ8NeH8lVQLdA==
   integrity sha512-S5Gun0LmDuIDCkGwLADjcWY4eOU64p7OQH30+yCII1GrS0CSGAPlMzJ0p0zOieGUilcvmyCtWRZ8NeH8lVQLdA==
 
 
+"@douyinfe/[email protected]":
+  version "2.62.1"
+  resolved "https://registry.yarnpkg.com/@douyinfe/semi-animation-styled/-/semi-animation-styled-2.62.1.tgz#a9f4c2eb59f2a570c4ee2f232a1a9697fd93e972"
+  integrity sha512-TtHDhih0/GDDi4Y6SS36zX2htK9XXEHBOuk6FhWQncymb/92peOtsaIrabOv/DtpYqa4rt+omttMzUevcfkIPw==
+
 "@douyinfe/[email protected]":
 "@douyinfe/[email protected]":
   version "2.60.0"
   version "2.60.0"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-animation/-/semi-animation-2.60.0.tgz#0194eeb5342b092a711a95e5b9ba2a8346fbe0c5"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-animation/-/semi-animation-2.60.0.tgz#0194eeb5342b092a711a95e5b9ba2a8346fbe0c5"
@@ -1615,6 +1629,13 @@
   dependencies:
   dependencies:
     bezier-easing "^2.1.0"
     bezier-easing "^2.1.0"
 
 
+"@douyinfe/[email protected]":
+  version "2.62.1"
+  resolved "https://registry.yarnpkg.com/@douyinfe/semi-animation/-/semi-animation-2.62.1.tgz#fbe430546d57fc4f31408770b6326f15b9b243dd"
+  integrity sha512-mZCFeLuFyoIMRSw1zBTzm9rxKryOCkdf1RppkuX0J7ptLiOOP7wQ03KabetpRIeKgrvqHXhkopebz9aZt2vkMA==
+  dependencies:
+    bezier-easing "^2.1.0"
+
 "@douyinfe/[email protected]":
 "@douyinfe/[email protected]":
   version "2.60.0"
   version "2.60.0"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-foundation/-/semi-foundation-2.60.0.tgz#7833e566284c8e7ce4169fb4f38bb1dbcb4aee08"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-foundation/-/semi-foundation-2.60.0.tgz#7833e566284c8e7ce4169fb4f38bb1dbcb4aee08"
@@ -1630,6 +1651,25 @@
     memoize-one "^5.2.1"
     memoize-one "^5.2.1"
     scroll-into-view-if-needed "^2.2.24"
     scroll-into-view-if-needed "^2.2.24"
 
 
+"@douyinfe/[email protected]":
+  version "2.62.1"
+  resolved "https://registry.yarnpkg.com/@douyinfe/semi-foundation/-/semi-foundation-2.62.1.tgz#dcd03ad9e5ac71a7ec628c2d46b698cd98ec2853"
+  integrity sha512-Zo5CHEQSPCc4o9wPGsunrR5vyZLaE6HNHiqrkwXycc3mHWwRYh/Z+GvLlev1V1cHaCW/1zZ6i9QdMnBeWZP/1Q==
+  dependencies:
+    "@douyinfe/semi-animation" "2.62.1"
+    "@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":
 "@douyinfe/[email protected]", "@douyinfe/semi-icons@latest":
   version "2.60.0"
   version "2.60.0"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-icons/-/semi-icons-2.60.0.tgz#d3a6864517d835720137c5bd195a80a67746db57"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-icons/-/semi-icons-2.60.0.tgz#d3a6864517d835720137c5bd195a80a67746db57"
@@ -1637,11 +1677,23 @@
   dependencies:
   dependencies:
     classnames "^2.2.6"
     classnames "^2.2.6"
 
 
+"@douyinfe/[email protected]", "@douyinfe/semi-icons@^2.0.0":
+  version "2.62.1"
+  resolved "https://registry.yarnpkg.com/@douyinfe/semi-icons/-/semi-icons-2.62.1.tgz#26f8fdac434694035030c77b1f06abdf8d37545c"
+  integrity sha512-mwDNe42imzKDmQsVSfikqlilSE/62K0j2TZMvpPuigD3Chu9NoaEWReDzKdYQ2IES7PHBttQ9YJSbb3EtJ1cRw==
+  dependencies:
+    classnames "^2.2.6"
+
 "@douyinfe/[email protected]":
 "@douyinfe/[email protected]":
   version "2.60.0"
   version "2.60.0"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-illustrations/-/semi-illustrations-2.60.0.tgz#9cef572da3c0f8b36e88ba1cbc3a44365e7d82d5"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-illustrations/-/semi-illustrations-2.60.0.tgz#9cef572da3c0f8b36e88ba1cbc3a44365e7d82d5"
   integrity sha512-xoXG9LBxXaukWNSxY8l1aRUv0NGYGtfH3O/kwuQgU4lTQtWHHBKYSfieiVvFJBoAVd1gsl29nqq/x+oIn0LizQ==
   integrity sha512-xoXG9LBxXaukWNSxY8l1aRUv0NGYGtfH3O/kwuQgU4lTQtWHHBKYSfieiVvFJBoAVd1gsl29nqq/x+oIn0LizQ==
 
 
+"@douyinfe/[email protected]":
+  version "2.62.1"
+  resolved "https://registry.yarnpkg.com/@douyinfe/semi-illustrations/-/semi-illustrations-2.62.1.tgz#4425481fd4effaf0276f7ac8feecee6403e33d36"
+  integrity sha512-fk737fJ6YnYWoMdTG2RcKDesZAnOzD35v3ZRd+u/qjPoAUiX6YY+R8wu+sh/IvN2hHURH+i+8UOtBxXE6E42xQ==
+
 "@douyinfe/[email protected]":
 "@douyinfe/[email protected]":
   version "2.23.2"
   version "2.23.2"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-scss-compile/-/semi-scss-compile-2.23.2.tgz#30884bb194ee9ae1e81877985e5663c3297c1ced"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-scss-compile/-/semi-scss-compile-2.23.2.tgz#30884bb194ee9ae1e81877985e5663c3297c1ced"
@@ -1683,20 +1735,20 @@
     lodash-es "^4.17.21"
     lodash-es "^4.17.21"
     react-i18next "^11.12.0"
     react-i18next "^11.12.0"
 
 
-"@douyinfe/semi-site-markdown-blocks@^0.0.17":
-  version "0.0.17"
-  resolved "https://registry.yarnpkg.com/@douyinfe/semi-site-markdown-blocks/-/semi-site-markdown-blocks-0.0.17.tgz#36b5dd8c521c2767bcf151af4c832166d9631a6e"
-  integrity sha512-uPVEO8dDJMNoUgiq1mrqqXZW1LTFJ1V+3+zW58mAc9ghZABl4v4DZpsGudft+nUuYGetbosGg3rKa3aXmrFFrg==
+"@douyinfe/semi-site-markdown-blocks@^0.0.18":
+  version "0.0.18"
+  resolved "https://registry.yarnpkg.com/@douyinfe/semi-site-markdown-blocks/-/semi-site-markdown-blocks-0.0.18.tgz#e0c0a29fc5b9574d05a22f23cb8fa8851f6ecb6e"
+  integrity sha512-vx1oi8j08lw1iE/RZFyXCcwuWA8XAUQ9IXUufrV/or1ReRRlziBt5MR4PXoKjr/oW+VgtYbyBdowT9huSG3epg==
   dependencies:
   dependencies:
-    "@douyinfe/semi-site-playground" "0.0.13"
+    "@douyinfe/semi-site-playground" "0.0.14"
     classnames "^2.2.6"
     classnames "^2.2.6"
     prism-react-renderer "^1.0.1"
     prism-react-renderer "^1.0.1"
     prop-types "^15.7.2"
     prop-types "^15.7.2"
 
 
-"@douyinfe/[email protected]3":
-  version "0.0.13"
-  resolved "https://registry.yarnpkg.com/@douyinfe/semi-site-playground/-/semi-site-playground-0.0.13.tgz#70adad18d7a99944ad8529596c554ab49897ecf2"
-  integrity sha512-z5Kj53K1dM02oSuR7Ca10aVsoXo6q/E0UOX7VF2VBmi8vShknL1VRMXsxev20IFSVxZOXlAYGO4SkL1KqWxdSg==
+"@douyinfe/[email protected]4":
+  version "0.0.14"
+  resolved "https://registry.yarnpkg.com/@douyinfe/semi-site-playground/-/semi-site-playground-0.0.14.tgz#c3f9a6adb854f93fbc1c4fd4b75533c1718c18ea"
+  integrity sha512-EYm9ns+IuUhGjVLwTx7+kETdbsLdtQHVwdJj66xtoIBaJHcaG1wy7iJzIIhbf8eOZdP4bT0Q8P5kb5aKdVA4+g==
   dependencies:
   dependencies:
     "@douyinfe/semi-icons" "^2.0.0"
     "@douyinfe/semi-icons" "^2.0.0"
     "@douyinfe/semi-ui" "^2.0.0"
     "@douyinfe/semi-ui" "^2.0.0"
@@ -1718,6 +1770,33 @@
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-theme-default/-/semi-theme-default-2.61.0.tgz#a7e9bf9534721c12af1d0eeb5d5a2de615896a23"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-theme-default/-/semi-theme-default-2.61.0.tgz#a7e9bf9534721c12af1d0eeb5d5a2de615896a23"
   integrity sha512-obn/DOw4vZyKFAlWvZxHTpBLAK9FO9kygTSm2GROgvi+UDB2PPU6l20cuUCsdGUNWJRSqYlTTVZ1tNYIyFZ5Sg==
   integrity sha512-obn/DOw4vZyKFAlWvZxHTpBLAK9FO9kygTSm2GROgvi+UDB2PPU6l20cuUCsdGUNWJRSqYlTTVZ1tNYIyFZ5Sg==
 
 
+"@douyinfe/semi-ui@^2.0.0":
+  version "2.62.1"
+  resolved "https://registry.yarnpkg.com/@douyinfe/semi-ui/-/semi-ui-2.62.1.tgz#cfe5010157d1662fc6a050cd4a8ab5f6d2dd9811"
+  integrity sha512-igNCw3XM56mF00s4vUFY6JrEyga4ZNnR+a3bpQyGLJyEfynmuMCVKpBbFcyDpBI7uYjIogh8REeD/C9nKw/L4Q==
+  dependencies:
+    "@dnd-kit/core" "^6.0.8"
+    "@dnd-kit/sortable" "^7.0.2"
+    "@dnd-kit/utilities" "^3.2.1"
+    "@douyinfe/semi-animation" "2.62.1"
+    "@douyinfe/semi-animation-react" "2.62.1"
+    "@douyinfe/semi-foundation" "2.62.1"
+    "@douyinfe/semi-icons" "2.62.1"
+    "@douyinfe/semi-illustrations" "2.62.1"
+    "@douyinfe/semi-theme-default" "2.61.0"
+    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":
 "@douyinfe/semi-ui@latest":
   version "2.60.0"
   version "2.60.0"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-ui/-/semi-ui-2.60.0.tgz#a5dd92e7264c4f9ce6910f365a46f95cfc3816f9"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-ui/-/semi-ui-2.60.0.tgz#a5dd92e7264c4f9ce6910f365a46f95cfc3816f9"
@@ -11869,6 +11948,11 @@ eslint-plugin-react@^7.20.6, eslint-plugin-react@^7.24.0:
     semver "^6.3.1"
     semver "^6.3.1"
     string.prototype.matchall "^4.0.11"
     string.prototype.matchall "^4.0.11"
 
 
+eslint-plugin-semi-design@^2.33.0:
+  version "2.62.1"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-semi-design/-/eslint-plugin-semi-design-2.62.1.tgz#fa681b69607bcae61579bd6a63796f0570b568da"
+  integrity sha512-hO/Zxsna/azEE9dZxaxd2n7BnyG4qhK8cc98BthiiJWtY5e8vSGp6SzUOYv/5vMlO0SJ+M4O7UH1aaZrKYT2Zw==
+
 eslint-rule-composer@^0.3.0:
 eslint-rule-composer@^0.3.0:
   version "0.3.0"
   version "0.3.0"
   resolved "https://registry.yarnpkg.com/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz#79320c927b0c5c0d3d3d2b76c8b4a488f25bbaf9"
   resolved "https://registry.yarnpkg.com/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz#79320c927b0c5c0d3d3d2b76c8b4a488f25bbaf9"