Files
DB-GPT/datacenter/components/chatBox.tsx
changhuiping.chp 6a2df06f88 feat: 布局初版
2023-06-25 18:09:53 +08:00

235 lines
5.9 KiB
TypeScript

import { zodResolver } from '@hookform/resolvers/zod';
import SendRoundedIcon from '@mui/icons-material/SendRounded';
import Button from '@mui/joy/Button';
import Card from '@mui/joy/Card';
import CircularProgress from '@mui/joy/CircularProgress';
import IconButton from '@mui/joy/IconButton';
import Input from '@mui/joy/Input';
import Stack from '@mui/joy/Stack';
import React, { useState } from 'react';
import { useForm } from 'react-hook-form';
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import { z } from 'zod';
import { Message } from '@/types';
type Props = {
messages: Message[];
onSubmit: (message: string) => Promise<any>;
messageTemplates?: string[];
initialMessage?: string;
readOnly?: boolean;
};
const Schema = z.object({ query: z.string().min(1) });
const ChatBoxComp = ({
messages,
onSubmit,
messageTemplates,
initialMessage,
readOnly,
}: Props) => {
const scrollableRef = React.useRef<HTMLDivElement>(null);
const [isLoading, setIsLoading] = useState(false);
const [firstMsg, setFirstMsg] = useState<Message>();
const [hideTemplateMessages, setHideTemplateMessages] = useState(false);
console.log(messages, 'mmm');
const methods = useForm<z.infer<typeof Schema>>({
resolver: zodResolver(Schema),
defaultValues: {},
});
const submit = async ({ query }: z.infer<typeof Schema>) => {
try {
setIsLoading(true);
setHideTemplateMessages(true);
methods.reset();
await onSubmit(query);
} catch (err) {
} finally {
setIsLoading(false);
}
};
React.useEffect(() => {
if (!scrollableRef.current) {
return;
}
scrollableRef.current.scrollTo(0, scrollableRef.current.scrollHeight);
}, [messages?.length]);
React.useEffect(() => {
setTimeout(() => {
setFirstMsg(
initialMessage ? { from: 'agent', message: initialMessage } : undefined
);
}, 0);
}, [initialMessage]);
return (
<Stack
direction={'column'}
gap={2}
sx={{
display: 'flex',
flex: 1,
flexBasis: '100%',
maxWidth: '700px',
width: '100%',
height: '100%',
maxHeight: '100%',
minHeight: '100%',
mx: 'auto',
}}
>
<Stack
ref={scrollableRef}
direction={'column'}
gap={2}
sx={{
boxSizing: 'border-box',
maxWidth: '100%',
width: '100%',
mx: 'auto',
flex: 1,
maxHeight: '100%',
overflowY: 'auto',
p: 2,
border: '1px solid',
borderColor: 'var(--joy-palette-divider)'
}}
>
{firstMsg && (
<Card
size="sm"
variant={'outlined'}
color={'primary'}
className="message-agent"
sx={{
mr: 'auto',
ml: 'none',
whiteSpace: 'pre-wrap',
}}
>
{firstMsg?.message}
</Card>
)}
{messages.map((each, index) => (
<Stack
key={index}
sx={{
mr: each.from === 'agent' ? 'auto' : 'none',
ml: each.from === 'human' ? 'auto' : 'none',
}}
>
<Card
size="sm"
variant={'outlined'}
className={
each.from === 'agent' ? 'message-agent' : 'message-human'
}
color={each.from === 'agent' ? 'primary' : 'neutral'}
sx={(theme) => ({
px: 2,
'ol, ul': {
my: 0,
pl: 2,
},
ol: {
listStyle: 'numeric',
},
ul: {
listStyle: 'disc',
mb: 2,
},
li: {
my: 1,
},
a: {
textDecoration: 'underline',
},
})}
>
<ReactMarkdown remarkPlugins={[remarkGfm]} linkTarget={'_blank'}>
{each.message}
</ReactMarkdown>
</Card>
</Stack>
))}
{isLoading && (
<CircularProgress
variant="soft"
color="neutral"
size="sm"
sx={{ mx: 'auto', my: 2 }}
/>
)}
</Stack>
{!readOnly && (
<form
style={{
maxWidth: '100%',
width: '100%',
position: 'relative',
display: 'flex',
marginTop: 'auto',
overflow: 'visible',
background: 'none',
justifyContent: 'center',
marginLeft: 'auto',
marginRight: 'auto',
}}
onSubmit={(e) => {
e.stopPropagation();
methods.handleSubmit(submit)(e);
}}
>
{!hideTemplateMessages && (messageTemplates?.length || 0) > 0 && (
<Stack
direction="row"
gap={1}
sx={{
position: 'absolute',
zIndex: 1,
transform: 'translateY(-100%)',
flexWrap: 'wrap',
mt: -1,
left: '0',
}}
>
{messageTemplates?.map((each, idx) => (
<Button
key={idx}
size="sm"
variant="soft"
onClick={() => submit({ query: each })}
>
{each}
</Button>
))}
</Stack>
)}
<Input
sx={{ width: '100%' }}
variant="outlined"
endDecorator={
<IconButton type="submit" disabled={isLoading}>
<SendRoundedIcon />
</IconButton>
}
{...methods.register('query')}
/>
</form>
)}
</Stack>
);
}
export default ChatBoxComp;