refactor: rename placeholder to default in form input (#31452)

This commit is contained in:
Wu Tianwei 2026-01-23 18:01:44 +08:00 committed by GitHub
parent f73db70cec
commit d3d42e3a8e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 107 additions and 91 deletions

View File

@ -27,7 +27,7 @@ export type FormData = {
site: any
form_content: string
inputs: FormInputItem[]
resolved_placeholder_values: Record<string, string>
resolved_default_values: Record<string, string>
user_actions: UserAction[]
expiration_time: number
}
@ -67,10 +67,10 @@ const FormContent = () => {
return
const initialInputs: Record<string, string> = {}
formData.inputs.forEach((item) => {
initialInputs[item.output_variable_name] = item.placeholder.type === 'variable' ? formData.resolved_placeholder_values[item.output_variable_name] || '' : item.placeholder.value
initialInputs[item.output_variable_name] = item.default.type === 'variable' ? formData.resolved_default_values[item.output_variable_name] || '' : item.default.value
})
setInputs(initialInputs)
}, [formData?.inputs, formData?.resolved_placeholder_values])
}, [formData?.inputs, formData?.resolved_default_values])
// use immer
const handleInputsChange = (name: string, value: string) => {

View File

@ -216,11 +216,11 @@ const GenerationItem: FC<IGenerationItemProps> = ({
switchTab('DETAIL')
}, [workflowProcessData?.files?.length, workflowProcessData?.resultText, workflowProcessData?.humanInputFormDataList, workflowProcessData?.humanInputFilledFormDataList])
const handleSubmitHumanInputForm = useCallback(async (formToken: string, formData: any) => {
if (isInstalledApp)
if (appSourceType === AppSourceType.installedApp)
await submitHumanInputFormService(formToken, formData)
else
await submitHumanInputForm(formToken, formData)
}, [isInstalledApp])
}, [appSourceType])
return (
<>

View File

@ -166,9 +166,9 @@ const ChatWrapper = () => {
handleSwitchSibling(
lastPausedNode.id,
{
onGetSuggestedQuestions: responseItemId => fetchSuggestedQuestions(responseItemId, isInstalledApp, appId),
onGetSuggestedQuestions: responseItemId => fetchSuggestedQuestions(responseItemId, appSourceType, appId),
onConversationComplete: currentConversationId ? undefined : handleNewConversationCompleted,
isPublicAPI: !isInstalledApp,
isPublicAPI: appSourceType === AppSourceType.webApp,
},
)
}
@ -189,10 +189,10 @@ const ChatWrapper = () => {
{
onGetSuggestedQuestions: responseItemId => fetchSuggestedQuestions(responseItemId, appSourceType, appId),
onConversationComplete: isHistoryConversation ? undefined : handleNewConversationCompleted,
isPublicAPI: !isInstalledApp,
isPublicAPI: appSourceType === AppSourceType.webApp,
},
)
}, [inputsForms, currentConversationId, currentConversationInputs, newConversationInputs, chatList, handleSend, isInstalledApp, appId, handleNewConversationCompleted])
}, [inputsForms, currentConversationId, currentConversationInputs, newConversationInputs, chatList, handleSend, appSourceType, appId, isHistoryConversation, handleNewConversationCompleted])
const doRegenerate = useCallback((chatItem: ChatItem, editedQuestion?: { message: string, files?: FileEntity[] }) => {
const question = editedQuestion ? chatItem : chatList.find(item => item.id === chatItem.parentMessageId)!
@ -202,11 +202,11 @@ const ChatWrapper = () => {
const doSwitchSibling = useCallback((siblingMessageId: string) => {
handleSwitchSibling(siblingMessageId, {
onGetSuggestedQuestions: responseItemId => fetchSuggestedQuestions(responseItemId, isInstalledApp, appId),
onGetSuggestedQuestions: responseItemId => fetchSuggestedQuestions(responseItemId, appSourceType, appId),
onConversationComplete: currentConversationId ? undefined : handleNewConversationCompleted,
isPublicAPI: !isInstalledApp,
isPublicAPI: appSourceType === AppSourceType.webApp,
})
}, [handleSwitchSibling, isInstalledApp, appId, currentConversationId, handleNewConversationCompleted])
}, [handleSwitchSibling, currentConversationId, handleNewConversationCompleted, appSourceType, appId])
const messageList = useMemo(() => {
if (currentConversationId || chatList.length > 1)

View File

@ -13,7 +13,7 @@ const HumanInputForm = ({
onSubmit,
}: HumanInputFormProps) => {
const formToken = formData.form_token
const defaultInputs = initializeInputs(formData.inputs, formData.resolved_placeholder_values || {})
const defaultInputs = initializeInputs(formData.inputs, formData.resolved_default_values || {})
const contentList = splitByOutputVar(formData.form_content)
const [inputs, setInputs] = useState(defaultInputs)
const [isSubmitting, setIsSubmitting] = useState(false)

View File

@ -34,7 +34,7 @@ export const initializeInputs = (formInputs: FormInputItem[], defaultValues: Rec
const initialInputs: Record<string, any> = {}
formInputs.forEach((item) => {
if (item.type === 'text-input' || item.type === 'paragraph')
initialInputs[item.output_variable_name] = item.placeholder.type === 'variable' ? defaultValues[item.output_variable_name] || '' : item.placeholder.value
initialInputs[item.output_variable_name] = item.default.type === 'variable' ? defaultValues[item.output_variable_name] || '' : item.default.value
else
initialInputs[item.output_variable_name] = undefined
})

View File

@ -39,7 +39,7 @@ const HITLInputComponentUI: FC<HITLInputComponentUIProps> = ({
formInput = {
type: InputVarType.paragraph,
output_variable_name: varName,
placeholder: {
default: {
type: 'constant',
selector: [],
value: '',
@ -95,9 +95,9 @@ const HITLInputComponentUI: FC<HITLInputComponentUIProps> = ({
hideEditModal()
}, [hideEditModal, onChange, onRename, varName])
const isPlaceholderVariable = useMemo(() => {
return formInput.placeholder.type === 'variable'
}, [formInput.placeholder.type])
const isDefaultValueVariable = useMemo(() => {
return formInput.default?.type === 'variable'
}, [formInput.default?.type])
return (
<div
@ -113,10 +113,10 @@ const HITLInputComponentUI: FC<HITLInputComponentUIProps> = ({
<div className="flex w-full items-center gap-x-0.5 pr-8">
<div className="min-w-0 grow">
{/* Placeholder Info */}
{isPlaceholderVariable && (
{/* Default Value Info */}
{isDefaultValueVariable && (
<VariableBlock
variables={formInput.placeholder.selector}
variables={formInput.default?.selector}
workflowNodesMap={workflowNodesMap}
getVarType={getVarType}
environmentVariables={environmentVariables}
@ -124,8 +124,8 @@ const HITLInputComponentUI: FC<HITLInputComponentUIProps> = ({
ragVariables={ragVariables}
/>
)}
{!isPlaceholderVariable && (
<div className="system-xs-medium max-w-full truncate text-components-input-text-filled">{formInput.placeholder.value}</div>
{!isDefaultValueVariable && (
<div className="system-xs-medium max-w-full truncate text-components-input-text-filled">{formInput.default?.value}</div>
)}
</div>

View File

@ -1,4 +1,5 @@
import type { FormInputItem, FormInputItemPlaceholder } from '@/app/components/workflow/nodes/human-input/types'
import type { FormInputItem, FormInputItemDefault } from '@/app/components/workflow/nodes/human-input/types'
import type { ValueSelector } from '@/app/components/workflow/types'
import { produce } from 'immer'
import * as React from 'react'
import { useCallback, useEffect, useMemo, useState } from 'react'
@ -6,14 +7,12 @@ import { useTranslation } from 'react-i18next'
import Input from '@/app/components/base/input'
import { InputVarType } from '@/app/components/workflow/types'
import { getKeyboardKeyNameBySystem } from '@/app/components/workflow/utils'
// import PromptEditor from '@/app/components/base/prompt-editor'
// import TagLabel from './tag-label'
import Button from '../../../button'
import PrePopulate from './pre-populate'
const i18nPrefix = 'nodes.humanInput.insertInputField'
type Props = {
type InputFieldProps = {
nodeId: string
isEdit: boolean
payload?: FormInputItem
@ -23,9 +22,9 @@ type Props = {
const defaultPayload: FormInputItem = {
type: InputVarType.paragraph,
output_variable_name: '',
placeholder: { type: 'constant', selector: [], value: '' },
default: { type: 'constant', selector: [], value: '' },
}
const InputField: React.FC<Props> = ({
const InputField: React.FC<InputFieldProps> = ({
nodeId,
isEdit,
payload,
@ -47,17 +46,23 @@ const InputField: React.FC<Props> = ({
return
onChange(tempPayload)
}, [nameValid, onChange, tempPayload])
const placeholderConfig = tempPayload.placeholder
const handlePlaceholderChange = useCallback((key: keyof FormInputItemPlaceholder) => {
return (value: any) => {
const defaultValueConfig = tempPayload.default
const handleDefaultValueChange = useCallback((key: keyof FormInputItemDefault) => {
return (value: ValueSelector | string) => {
const nextValue = produce(tempPayload, (draft) => {
if (!draft.placeholder)
draft.placeholder = { type: 'constant', selector: [], value: '' }
draft.placeholder[key] = value
if (key === 'selector')
draft.placeholder.type = 'variable'
else if (key === 'value')
draft.placeholder.type = 'constant'
if (!draft.default)
draft.default = { type: 'constant', selector: [], value: '' }
if (key === 'selector') {
draft.default.type = 'variable'
draft.default.selector = value as ValueSelector
}
else if (key === 'value') {
draft.default.type = 'constant'
draft.default.value = value as string
}
else if (key === 'type') {
draft.default.type = value as 'constant' | 'variable'
}
})
setTempPayload(nextValue)
}
@ -104,15 +109,15 @@ const InputField: React.FC<Props> = ({
{t(`${i18nPrefix}.prePopulateField`, { ns: 'workflow' })}
</div>
<PrePopulate
isVariable={placeholderConfig?.type === 'variable'}
isVariable={defaultValueConfig?.type === 'variable'}
onIsVariableChange={(isVariable) => {
handlePlaceholderChange('type')(isVariable ? 'variable' : 'constant')
handleDefaultValueChange('type')(isVariable ? 'variable' : 'constant')
}}
nodeId={nodeId}
valueSelector={placeholderConfig?.selector}
onValueSelectorChange={handlePlaceholderChange('selector')}
value={placeholderConfig?.value}
onValueChange={handlePlaceholderChange('value')}
valueSelector={defaultValueConfig?.selector}
onValueSelectorChange={handleDefaultValueChange('selector')}
value={defaultValueConfig?.value}
onValueChange={handleDefaultValueChange('value')}
/>
</div>
<div className="mt-4 flex justify-end space-x-2">

View File

@ -5,7 +5,6 @@ import type { WorkflowProcess } from '@/app/components/base/chat/types'
import type { FileEntity } from '@/app/components/base/file-uploader/types'
import type { PromptConfig } from '@/models/debug'
import type { SiteInfo } from '@/models/share'
import type { AppSourceType } from '@/service/share'
import type {
IOtherOptions,
} from '@/service/base'
@ -31,7 +30,14 @@ import { TEXT_GENERATION_TIMEOUT_MS } from '@/config'
import {
sseGet,
} from '@/service/base'
import { sendCompletionMessage, sendWorkflowMessage, stopChatMessageResponding, stopWorkflowMessage, updateFeedback } from '@/service/share'
import {
AppSourceType,
sendCompletionMessage,
sendWorkflowMessage,
stopChatMessageResponding,
stopWorkflowMessage,
updateFeedback,
} from '@/service/share'
import { TransferMethod } from '@/types/app'
import { sleep } from '@/utils'
import { formatBooleanInputs } from '@/utils/model-config'
@ -163,7 +169,7 @@ const Result: FC<IResultProps> = ({
finally {
setIsStopping(false)
}
}, [appId, currentTaskId, appSourceType, appId, isStopping, isWorkflow, notify])
}, [appId, currentTaskId, appSourceType, isStopping, isWorkflow, notify])
useEffect(() => {
if (!onRunControlChange)
@ -288,7 +294,7 @@ const Result: FC<IResultProps> = ({
if (isWorkflow) {
const otherOptions: IOtherOptions = {
isPublicAPI: !isInstalledApp,
isPublicAPI: appSourceType === AppSourceType.webApp,
onWorkflowStarted: ({ workflow_run_id, task_id }) => {
const workflowProcessData = getWorkflowProcessData()
if (workflowProcessData && workflowProcessData.tracing.length > 0) {
@ -368,10 +374,15 @@ const Result: FC<IResultProps> = ({
}))
},
onNodeStarted: ({ data }) => {
if (data.iteration_id)
return
if (data.loop_id)
return
const workflowProcessData = getWorkflowProcessData()
if (workflowProcessData && workflowProcessData.tracing.length > 0) {
setWorkflowProcessData(produce(workflowProcessData, (draft) => {
const currentIndex = draft.tracing!.findIndex(item => item.node_id === data.node_id)
setWorkflowProcessData(produce(workflowProcessData!, (draft) => {
if (draft.tracing.length > 0) {
const currentIndex = draft.tracing.findIndex(item => item.node_id === data.node_id)
if (currentIndex > -1) {
draft.expand = true
draft.tracing![currentIndex] = {
@ -380,24 +391,24 @@ const Result: FC<IResultProps> = ({
expand: true,
}
}
}))
}
else {
if (data.iteration_id)
return
if (data.loop_id)
return
setWorkflowProcessData(produce(getWorkflowProcessData()!, (draft) => {
else {
draft.expand = true
draft.tracing.push({
...data,
status: NodeRunningStatus.Running,
expand: true,
})
}
}
else {
draft.expand = true
draft.tracing!.push({
...data,
status: NodeRunningStatus.Running,
expand: true,
})
}))
}
}
}))
},
onNodeFinished: ({ data }) => {
if (data.iteration_id)
@ -496,12 +507,13 @@ const Result: FC<IResultProps> = ({
}))
},
onHumanInputRequired: ({ data: humanInputRequiredData }) => {
setWorkflowProcessData(produce(getWorkflowProcessData()!, (draft) => {
const workflowProcessData = getWorkflowProcessData()
setWorkflowProcessData(produce(workflowProcessData!, (draft) => {
if (!draft.humanInputFormDataList) {
draft.humanInputFormDataList = [humanInputRequiredData]
}
else {
const currentFormIndex = draft.humanInputFormDataList.findIndex(item => item.node_id === data.node_id)
const currentFormIndex = draft.humanInputFormDataList.findIndex(item => item.node_id === humanInputRequiredData.node_id)
if (currentFormIndex > -1) {
draft.humanInputFormDataList[currentFormIndex] = humanInputRequiredData
}
@ -509,6 +521,10 @@ const Result: FC<IResultProps> = ({
draft.humanInputFormDataList.push(humanInputRequiredData)
}
}
const currentIndex = draft.tracing!.findIndex(item => item.node_id === humanInputRequiredData.node_id)
if (currentIndex > -1) {
draft.tracing![currentIndex].status = NodeRunningStatus.Paused
}
}))
},
onHumanInputFormFilled: ({ data: humanInputFilledFormData }) => {

View File

@ -91,14 +91,14 @@ const EmailSenderModal = ({
const accounts = members?.accounts || []
const generatedInputs = useMemo(() => {
const placeholderValueSelectors = (formInputs || []).reduce((acc, input) => {
if (input.placeholder.type === 'variable') {
acc.push(input.placeholder.selector)
const defaultValueSelectors = (formInputs || []).reduce((acc, input) => {
if (input.default.type === 'variable') {
acc.push(input.default.selector)
}
return acc
}, [] as ValueSelector[])
const valueSelectors = doGetInputVars((formContent || '') + (config?.body || ''))
const variables = unionBy([...valueSelectors, ...placeholderValueSelectors], item => item.join('.')).map((item) => {
const variables = unionBy([...valueSelectors, ...defaultValueSelectors], item => item.join('.')).map((item) => {
const varInfo = getNodeInfoById(availableNodes, item[0])?.data
return {

View File

@ -75,9 +75,9 @@ const FormContentPreview: FC<FormContentPreviewProps> = ({
</div>
)
}
const placeholder = input.placeholder
const defaultInput = input.default
return (
<Note placeholder={placeholder!} />
<Note defaultInput={defaultInput!} />
)
})(),
}}

View File

@ -25,7 +25,7 @@ const FormContent = ({
onSubmit,
}: Props) => {
const { t } = useTranslation()
const defaultInputs = initializeInputs(data.inputs, data.resolved_placeholder_values || {})
const defaultInputs = initializeInputs(data.inputs, data.resolved_default_values || {})
const contentList = splitByOutputVar(data.form_content)
const [inputs, setInputs] = useState(defaultInputs)
const [isSubmitting, setIsSubmitting] = useState(false)

View File

@ -1,5 +1,5 @@
import type * as React from 'react'
import type { FormInputItemPlaceholder } from '../types'
import type { FormInputItemDefault } from '../types'
const variableRegex = /\{\{#(.+?)#\}\}/g
const noteRegex = /\{\{#\$(.+?)#\}\}/g
@ -123,11 +123,11 @@ export const Variable: React.FC<{ path: string }> = ({ path }) => {
)
}
export const Note: React.FC<{ placeholder: FormInputItemPlaceholder }> = ({ placeholder }) => {
const isVariable = placeholder.type === 'variable'
export const Note: React.FC<{ defaultInput: FormInputItemDefault }> = ({ defaultInput }) => {
const isVariable = defaultInput.type === 'variable'
return (
<div className="my-3 rounded-[10px] bg-components-input-bg-normal px-2.5 py-2">
{isVariable ? <Variable path={`{{${placeholder.selector.join('.')}}}`} /> : <span>{placeholder.value}</span>}
{isVariable ? <Variable path={`{{${defaultInput.selector.join('.')}}}`} /> : <span>{defaultInput.value}</span>}
</div>
)
}

View File

@ -32,15 +32,15 @@ const useSingleRunFormParams = ({
const [formData, setFormData] = useState<HumanInputFormData | null>(null)
const [requiredInputs, setRequiredInputs] = useState<Record<string, any>>({})
const generatedInputs = useMemo(() => {
const placeholderInputs = inputs.inputs.reduce((acc, input) => {
if (input.placeholder.type === 'variable') {
acc.push(...getInputVars([`{{#${input.placeholder.selector.join('.')}#}}`]))
const defaultInputs = inputs.inputs.reduce((acc, input) => {
if (input.default.type === 'variable') {
acc.push(...getInputVars([`{{#${input.default.selector.join('.')}#}}`]))
}
return acc
}, [] as InputVar[])
if (!inputs.form_content)
return placeholderInputs
return [...placeholderInputs, ...getInputVars([inputs.form_content]).filter(item => !isOutput(item.value_selector || []))]
return defaultInputs
return [...defaultInputs, ...getInputVars([inputs.form_content]).filter(item => !isOutput(item.value_selector || []))]
}, [getInputVars, inputs.form_content, inputs.inputs])
const forms = useMemo(() => {

View File

@ -59,7 +59,7 @@ export type UserAction = {
button_style: UserActionButtonType
}
export type FormInputItemPlaceholder = {
export type FormInputItemDefault = {
selector: ValueSelector
type: 'variable' | 'constant'
value: string
@ -68,5 +68,5 @@ export type FormInputItemPlaceholder = {
export type FormInputItem = {
type: InputVarType
output_variable_name: string
placeholder: FormInputItemPlaceholder
default: FormInputItemDefault
}

View File

@ -104,7 +104,7 @@
"count": 1
},
"ts/no-explicit-any": {
"count": 4
"count": 2
}
},
"app/(shareLayout)/components/splash.tsx": {
@ -1392,11 +1392,6 @@
"count": 1
}
},
"app/components/base/prompt-editor/plugins/hitl-input-block/input-field.tsx": {
"ts/no-explicit-any": {
"count": 1
}
},
"app/components/base/prompt-editor/plugins/on-blur-or-focus-block.tsx": {
"ts/no-explicit-any": {
"count": 1

View File

@ -321,7 +321,7 @@ export type HumanInputFormData = {
inputs: FormInputItem[]
actions: UserAction[]
form_token: string
resolved_placeholder_values: Record<string, string>
resolved_default_values: Record<string, string>
display_in_ui: boolean
}