| @@ -1,39 +1,39 @@ | |||
| 'use client' | |||
| import React, { FC, useEffect, useState, useRef } from 'react' | |||
| import type { FC } from 'react' | |||
| import React, { useEffect, useRef, useState } from 'react' | |||
| import { useTranslation } from 'react-i18next' | |||
| import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' | |||
| import cn from 'classnames' | |||
| import { useBoolean, useClickAway } from 'ahooks' | |||
| import { XMarkIcon } from '@heroicons/react/24/outline' | |||
| import TabHeader from '../../base/tab-header' | |||
| import Button from '../../base/button' | |||
| import s from './style.module.css' | |||
| import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' | |||
| import ConfigScence from '@/app/components/share/text-generation/config-scence' | |||
| import NoData from '@/app/components/share/text-generation/no-data' | |||
| // import History from '@/app/components/share/text-generation/history' | |||
| import { fetchAppInfo, fetchAppParams, sendCompletionMessage, updateFeedback, saveMessage, fetchSavedMessage as doFetchSavedMessage, removeMessage } from '@/service/share' | |||
| import { fetchSavedMessage as doFetchSavedMessage, fetchAppInfo, fetchAppParams, removeMessage, saveMessage, sendCompletionMessage, updateFeedback } from '@/service/share' | |||
| import type { SiteInfo } from '@/models/share' | |||
| import type { PromptConfig, MoreLikeThisConfig, SavedMessage } from '@/models/debug' | |||
| import type { MoreLikeThisConfig, PromptConfig, SavedMessage } from '@/models/debug' | |||
| import Toast from '@/app/components/base/toast' | |||
| import AppIcon from '@/app/components/base/app-icon' | |||
| import { Feedbacktype } from '@/app/components/app/chat' | |||
| import type { Feedbacktype } from '@/app/components/app/chat' | |||
| import { changeLanguage } from '@/i18n/i18next-config' | |||
| import Loading from '@/app/components/base/loading' | |||
| import { userInputsFormToPromptVariables } from '@/utils/model-config' | |||
| import TextGenerationRes from '@/app/components/app/text-generate/item' | |||
| import SavedItems from '@/app/components/app/text-generate/saved-items' | |||
| import TabHeader from '../../base/tab-header' | |||
| import { XMarkIcon } from '@heroicons/react/24/outline' | |||
| import s from './style.module.css' | |||
| import Button from '../../base/button' | |||
| import { App } from '@/types/app' | |||
| import { InstalledApp } from '@/models/explore' | |||
| import type { InstalledApp } from '@/models/explore' | |||
| import { appDefaultIconBackground } from '@/config' | |||
| export type IMainProps = { | |||
| isInstalledApp?: boolean, | |||
| installedAppInfo? : InstalledApp | |||
| isInstalledApp?: boolean | |||
| installedAppInfo?: InstalledApp | |||
| } | |||
| const TextGeneration: FC<IMainProps> = ({ | |||
| isInstalledApp = false, | |||
| installedAppInfo | |||
| installedAppInfo, | |||
| }) => { | |||
| const { t } = useTranslation() | |||
| const media = useBreakpoints() | |||
| @@ -56,7 +56,7 @@ const TextGeneration: FC<IMainProps> = ({ | |||
| const [messageId, setMessageId] = useState<string | null>(null) | |||
| const [feedback, setFeedback] = useState<Feedbacktype>({ | |||
| rating: null | |||
| rating: null, | |||
| }) | |||
| const handleFeedback = async (feedback: Feedbacktype) => { | |||
| @@ -93,21 +93,20 @@ const TextGeneration: FC<IMainProps> = ({ | |||
| const checkCanSend = () => { | |||
| const prompt_variables = promptConfig?.prompt_variables | |||
| if (!prompt_variables || prompt_variables?.length === 0) { | |||
| if (!prompt_variables || prompt_variables?.length === 0) | |||
| return true | |||
| } | |||
| let hasEmptyInput = false | |||
| const requiredVars = prompt_variables?.filter(({ key, name, required }) => { | |||
| const res = (!key || !key.trim()) || (!name || !name.trim()) || (required || required === undefined || required === null) | |||
| return res | |||
| }) || [] // compatible with old version | |||
| requiredVars.forEach(({ key }) => { | |||
| if (hasEmptyInput) { | |||
| if (hasEmptyInput) | |||
| return | |||
| } | |||
| if (!inputs[key]) { | |||
| if (!inputs[key]) | |||
| hasEmptyInput = true | |||
| } | |||
| }) | |||
| if (hasEmptyInput) { | |||
| @@ -138,16 +137,17 @@ const TextGeneration: FC<IMainProps> = ({ | |||
| setMessageId(null) | |||
| setFeedback({ | |||
| rating: null | |||
| rating: null, | |||
| }) | |||
| setCompletionRes('') | |||
| const res: string[] = [] | |||
| let tempMessageId = '' | |||
| if (!isPC) { | |||
| if (!isPC) | |||
| // eslint-disable-next-line @typescript-eslint/no-use-before-define | |||
| showResSidebar() | |||
| } | |||
| setResponsingTrue() | |||
| sendCompletionMessage(data, { | |||
| onData: (data: string, _isFirstMessage: boolean, { messageId }: any) => { | |||
| @@ -161,20 +161,22 @@ const TextGeneration: FC<IMainProps> = ({ | |||
| }, | |||
| onError() { | |||
| setResponsingFalse() | |||
| } | |||
| }, | |||
| }, isInstalledApp, installedAppInfo?.id) | |||
| } | |||
| const fetchInitData = () => { | |||
| return Promise.all([isInstalledApp ? { | |||
| app_id: installedAppInfo?.id, | |||
| site: { | |||
| title: installedAppInfo?.app.name, | |||
| prompt_public: false, | |||
| copyright: '' | |||
| }, | |||
| plan: 'basic', | |||
| }: fetchAppInfo(), fetchAppParams(isInstalledApp, installedAppInfo?.id)]) | |||
| return Promise.all([isInstalledApp | |||
| ? { | |||
| app_id: installedAppInfo?.id, | |||
| site: { | |||
| title: installedAppInfo?.app.name, | |||
| prompt_public: false, | |||
| copyright: '', | |||
| }, | |||
| plan: 'basic', | |||
| } | |||
| : fetchAppInfo(), fetchAppParams(isInstalledApp, installedAppInfo?.id)]) | |||
| } | |||
| useEffect(() => { | |||
| @@ -195,7 +197,7 @@ const TextGeneration: FC<IMainProps> = ({ | |||
| })() | |||
| }, []) | |||
| // Can Use metadata(https://beta.nextjs.org/docs/api-reference/metadata) to set title. But it only works in server side client. | |||
| // Can Use metadata(https://beta.nextjs.org/docs/api-reference/metadata) to set title. But it only works in server side client. | |||
| useEffect(() => { | |||
| if (siteInfo?.title) | |||
| document.title = `${siteInfo.title} - Powered by Dify` | |||
| @@ -204,7 +206,7 @@ const TextGeneration: FC<IMainProps> = ({ | |||
| const [isShowResSidebar, { setTrue: showResSidebar, setFalse: hideResSidebar }] = useBoolean(false) | |||
| const resRef = useRef<HTMLDivElement>(null) | |||
| useClickAway(() => { | |||
| hideResSidebar(); | |||
| hideResSidebar() | |||
| }, resRef) | |||
| const renderRes = ( | |||
| @@ -212,10 +214,10 @@ const TextGeneration: FC<IMainProps> = ({ | |||
| ref={resRef} | |||
| className={ | |||
| cn( | |||
| "flex flex-col h-full shrink-0", | |||
| 'flex flex-col h-full shrink-0', | |||
| isPC ? 'px-10 py-8' : 'bg-gray-50', | |||
| isTablet && 'p-6', isMoble && 'p-4') | |||
| } | |||
| } | |||
| > | |||
| <> | |||
| <div className='shrink-0 flex items-center justify-between'> | |||
| @@ -233,32 +235,34 @@ const TextGeneration: FC<IMainProps> = ({ | |||
| )} | |||
| </div> | |||
| <div className='grow'> | |||
| {(isResponsing && !completionRes) ? ( | |||
| <div className='flex h-full w-full justify-center items-center'> | |||
| <Loading type='area' /> | |||
| </div>) : ( | |||
| <> | |||
| {isNoData | |||
| ? <NoData /> | |||
| : ( | |||
| <TextGenerationRes | |||
| className='mt-3' | |||
| content={completionRes} | |||
| messageId={messageId} | |||
| isInWebApp | |||
| moreLikeThis={moreLikeThisConifg?.enabled} | |||
| onFeedback={handleFeedback} | |||
| feedback={feedback} | |||
| onSave={handleSaveMessage} | |||
| isMobile={isMoble} | |||
| isInstalledApp={isInstalledApp} | |||
| installedAppId={installedAppInfo?.id} | |||
| /> | |||
| ) | |||
| } | |||
| </> | |||
| )} | |||
| <div className='grow overflow-y-auto'> | |||
| {(isResponsing && !completionRes) | |||
| ? ( | |||
| <div className='flex h-full w-full justify-center items-center'> | |||
| <Loading type='area' /> | |||
| </div>) | |||
| : ( | |||
| <> | |||
| {isNoData | |||
| ? <NoData /> | |||
| : ( | |||
| <TextGenerationRes | |||
| className='mt-3' | |||
| content={completionRes} | |||
| messageId={messageId} | |||
| isInWebApp | |||
| moreLikeThis={moreLikeThisConifg?.enabled} | |||
| onFeedback={handleFeedback} | |||
| feedback={feedback} | |||
| onSave={handleSaveMessage} | |||
| isMobile={isMoble} | |||
| isInstalledApp={isInstalledApp} | |||
| installedAppId={installedAppInfo?.id} | |||
| /> | |||
| ) | |||
| } | |||
| </> | |||
| )} | |||
| </div> | |||
| </> | |||
| </div> | |||
| @@ -267,19 +271,18 @@ const TextGeneration: FC<IMainProps> = ({ | |||
| if (!appId || !siteInfo || !promptConfig) | |||
| return <Loading type='app' /> | |||
| return ( | |||
| <> | |||
| <div className={cn( | |||
| isPC && 'flex', | |||
| isInstalledApp ? s.installedApp : 'h-screen', | |||
| 'bg-gray-50' | |||
| 'bg-gray-50', | |||
| )}> | |||
| {/* Left */} | |||
| <div className={cn( | |||
| isPC ? 'w-[600px] max-w-[50%] p-8' : 'p-4', | |||
| isInstalledApp && 'rounded-l-2xl', | |||
| "shrink-0 relative flex flex-col pb-10 h-full border-r border-gray-100 bg-white" | |||
| 'shrink-0 relative flex flex-col pb-10 h-full border-r border-gray-100 bg-white', | |||
| )}> | |||
| <div className='mb-6'> | |||
| <div className='flex justify-between items-center'> | |||
| @@ -307,12 +310,16 @@ const TextGeneration: FC<IMainProps> = ({ | |||
| items={[ | |||
| { id: 'create', name: t('share.generation.tabs.create') }, | |||
| { | |||
| id: 'saved', name: t('share.generation.tabs.saved'), extra: savedMessages.length > 0 ? ( | |||
| <div className='ml-1 flext items-center h-5 px-1.5 rounded-md border border-gray-200 text-gray-500 text-xs font-medium'> | |||
| {savedMessages.length} | |||
| </div> | |||
| ) : null | |||
| } | |||
| id: 'saved', | |||
| name: t('share.generation.tabs.saved'), | |||
| extra: savedMessages.length > 0 | |||
| ? ( | |||
| <div className='ml-1 flext items-center h-5 px-1.5 rounded-md border border-gray-200 text-gray-500 text-xs font-medium'> | |||
| {savedMessages.length} | |||
| </div> | |||
| ) | |||
| : null, | |||
| }, | |||
| ]} | |||
| value={currTab} | |||
| onChange={setCurrTab} | |||
| @@ -340,12 +347,11 @@ const TextGeneration: FC<IMainProps> = ({ | |||
| )} | |||
| </div> | |||
| {/* copyright */} | |||
| <div className={cn( | |||
| isInstalledApp ? 'left-[248px]' : 'left-8', | |||
| 'fixed bottom-4 flex space-x-2 text-gray-400 font-normal text-xs' | |||
| )}> | |||
| 'fixed bottom-4 flex space-x-2 text-gray-400 font-normal text-xs', | |||
| )}> | |||
| <div className="">© {siteInfo.copyright || siteInfo.title} {(new Date()).getFullYear()}</div> | |||
| {siteInfo.privacy_policy && ( | |||
| <> | |||
| @@ -373,7 +379,7 @@ const TextGeneration: FC<IMainProps> = ({ | |||
| <div | |||
| className={cn('fixed z-50 inset-0', isTablet ? 'pl-[128px]' : 'pl-6')} | |||
| style={{ | |||
| background: 'rgba(35, 56, 118, 0.2)' | |||
| background: 'rgba(35, 56, 118, 0.2)', | |||
| }} | |||
| > | |||
| {renderRes} | |||