mirror of
https://github.com/langgenius/dify.git
synced 2026-07-25 05:28:35 +08:00
260 lines
7.5 KiB
TypeScript
260 lines
7.5 KiB
TypeScript
import type {
|
|
ChatVariableTranslator,
|
|
ObjectValueItem,
|
|
ToastPayload,
|
|
} from './variable-modal.helpers'
|
|
import type { ConversationVariable } from '@/app/components/workflow/types'
|
|
import { useMemo, useState } from 'react'
|
|
import { v4 as uuid4 } from 'uuid'
|
|
import { DEFAULT_OBJECT_VALUE } from '@/app/components/workflow/panel/chat-variable-panel/components/object-value-item'
|
|
import { ChatVarType } from '@/app/components/workflow/panel/chat-variable-panel/type'
|
|
import {
|
|
buildObjectValueItems,
|
|
formatChatVariableValue,
|
|
formatObjectValueFromList,
|
|
getEditorMinHeight,
|
|
getPlaceholderByType,
|
|
getTypeChangeState,
|
|
MAX_DESCRIPTION_LENGTH,
|
|
parseEditorContent,
|
|
validateVariableName,
|
|
} from './variable-modal.helpers'
|
|
|
|
type UseVariableModalStateOptions = {
|
|
chatVar?: ConversationVariable
|
|
conversationVariables: ConversationVariable[]
|
|
notify: (props: ToastPayload) => void
|
|
onClose: () => void
|
|
onSave: (chatVar: ConversationVariable) => void
|
|
t: ChatVariableTranslator
|
|
}
|
|
|
|
type VariableModalState = {
|
|
description: string
|
|
editInJSON: boolean
|
|
editorContent?: string
|
|
name: string
|
|
objectValue: ObjectValueItem[]
|
|
type: ChatVarType
|
|
value: unknown
|
|
}
|
|
|
|
const buildObjectValueListFromRecord = (record: Record<string, string | number>) => {
|
|
return Object.keys(record).map((key) => ({
|
|
key,
|
|
type: typeof record[key] === 'string' ? ChatVarType.String : ChatVarType.Number,
|
|
value: record[key],
|
|
}))
|
|
}
|
|
|
|
const buildInitialState = (chatVar?: ConversationVariable): VariableModalState => {
|
|
if (!chatVar) {
|
|
return {
|
|
description: '',
|
|
editInJSON: false,
|
|
editorContent: undefined,
|
|
name: '',
|
|
objectValue: [DEFAULT_OBJECT_VALUE],
|
|
type: ChatVarType.String,
|
|
value: undefined,
|
|
}
|
|
}
|
|
|
|
return {
|
|
description: chatVar.description,
|
|
editInJSON: chatVar.value_type === ChatVarType.ArrayObject,
|
|
editorContent:
|
|
chatVar.value_type === ChatVarType.ArrayObject ? JSON.stringify(chatVar.value) : undefined,
|
|
name: chatVar.name,
|
|
objectValue: buildObjectValueItems(chatVar),
|
|
type: chatVar.value_type,
|
|
value: chatVar.value,
|
|
}
|
|
}
|
|
|
|
export const useVariableModalState = ({
|
|
chatVar,
|
|
conversationVariables,
|
|
notify,
|
|
onClose,
|
|
onSave,
|
|
t,
|
|
}: UseVariableModalStateOptions) => {
|
|
const [state, setState] = useState<VariableModalState>(() => buildInitialState(chatVar))
|
|
|
|
const editorMinHeight = useMemo(() => getEditorMinHeight(state.type), [state.type])
|
|
const placeholder = useMemo(() => getPlaceholderByType(state.type), [state.type])
|
|
|
|
const handleVarNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
setState((prev) => ({ ...prev, name: e.target.value || '' }))
|
|
}
|
|
|
|
const handleTypeChange = (nextType: ChatVarType) => {
|
|
const nextState = getTypeChangeState(nextType)
|
|
setState((prev) => ({
|
|
...prev,
|
|
editInJSON: nextState.editInJSON,
|
|
editorContent: nextState.editorContent,
|
|
objectValue: nextState.objectValue ?? prev.objectValue,
|
|
type: nextType,
|
|
value: nextState.value,
|
|
}))
|
|
}
|
|
|
|
const handleStringOrNumberChange = (nextValue: Array<string | number | undefined>) => {
|
|
setState((prev) => ({ ...prev, value: nextValue[0] }))
|
|
}
|
|
|
|
const handleEditorChange = (nextEditInJSON: boolean) => {
|
|
setState((prev) => {
|
|
const nextState: VariableModalState = {
|
|
...prev,
|
|
editInJSON: nextEditInJSON,
|
|
}
|
|
|
|
if (prev.type === ChatVarType.Object) {
|
|
if (nextEditInJSON) {
|
|
const nextValue = prev.objectValue.some((item) => item.key)
|
|
? formatObjectValueFromList(prev.objectValue)
|
|
: undefined
|
|
nextState.value = nextValue
|
|
nextState.editorContent = JSON.stringify(nextValue)
|
|
return nextState
|
|
}
|
|
|
|
if (!prev.editorContent) {
|
|
nextState.value = undefined
|
|
nextState.objectValue = [DEFAULT_OBJECT_VALUE]
|
|
return nextState
|
|
}
|
|
|
|
try {
|
|
const nextValue = JSON.parse(prev.editorContent) as Record<string, string | number>
|
|
nextState.value = nextValue
|
|
nextState.objectValue = buildObjectValueListFromRecord(nextValue)
|
|
} catch {
|
|
// ignore JSON.parse errors
|
|
}
|
|
return nextState
|
|
}
|
|
|
|
if (prev.type === ChatVarType.ArrayString || prev.type === ChatVarType.ArrayNumber) {
|
|
if (nextEditInJSON) {
|
|
const compactValues = Array.isArray(prev.value)
|
|
? prev.value.filter((item) => item !== null && item !== undefined && item !== '')
|
|
: []
|
|
const nextValue = compactValues.length ? compactValues : undefined
|
|
nextState.value = nextValue
|
|
if (!prev.editorContent) nextState.editorContent = JSON.stringify(nextValue)
|
|
return nextState
|
|
}
|
|
|
|
nextState.value = Array.isArray(prev.value) && prev.value.length ? prev.value : [undefined]
|
|
return nextState
|
|
}
|
|
|
|
if (prev.type === ChatVarType.ArrayBoolean && Array.isArray(prev.value) && nextEditInJSON)
|
|
nextState.editorContent = JSON.stringify(
|
|
prev.value.map((item) => (item ? 'True' : 'False')),
|
|
)
|
|
|
|
return nextState
|
|
})
|
|
}
|
|
|
|
const handleEditorValueChange = (content: string) => {
|
|
setState((prev) => {
|
|
const nextState: VariableModalState = {
|
|
...prev,
|
|
editorContent: content,
|
|
}
|
|
|
|
if (!content) {
|
|
nextState.value = undefined
|
|
return nextState
|
|
}
|
|
|
|
try {
|
|
nextState.value = parseEditorContent({ content, type: prev.type })
|
|
} catch {
|
|
// ignore JSON.parse errors
|
|
}
|
|
|
|
return nextState
|
|
})
|
|
}
|
|
|
|
const handleSave = () => {
|
|
if (!validateVariableName({ name: state.name, notify, t })) return
|
|
|
|
if (!chatVar && conversationVariables.some((item) => item.name === state.name)) {
|
|
notify({
|
|
type: 'error',
|
|
message: t(($) => $['varKeyError.keyAlreadyExists'], {
|
|
ns: 'appDebug',
|
|
key: t(($) => $['chatVariable.modal.name'], { ns: 'workflow' }),
|
|
}),
|
|
})
|
|
return
|
|
}
|
|
|
|
if (
|
|
state.type === ChatVarType.Object &&
|
|
state.objectValue.some((item) => !item.key && item.value !== undefined && item.value !== '')
|
|
) {
|
|
notify({
|
|
type: 'error',
|
|
message: t(($) => $['chatVariable.modal.objectKeyRequired'], { ns: 'workflow' }),
|
|
})
|
|
return
|
|
}
|
|
|
|
if (state.description.length > MAX_DESCRIPTION_LENGTH) {
|
|
notify({
|
|
type: 'error',
|
|
message: t(($) => $['chatVariable.modal.descriptionTooLong'], {
|
|
maxLength: MAX_DESCRIPTION_LENGTH,
|
|
ns: 'workflow',
|
|
}),
|
|
})
|
|
return
|
|
}
|
|
|
|
onSave({
|
|
description: state.description,
|
|
id: chatVar ? chatVar.id : uuid4(),
|
|
name: state.name,
|
|
value: formatChatVariableValue({
|
|
editInJSON: state.editInJSON,
|
|
objectValue: state.objectValue,
|
|
type: state.type,
|
|
value: state.value,
|
|
}),
|
|
value_type: state.type,
|
|
})
|
|
onClose()
|
|
}
|
|
|
|
return {
|
|
description: state.description,
|
|
editInJSON: state.editInJSON,
|
|
editorContent: state.editorContent,
|
|
editorMinHeight,
|
|
handleEditorChange,
|
|
handleEditorValueChange,
|
|
handleSave,
|
|
handleStringOrNumberChange,
|
|
handleTypeChange,
|
|
handleVarNameChange,
|
|
name: state.name,
|
|
objectValue: state.objectValue,
|
|
placeholder,
|
|
setDescription: (description: string) => setState((prev) => ({ ...prev, description })),
|
|
setObjectValue: (objectValue: ObjectValueItem[]) =>
|
|
setState((prev) => ({ ...prev, objectValue })),
|
|
setValue: (value: unknown) => setState((prev) => ({ ...prev, value })),
|
|
type: state.type,
|
|
value: state.value,
|
|
}
|
|
}
|