|  | @@ -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}
 |