| @@ -29,6 +29,7 @@ const ChatWrapper = () => { | |||
| appPrevChatTree, | |||
| currentConversationId, | |||
| currentConversationItem, | |||
| currentConversationInputs, | |||
| inputsForms, | |||
| newConversationInputs, | |||
| newConversationInputsRef, | |||
| @@ -69,7 +70,7 @@ const ChatWrapper = () => { | |||
| } = useChat( | |||
| appConfig, | |||
| { | |||
| inputs: (currentConversationId ? currentConversationItem?.inputs : newConversationInputs) as any, | |||
| inputs: (currentConversationId ? currentConversationInputs : newConversationInputs) as any, | |||
| inputsForm: inputsForms, | |||
| }, | |||
| appPrevChatTree, | |||
| @@ -77,7 +78,7 @@ const ChatWrapper = () => { | |||
| clearChatList, | |||
| setClearChatList, | |||
| ) | |||
| const inputsFormValue = currentConversationId ? currentConversationItem?.inputs : newConversationInputsRef?.current | |||
| const inputsFormValue = currentConversationId ? currentConversationInputs : newConversationInputsRef?.current | |||
| const inputDisabled = useMemo(() => { | |||
| let hasEmptyInput = '' | |||
| let fileIsUploading = false | |||
| @@ -124,7 +125,7 @@ const ChatWrapper = () => { | |||
| const data: any = { | |||
| query: message, | |||
| files, | |||
| inputs: currentConversationId ? currentConversationItem?.inputs : newConversationInputs, | |||
| inputs: currentConversationId ? currentConversationInputs : newConversationInputs, | |||
| conversation_id: currentConversationId, | |||
| parent_message_id: (isRegenerate ? parentAnswer?.id : getLastAnswer(chatList)?.id) || null, | |||
| } | |||
| @@ -144,6 +145,7 @@ const ChatWrapper = () => { | |||
| handleSend, | |||
| currentConversationId, | |||
| currentConversationItem, | |||
| currentConversationInputs, | |||
| newConversationInputs, | |||
| isInstalledApp, | |||
| appId, | |||
| @@ -245,7 +247,7 @@ const ChatWrapper = () => { | |||
| chatFooterClassName='pb-4' | |||
| chatFooterInnerClassName={`mx-auto w-full max-w-[768px] ${isMobile ? 'px-2' : 'px-4'}`} | |||
| onSend={doSend} | |||
| inputs={currentConversationId ? currentConversationItem?.inputs as any : newConversationInputs} | |||
| inputs={currentConversationId ? currentConversationInputs as any : newConversationInputs} | |||
| inputsForm={inputsForms} | |||
| onRegenerate={doRegenerate} | |||
| onStopResponding={handleStop} | |||
| @@ -54,6 +54,8 @@ export type ChatWithHistoryContextValue = { | |||
| setClearChatList: (state: boolean) => void | |||
| isResponding?: boolean | |||
| setIsResponding: (state: boolean) => void, | |||
| currentConversationInputs: Record<string, any> | null, | |||
| setCurrentConversationInputs: (v: Record<string, any>) => void, | |||
| } | |||
| export const ChatWithHistoryContext = createContext<ChatWithHistoryContextValue>({ | |||
| @@ -85,5 +87,7 @@ export const ChatWithHistoryContext = createContext<ChatWithHistoryContextValue> | |||
| setClearChatList: () => {}, | |||
| isResponding: false, | |||
| setIsResponding: () => {}, | |||
| currentConversationInputs: {}, | |||
| setCurrentConversationInputs: () => {}, | |||
| }) | |||
| export const useChatWithHistoryContext = () => useContext(ChatWithHistoryContext) | |||
| @@ -120,7 +120,7 @@ const HeaderInMobile = () => { | |||
| <div className='system-xl-semibold grow text-text-secondary'>{t('share.chat.chatSettingsTitle')}</div> | |||
| </div> | |||
| <div className='p-4'> | |||
| <InputsFormContent showTip /> | |||
| <InputsFormContent /> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| @@ -263,6 +263,17 @@ export const useChatWithHistory = (installedAppInfo?: InstalledApp) => { | |||
| return conversationItem | |||
| }, [conversationList, currentConversationId, pinnedConversationList]) | |||
| const currentConversationLatestInputs = useMemo(() => { | |||
| if (!currentConversationId || !appChatListData?.data.length) | |||
| return {} | |||
| return appChatListData.data.slice().pop().inputs || {} | |||
| }, [appChatListData, currentConversationId]) | |||
| const [currentConversationInputs, setCurrentConversationInputs] = useState<Record<string, any>>(currentConversationLatestInputs || {}) | |||
| useEffect(() => { | |||
| if (currentConversationItem) | |||
| setCurrentConversationInputs(currentConversationLatestInputs || {}) | |||
| }, [currentConversationItem, currentConversationLatestInputs]) | |||
| const { notify } = useToastContext() | |||
| const checkInputsRequired = useCallback((silent?: boolean) => { | |||
| let hasEmptyInput = '' | |||
| @@ -464,5 +475,7 @@ export const useChatWithHistory = (installedAppInfo?: InstalledApp) => { | |||
| setClearChatList, | |||
| isResponding, | |||
| setIsResponding, | |||
| currentConversationInputs, | |||
| setCurrentConversationInputs, | |||
| } | |||
| } | |||
| @@ -157,6 +157,8 @@ const ChatWithHistoryWrap: FC<ChatWithHistoryWrapProps> = ({ | |||
| setClearChatList, | |||
| isResponding, | |||
| setIsResponding, | |||
| currentConversationInputs, | |||
| setCurrentConversationInputs, | |||
| } = useChatWithHistory(installedAppInfo) | |||
| return ( | |||
| @@ -198,6 +200,8 @@ const ChatWithHistoryWrap: FC<ChatWithHistoryWrapProps> = ({ | |||
| setClearChatList, | |||
| isResponding, | |||
| setIsResponding, | |||
| currentConversationInputs, | |||
| setCurrentConversationInputs, | |||
| }}> | |||
| <ChatWithHistory className={className} /> | |||
| </ChatWithHistoryContext.Provider> | |||
| @@ -17,20 +17,24 @@ const InputsFormContent = ({ showTip }: Props) => { | |||
| appParams, | |||
| inputsForms, | |||
| currentConversationId, | |||
| currentConversationItem, | |||
| currentConversationInputs, | |||
| setCurrentConversationInputs, | |||
| newConversationInputs, | |||
| newConversationInputsRef, | |||
| handleNewConversationInputsChange, | |||
| } = useChatWithHistoryContext() | |||
| const inputsFormValue = currentConversationId ? currentConversationItem?.inputs : newConversationInputs | |||
| const readonly = !!currentConversationId | |||
| const inputsFormValue = currentConversationId ? currentConversationInputs : newConversationInputs | |||
| const handleFormChange = useCallback((variable: string, value: any) => { | |||
| setCurrentConversationInputs({ | |||
| ...currentConversationInputs, | |||
| [variable]: value, | |||
| }) | |||
| handleNewConversationInputsChange({ | |||
| ...newConversationInputsRef.current, | |||
| [variable]: value, | |||
| }) | |||
| }, [newConversationInputsRef, handleNewConversationInputsChange]) | |||
| }, [newConversationInputsRef, handleNewConversationInputsChange, currentConversationInputs, setCurrentConversationInputs]) | |||
| return ( | |||
| <div className='space-y-4'> | |||
| @@ -47,8 +51,6 @@ const InputsFormContent = ({ showTip }: Props) => { | |||
| value={inputsFormValue?.[form.variable] || ''} | |||
| onChange={e => handleFormChange(form.variable, e.target.value)} | |||
| placeholder={form.label} | |||
| readOnly={readonly} | |||
| disabled={readonly} | |||
| /> | |||
| )} | |||
| {form.type === InputVarType.number && ( | |||
| @@ -57,8 +59,6 @@ const InputsFormContent = ({ showTip }: Props) => { | |||
| value={inputsFormValue?.[form.variable] || ''} | |||
| onChange={e => handleFormChange(form.variable, e.target.value)} | |||
| placeholder={form.label} | |||
| readOnly={readonly} | |||
| disabled={readonly} | |||
| /> | |||
| )} | |||
| {form.type === InputVarType.paragraph && ( | |||
| @@ -66,8 +66,6 @@ const InputsFormContent = ({ showTip }: Props) => { | |||
| value={inputsFormValue?.[form.variable] || ''} | |||
| onChange={e => handleFormChange(form.variable, e.target.value)} | |||
| placeholder={form.label} | |||
| readOnly={readonly} | |||
| disabled={readonly} | |||
| /> | |||
| )} | |||
| {form.type === InputVarType.select && ( | |||
| @@ -77,7 +75,6 @@ const InputsFormContent = ({ showTip }: Props) => { | |||
| items={form.options.map((option: string) => ({ value: option, name: option }))} | |||
| onSelect={item => handleFormChange(form.variable, item.value as string)} | |||
| placeholder={form.label} | |||
| readonly={readonly} | |||
| /> | |||
| )} | |||
| {form.type === InputVarType.singleFile && ( | |||
| @@ -38,7 +38,7 @@ const InputsFormNode = ({ | |||
| <Message3Fill className='h-6 w-6 shrink-0' /> | |||
| <div className='system-xl-semibold grow text-text-secondary'>{t('share.chat.chatSettingsTitle')}</div> | |||
| {collapsed && ( | |||
| <Button className='uppercase text-text-tertiary' size='small' variant='ghost' onClick={() => setCollapsed(false)}>{currentConversationId ? t('common.operation.view') : t('common.operation.edit')}</Button> | |||
| <Button className='uppercase text-text-tertiary' size='small' variant='ghost' onClick={() => setCollapsed(false)}>{t('common.operation.edit')}</Button> | |||
| )} | |||
| {!collapsed && currentConversationId && ( | |||
| <Button className='uppercase text-text-tertiary' size='small' variant='ghost' onClick={() => setCollapsed(true)}>{t('common.operation.close')}</Button> | |||
| @@ -46,7 +46,7 @@ const InputsFormNode = ({ | |||
| </div> | |||
| {!collapsed && ( | |||
| <div className={cn('p-6', isMobile && 'p-4')}> | |||
| <InputsFormContent showTip={!!currentConversationId} /> | |||
| <InputsFormContent /> | |||
| </div> | |||
| )} | |||
| {!collapsed && !currentConversationId && ( | |||
| @@ -36,7 +36,7 @@ const ViewFormDropdown = () => { | |||
| <div className='system-xl-semibold grow text-text-secondary'>{t('share.chat.chatSettingsTitle')}</div> | |||
| </div> | |||
| <div className='p-6'> | |||
| <InputsFormContent showTip /> | |||
| <InputsFormContent /> | |||
| </div> | |||
| </div> | |||
| </PortalToFollowElemContent> | |||
| @@ -32,6 +32,7 @@ const ChatWrapper = () => { | |||
| appPrevChatList, | |||
| currentConversationId, | |||
| currentConversationItem, | |||
| currentConversationInputs, | |||
| inputsForms, | |||
| newConversationInputs, | |||
| newConversationInputsRef, | |||
| @@ -70,7 +71,7 @@ const ChatWrapper = () => { | |||
| } = useChat( | |||
| appConfig, | |||
| { | |||
| inputs: (currentConversationId ? currentConversationItem?.inputs : newConversationInputs) as any, | |||
| inputs: (currentConversationId ? currentConversationInputs : newConversationInputs) as any, | |||
| inputsForm: inputsForms, | |||
| }, | |||
| appPrevChatList, | |||
| @@ -78,7 +79,7 @@ const ChatWrapper = () => { | |||
| clearChatList, | |||
| setClearChatList, | |||
| ) | |||
| const inputsFormValue = currentConversationId ? currentConversationItem?.inputs : newConversationInputsRef?.current | |||
| const inputsFormValue = currentConversationId ? currentConversationInputs : newConversationInputsRef?.current | |||
| const inputDisabled = useMemo(() => { | |||
| let hasEmptyInput = '' | |||
| let fileIsUploading = false | |||
| @@ -123,7 +124,7 @@ const ChatWrapper = () => { | |||
| const data: any = { | |||
| query: message, | |||
| files, | |||
| inputs: currentConversationId ? currentConversationItem?.inputs : newConversationInputs, | |||
| inputs: currentConversationId ? currentConversationInputs : newConversationInputs, | |||
| conversation_id: currentConversationId, | |||
| parent_message_id: (isRegenerate ? parentAnswer?.id : getLastAnswer(chatList)?.id) || null, | |||
| } | |||
| @@ -241,7 +242,7 @@ const ChatWrapper = () => { | |||
| chatFooterClassName={cn('pb-4', !isMobile && 'rounded-b-2xl')} | |||
| chatFooterInnerClassName={cn('mx-auto w-full max-w-full px-4', isMobile && 'px-2')} | |||
| onSend={doSend} | |||
| inputs={currentConversationId ? currentConversationItem?.inputs as any : newConversationInputs} | |||
| inputs={currentConversationId ? currentConversationInputs as any : newConversationInputs} | |||
| inputsForm={inputsForms} | |||
| onRegenerate={doRegenerate} | |||
| onStopResponding={handleStop} | |||
| @@ -46,6 +46,8 @@ export type EmbeddedChatbotContextValue = { | |||
| setClearChatList: (state: boolean) => void | |||
| isResponding?: boolean | |||
| setIsResponding: (state: boolean) => void, | |||
| currentConversationInputs: Record<string, any> | null, | |||
| setCurrentConversationInputs: (v: Record<string, any>) => void, | |||
| } | |||
| export const EmbeddedChatbotContext = createContext<EmbeddedChatbotContextValue>({ | |||
| @@ -70,5 +72,7 @@ export const EmbeddedChatbotContext = createContext<EmbeddedChatbotContextValue> | |||
| setClearChatList: () => {}, | |||
| isResponding: false, | |||
| setIsResponding: () => {}, | |||
| currentConversationInputs: {}, | |||
| setCurrentConversationInputs: () => {}, | |||
| }) | |||
| export const useEmbeddedChatbotContext = () => useContext(EmbeddedChatbotContext) | |||
| @@ -239,6 +239,17 @@ export const useEmbeddedChatbot = () => { | |||
| return conversationItem | |||
| }, [conversationList, currentConversationId, pinnedConversationList]) | |||
| const currentConversationLatestInputs = useMemo(() => { | |||
| if (!currentConversationId || !appChatListData?.data.length) | |||
| return {} | |||
| return appChatListData.data.slice().pop().inputs || {} | |||
| }, [appChatListData, currentConversationId]) | |||
| const [currentConversationInputs, setCurrentConversationInputs] = useState<Record<string, any>>(currentConversationLatestInputs || {}) | |||
| useEffect(() => { | |||
| if (currentConversationItem) | |||
| setCurrentConversationInputs(currentConversationLatestInputs || {}) | |||
| }, [currentConversationItem, currentConversationLatestInputs]) | |||
| const { notify } = useToastContext() | |||
| const checkInputsRequired = useCallback((silent?: boolean) => { | |||
| let hasEmptyInput = '' | |||
| @@ -347,5 +358,7 @@ export const useEmbeddedChatbot = () => { | |||
| setClearChatList, | |||
| isResponding, | |||
| setIsResponding, | |||
| currentConversationInputs, | |||
| setCurrentConversationInputs, | |||
| } | |||
| } | |||
| @@ -160,6 +160,8 @@ const EmbeddedChatbotWrapper = () => { | |||
| setClearChatList, | |||
| isResponding, | |||
| setIsResponding, | |||
| currentConversationInputs, | |||
| setCurrentConversationInputs, | |||
| } = useEmbeddedChatbot() | |||
| return <EmbeddedChatbotContext.Provider value={{ | |||
| @@ -193,6 +195,8 @@ const EmbeddedChatbotWrapper = () => { | |||
| setClearChatList, | |||
| isResponding, | |||
| setIsResponding, | |||
| currentConversationInputs, | |||
| setCurrentConversationInputs, | |||
| }}> | |||
| <Chatbot /> | |||
| </EmbeddedChatbotContext.Provider> | |||
| @@ -17,20 +17,25 @@ const InputsFormContent = ({ showTip }: Props) => { | |||
| appParams, | |||
| inputsForms, | |||
| currentConversationId, | |||
| currentConversationItem, | |||
| currentConversationInputs, | |||
| setCurrentConversationInputs, | |||
| newConversationInputs, | |||
| newConversationInputsRef, | |||
| handleNewConversationInputsChange, | |||
| } = useEmbeddedChatbotContext() | |||
| const inputsFormValue = currentConversationId ? currentConversationItem?.inputs : newConversationInputs | |||
| const inputsFormValue = currentConversationId ? currentConversationInputs : newConversationInputs | |||
| const readonly = !!currentConversationId | |||
| const handleFormChange = useCallback((variable: string, value: any) => { | |||
| setCurrentConversationInputs({ | |||
| ...currentConversationInputs, | |||
| [variable]: value, | |||
| }) | |||
| handleNewConversationInputsChange({ | |||
| ...newConversationInputsRef.current, | |||
| [variable]: value, | |||
| }) | |||
| }, [newConversationInputsRef, handleNewConversationInputsChange]) | |||
| }, [newConversationInputsRef, handleNewConversationInputsChange, currentConversationInputs, setCurrentConversationInputs]) | |||
| return ( | |||
| <div className='space-y-4'> | |||
| @@ -47,8 +52,6 @@ const InputsFormContent = ({ showTip }: Props) => { | |||
| value={inputsFormValue?.[form.variable] || ''} | |||
| onChange={e => handleFormChange(form.variable, e.target.value)} | |||
| placeholder={form.label} | |||
| readOnly={readonly} | |||
| disabled={readonly} | |||
| /> | |||
| )} | |||
| {form.type === InputVarType.number && ( | |||
| @@ -57,8 +60,6 @@ const InputsFormContent = ({ showTip }: Props) => { | |||
| value={inputsFormValue?.[form.variable] || ''} | |||
| onChange={e => handleFormChange(form.variable, e.target.value)} | |||
| placeholder={form.label} | |||
| readOnly={readonly} | |||
| disabled={readonly} | |||
| /> | |||
| )} | |||
| {form.type === InputVarType.paragraph && ( | |||
| @@ -66,8 +67,6 @@ const InputsFormContent = ({ showTip }: Props) => { | |||
| value={inputsFormValue?.[form.variable] || ''} | |||
| onChange={e => handleFormChange(form.variable, e.target.value)} | |||
| placeholder={form.label} | |||
| readOnly={readonly} | |||
| disabled={readonly} | |||
| /> | |||
| )} | |||
| {form.type === InputVarType.select && ( | |||
| @@ -77,7 +76,6 @@ const InputsFormContent = ({ showTip }: Props) => { | |||
| items={form.options.map((option: string) => ({ value: option, name: option }))} | |||
| onSelect={item => handleFormChange(form.variable, item.value as string)} | |||
| placeholder={form.label} | |||
| readonly={readonly} | |||
| /> | |||
| )} | |||
| {form.type === InputVarType.singleFile && ( | |||
| @@ -38,7 +38,7 @@ const InputsFormNode = ({ | |||
| <Message3Fill className='h-6 w-6 shrink-0' /> | |||
| <div className='system-xl-semibold grow text-text-secondary'>{t('share.chat.chatSettingsTitle')}</div> | |||
| {collapsed && ( | |||
| <Button className='uppercase text-text-tertiary' size='small' variant='ghost' onClick={() => setCollapsed(false)}>{currentConversationId ? t('common.operation.view') : t('common.operation.edit')}</Button> | |||
| <Button className='uppercase text-text-tertiary' size='small' variant='ghost' onClick={() => setCollapsed(false)}>{t('common.operation.edit')}</Button> | |||
| )} | |||
| {!collapsed && currentConversationId && ( | |||
| <Button className='uppercase text-text-tertiary' size='small' variant='ghost' onClick={() => setCollapsed(true)}>{t('common.operation.close')}</Button> | |||
| @@ -46,7 +46,7 @@ const InputsFormNode = ({ | |||
| </div> | |||
| {!collapsed && ( | |||
| <div className={cn('p-6', isMobile && 'p-4')}> | |||
| <InputsFormContent showTip={!!currentConversationId} /> | |||
| <InputsFormContent /> | |||
| </div> | |||
| )} | |||
| {!collapsed && !currentConversationId && ( | |||
| @@ -40,7 +40,7 @@ const ViewFormDropdown = ({ iconColor }: Props) => { | |||
| <div className='system-xl-semibold grow text-text-secondary'>{t('share.chat.chatSettingsTitle')}</div> | |||
| </div> | |||
| <div className='p-6'> | |||
| <InputsFormContent showTip /> | |||
| <InputsFormContent /> | |||
| </div> | |||
| </div> | |||
| </PortalToFollowElemContent> | |||