### What problem does this PR solve? Feat: Delete the operator node and hide the corresponding sheet #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality)tags/v0.20.0
| import message from '@/components/ui/message'; | |||||
| import { Authorization } from '@/constants/authorization'; | import { Authorization } from '@/constants/authorization'; | ||||
| import { IReferenceObject } from '@/interfaces/database/chat'; | import { IReferenceObject } from '@/interfaces/database/chat'; | ||||
| import { BeginQuery } from '@/pages/agent/interface'; | import { BeginQuery } from '@/pages/agent/interface'; | ||||
| const val = JSON.parse(value?.data || ''); | const val = JSON.parse(value?.data || ''); | ||||
| console.info('data:', val); | console.info('data:', val); | ||||
| if (val.code === 500) { | |||||
| message.error(val.message); | |||||
| } | |||||
| setAnswerList((list) => { | setAnswerList((list) => { | ||||
| const nextList = [...list]; | const nextList = [...list]; | 
| import { useBeforeDelete } from '../hooks/use-before-delete'; | import { useBeforeDelete } from '../hooks/use-before-delete'; | ||||
| import { useCacheChatLog } from '../hooks/use-cache-chat-log'; | import { useCacheChatLog } from '../hooks/use-cache-chat-log'; | ||||
| import { useMoveNote } from '../hooks/use-move-note'; | import { useMoveNote } from '../hooks/use-move-note'; | ||||
| import { useShowDrawer, useShowLogSheet } from '../hooks/use-show-drawer'; | |||||
| import { | |||||
| useHideFormSheetOnNodeDeletion, | |||||
| useShowDrawer, | |||||
| useShowLogSheet, | |||||
| } from '../hooks/use-show-drawer'; | |||||
| import { LogSheet } from '../log-sheet'; | import { LogSheet } from '../log-sheet'; | ||||
| import RunSheet from '../run-sheet'; | import RunSheet from '../run-sheet'; | ||||
| import { ButtonEdge } from './edge'; | import { ButtonEdge } from './edge'; | ||||
| setLastSendLoading(false); | setLastSendLoading(false); | ||||
| } | } | ||||
| }; | }; | ||||
| useHideFormSheetOnNodeDeletion({ hideFormDrawer }); | |||||
| return ( | return ( | ||||
| <div className={styles.canvasWrapper}> | <div className={styles.canvasWrapper}> | ||||
| <svg | <svg | 
| id={NodeHandleId.Start} | id={NodeHandleId.Start} | ||||
| ></CommonHandle> | ></CommonHandle> | ||||
| <section className="flex items-center justify-center gap-2"> | |||||
| <section className="flex items-center gap-2"> | |||||
| <OperatorIcon name={data.label as Operator}></OperatorIcon> | <OperatorIcon name={data.label as Operator}></OperatorIcon> | ||||
| <div className="truncate text-center font-semibold text-sm"> | <div className="truncate text-center font-semibold text-sm"> | ||||
| {t(`flow.begin`)} | {t(`flow.begin`)} | 
| <section className="px-1 py-2 flex gap-2 bg-background-highlight items-center note-drag-handle rounded-s-md"> | <section className="px-1 py-2 flex gap-2 bg-background-highlight items-center note-drag-handle rounded-s-md"> | ||||
| <NotebookPen className="size-4" /> | <NotebookPen className="size-4" /> | ||||
| <Form {...nameForm}> | <Form {...nameForm}> | ||||
| <form> | |||||
| <form className="flex-1"> | |||||
| <FormField | <FormField | ||||
| control={nameForm.control} | control={nameForm.control} | ||||
| name="name" | name="name" | 
| import { Message } from '@/interfaces/database/chat'; | import { Message } from '@/interfaces/database/chat'; | ||||
| import { buildMessageUuidWithRole } from '@/utils/chat'; | import { buildMessageUuidWithRole } from '@/utils/chat'; | ||||
| import { get } from 'lodash'; | import { get } from 'lodash'; | ||||
| import { useCallback } from 'react'; | |||||
| import { memo, useCallback } from 'react'; | |||||
| import { useParams } from 'umi'; | import { useParams } from 'umi'; | ||||
| import DebugContent from '../debug-content'; | import DebugContent from '../debug-content'; | ||||
| import { BeginQuery } from '../interface'; | import { BeginQuery } from '../interface'; | ||||
| import { buildBeginQueryWithObject } from '../utils'; | import { buildBeginQueryWithObject } from '../utils'; | ||||
| const AgentChatBox = () => { | |||||
| function AgentChatBox() { | |||||
| const { | const { | ||||
| value, | value, | ||||
| ref, | ref, | ||||
| })} | })} | ||||
| {/* </Spin> */} | {/* </Spin> */} | ||||
| </div> | </div> | ||||
| <div ref={ref} /> | |||||
| <div ref={ref.scrollRef} /> | |||||
| </div> | </div> | ||||
| <NextMessageInput | <NextMessageInput | ||||
| value={value} | value={value} | ||||
| ></PdfDrawer> | ></PdfDrawer> | ||||
| </> | </> | ||||
| ); | ); | ||||
| }; | |||||
| } | |||||
| export default AgentChatBox; | |||||
| export default memo(AgentChatBox); | 
| params.session_id = sessionId; | params.session_id = sessionId; | ||||
| } | } | ||||
| const res = await send(params); | |||||
| clearUploadResponseList(); | |||||
| try { | |||||
| const res = await send(params); | |||||
| if (receiveMessageError(res)) { | |||||
| sonnerMessage.error(res?.data?.message); | |||||
| clearUploadResponseList(); | |||||
| // cancel loading | |||||
| setValue(message.content); | |||||
| removeLatestMessage(); | |||||
| } else { | |||||
| // refetch(); // pull the message list after sending the message successfully | |||||
| if (receiveMessageError(res)) { | |||||
| sonnerMessage.error(res?.data?.message); | |||||
| // cancel loading | |||||
| setValue(message.content); | |||||
| removeLatestMessage(); | |||||
| } else { | |||||
| // refetch(); // pull the message list after sending the message successfully | |||||
| } | |||||
| } catch (error) { | |||||
| console.log('🚀 ~ useSendAgentMessage ~ error:', error); | |||||
| } | } | ||||
| }, | }, | ||||
| [ | [ | 
| showLogSheet: handleShow, | showLogSheet: handleShow, | ||||
| }; | }; | ||||
| } | } | ||||
| export function useHideFormSheetOnNodeDeletion({ | |||||
| hideFormDrawer, | |||||
| }: Pick<ReturnType<typeof useShowFormDrawer>, 'hideFormDrawer'>) { | |||||
| const { nodes, clickedNodeId } = useGraphStore((state) => state); | |||||
| useEffect(() => { | |||||
| if (!nodes.some((x) => x.id === clickedNodeId)) { | |||||
| hideFormDrawer(); | |||||
| } | |||||
| }, [clickedNodeId, hideFormDrawer, nodes]); | |||||
| } | 
| import { IconFont } from '@/components/icon-font'; | import { IconFont } from '@/components/icon-font'; | ||||
| import SvgIcon from '@/components/svg-icon'; | import SvgIcon from '@/components/svg-icon'; | ||||
| import { cn } from '@/lib/utils'; | import { cn } from '@/lib/utils'; | ||||
| import { CirclePlay } from 'lucide-react'; | |||||
| import { HousePlus } from 'lucide-react'; | |||||
| import { Operator } from './constant'; | import { Operator } from './constant'; | ||||
| interface IProps { | interface IProps { | ||||
| export const OperatorIconMap = { | export const OperatorIconMap = { | ||||
| [Operator.Retrieval]: 'KR', | [Operator.Retrieval]: 'KR', | ||||
| [Operator.Begin]: CirclePlay, | |||||
| [Operator.Begin]: 'house-plus', | |||||
| [Operator.Categorize]: 'a-QuestionClassification', | [Operator.Categorize]: 'a-QuestionClassification', | ||||
| [Operator.Message]: 'reply', | [Operator.Message]: 'reply', | ||||
| [Operator.Iteration]: 'loop', | [Operator.Iteration]: 'loop', | ||||
| const OperatorIcon = ({ name, className }: IProps) => { | const OperatorIcon = ({ name, className }: IProps) => { | ||||
| const Icon = OperatorIconMap[name as keyof typeof OperatorIconMap] || Empty; | const Icon = OperatorIconMap[name as keyof typeof OperatorIconMap] || Empty; | ||||
| if (name === Operator.Begin) { | |||||
| return ( | |||||
| <div className="inline-block p-1 bg-background-checked rounded-sm"> | |||||
| <HousePlus className="rounded size-3" /> | |||||
| </div> | |||||
| ); | |||||
| } | |||||
| if (name in SVGIconMap) { | if (name in SVGIconMap) { | ||||
| return ( | return ( | ||||
| <SvgIcon | <SvgIcon |