feat: can remove

This commit is contained in:
Joel 2025-08-08 15:08:02 +08:00
parent a18bcf3957
commit 89963ecf59
10 changed files with 100 additions and 25 deletions

View File

@ -266,6 +266,7 @@ const PromptEditor: FC<PromptEditorProps> = ({
nodeTitle={hitlInputBlock.nodeTitle}
formInputs={hitlInputBlock.formInputs}
onFormInputsChange={hitlInputBlock.onFormInputsChange}
onFormInputItemRemove={hitlInputBlock.onFormInputItemRemove}
/>
</>
)

View File

@ -19,6 +19,7 @@ type Props = {
isSelected: boolean
formInput?: FormInputItem
onChange: (input: FormInputItem) => void
onRemove: (varName: string) => void
}
const ComponentUI: FC<Props> = ({
@ -35,6 +36,7 @@ const ComponentUI: FC<Props> = ({
},
},
onChange,
onRemove,
}) => {
const { t } = useTranslation()
const [isShowEditModal, {
@ -42,6 +44,7 @@ const ComponentUI: FC<Props> = ({
setFalse: hideEditModal,
}] = useBoolean(false)
// Lexical delegate the click make it unable to add click by the method of react
const editBtnRef = useRef<HTMLDivElement>(null)
useEffect(() => {
const editBtn = editBtnRef.current
@ -54,6 +57,18 @@ const ComponentUI: FC<Props> = ({
}
}, [])
const removeBtnRef = useRef<HTMLDivElement>(null)
useEffect(() => {
const removeBtn = removeBtnRef.current
if (removeBtn)
removeBtn.addEventListener('click', () => onRemove(varName))
return () => {
if (removeBtn)
removeBtn.removeEventListener('click', () => onRemove(varName))
}
}, [onRemove, varName])
const handleChange = useCallback((newPayload: FormInputItem) => {
onChange(newPayload)
hideEditModal()
@ -93,7 +108,7 @@ const ComponentUI: FC<Props> = ({
</ActionButton>
</div>
<div className='flex h-full items-center' >
<div className='flex h-full items-center' ref={removeBtnRef}>
<ActionButton size='s'>
<RiDeleteBinLine className='size-4 text-text-tertiary' />
</ActionButton>

View File

@ -5,20 +5,22 @@ import ComponentUi from './component-ui'
import type { FormInputItem } from '@/app/components/workflow/nodes/human-input/types'
import produce from 'immer'
type QueryBlockComponentProps = {
type Props = {
nodeKey: string
nodeTitle: string
varName: string
formInputs?: FormInputItem[]
onChange: (inputs: FormInputItem[]) => void
onRemove: (varName: string) => void
}
const HITLInputComponent: FC<QueryBlockComponentProps> = ({
const HITLInputComponent: FC<Props> = ({
nodeKey,
nodeTitle,
varName,
formInputs = [],
onChange,
onRemove,
}) => {
const [ref, isSelected] = useSelectOrDelete(nodeKey, DELETE_HITL_INPUT_BLOCK_COMMAND)
const payload = formInputs.find(item => item.output_variable_name === varName)
@ -46,6 +48,7 @@ const HITLInputComponent: FC<QueryBlockComponentProps> = ({
isSelected={isSelected}
formInput={payload}
onChange={handleChange}
onRemove={onRemove}
/>
</div>
)

View File

@ -23,6 +23,7 @@ const HITLInputReplacementBlock = ({
nodeTitle,
formInputs,
onFormInputsChange,
onFormInputItemRemove,
}: HITLInputBlockType) => {
const [editor] = useLexicalComposerContext()
@ -33,8 +34,14 @@ const HITLInputReplacementBlock = ({
const createHITLInputBlockNode = useCallback((textNode: TextNode): QueryBlockNode => {
const varName = textNode.getTextContent().split('.')[1].replace(/#}}$/, '')
return $applyNodeReplacement($createHITLInputNode(varName, nodeTitle, formInputs || [], onFormInputsChange!))
}, [nodeTitle, formInputs, onFormInputsChange])
return $applyNodeReplacement($createHITLInputNode(
varName,
nodeTitle,
formInputs || [],
onFormInputsChange!,
onFormInputItemRemove!,
))
}, [nodeTitle, formInputs, onFormInputsChange, onFormInputItemRemove])
const getMatch = useCallback((text: string) => {
const matchArr = REGEX.exec(text)

View File

@ -8,6 +8,7 @@ export type SerializedNode = SerializedLexicalNode & {
nodeTitle: string
formInputs: FormInputItem[]
onFormInputsChange: (inputs: FormInputItem[]) => void
onFormInputItemRemove: (varName: string) => void
}
export class HITLInputNode extends DecoratorNode<React.JSX.Element> {
@ -15,6 +16,7 @@ export class HITLInputNode extends DecoratorNode<React.JSX.Element> {
__nodeTitle: string
__formInputs?: FormInputItem[]
__onFormInputsChange: (inputs: FormInputItem[]) => void
__onFormInputItemRemove: (varName: string) => void
isIsolated(): boolean {
return true // This is necessary for drag-and-drop to work correctly
@ -48,21 +50,34 @@ export class HITLInputNode extends DecoratorNode<React.JSX.Element> {
return self.__onFormInputsChange
}
getOnFormInputItemRemove(): (varName: string) => void {
const self = this.getLatest()
return self.__onFormInputItemRemove
}
static clone(node: HITLInputNode): HITLInputNode {
return new HITLInputNode(node.__variableName, node.__nodeTitle, node.__formInputs || [], node.__onFormInputsChange, node.__key)
return new HITLInputNode(
node.__variableName,
node.__nodeTitle,
node.__formInputs || [],
node.__onFormInputsChange,
node.__onFormInputItemRemove,
node.__key,
)
}
isInline(): boolean {
return true
}
constructor(varName: string, nodeTitle: string, formInputs: FormInputItem[], onFormInputsChange: (inputs: FormInputItem[]) => void, key?: NodeKey) {
constructor(varName: string, nodeTitle: string, formInputs: FormInputItem[], onFormInputsChange: (inputs: FormInputItem[]) => void, onFormInputItemRemove: (varName: string) => void, key?: NodeKey) {
super(key)
this.__variableName = varName
this.__nodeTitle = nodeTitle
this.__formInputs = formInputs
this.__onFormInputsChange = onFormInputsChange
this.__onFormInputItemRemove = onFormInputItemRemove
}
createDOM(): HTMLElement {
@ -82,11 +97,18 @@ export class HITLInputNode extends DecoratorNode<React.JSX.Element> {
nodeTitle={this.getNodeTitle()}
formInputs={this.getFormInputs()}
onChange={this.getOnFormInputsChange()}
onRemove={this.getOnFormInputItemRemove()}
/>
}
static importJSON(serializedNode: SerializedNode): HITLInputNode {
const node = $createHITLInputNode(serializedNode.variableName, serializedNode.nodeTitle, serializedNode.formInputs, serializedNode.onFormInputsChange)
const node = $createHITLInputNode(
serializedNode.variableName,
serializedNode.nodeTitle,
serializedNode.formInputs,
serializedNode.onFormInputsChange,
serializedNode.onFormInputItemRemove,
)
return node
}
@ -99,6 +121,7 @@ export class HITLInputNode extends DecoratorNode<React.JSX.Element> {
nodeTitle: this.getNodeTitle(),
formInputs: this.getFormInputs(),
onFormInputsChange: this.getOnFormInputsChange(),
onFormInputItemRemove: this.getOnFormInputItemRemove(),
}
}
@ -107,8 +130,14 @@ export class HITLInputNode extends DecoratorNode<React.JSX.Element> {
}
}
export function $createHITLInputNode(variableName: string, nodeTitle: string, formInputs: FormInputItem[], onFormInputsChange: (inputs: FormInputItem[]) => void): HITLInputNode {
return new HITLInputNode(variableName, nodeTitle, formInputs, onFormInputsChange)
export function $createHITLInputNode(variableName: string, nodeTitle: string, formInputs: FormInputItem[], onFormInputsChange: (inputs: FormInputItem[]) => void, onFormInputItemRemove: (varName: string) => void): HITLInputNode {
return new HITLInputNode(
variableName,
nodeTitle,
formInputs,
onFormInputsChange,
onFormInputItemRemove,
)
}
export function $isHITLInputNode(

View File

@ -83,6 +83,7 @@ export type HITLInputBlockType = {
nodeTitle: string
formInputs?: FormInputItem[]
onFormInputsChange?: (inputs: FormInputItem[]) => void
onFormInputItemRemove: (varName: string) => void
}
export type MenuTextMatch = {

View File

@ -14,7 +14,9 @@ type Props = {
onChange: (value: string) => void
formInputs: FormInputItem[]
onFormInputsChange: (payload: FormInputItem[]) => void
onFormInputItemRemove: (varName: string) => void
nodeTitle: string
editorKey: number
}
const FormContent: FC<Props> = ({
@ -23,7 +25,9 @@ const FormContent: FC<Props> = ({
onChange,
formInputs,
onFormInputsChange,
onFormInputItemRemove,
nodeTitle,
editorKey,
}) => {
const { t } = useTranslation()
const filterVar = () => true
@ -40,6 +44,7 @@ const FormContent: FC<Props> = ({
return (
<div>
<PromptEditor
key={editorKey}
value={value}
onChange={onChange}
className='min-h-[80px]'
@ -48,6 +53,7 @@ const FormContent: FC<Props> = ({
formInputs,
nodeTitle,
onFormInputsChange,
onFormInputItemRemove,
}}
workflowVariableBlock={{
show: true,

View File

@ -36,6 +36,8 @@ const Panel: FC<NodePanelProps<HumanInputNodeType>> = ({
handleTimeoutChange,
handleFormContentChange,
handleFormInputsChange,
handleFormInputItemRemove,
editorKey,
} = useConfig(id, data)
const { availableVars, availableNodesWithParent } = useAvailableVarList(id, {
@ -68,12 +70,14 @@ const Panel: FC<NodePanelProps<HumanInputNodeType>> = ({
</div>
</div>
<FormContent
editorKey={editorKey}
nodeId={id}
value={inputs.form_content}
onChange={handleFormContentChange}
nodeTitle={inputs.title}
formInputs={inputs.inputs}
onFormInputsChange={handleFormInputsChange}
onFormInputItemRemove={handleFormInputItemRemove}
/>
</div>
{/* user actions */}

View File

@ -1,24 +1,44 @@
import { useCallback, useEffect, useRef, useState } from 'react'
import useNodeCrud from '../_base/hooks/use-node-crud'
import type { FormInputItem, HumanInputNodeType } from './types'
import produce from 'immer'
const useFormContent = (id: string, payload: HumanInputNodeType) => {
const [editorKey, setEditorKey] = useState(0)
const { inputs, setInputs } = useNodeCrud<HumanInputNodeType>(id, payload)
const handleFormContentChange = (value: string) => {
const inputsRef = useRef(inputs)
useEffect(() => {
inputsRef.current = inputs
}, [inputs])
const handleFormContentChange = useCallback((value: string) => {
setInputs({
...inputs,
form_content: value,
})
}
}, [inputs, setInputs])
const handleFormInputsChange = (formInputs: FormInputItem[]) => {
const handleFormInputsChange = useCallback((formInputs: FormInputItem[]) => {
setInputs({
...inputs,
inputs: formInputs,
})
}
}, [inputs, setInputs])
const handleFormInputItemRemove = useCallback((varName: string) => {
const inputs = inputsRef.current
const newInputs = produce(inputs, (draft) => {
draft.form_content = draft.form_content.replaceAll(`{{#$output.${varName}#}}`, '')
draft.inputs = draft.inputs.filter(item => item.output_variable_name !== varName)
})
setInputs(newInputs)
setEditorKey(editorKey + 1)
}, [inputs, setInputs, editorKey])
return {
editorKey,
handleFormContentChange,
handleFormInputsChange,
handleFormInputItemRemove,
}
}

View File

@ -1,11 +0,0 @@
'use client'
import InputField from '@/app/components/base/prompt-editor/plugins/hitl-input-block/input-field'
const Page = () => {
return (
<InputField />
)
}
export default Page