component.tsx 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. import {
  2. memo,
  3. useCallback,
  4. useEffect,
  5. useState,
  6. } from 'react'
  7. import { useTranslation } from 'react-i18next'
  8. import {
  9. COMMAND_PRIORITY_EDITOR,
  10. } from 'lexical'
  11. import { mergeRegister } from '@lexical/utils'
  12. import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
  13. import { useReactFlow, useStoreApi } from 'reactflow'
  14. import { useSelectOrDelete } from '../../hooks'
  15. import type { WorkflowNodesMap } from './node'
  16. import { WorkflowVariableBlockNode } from './node'
  17. import {
  18. DELETE_WORKFLOW_VARIABLE_BLOCK_COMMAND,
  19. UPDATE_WORKFLOW_NODES_MAP,
  20. } from './index'
  21. import { isConversationVar, isENV, isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils'
  22. import Tooltip from '@/app/components/base/tooltip'
  23. import { isExceptionVariable } from '@/app/components/workflow/utils'
  24. import VarFullPathPanel from '@/app/components/workflow/nodes/_base/components/variable/var-full-path-panel'
  25. import { Type } from '@/app/components/workflow/nodes/llm/types'
  26. import type { ValueSelector, Var } from '@/app/components/workflow/types'
  27. import {
  28. VariableLabelInEditor,
  29. } from '@/app/components/workflow/nodes/_base/components/variable/variable-label'
  30. type WorkflowVariableBlockComponentProps = {
  31. nodeKey: string
  32. variables: string[]
  33. workflowNodesMap: WorkflowNodesMap
  34. environmentVariables?: Var[]
  35. conversationVariables?: Var[]
  36. getVarType?: (payload: {
  37. nodeId: string,
  38. valueSelector: ValueSelector,
  39. }) => Type
  40. }
  41. const WorkflowVariableBlockComponent = ({
  42. nodeKey,
  43. variables,
  44. workflowNodesMap = {},
  45. getVarType,
  46. environmentVariables,
  47. conversationVariables,
  48. }: WorkflowVariableBlockComponentProps) => {
  49. const { t } = useTranslation()
  50. const [editor] = useLexicalComposerContext()
  51. const [ref, isSelected] = useSelectOrDelete(nodeKey, DELETE_WORKFLOW_VARIABLE_BLOCK_COMMAND)
  52. const variablesLength = variables.length
  53. const isShowAPart = variablesLength > 2
  54. const varName = (
  55. () => {
  56. const isSystem = isSystemVar(variables)
  57. const varName = variables[variablesLength - 1]
  58. return `${isSystem ? 'sys.' : ''}${varName}`
  59. }
  60. )()
  61. const [localWorkflowNodesMap, setLocalWorkflowNodesMap] = useState<WorkflowNodesMap>(workflowNodesMap)
  62. const node = localWorkflowNodesMap![variables[0]]
  63. const isEnv = isENV(variables)
  64. const isChatVar = isConversationVar(variables)
  65. const isException = isExceptionVariable(varName, node?.type)
  66. let variableValid = true
  67. if (isEnv) {
  68. if (environmentVariables)
  69. variableValid = environmentVariables.some(v => v.variable === `${variables?.[0] ?? ''}.${variables?.[1] ?? ''}`)
  70. }
  71. else if (isChatVar) {
  72. if (conversationVariables)
  73. variableValid = conversationVariables.some(v => v.variable === `${variables?.[0] ?? ''}.${variables?.[1] ?? ''}`)
  74. }
  75. else {
  76. variableValid = !!node
  77. }
  78. const reactflow = useReactFlow()
  79. const store = useStoreApi()
  80. useEffect(() => {
  81. if (!editor.hasNodes([WorkflowVariableBlockNode]))
  82. throw new Error('WorkflowVariableBlockPlugin: WorkflowVariableBlock not registered on editor')
  83. return mergeRegister(
  84. editor.registerCommand(
  85. UPDATE_WORKFLOW_NODES_MAP,
  86. (workflowNodesMap: WorkflowNodesMap) => {
  87. setLocalWorkflowNodesMap(workflowNodesMap)
  88. return true
  89. },
  90. COMMAND_PRIORITY_EDITOR,
  91. ),
  92. )
  93. }, [editor])
  94. const handleVariableJump = useCallback(() => {
  95. const workflowContainer = document.getElementById('workflow-container')
  96. const {
  97. clientWidth,
  98. clientHeight,
  99. } = workflowContainer!
  100. const {
  101. setViewport,
  102. } = reactflow
  103. const { transform } = store.getState()
  104. const zoom = transform[2]
  105. const position = node.position
  106. setViewport({
  107. x: (clientWidth - 400 - node.width! * zoom) / 2 - position!.x * zoom,
  108. y: (clientHeight - node.height! * zoom) / 2 - position!.y * zoom,
  109. zoom: transform[2],
  110. })
  111. }, [node, reactflow, store])
  112. const Item = (
  113. <VariableLabelInEditor
  114. nodeType={node?.type}
  115. nodeTitle={node?.title}
  116. variables={variables}
  117. onClick={(e) => {
  118. e.stopPropagation()
  119. handleVariableJump()
  120. }}
  121. isExceptionVariable={isException}
  122. errorMsg={!variableValid ? t('workflow.errorMsg.invalidVariable') : undefined}
  123. isSelected={isSelected}
  124. ref={ref}
  125. notShowFullPath={isShowAPart}
  126. />
  127. )
  128. if (!node)
  129. return Item
  130. return (
  131. <Tooltip
  132. noDecoration
  133. popupContent={
  134. <VarFullPathPanel
  135. nodeName={node.title}
  136. path={variables.slice(1)}
  137. varType={getVarType ? getVarType({
  138. nodeId: variables[0],
  139. valueSelector: variables,
  140. }) : Type.string}
  141. nodeType={node?.type}
  142. />}
  143. disabled={!isShowAPart}
  144. >
  145. <div>{Item}</div>
  146. </Tooltip>
  147. )
  148. }
  149. export default memo(WorkflowVariableBlockComponent)