Commit 29f5cf05 by Xianquan Committed by GitHub

chore: polish chat ui behavior (#7068)

parent 7fe33fb2
...@@ -42,7 +42,7 @@ function MyLink(e: any) { ...@@ -42,7 +42,7 @@ function MyLink(e: any) {
); );
} }
const Guide = ({ text }: { text: string }) => { const Guide = ({ text, className }: { text: string; className?: string }) => {
const formatText = useMemo( const formatText = useMemo(
() => text.replace(/\[(.*?)\]($|\n)/g, '[$1]()').replace(/\\n/g, '\n '), () => text.replace(/\[(.*?)\]($|\n)/g, '[$1]()').replace(/\\n/g, '\n '),
[text] [text]
...@@ -50,7 +50,7 @@ const Guide = ({ text }: { text: string }) => { ...@@ -50,7 +50,7 @@ const Guide = ({ text }: { text: string }) => {
return ( return (
<ReactMarkdown <ReactMarkdown
className={`markdown ${styles.markdown}`} className={`markdown ${styles.markdown} ${className || ''}`}
remarkPlugins={[RemarkGfm, RemarkMath, RemarkBreaks]} remarkPlugins={[RemarkGfm, RemarkMath, RemarkBreaks]}
rehypePlugins={[RehypeKatex]} rehypePlugins={[RehypeKatex]}
components={{ components={{
......
...@@ -59,7 +59,7 @@ const MarkdownRender = ({ ...@@ -59,7 +59,7 @@ const MarkdownRender = ({
return { return {
img: (props: any) => <Image {...props} alt={props.alt} chatAuthData={chatAuthData} />, img: (props: any) => <Image {...props} alt={props.alt} chatAuthData={chatAuthData} />,
pre: RewritePre, pre: RewritePre,
code: Code, code: (props: any) => <Code {...props} markdownClassName={className} />,
table: MarkdownTable as any, table: MarkdownTable as any,
a: (props: any) => ( a: (props: any) => (
<A <A
...@@ -104,7 +104,7 @@ export default React.memo(Markdown); ...@@ -104,7 +104,7 @@ export default React.memo(Markdown);
/* Custom dom */ /* Custom dom */
function Code(e: any) { function Code(e: any) {
const { className, codeBlock, children } = e; const { className, codeBlock, children, markdownClassName } = e;
const match = /language-(\w+)/.exec(className || ''); const match = /language-(\w+)/.exec(className || '');
const codeType = match?.[1]?.toLowerCase(); const codeType = match?.[1]?.toLowerCase();
...@@ -115,7 +115,7 @@ function Code(e: any) { ...@@ -115,7 +115,7 @@ function Code(e: any) {
return <MermaidCodeBlock code={strChildren} />; return <MermaidCodeBlock code={strChildren} />;
} }
if (codeType === CodeClassNameEnum.guide) { if (codeType === CodeClassNameEnum.guide) {
return <ChatGuide text={strChildren} />; return <ChatGuide text={strChildren} className={markdownClassName} />;
} }
if (codeType === CodeClassNameEnum.questionguide) { if (codeType === CodeClassNameEnum.questionguide) {
return <QuestionGuide text={strChildren} />; return <QuestionGuide text={strChildren} />;
...@@ -145,7 +145,7 @@ function Code(e: any) { ...@@ -145,7 +145,7 @@ function Code(e: any) {
{children} {children}
</CodeLight> </CodeLight>
); );
}, [codeType, className, codeBlock, match, children, strChildren]); }, [codeType, className, codeBlock, match, children, strChildren, markdownClassName]);
return Component; return Component;
} }
......
...@@ -527,7 +527,7 @@ const ChatInput = ({ ...@@ -527,7 +527,7 @@ const ChatInput = ({
{!mobilePreSpeak && <Box>{RenderButtonGroup}</Box>} {!mobilePreSpeak && <Box>{RenderButtonGroup}</Box>}
</Flex> </Flex>
<ComplianceTip type={'chat'} pt={0} pb={[0, 6]} /> <ComplianceTip type={'chat'} pt={0} pb={0} />
</Box> </Box>
); );
}; };
......
import { Box, Card } from '@chakra-ui/react'; import { Box } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import { MessageCardStyle } from '../constants';
import Markdown from '@/components/Markdown'; import Markdown from '@/components/Markdown';
import ChatAvatar from './ChatAvatar'; import styles from './AIChatBubble/index.module.scss';
import { useContextSelector } from 'use-context-selector';
import { ChatItemContext } from '@/web/core/chat/context/chatItemContext';
const WelcomeBox = ({ welcomeText }: { welcomeText: string }) => { const WelcomeBox = ({ welcomeText }: { welcomeText: string }) => {
const appAvatar = useContextSelector(ChatItemContext, (v) => v.chatBoxData?.app?.avatar);
return ( return (
<Box py={3}> <Box py={3} w={'100%'}>
{/* avatar */} <Box className="chat-box-card" w={'100%'} maxW={['calc(100% - 25px)', '700px']} mx={'auto'}>
<ChatAvatar src={appAvatar} type={'AI'} /> <Box color={'myGray.900'} textAlign={'left'} fontSize={'16px'} lineHeight={1.75}>
{/* message */} <Markdown
<Box textAlign={'left'}> className={styles.markdown}
<Card source={`~~~guide \n${welcomeText}`}
order={2} forbidZhFormat
mt={2} />
{...MessageCardStyle} </Box>
bg={'white'}
boxShadow={'0 0 8px rgba(0,0,0,0.15)'}
>
<Markdown source={`~~~guide \n${welcomeText}`} forbidZhFormat />
</Card>
</Box> </Box>
</Box> </Box>
); );
......
...@@ -63,7 +63,6 @@ const AppChatWindow = () => { ...@@ -63,7 +63,6 @@ const AppChatWindow = () => {
const chatSettings = useContextSelector(ChatPageContext, (v) => v.chatSettings); const chatSettings = useContextSelector(ChatPageContext, (v) => v.chatSettings);
const handlePaneChange = useContextSelector(ChatPageContext, (v) => v.handlePaneChange); const handlePaneChange = useContextSelector(ChatPageContext, (v) => v.handlePaneChange);
const refreshRecentlyUsed = useContextSelector(ChatPageContext, (v) => v.refreshRecentlyUsed); const refreshRecentlyUsed = useContextSelector(ChatPageContext, (v) => v.refreshRecentlyUsed);
const collapseSidebar = useContextSelector(ChatPageContext, (v) => v.collapseSidebar);
const { loading } = useRequest( const { loading } = useRequest(
async () => { async () => {
...@@ -113,8 +112,6 @@ const AppChatWindow = () => { ...@@ -113,8 +112,6 @@ const AppChatWindow = () => {
return Promise.reject('appId is empty'); return Promise.reject('appId is empty');
} }
collapseSidebar();
const histories = messages.slice(-1); const histories = messages.slice(-1);
const { responseText } = await streamFetch({ const { responseText } = await streamFetch({
data: { data: {
...@@ -150,8 +147,7 @@ const AppChatWindow = () => { ...@@ -150,8 +147,7 @@ const AppChatWindow = () => {
forbidLoadChatRef, forbidLoadChatRef,
isShowCite, isShowCite,
showSkillReferences, showSkillReferences,
refreshRecentlyUsed, refreshRecentlyUsed
collapseSidebar
] ]
); );
......
...@@ -41,33 +41,6 @@ const isMultimodalEmbeddingModel = (model?: SystemModelItemType) => { ...@@ -41,33 +41,6 @@ const isMultimodalEmbeddingModel = (model?: SystemModelItemType) => {
return model?.type === ModelTypeEnum.embedding && !!model.vision; return model?.type === ModelTypeEnum.embedding && !!model.vision;
}; };
const SelectorActiveModelTags = React.memo(function SelectorActiveModelTags({
model
}: {
model?: SystemModelItemType;
}) {
const showTestModeTip = isTestModeModel(model);
const showMultimodalTip = isMultimodalEmbeddingModel(model);
if (!showTestModeTip && !showMultimodalTip) return null;
return (
<Box
position={'absolute'}
top={'50%'}
right={'40px'}
transform={'translateY(-50%)'}
zIndex={3}
display={'flex'}
alignItems={'center'}
gap={1}
>
{showTestModeTip && <TestModeBetaTag />}
{showMultimodalTip && <MultimodalTag />}
</Box>
);
});
const ModelOptionLabel = React.memo(function ModelOptionLabel({ const ModelOptionLabel = React.memo(function ModelOptionLabel({
name, name,
showTestModeTip, showTestModeTip,
...@@ -80,7 +53,7 @@ const ModelOptionLabel = React.memo(function ModelOptionLabel({ ...@@ -80,7 +53,7 @@ const ModelOptionLabel = React.memo(function ModelOptionLabel({
noOfLines?: ResponsiveValue<number>; noOfLines?: ResponsiveValue<number>;
}) { }) {
return ( return (
<Flex alignItems={'center'} flex={'1 1 0'} w={'100%'} minW={0} overflow={'hidden'}> <Flex alignItems={'center'} flex={'1 1 0'} minW={0} overflow={'hidden'}>
<Box noOfLines={noOfLines ?? 1} flex={'1 1 0'} minW={0} overflow={'hidden'}> <Box noOfLines={noOfLines ?? 1} flex={'1 1 0'} minW={0} overflow={'hidden'}>
{name} {name}
</Box> </Box>
...@@ -200,12 +173,12 @@ const OneRowSelector = ({ ...@@ -200,12 +173,12 @@ const OneRowSelector = ({
list={avatarList} list={avatarList}
valueLabel={ valueLabel={
selectedModelData ? ( selectedModelData ? (
<Flex alignItems={'center'} py={1} minW={0} overflow={'hidden'}> <Flex alignItems={'center'} py={1} minW={0} overflow={'hidden'} w={'100%'}>
<ModelOptionLabel <ModelOptionLabel
name={selectedModelData.name} name={selectedModelData.name}
noOfLines={noOfLines} noOfLines={noOfLines}
showTestModeTip={false} showTestModeTip={isTestModeModel(selectedModelData)}
showMultimodalTip={false} showMultimodalTip={isMultimodalEmbeddingModel(selectedModelData)}
/> />
</Flex> </Flex>
) : undefined ) : undefined
...@@ -220,7 +193,6 @@ const OneRowSelector = ({ ...@@ -220,7 +193,6 @@ const OneRowSelector = ({
}} }}
/> />
</MyTooltip> </MyTooltip>
<SelectorActiveModelTags model={selectedModelData} />
</Box> </Box>
); );
}; };
...@@ -240,7 +212,6 @@ const MultipleRowSelector = ({ ...@@ -240,7 +212,6 @@ const MultipleRowSelector = ({
ttsModelList, ttsModelList,
sttModelList, sttModelList,
reRankModelList, reRankModelList,
getModelProvider,
getModelProviders, getModelProviders,
getMyModelList getMyModelList
} = useSystemStore(); } = useSystemStore();
...@@ -340,12 +311,12 @@ const MultipleRowSelector = ({ ...@@ -340,12 +311,12 @@ const MultipleRowSelector = ({
if (!selectedModelData) return <>{t('common:not_model_config')}</>; if (!selectedModelData) return <>{t('common:not_model_config')}</>;
return ( return (
<Flex alignItems={'center'} py={1} minW={0} overflow={'hidden'}> <Flex alignItems={'center'} py={1} minW={0} overflow={'hidden'} w={'100%'}>
<ModelOptionLabel <ModelOptionLabel
name={selectedModelData.name} name={selectedModelData.name}
noOfLines={noOfLines} noOfLines={noOfLines}
showTestModeTip={false} showTestModeTip={isTestModeModel(selectedModelData)}
showMultimodalTip={false} showMultimodalTip={isMultimodalEmbeddingModel(selectedModelData)}
/> />
</Flex> </Flex>
); );
...@@ -377,7 +348,6 @@ const MultipleRowSelector = ({ ...@@ -377,7 +348,6 @@ const MultipleRowSelector = ({
}} }}
/> />
</MyTooltip> </MyTooltip>
<SelectorActiveModelTags model={selectedModelData} />
</Box> </Box>
); );
}; };
......
...@@ -96,7 +96,6 @@ const HomeChatWindow = () => { ...@@ -96,7 +96,6 @@ const HomeChatWindow = () => {
const handlePaneChange = useContextSelector(ChatPageContext, (v) => v.handlePaneChange); const handlePaneChange = useContextSelector(ChatPageContext, (v) => v.handlePaneChange);
const homeAppId = useContextSelector(ChatPageContext, (v) => v.chatSettings?.appId || ''); const homeAppId = useContextSelector(ChatPageContext, (v) => v.chatSettings?.appId || '');
const refreshRecentlyUsed = useContextSelector(ChatPageContext, (v) => v.refreshRecentlyUsed); const refreshRecentlyUsed = useContextSelector(ChatPageContext, (v) => v.refreshRecentlyUsed);
const collapseSidebar = useContextSelector(ChatPageContext, (v) => v.collapseSidebar);
const chatRecords = useContextSelector(ChatRecordContext, (v) => v.chatRecords); const chatRecords = useContextSelector(ChatRecordContext, (v) => v.chatRecords);
...@@ -230,8 +229,6 @@ const HomeChatWindow = () => { ...@@ -230,8 +229,6 @@ const HomeChatWindow = () => {
return Promise.reject('appId is empty'); return Promise.reject('appId is empty');
} }
collapseSidebar();
const histories = messages.slice(-1); const histories = messages.slice(-1);
// Home chat uses the selected model and tools. Quick apps enter the normal app chat pane. // Home chat uses the selected model and tools. Quick apps enter the normal app chat pane.
...@@ -294,7 +291,7 @@ const HomeChatWindow = () => { ...@@ -294,7 +291,7 @@ const HomeChatWindow = () => {
<> <>
{/* 模型选择 */} {/* 模型选择 */}
{isPc && availableModels.length > 0 && ( {isPc && availableModels.length > 0 && (
<Box w={[0, 'auto']} flex={['1 0 0', '0 0 auto']}> <Box w={['0', '128px']} flex={['1 1 0', '0 0 128px']} minW={0}>
<ChatAIModelSelector <ChatAIModelSelector
cacheModel={false} cacheModel={false}
h={'36px'} h={'36px'}
......
...@@ -80,13 +80,21 @@ const MobileModelSelectorDrawer = ({ isOpen, modelList, value, onChange, onClose ...@@ -80,13 +80,21 @@ const MobileModelSelectorDrawer = ({ isOpen, modelList, value, onChange, onClose
outline: 'none' outline: 'none'
}} }}
> >
<Box bg="white" borderTopRadius="16px" px={4} pb="42px"> <Box
<Flex justifyContent="center" py="16px"> bg="white"
borderTopRadius="16px"
px={4}
pb="42px"
maxH="min(72dvh, 560px)"
display="flex"
flexDirection="column"
>
<Flex justifyContent="center" py="16px" flexShrink={0}>
<Drawer.Handle style={{ backgroundColor: 'var(--chakra-colors-myGray-400)' }} /> <Drawer.Handle style={{ backgroundColor: 'var(--chakra-colors-myGray-400)' }} />
</Flex> </Flex>
{!activeProvider ? ( {!activeProvider ? (
<Box pb={4}> <Box pb={4} flex="1 1 0" minH={0} overflowY="auto">
{providerGroups.map((provider) => ( {providerGroups.map((provider) => (
<Flex <Flex
key={provider.id} key={provider.id}
...@@ -116,8 +124,8 @@ const MobileModelSelectorDrawer = ({ isOpen, modelList, value, onChange, onClose ...@@ -116,8 +124,8 @@ const MobileModelSelectorDrawer = ({ isOpen, modelList, value, onChange, onClose
))} ))}
</Box> </Box>
) : ( ) : (
<> <Flex flexDirection="column" flex="1 1 0" minH={0}>
<Flex h="48px" alignItems="center"> <Flex h="48px" alignItems="center" flexShrink={0}>
<IconButton <IconButton
aria-label="Back" aria-label="Back"
icon={ icon={
...@@ -156,7 +164,7 @@ const MobileModelSelectorDrawer = ({ isOpen, modelList, value, onChange, onClose ...@@ -156,7 +164,7 @@ const MobileModelSelectorDrawer = ({ isOpen, modelList, value, onChange, onClose
onClick={onClose} onClick={onClose}
/> />
</Flex> </Flex>
<Box pb={4}> <Box pb={4} flex="1 1 0" minH={0} overflowY="auto">
{activeProvider.children.map((model) => { {activeProvider.children.map((model) => {
const isSelected = model.model === value; const isSelected = model.model === value;
...@@ -184,7 +192,7 @@ const MobileModelSelectorDrawer = ({ isOpen, modelList, value, onChange, onClose ...@@ -184,7 +192,7 @@ const MobileModelSelectorDrawer = ({ isOpen, modelList, value, onChange, onClose
); );
})} })}
</Box> </Box>
</> </Flex>
)} )}
</Box> </Box>
</Drawer.Content> </Drawer.Content>
......
...@@ -29,8 +29,6 @@ export type ChatPageContextValue = { ...@@ -29,8 +29,6 @@ export type ChatPageContextValue = {
) => void; ) => void;
collapse: CollapseStatusType; collapse: CollapseStatusType;
onTriggerCollapse: () => void; onTriggerCollapse: () => void;
/** 收起 /chat 页面左侧应用侧边栏;用于会话生成等需要扩大对话区域的场景。 */
collapseSidebar: () => void;
// Chat settings // Chat settings
chatSettings: ChatSettingType | undefined; chatSettings: ChatSettingType | undefined;
refreshChatSetting: () => Promise<ChatSettingType | undefined>; refreshChatSetting: () => Promise<ChatSettingType | undefined>;
...@@ -48,7 +46,6 @@ export const ChatPageContext = createContext<ChatPageContextValue>({ ...@@ -48,7 +46,6 @@ export const ChatPageContext = createContext<ChatPageContextValue>({
handlePaneChange: () => {}, handlePaneChange: () => {},
collapse: defaultCollapseStatus, collapse: defaultCollapseStatus,
onTriggerCollapse: () => {}, onTriggerCollapse: () => {},
collapseSidebar: () => {},
chatSettings: undefined, chatSettings: undefined,
logos: { wideLogoUrl: '', squareLogoUrl: '' }, logos: { wideLogoUrl: '', squareLogoUrl: '' },
refreshChatSetting: function (): Promise<ChatSettingType | undefined> { refreshChatSetting: function (): Promise<ChatSettingType | undefined> {
...@@ -185,9 +182,6 @@ export const ChatPageContextProvider = ({ ...@@ -185,9 +182,6 @@ export const ChatPageContextProvider = ({
const onTriggerCollapse = useCallback(() => { const onTriggerCollapse = useCallback(() => {
setCollapse(collapse === 0 ? 1 : 0); setCollapse(collapse === 0 ? 1 : 0);
}, [collapse]); }, [collapse]);
const collapseSidebar = useCallback(() => {
setCollapse(1);
}, []);
const value: ChatPageContextValue = useMemoEnhance( const value: ChatPageContextValue = useMemoEnhance(
() => ({ () => ({
...@@ -195,7 +189,6 @@ export const ChatPageContextProvider = ({ ...@@ -195,7 +189,6 @@ export const ChatPageContextProvider = ({
handlePaneChange, handlePaneChange,
collapse, collapse,
onTriggerCollapse, onTriggerCollapse,
collapseSidebar,
chatSettings, chatSettings,
refreshChatSetting, refreshChatSetting,
logos, logos,
...@@ -209,7 +202,6 @@ export const ChatPageContextProvider = ({ ...@@ -209,7 +202,6 @@ export const ChatPageContextProvider = ({
handlePaneChange, handlePaneChange,
collapse, collapse,
onTriggerCollapse, onTriggerCollapse,
collapseSidebar,
chatSettings, chatSettings,
refreshChatSetting, refreshChatSetting,
logos, logos,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment