Skip to content

Commit cf98b0f

Browse files
committed
✨ feat: support useChat and chatRef
1 parent 8102ebe commit cf98b0f

File tree

12 files changed

+300
-66
lines changed

12 files changed

+300
-66
lines changed

.dumirc.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@ export default defineConfig({
55
themeConfig: {
66
name: '@ant-design/pro-chat',
77
github: homepage,
8+
siteToken: {
9+
demoInheritSiteTheme: true,
10+
},
811
},
12+
mfsu: false,
913
outputPath: 'docs-dist',
1014
html2sketch: {},
1115
extraBabelPlugins: ['antd-style'],

src/ProChat/container/Provider.tsx

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,45 @@
1+
import StoreUpdater, { ProChatChatReference } from '@/ProChat/container/StoreUpdater';
12
import { memo, ReactNode } from 'react';
23
import { DevtoolsOptions } from 'zustand/middleware';
34
import { ChatProps, createStore, Provider, useStoreApi } from '../store';
45

56
interface ProChatProviderProps extends ChatProps {
67
children: ReactNode;
78
devtoolOptions?: boolean | DevtoolsOptions;
9+
chatRef?: ProChatChatReference;
810
}
911

1012
export const ProChatProvider = memo<ProChatProviderProps>(
11-
({ children, devtoolOptions, ...props }) => {
13+
({
14+
children,
15+
devtoolOptions,
16+
chats,
17+
onChatsChange,
18+
loading,
19+
helloMessage,
20+
userMeta,
21+
assistantMeta,
22+
request,
23+
chatRef,
24+
...props
25+
}) => {
1226
let isWrapped = true;
1327

14-
const Content = <>{children}</>;
28+
const Content = (
29+
<>
30+
{children}
31+
<StoreUpdater
32+
chatRef={chatRef}
33+
init={!loading}
34+
helloMessage={helloMessage}
35+
chats={chats}
36+
userMeta={userMeta}
37+
request={request}
38+
assistantMeta={assistantMeta}
39+
onChatsChange={onChatsChange}
40+
/>
41+
</>
42+
);
1543

1644
try {
1745
useStoreApi();
@@ -23,6 +51,20 @@ export const ProChatProvider = memo<ProChatProviderProps>(
2351
return Content;
2452
}
2553

26-
return <Provider createStore={() => createStore(props, devtoolOptions)}>{Content}</Provider>;
54+
return (
55+
<Provider createStore={() => createStore(props, devtoolOptions)}>
56+
{Content}
57+
<StoreUpdater
58+
chatRef={chatRef}
59+
init={!loading}
60+
helloMessage={helloMessage}
61+
chats={chats}
62+
userMeta={userMeta}
63+
request={request}
64+
assistantMeta={assistantMeta}
65+
onChatsChange={onChatsChange}
66+
/>
67+
</Provider>
68+
);
2769
},
2870
);

src/ProChat/container/StoreUpdater.tsx

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,31 @@
1-
import { memo } from 'react';
1+
import { memo, MutableRefObject, useImperativeHandle } from 'react';
22
import { createStoreUpdater } from 'zustand-utils';
33

4+
import { ProChatInstance, useProChat } from '../hooks/useProChat';
45
import { ChatProps, ChatState, useStoreApi } from '../store';
56

6-
export type StoreUpdaterProps = Partial<
7-
Pick<ChatState, 'chats' | 'config' | 'init' | 'onChatsChange' | 'helloMessage' | 'request'>
8-
> &
9-
Pick<ChatProps, 'userMeta' | 'assistantMeta'>;
7+
export type ProChatChatReference = MutableRefObject<ProChatInstance | undefined>;
8+
9+
export interface StoreUpdaterProps
10+
extends Partial<
11+
Pick<ChatState, 'chats' | 'config' | 'init' | 'onChatsChange' | 'helloMessage' | 'request'>
12+
>,
13+
Pick<ChatProps, 'userMeta' | 'assistantMeta'> {
14+
chatRef?: ProChatChatReference;
15+
}
1016

1117
const StoreUpdater = memo<StoreUpdaterProps>(
12-
({ init, onChatsChange, request, userMeta, assistantMeta, helloMessage, chats, config }) => {
18+
({
19+
init,
20+
onChatsChange,
21+
chatRef,
22+
request,
23+
userMeta,
24+
assistantMeta,
25+
helloMessage,
26+
chats,
27+
config,
28+
}) => {
1329
const storeApi = useStoreApi();
1430
const useStoreUpdater = createStoreUpdater(storeApi);
1531

@@ -25,6 +41,10 @@ const StoreUpdater = memo<StoreUpdaterProps>(
2541
useStoreUpdater('onChatsChange', onChatsChange);
2642

2743
useStoreUpdater('request', request);
44+
45+
const instance = useProChat();
46+
useImperativeHandle(chatRef, () => instance);
47+
2848
return null;
2949
},
3050
);

src/ProChat/container/index.tsx

Lines changed: 4 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -6,61 +6,24 @@ import App from './App';
66
import { DevtoolsOptions } from 'zustand/middleware';
77
import { ChatProps } from '../store';
88
import { ProChatProvider } from './Provider';
9-
import StoreUpdater from './StoreUpdater';
9+
import { ProChatChatReference } from './StoreUpdater';
1010

1111
export interface ProChatProps extends ChatProps {
1212
renderInput?: ReactNode;
1313
__PRO_CHAT_STORE_DEVTOOLS__?: boolean | DevtoolsOptions;
1414
showTitle?: boolean;
1515
style?: CSSProperties;
1616
className?: string;
17+
chatRef?: ProChatChatReference;
1718
}
1819

1920
export const ProChat = memo<ProChatProps>(
20-
({
21-
renderInput,
22-
__PRO_CHAT_STORE_DEVTOOLS__,
23-
chats,
24-
onChatsChange,
25-
initialChats,
26-
loading,
27-
helloMessage,
28-
userMeta,
29-
assistantMeta,
30-
showTitle,
31-
request,
32-
onResetMessage,
33-
style,
34-
className,
35-
autocompleteRequest,
36-
...props
37-
}) => {
21+
({ renderInput, __PRO_CHAT_STORE_DEVTOOLS__, showTitle, style, className, ...props }) => {
3822
return (
39-
<ProChatProvider
40-
initialChats={initialChats}
41-
chats={chats}
42-
loading={loading}
43-
helloMessage={helloMessage}
44-
userMeta={userMeta}
45-
assistantMeta={assistantMeta}
46-
request={request}
47-
onResetMessage={onResetMessage}
48-
autocompleteRequest={autocompleteRequest}
49-
{...props}
50-
devtoolOptions={__PRO_CHAT_STORE_DEVTOOLS__}
51-
>
23+
<ProChatProvider {...props} devtoolOptions={__PRO_CHAT_STORE_DEVTOOLS__}>
5224
<Container>
5325
<App chatInput={renderInput} showTitle={showTitle} style={style} className={className} />
5426
</Container>
55-
<StoreUpdater
56-
init={!loading}
57-
helloMessage={helloMessage}
58-
chats={chats}
59-
userMeta={userMeta}
60-
request={request}
61-
assistantMeta={assistantMeta}
62-
onChatsChange={onChatsChange}
63-
/>
6427
</ProChatProvider>
6528
);
6629
},

src/ProChat/demos/initialChats.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export default () => {
1010
const theme = useTheme();
1111
return (
1212
<div style={{ background: theme.colorBgLayout }}>
13-
<ProChat initialChats={example.chats} config={example.config} />
13+
<ProChat initialChats={example.chats} />
1414
</div>
1515
);
1616
};

src/ProChat/demos/meta.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export default () => {
1313
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg',
1414
title: 'Ant Design',
1515
}}
16-
assistantMeta={{ avatar: '🛸', title: '三体世界', backgroundColor: 'blue' }}
16+
assistantMeta={{ avatar: '🛸', title: '三体世界', backgroundColor: '#67dedd' }}
1717
initialChats={chats.chats}
1818
/>
1919
</div>

src/ProChat/demos/use-pro-chat.tsx

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/**
2+
* compact: true
3+
*/
4+
import { ProChat, ProChatProvider, useProChat } from '@ant-design/pro-chat';
5+
import { Button, Divider, Flex, message } from 'antd';
6+
import { useTheme } from 'antd-style';
7+
8+
import { MockResponse } from '@/ProChat/mocks/streamResponse';
9+
import { example } from '../mocks/basic';
10+
11+
const Chat = () => {
12+
const theme = useTheme();
13+
return (
14+
<div style={{ background: theme.colorBgLayout }}>
15+
<ProChat
16+
request={async (messages) => {
17+
const mockedData: string = `这是一段模拟的流式字符串数据。本次会话传入了${messages.length}条消息`;
18+
19+
const mockResponse = new MockResponse(mockedData, 100);
20+
21+
return mockResponse.getResponse();
22+
}}
23+
/>
24+
</div>
25+
);
26+
};
27+
28+
const Control = () => {
29+
const proChat = useProChat();
30+
31+
return (
32+
<Flex style={{ padding: 24 }} gap={8} justify={'space-between'}>
33+
<Flex gap={8}>
34+
<Button
35+
type={'primary'}
36+
onClick={() => {
37+
proChat.sendMessage('这是程序化发送的消息');
38+
}}
39+
>
40+
发送一条消息
41+
</Button>
42+
<Button
43+
onClick={() => {
44+
const messages = proChat.getChatMessages();
45+
46+
const msg = messages.at(-1);
47+
if (msg) {
48+
message.info(msg.content);
49+
} else {
50+
message.warning('会话为空');
51+
}
52+
}}
53+
>
54+
获取最新会话消息
55+
</Button>
56+
</Flex>
57+
58+
<Button
59+
onClick={() => {
60+
const messages = proChat.getChatMessages();
61+
const { id, content } = messages[0] || {};
62+
63+
if (!id) return;
64+
proChat.setMessageContent(id, content + '👋');
65+
}}
66+
>
67+
修改首条消息,添加表情:👋
68+
</Button>
69+
<Flex gap={8}>
70+
<Button
71+
danger
72+
onClick={() => {
73+
const messages = proChat.getChatMessages();
74+
proChat.deleteMessage(messages[0].id);
75+
message.success('已删除第一条消息');
76+
}}
77+
>
78+
删除第一条消息
79+
</Button>
80+
<Button
81+
type={'primary'}
82+
danger
83+
onClick={() => {
84+
proChat.clearMessage();
85+
}}
86+
>
87+
清空消息
88+
</Button>
89+
</Flex>
90+
</Flex>
91+
);
92+
};
93+
94+
export default () => (
95+
<ProChatProvider initialChats={example.chats}>
96+
<Control />
97+
<Divider>🔼 程序化控制 | 🔽 用户控制</Divider>
98+
<Chat />
99+
</ProChatProvider>
100+
);

src/ProChat/demos/use-ref.tsx

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/**
2+
* compact: true
3+
*/
4+
import { ProChat, ProChatInstance } from '@ant-design/pro-chat';
5+
import { useTheme } from 'antd-style';
6+
import { useRef } from 'react';
7+
8+
import { MockResponse } from '@/ProChat/mocks/streamResponse';
9+
import { Button } from 'antd';
10+
import { example } from '../mocks/basic';
11+
12+
export default () => {
13+
const theme = useTheme();
14+
const proChatRef = useRef<ProChatInstance>();
15+
16+
return (
17+
<div style={{ background: theme.colorBgLayout }}>
18+
<Button
19+
type={'primary'}
20+
onClick={() => {
21+
if (!proChatRef.current) return;
22+
const messages = proChatRef.current.getChatMessages();
23+
const { id, content } = messages[0] || {};
24+
25+
if (!id) return;
26+
proChatRef.current.setMessageContent(id, content + '👋');
27+
}}
28+
>
29+
修改首条消息,添加表情:👋
30+
</Button>
31+
<ProChat
32+
initialChats={example.chats}
33+
chatRef={proChatRef}
34+
request={async (messages) => {
35+
const mockedData: string = `这是一段模拟的流式字符串数据。本次会话传入了${messages.length}条消息`;
36+
37+
const mockResponse = new MockResponse(mockedData, 100);
38+
39+
return mockResponse.getResponse();
40+
}}
41+
/>
42+
</div>
43+
);
44+
};

0 commit comments

Comments
 (0)