/* eslint-disable multiline-ternary */ 'use client' import type { FC } from 'react' import React, { useEffect, useRef, useState } from 'react' import produce from 'immer' import cn from 'classnames' import { useTranslation } from 'react-i18next' import { useBoolean } from 'ahooks' import { ReactSortable } from 'react-sortablejs' import { useFeatures, useFeaturesStore, } from '../../hooks' import type { OnFeaturesChange } from '../../types' import Panel from '@/app/components/app/configuration/base/feature-panel' import Button from '@/app/components/base/button' import OperationBtn from '@/app/components/app/configuration/base/operation-btn' import { getInputKeys } from '@/app/components/base/block-input' import ConfirmAddVar from '@/app/components/app/configuration/config-prompt/confirm-add-var' import { getNewVar } from '@/utils/var' import { varHighlightHTML } from '@/app/components/app/configuration/base/var-highlight' import { Plus, Trash03 } from '@/app/components/base/icons/src/vender/line/general' import type { PromptVariable } from '@/models/debug' const MAX_QUESTION_NUM = 5 export type OpeningStatementProps = { onChange?: OnFeaturesChange readonly?: boolean promptVariables?: PromptVariable[] onAutoAddPromptVariable: (variable: PromptVariable[]) => void } // regex to match the {{}} and replace it with a span const regex = /\{\{([^}]+)\}\}/g const OpeningStatement: FC = ({ onChange, readonly, promptVariables = [], onAutoAddPromptVariable, }) => { const { t } = useTranslation() const featureStore = useFeaturesStore() const openingStatement = useFeatures(s => s.features.opening) const value = openingStatement?.opening_statement || '' const suggestedQuestions = openingStatement?.suggested_questions || [] const [notIncludeKeys, setNotIncludeKeys] = useState([]) const hasValue = !!(value || '').trim() const inputRef = useRef(null) const [isFocus, { setTrue: didSetFocus, setFalse: setBlur }] = useBoolean(false) const setFocus = () => { didSetFocus() setTimeout(() => { const input = inputRef.current if (input) { input.focus() input.setSelectionRange(input.value.length, input.value.length) } }, 0) } const [tempValue, setTempValue] = useState(value) useEffect(() => { setTempValue(value || '') }, [value]) const [tempSuggestedQuestions, setTempSuggestedQuestions] = useState(suggestedQuestions || []) const notEmptyQuestions = tempSuggestedQuestions.filter(question => !!question && question.trim()) const coloredContent = (tempValue || '') .replace(//g, '>') .replace(regex, varHighlightHTML({ name: '$1' })) // `{{$1}}` .replace(/\n/g, '
') const handleEdit = () => { if (readonly) return setFocus() } const [isShowConfirmAddVar, { setTrue: showConfirmAddVar, setFalse: hideConfirmAddVar }] = useBoolean(false) const handleCancel = () => { setBlur() setTempValue(value) setTempSuggestedQuestions(suggestedQuestions) } const handleConfirm = () => { const keys = getInputKeys(tempValue) const promptKeys = promptVariables.map(item => item.key) let notIncludeKeys: string[] = [] if (promptKeys.length === 0) { if (keys.length > 0) notIncludeKeys = keys } else { notIncludeKeys = keys.filter(key => !promptKeys.includes(key)) } if (notIncludeKeys.length > 0) { setNotIncludeKeys(notIncludeKeys) showConfirmAddVar() return } setBlur() const { getState } = featureStore! const { features, setFeatures, } = getState() const newFeatures = produce(features, (draft) => { if (draft.opening) { draft.opening.opening_statement = tempValue draft.opening.suggested_questions = tempSuggestedQuestions } }) setFeatures(newFeatures) if (onChange) onChange(newFeatures) } const cancelAutoAddVar = () => { const { getState } = featureStore! const { features, setFeatures, } = getState() const newFeatures = produce(features, (draft) => { if (draft.opening) draft.opening.opening_statement = tempValue }) setFeatures(newFeatures) if (onChange) onChange(newFeatures) hideConfirmAddVar() setBlur() } const autoAddVar = () => { const { getState } = featureStore! const { features, setFeatures, } = getState() const newFeatures = produce(features, (draft) => { if (draft.opening) draft.opening.opening_statement = tempValue }) setFeatures(newFeatures) if (onChange) onChange(newFeatures) onAutoAddPromptVariable([...notIncludeKeys.map(key => getNewVar(key, 'string'))]) hideConfirmAddVar() setBlur() } const headerRight = !readonly ? ( isFocus ? (
{t('common.operation.cancel')}
) : ( ) ) : null const renderQuestions = () => { return isFocus ? (
{t('appDebug.openingStatement.openingQuestion')}
ยท
{tempSuggestedQuestions.length}/{MAX_QUESTION_NUM}
{ return { id: index, name, } })} setList={list => setTempSuggestedQuestions(list.map(item => item.name))} handle='.handle' ghostClass="opacity-50" animation={150} > {tempSuggestedQuestions.map((question, index) => { return (
{ const value = e.target.value setTempSuggestedQuestions(tempSuggestedQuestions.map((item, i) => { if (index === i) return value return item })) }} className={'w-full overflow-x-auto pl-1.5 pr-8 text-sm leading-9 text-gray-900 border-0 grow h-9 bg-transparent focus:outline-none cursor-pointer rounded-lg'} />
{ setTempSuggestedQuestions(tempSuggestedQuestions.filter((_, i) => index !== i)) }} >
) })}
{tempSuggestedQuestions.length < MAX_QUESTION_NUM && (
{ setTempSuggestedQuestions([...tempSuggestedQuestions, '']) }} className='mt-1 flex items-center h-9 px-3 gap-2 rounded-lg cursor-pointer text-gray-400 bg-gray-100 hover:bg-gray-200'>
{t('appDebug.variableConig.addOption')}
)}
) : (
{notEmptyQuestions.map((question, index) => { return (
{question}
) })}
) } return ( } headerRight={headerRight} hasHeaderBottomBorder={!hasValue} isFocus={isFocus} >
{(hasValue || (!hasValue && isFocus)) ? ( <> {isFocus ? (
) : (
)} {renderQuestions()} ) : (
{t('appDebug.openingStatement.noDataPlaceHolder')}
)} {isShowConfirmAddVar && ( )}
) } export default React.memo(OpeningStatement)