diff --git a/app/components/base/button/index.tsx b/app/components/base/button/index.tsx index 33aebb6..518a849 100644 --- a/app/components/base/button/index.tsx +++ b/app/components/base/button/index.tsx @@ -21,6 +21,9 @@ const Button: FC = ({ }) => { let style = 'cursor-pointer' switch (type) { + case 'link': + style = disabled ? 'border-solid border border-gray-200 bg-gray-200 cursor-not-allowed text-gray-800' : 'border-solid border border-gray-200 cursor-pointer text-blue-600 bg-white hover:shadow-sm hover:border-gray-300' + break case 'primary': style = (disabled || loading) ? 'bg-primary-600/75 cursor-not-allowed text-white' : 'bg-primary-600 hover:bg-primary-600/75 hover:shadow-md cursor-pointer text-white hover:shadow-sm' break diff --git a/app/components/chat/answer/index.tsx b/app/components/chat/answer/index.tsx index 8505e65..2bce5b9 100644 --- a/app/components/chat/answer/index.tsx +++ b/app/components/chat/answer/index.tsx @@ -13,6 +13,7 @@ import type { ChatItem, MessageRating, VisionFile } from '@/types/app' import Tooltip from '@/app/components/base/tooltip' import WorkflowProcess from '@/app/components/workflow/workflow-process' import { Markdown } from '@/app/components/base/markdown' +import Button from '@/app/components/base/button' import type { Emoji } from '@/types/tools' const OperationBtn = ({ innerContent, onClick, className }: { innerContent: React.ReactNode; onClick?: () => void; className?: string }) => ( @@ -60,6 +61,7 @@ type IAnswerProps = { onFeedback?: FeedbackFunc isResponding?: boolean allToolIcons?: Record + suggestionClick?: (suggestion: string) => void } // The component needs to maintain its own state to control whether to display input component @@ -69,8 +71,9 @@ const Answer: FC = ({ onFeedback, isResponding, allToolIcons, + suggestionClick = () => { }, }) => { - const { id, content, feedback, agent_thoughts, workflowProcess } = item + const { id, content, feedback, agent_thoughts, workflowProcess, suggestedQuestions = [] } = item const isAgentMode = !!agent_thoughts && agent_thoughts.length > 0 const { t } = useTranslation() @@ -192,6 +195,17 @@ const Answer: FC = ({ : ( ))} + {suggestedQuestions.length > 0 && ( +
+
+ {suggestedQuestions.map((suggestion, index) => ( +
+ +
+ ))} +
+
+ )}
{!feedbackDisabled && !item.feedbackDisabled && renderItemOperation()} diff --git a/app/components/chat/index.tsx b/app/components/chat/index.tsx index e257a6c..0f95615 100644 --- a/app/components/chat/index.tsx +++ b/app/components/chat/index.tsx @@ -52,9 +52,12 @@ const Chat: FC = ({ const isUseInputMethod = useRef(false) const [query, setQuery] = React.useState('') + const queryRef = useRef('') + const handleContentChange = (e: any) => { const value = e.target.value setQuery(value) + queryRef.current = value } const logError = (message: string) => { @@ -62,16 +65,19 @@ const Chat: FC = ({ } const valid = () => { + const query = queryRef.current if (!query || query.trim() === '') { - logError('Message cannot be empty') + logError(t('app.errorMessage.valueOfVarRequired')) return false } return true } useEffect(() => { - if (controlClearQuery) + if (controlClearQuery) { setQuery('') + queryRef.current = '' + } }, [controlClearQuery]) const { files, @@ -86,7 +92,7 @@ const Chat: FC = ({ const handleSend = () => { if (!valid() || (checkCanSend && !checkCanSend())) return - onSend(query, files.filter(file => file.progress !== -1).map(fileItem => ({ + onSend(queryRef.current, files.filter(file => file.progress !== -1).map(fileItem => ({ type: 'image', transfer_method: fileItem.type, url: fileItem.url, @@ -95,8 +101,10 @@ const Chat: FC = ({ if (!files.find(item => item.type === TransferMethod.local_file && !item.fileId)) { if (files.length) onClear() - if (!isResponding) + if (!isResponding) { setQuery('') + queryRef.current = '' + } } } @@ -112,11 +120,19 @@ const Chat: FC = ({ const handleKeyDown = (e: any) => { isUseInputMethod.current = e.nativeEvent.isComposing if (e.code === 'Enter' && !e.shiftKey) { - setQuery(query.replace(/\n$/, '')) + const result = query.replace(/\n$/, '') + setQuery(result) + queryRef.current = result e.preventDefault() } } + const suggestionClick = (suggestion: string) => { + setQuery(suggestion) + queryRef.current = suggestion + handleSend() + } + return (
{/* Chat List */} @@ -130,6 +146,7 @@ const Chat: FC = ({ feedbackDisabled={feedbackDisabled} onFeedback={onFeedback} isResponding={isResponding && isLast} + suggestionClick={suggestionClick} /> } return ( diff --git a/app/components/index.tsx b/app/components/index.tsx index df40f9a..9b16f26 100644 --- a/app/components/index.tsx +++ b/app/components/index.tsx @@ -101,6 +101,7 @@ const Main: FC = () => { const conversationName = currConversationInfo?.name || t('app.chat.newChatDefaultName') as string const conversationIntroduction = currConversationInfo?.introduction || '' + const suggestedQuestions = currConversationInfo?.suggested_questions || [] const handleConversationSwitch = () => { if (!inited) @@ -117,6 +118,7 @@ const Main: FC = () => { setExistConversationInfo({ name: item?.name || '', introduction: notSyncToStateIntroduction, + suggested_questions: suggestedQuestions, }) } else { @@ -192,6 +194,7 @@ const Main: FC = () => { name: t('app.chat.newChatDefaultName'), inputs: newConversationInputs, introduction: conversationIntroduction, + suggested_questions: suggestedQuestions, }) })) } @@ -209,6 +212,7 @@ const Main: FC = () => { isAnswer: true, feedbackDisabled: true, isOpeningStatement: isShowPrompt, + suggestedQuestions: suggestedQuestions, } if (calculatedIntroduction) return [openStatement] @@ -233,15 +237,24 @@ const Main: FC = () => { return } const _conversationId = getConversationIdFromStorage(APP_ID) - const isNotNewConversation = conversations.some(item => item.id === _conversationId) + const currentConversation = conversations.find(item => item.id === _conversationId) + const isNotNewConversation = !!currentConversation // fetch new conversation info - const { user_input_form, opening_statement: introduction, file_upload, system_parameters }: any = appParams + const { user_input_form, opening_statement: introduction, file_upload, system_parameters, suggested_questions = [] }: any = appParams setLocaleOnClient(APP_INFO.default_language, true) setNewConversationInfo({ name: t('app.chat.newChatDefaultName'), introduction, + suggested_questions }) + if (isNotNewConversation) { + setExistConversationInfo({ + name: currentConversation.name || t('app.chat.newChatDefaultName'), + introduction, + suggested_questions + }) + } const prompt_variables = userInputsFormToPromptVariables(user_input_form) setPromptConfig({ prompt_template: promptTemplate, diff --git a/types/app.ts b/types/app.ts index 06b287f..58a1641 100644 --- a/types/app.ts +++ b/types/app.ts @@ -102,7 +102,8 @@ export type ConversationItem = { id: string name: string inputs: Record | null - introduction: string + introduction: string, + suggested_questions?: string[] } export type AppInfo = {