wwf
2 天以前 a430284aa21e3ae1f0d5654e55b2ad2852519cc2
app/components/workflow/hooks/use-workflow-interactions.ts
@@ -8,15 +8,12 @@
import { useStore, useWorkflowStore } from '../store'
import {
  CUSTOM_NODE, DSL_EXPORT_CHECK,
  NODE_LAYOUT_HORIZONTAL_PADDING,
  NODE_LAYOUT_VERTICAL_PADDING,
  WORKFLOW_DATA_UPDATE,
} from '../constants'
import type { Node, WorkflowDataUpdater } from '../types'
import { BlockEnum, ControlMode } from '../types'
import { ControlMode } from '../types'
import {
  getLayoutByDagre,
  getLayoutForChildNodes,
  initialEdges,
  initialNodes,
} from '../utils'
@@ -25,8 +22,8 @@
  useSelectionInteractions,
  useWorkflowReadOnly,
} from '../hooks'
import { useEdgesInteractionsWithoutSync } from './use-edges-interactions-without-sync'
import { useNodesInteractionsWithoutSync } from './use-nodes-interactions-without-sync'
import { useEdgesInteractions } from './use-edges-interactions'
import { useNodesInteractions } from './use-nodes-interactions'
import { useNodesSyncDraft } from './use-nodes-sync-draft'
import { WorkflowHistoryEvent, useWorkflowHistory } from './use-workflow-history'
import { useEventEmitterContextContext } from '@/context/event-emitter'
@@ -37,8 +34,8 @@
export const useWorkflowInteractions = () => {
  const workflowStore = useWorkflowStore()
  const { handleNodeCancelRunningStatus } = useNodesInteractionsWithoutSync()
  const { handleEdgeCancelRunningStatus } = useEdgesInteractionsWithoutSync()
  const { handleNodeCancelRunningStatus } = useNodesInteractions()
  const { handleEdgeCancelRunningStatus } = useEdgesInteractions()
  const handleCancelDebugAndPreviewPanel = useCallback(() => {
    workflowStore.setState({
@@ -101,81 +98,10 @@
    } = store.getState()
    const { setViewport } = reactflow
    const nodes = getNodes()
    const loopAndIterationNodes = nodes.filter(
      node => (node.data.type === BlockEnum.Loop || node.data.type === BlockEnum.Iteration)
              && !node.parentId
              && node.type === CUSTOM_NODE,
    )
    const childLayoutsMap: Record<string, any> = {}
    loopAndIterationNodes.forEach((node) => {
      childLayoutsMap[node.id] = getLayoutForChildNodes(node.id, nodes, edges)
    })
    const containerSizeChanges: Record<string, { width: number, height: number }> = {}
    loopAndIterationNodes.forEach((parentNode) => {
      const childLayout = childLayoutsMap[parentNode.id]
      if (!childLayout) return
      let minX = Infinity
      let minY = Infinity
      let maxX = -Infinity
      let maxY = -Infinity
      let hasChildren = false
      const childNodes = nodes.filter(node => node.parentId === parentNode.id)
      childNodes.forEach((node) => {
        if (childLayout.node(node.id)) {
          hasChildren = true
          const childNodeWithPosition = childLayout.node(node.id)
          const nodeX = childNodeWithPosition.x - node.width! / 2
          const nodeY = childNodeWithPosition.y - node.height! / 2
          minX = Math.min(minX, nodeX)
          minY = Math.min(minY, nodeY)
          maxX = Math.max(maxX, nodeX + node.width!)
          maxY = Math.max(maxY, nodeY + node.height!)
        }
      })
      if (hasChildren) {
        const requiredWidth = maxX - minX + NODE_LAYOUT_HORIZONTAL_PADDING * 2
        const requiredHeight = maxY - minY + NODE_LAYOUT_VERTICAL_PADDING * 2
        containerSizeChanges[parentNode.id] = {
          width: Math.max(parentNode.width || 0, requiredWidth),
          height: Math.max(parentNode.height || 0, requiredHeight),
        }
      }
    })
    const nodesWithUpdatedSizes = produce(nodes, (draft) => {
      draft.forEach((node) => {
        if ((node.data.type === BlockEnum.Loop || node.data.type === BlockEnum.Iteration)
            && containerSizeChanges[node.id]) {
          node.width = containerSizeChanges[node.id].width
          node.height = containerSizeChanges[node.id].height
          if (node.data.type === BlockEnum.Loop) {
            node.data.width = containerSizeChanges[node.id].width
            node.data.height = containerSizeChanges[node.id].height
          }
          else if (node.data.type === BlockEnum.Iteration) {
            node.data.width = containerSizeChanges[node.id].width
            node.data.height = containerSizeChanges[node.id].height
          }
        }
      })
    })
    const layout = getLayoutByDagre(nodesWithUpdatedSizes, edges)
    const layout = getLayoutByDagre(nodes, edges)
    const rankMap = {} as Record<string, Node>
    nodesWithUpdatedSizes.forEach((node) => {
    nodes.forEach((node) => {
      if (!node.parentId && node.type === CUSTOM_NODE) {
        const rank = layout.node(node.id).rank!
@@ -189,7 +115,7 @@
      }
    })
    const newNodes = produce(nodesWithUpdatedSizes, (draft) => {
    const newNodes = produce(nodes, (draft) => {
      draft.forEach((node) => {
        if (!node.parentId && node.type === CUSTOM_NODE) {
          const nodeWithPosition = layout.node(node.id)
@@ -200,40 +126,7 @@
          }
        }
      })
      loopAndIterationNodes.forEach((parentNode) => {
        const childLayout = childLayoutsMap[parentNode.id]
        if (!childLayout) return
        const childNodes = draft.filter(node => node.parentId === parentNode.id)
        let minX = Infinity
        let minY = Infinity
        childNodes.forEach((node) => {
          if (childLayout.node(node.id)) {
            const childNodeWithPosition = childLayout.node(node.id)
            const nodeX = childNodeWithPosition.x - node.width! / 2
            const nodeY = childNodeWithPosition.y - node.height! / 2
            minX = Math.min(minX, nodeX)
            minY = Math.min(minY, nodeY)
          }
        })
        childNodes.forEach((node) => {
          if (childLayout.node(node.id)) {
            const childNodeWithPosition = childLayout.node(node.id)
            node.position = {
              x: NODE_LAYOUT_HORIZONTAL_PADDING + (childNodeWithPosition.x - node.width! / 2 - minX),
              y: NODE_LAYOUT_VERTICAL_PADDING + (childNodeWithPosition.y - node.height! / 2 - minY),
            }
          }
        })
      })
    })
    setNodes(newNodes)
    const zoom = 0.7
    setViewport({
@@ -246,7 +139,6 @@
      handleSyncWorkflowDraft()
    })
  }, [getNodesReadOnly, store, reactflow, workflowStore, handleSyncWorkflowDraft, saveStateToHistory])
  return {
    handleLayout,
  }
@@ -313,6 +205,7 @@
export const useWorkflowUpdate = () => {
  const reactflow = useReactFlow()
  const workflowStore = useWorkflowStore()
  const { eventEmitter } = useEventEmitterContextContext()
  const handleUpdateWorkflowCanvas = useCallback((payload: WorkflowDataUpdater) => {
@@ -332,8 +225,32 @@
    setViewport(viewport)
  }, [eventEmitter, reactflow])
  const handleRefreshWorkflowDraft = useCallback(() => {
    const {
      appId,
      setSyncWorkflowDraftHash,
      setIsSyncingWorkflowDraft,
      setEnvironmentVariables,
      setEnvSecrets,
      setConversationVariables,
    } = workflowStore.getState()
    setIsSyncingWorkflowDraft(true)
    fetchWorkflowDraft(`/apps/${appId}/workflows/draft`).then((response) => {
      handleUpdateWorkflowCanvas(response.graph as WorkflowDataUpdater)
      setSyncWorkflowDraftHash(response.hash)
      setEnvSecrets((response.environment_variables || []).filter(env => env.value_type === 'secret').reduce((acc, env) => {
        acc[env.id] = env.value
        return acc
      }, {} as Record<string, string>))
      setEnvironmentVariables(response.environment_variables?.map(env => env.value_type === 'secret' ? { ...env, value: '[__HIDDEN__]' } : env) || [])
      // #TODO chatVar sync#
      setConversationVariables(response.conversation_variables || [])
    }).finally(() => setIsSyncingWorkflowDraft(false))
  }, [handleUpdateWorkflowCanvas, workflowStore])
  return {
    handleUpdateWorkflowCanvas,
    handleRefreshWorkflowDraft,
  }
}
@@ -366,7 +283,7 @@
      a.download = `${appDetail.name}.yml`
      a.click()
    }
    catch {
    catch (e) {
      notify({ type: 'error', message: t('app.exportFailed') })
    }
    finally {
@@ -391,7 +308,7 @@
        },
      } as any)
    }
    catch {
    catch (e) {
      notify({ type: 'error', message: t('app.exportFailed') })
    }
  }, [appDetail, eventEmitter, handleExportDSL, notify, t])