diff --git a/web/app/components/workflow/candidate-node.tsx b/web/app/components/workflow/candidate-node.tsx index 35bcd5c201..708ccc3db4 100644 --- a/web/app/components/workflow/candidate-node.tsx +++ b/web/app/components/workflow/candidate-node.tsx @@ -4,7 +4,6 @@ import { import produce from 'immer' import { useReactFlow, - useStoreApi, useViewport, } from 'reactflow' import { useEventListener } from 'ahooks' @@ -19,9 +18,9 @@ import CustomNode from './nodes' import CustomNoteNode from './note-node' import { CUSTOM_NOTE_NODE } from './note-node/constants' import { BlockEnum } from './types' +import { useCollaborativeWorkflow } from '@/app/components/workflow/hooks/use-collaborative-workflow' const CandidateNode = () => { - const store = useStoreApi() const reactflow = useReactFlow() const workflowStore = useWorkflowStore() const candidateNode = useStore(s => s.candidateNode) @@ -29,18 +28,15 @@ const CandidateNode = () => { const { zoom } = useViewport() const { handleNodeSelect } = useNodesInteractions() const { saveStateToHistory } = useWorkflowHistory() + const collaborativeWorkflow = useCollaborativeWorkflow() useEventListener('click', (e) => { const { candidateNode, mousePosition } = workflowStore.getState() if (candidateNode) { e.preventDefault() - const { - getNodes, - setNodes, - } = store.getState() + const { nodes, setNodes } = collaborativeWorkflow.getState() const { screenToFlowPosition } = reactflow - const nodes = getNodes() const { x, y } = screenToFlowPosition({ x: mousePosition.pageX, y: mousePosition.pageY }) const newNodes = produce(nodes, (draft) => { draft.push({ diff --git a/web/app/components/workflow/hooks/use-workflow-interactions.ts b/web/app/components/workflow/hooks/use-workflow-interactions.ts index 11562df057..30e0d75151 100644 --- a/web/app/components/workflow/hooks/use-workflow-interactions.ts +++ b/web/app/components/workflow/hooks/use-workflow-interactions.ts @@ -1,7 +1,7 @@ import { useCallback, } from 'react' -import { useReactFlow, useStoreApi } from 'reactflow' +import { useReactFlow } from 'reactflow' import produce from 'immer' import { useStore, useWorkflowStore } from '../store' import { @@ -28,6 +28,7 @@ import { useNodesInteractionsWithoutSync } from './use-nodes-interactions-withou import { useNodesSyncDraft } from './use-nodes-sync-draft' import { WorkflowHistoryEvent, useWorkflowHistory } from './use-workflow-history' import { useEventEmitterContextContext } from '@/context/event-emitter' +import { useCollaborativeWorkflow } from '@/app/components/workflow/hooks/use-collaborative-workflow' export const useWorkflowInteractions = () => { const workflowStore = useWorkflowStore() @@ -87,23 +88,22 @@ export const useWorkflowMoveMode = () => { export const useWorkflowOrganize = () => { const workflowStore = useWorkflowStore() - const store = useStoreApi() const reactflow = useReactFlow() const { getNodesReadOnly } = useNodesReadOnly() const { saveStateToHistory } = useWorkflowHistory() const { handleSyncWorkflowDraft } = useNodesSyncDraft() + const collaborativeWorkflow = useCollaborativeWorkflow() const handleLayout = useCallback(async () => { if (getNodesReadOnly()) return workflowStore.setState({ nodeAnimation: true }) const { - getNodes, + nodes, edges, setNodes, - } = store.getState() + } = collaborativeWorkflow.getState() const { setViewport } = reactflow - const nodes = getNodes() const loopAndIterationNodes = nodes.filter( node => (node.data.type === BlockEnum.Loop || node.data.type === BlockEnum.Iteration) @@ -248,7 +248,7 @@ export const useWorkflowOrganize = () => { setTimeout(() => { handleSyncWorkflowDraft() }) - }, [getNodesReadOnly, store, reactflow, workflowStore, handleSyncWorkflowDraft, saveStateToHistory]) + }, [getNodesReadOnly, collaborativeWorkflow, reactflow, workflowStore, handleSyncWorkflowDraft, saveStateToHistory]) return { handleLayout, diff --git a/web/app/components/workflow/hooks/use-workflow.ts b/web/app/components/workflow/hooks/use-workflow.ts index 1fc1eedffa..08c6ff0599 100644 --- a/web/app/components/workflow/hooks/use-workflow.ts +++ b/web/app/components/workflow/hooks/use-workflow.ts @@ -6,7 +6,6 @@ import { useTranslation } from 'react-i18next' import { getIncomers, getOutgoers, - useStoreApi, } from 'reactflow' import type { Connection, @@ -35,6 +34,7 @@ import { CUSTOM_NOTE_NODE } from '../note-node/constants' import { findUsedVarNodes, getNodeOutputVars, updateNodeVars } from '../nodes/_base/components/variable/utils' import { useAvailableBlocks } from './use-available-blocks' import { useStore as useAppStore } from '@/app/components/app/store' +import { useCollaborativeWorkflow } from '@/app/components/workflow/hooks/use-collaborative-workflow' import { fetchAllBuiltInTools, fetchAllCustomTools, @@ -55,26 +55,19 @@ export const useIsChatMode = () => { export const useWorkflow = () => { const { t } = useTranslation() - const store = useStoreApi() + const collaborativeWorkflow = useCollaborativeWorkflow() const workflowStore = useWorkflowStore() const { getAvailableBlocks } = useAvailableBlocks() const { nodesMap } = useNodesMetaData() const getNodeById = useCallback((nodeId: string) => { - const { - getNodes, - } = store.getState() - const nodes = getNodes() + const { nodes } = collaborativeWorkflow.getState() const currentNode = nodes.find(node => node.id === nodeId) return currentNode - }, [store]) + }, [collaborativeWorkflow]) const getTreeLeafNodes = useCallback((nodeId: string) => { - const { - getNodes, - edges, - } = store.getState() - const nodes = getNodes() + const { nodes, edges } = collaborativeWorkflow.getState() const currentNode = nodes.find(node => node.id === nodeId) let startNodes = nodes.filter(node => nodesMap?.[node.data.type as BlockEnum]?.metaData.isStart) || [] @@ -117,14 +110,11 @@ export const useWorkflow = () => { return uniqBy(list, 'id').filter((item: Node) => { return SUPPORT_OUTPUT_VARS_NODE.includes(item.data.type) }) - }, [store, nodesMap]) + }, [collaborativeWorkflow, nodesMap]) const getBeforeNodesInSameBranch = useCallback((nodeId: string, newNodes?: Node[], newEdges?: Edge[]) => { - const { - getNodes, - edges, - } = store.getState() - const nodes = newNodes || getNodes() + const { nodes: oldNodes, edges } = collaborativeWorkflow.getState() + const nodes = newNodes || oldNodes const currentNode = nodes.find(node => node.id === nodeId) const list: Node[] = [] @@ -167,14 +157,11 @@ export const useWorkflow = () => { } return [] - }, [store]) + }, [collaborativeWorkflow]) const getBeforeNodesInSameBranchIncludeParent = useCallback((nodeId: string, newNodes?: Node[], newEdges?: Edge[]) => { const nodes = getBeforeNodesInSameBranch(nodeId, newNodes, newEdges) - const { - getNodes, - } = store.getState() - const allNodes = getNodes() + const { nodes: allNodes } = collaborativeWorkflow.getState() const node = allNodes.find(n => n.id === nodeId) const parentNodeId = node?.parentId const parentNode = allNodes.find(n => n.id === parentNodeId) @@ -182,14 +169,10 @@ export const useWorkflow = () => { nodes.push(parentNode) return nodes - }, [getBeforeNodesInSameBranch, store]) + }, [getBeforeNodesInSameBranch, collaborativeWorkflow]) const getAfterNodesInSameBranch = useCallback((nodeId: string) => { - const { - getNodes, - edges, - } = store.getState() - const nodes = getNodes() + const { nodes, edges } = collaborativeWorkflow.getState() const currentNode = nodes.find(node => node.id === nodeId)! if (!currentNode) @@ -213,40 +196,29 @@ export const useWorkflow = () => { }) return uniqBy(list, 'id') - }, [store]) + }, [collaborativeWorkflow]) const getBeforeNodeById = useCallback((nodeId: string) => { - const { - getNodes, - edges, - } = store.getState() - const nodes = getNodes() + const { nodes, edges } = collaborativeWorkflow.getState() const node = nodes.find(node => node.id === nodeId)! return getIncomers(node, nodes, edges) - }, [store]) + }, [collaborativeWorkflow]) const getIterationNodeChildren = useCallback((nodeId: string) => { - const { - getNodes, - } = store.getState() - const nodes = getNodes() + const { nodes } = collaborativeWorkflow.getState() return nodes.filter(node => node.parentId === nodeId) - }, [store]) + }, [collaborativeWorkflow]) const getLoopNodeChildren = useCallback((nodeId: string) => { - const { - getNodes, - } = store.getState() - const nodes = getNodes() + const { nodes } = collaborativeWorkflow.getState() return nodes.filter(node => node.parentId === nodeId) - }, [store]) + }, [collaborativeWorkflow]) const handleOutVarRenameChange = useCallback((nodeId: string, oldValeSelector: ValueSelector, newVarSelector: ValueSelector) => { - const { getNodes, setNodes } = store.getState() - const allNodes = getNodes() + const { nodes: allNodes, setNodes } = collaborativeWorkflow.getState() const affectedNodes = findUsedVarNodes(oldValeSelector, allNodes) if (affectedNodes.length > 0) { const newNodes = allNodes.map((node) => { @@ -257,7 +229,7 @@ export const useWorkflow = () => { }) setNodes(newNodes) } - }, [store]) + }, [collaborativeWorkflow]) const isVarUsedInNodes = useCallback((varSelector: ValueSelector) => { const nodeId = varSelector[0] @@ -268,11 +240,11 @@ export const useWorkflow = () => { const removeUsedVarInNodes = useCallback((varSelector: ValueSelector) => { const nodeId = varSelector[0] - const { getNodes, setNodes } = store.getState() + const { nodes, setNodes } = collaborativeWorkflow.getState() const afterNodes = getAfterNodesInSameBranch(nodeId) const effectNodes = findUsedVarNodes(varSelector, afterNodes) if (effectNodes.length > 0) { - const newNodes = getNodes().map((node) => { + const newNodes = nodes.map((node) => { if (effectNodes.find(n => n.id === node.id)) return updateNodeVars(node, varSelector, []) @@ -280,7 +252,7 @@ export const useWorkflow = () => { }) setNodes(newNodes) } - }, [getAfterNodesInSameBranch, store]) + }, [getAfterNodesInSameBranch, collaborativeWorkflow]) const isNodeVarsUsedInNodes = useCallback((node: Node, isChatMode: boolean) => { const outputVars = getNodeOutputVars(node, isChatMode) @@ -291,9 +263,7 @@ export const useWorkflow = () => { }, [isVarUsedInNodes]) const checkParallelLimit = useCallback((nodeId: string, nodeHandle = 'source') => { - const { - edges, - } = store.getState() + const { edges } = collaborativeWorkflow.getState() const connectedEdges = edges.filter(edge => edge.source === nodeId && edge.sourceHandle === nodeHandle) if (connectedEdges.length > MAX_PARALLEL_LIMIT - 1) { const { setShowTips } = workflowStore.getState() @@ -302,14 +272,10 @@ export const useWorkflow = () => { } return true - }, [store, workflowStore, t]) + }, [collaborativeWorkflow, workflowStore, t]) const getRootNodesById = useCallback((nodeId: string) => { - const { - getNodes, - edges, - } = store.getState() - const nodes = getNodes() + const { nodes, edges } = collaborativeWorkflow.getState() const currentNode = nodes.find(node => node.id === nodeId) const rootNodes: Node[] = [] @@ -349,7 +315,7 @@ export const useWorkflow = () => { return uniqBy(rootNodes, 'id') return [] - }, [store]) + }, [collaborativeWorkflow]) const getStartNodes = useCallback((nodes: Node[], currentNode?: Node) => { const { id, parentId } = currentNode || {} @@ -402,11 +368,7 @@ export const useWorkflow = () => { }, [t, workflowStore, getStartNodes]) const isValidConnection = useCallback(({ source, sourceHandle, target }: Connection) => { - const { - edges, - getNodes, - } = store.getState() - const nodes = getNodes() + const { nodes, edges } = collaborativeWorkflow.getState() const sourceNode: Node = nodes.find(node => node.id === source)! const targetNode: Node = nodes.find(node => node.id === target)! @@ -445,7 +407,7 @@ export const useWorkflow = () => { } return !hasCycle(targetNode) - }, [store, checkParallelLimit, getAvailableBlocks]) + }, [collaborativeWorkflow, checkParallelLimit, getAvailableBlocks]) return { getNodeById, @@ -550,13 +512,10 @@ export const useNodesReadOnly = () => { } export const useIsNodeInIteration = (iterationId: string) => { - const store = useStoreApi() + const collaborativeWorkflow = useCollaborativeWorkflow() const isNodeInIteration = useCallback((nodeId: string) => { - const { - getNodes, - } = store.getState() - const nodes = getNodes() + const { nodes } = collaborativeWorkflow.getState() const node = nodes.find(node => node.id === nodeId) if (!node) @@ -566,20 +525,17 @@ export const useIsNodeInIteration = (iterationId: string) => { return true return false - }, [iterationId, store]) + }, [iterationId, collaborativeWorkflow]) return { isNodeInIteration, } } export const useIsNodeInLoop = (loopId: string) => { - const store = useStoreApi() + const collaborativeWorkflow = useCollaborativeWorkflow() const isNodeInLoop = useCallback((nodeId: string) => { - const { - getNodes, - } = store.getState() - const nodes = getNodes() + const { nodes } = collaborativeWorkflow.getState() const node = nodes.find(node => node.id === nodeId) if (!node) @@ -589,7 +545,7 @@ export const useIsNodeInLoop = (loopId: string) => { return true return false - }, [loopId, store]) + }, [loopId, collaborativeWorkflow]) return { isNodeInLoop, } diff --git a/web/app/components/workflow/panel/chat-variable-panel/index.tsx b/web/app/components/workflow/panel/chat-variable-panel/index.tsx index f375c709f8..5b681311fa 100644 --- a/web/app/components/workflow/panel/chat-variable-panel/index.tsx +++ b/web/app/components/workflow/panel/chat-variable-panel/index.tsx @@ -3,9 +3,6 @@ import { useCallback, useState, } from 'react' -import { - useStoreApi, -} from 'reactflow' import { RiBookOpenLine, RiCloseLine } from '@remixicon/react' import { useTranslation } from 'react-i18next' import { useStore } from '@/app/components/workflow/store' @@ -25,15 +22,15 @@ import { useDocLink } from '@/context/i18n' import cn from '@/utils/classnames' import useInspectVarsCrud from '../../hooks/use-inspect-vars-crud' import { updateConversationVariables } from '@/service/workflow' +import { useCollaborativeWorkflow } from '@/app/components/workflow/hooks/use-collaborative-workflow' const ChatVariablePanel = () => { const { t } = useTranslation() const docLink = useDocLink() - const store = useStoreApi() const setShowChatVariablePanel = useStore(s => s.setShowChatVariablePanel) const varList = useStore(s => s.conversationVariables) as ConversationVariable[] const updateChatVarList = useStore(s => s.setConversationVariables) - const appId = useStore(s => s.appId) + const appId = useStore(s => s.appId) as string const { invalidateConversationVarValues, } = useInspectVarsCrud() @@ -44,27 +41,27 @@ const ChatVariablePanel = () => { const [showRemoveVarConfirm, setShowRemoveConfirm] = useState(false) const [cacheForDelete, setCacheForDelete] = useState() + const collaborativeWorkflow = useCollaborativeWorkflow() const getEffectedNodes = useCallback((chatVar: ConversationVariable) => { - const { getNodes } = store.getState() - const allNodes = getNodes() + const { nodes: allNodes } = collaborativeWorkflow.getState() return findUsedVarNodes( ['conversation', chatVar.name], allNodes, ) - }, [store]) + }, [collaborativeWorkflow]) const removeUsedVarInNodes = useCallback((chatVar: ConversationVariable) => { - const { getNodes, setNodes } = store.getState() + const { nodes, setNodes } = collaborativeWorkflow.getState() const effectedNodes = getEffectedNodes(chatVar) - const newNodes = getNodes().map((node) => { + const newNodes = nodes.map((node) => { if (effectedNodes.find(n => n.id === node.id)) return updateNodeVars(node, ['conversation', chatVar.name], []) return node }) setNodes(newNodes) - }, [getEffectedNodes, store]) + }, [getEffectedNodes, collaborativeWorkflow]) const handleEdit = (chatVar: ConversationVariable) => { setCurrentVar(chatVar) @@ -151,9 +148,9 @@ const ChatVariablePanel = () => { // side effects of rename conversation variable if (currentVar.name !== chatVar.name) { - const { getNodes, setNodes } = store.getState() + const { nodes, setNodes } = collaborativeWorkflow.getState() const effectedNodes = getEffectedNodes(currentVar) - const newNodes = getNodes().map((node) => { + const newNodes = nodes.map((node) => { if (effectedNodes.find(n => n.id === node.id)) return updateNodeVars(node, ['conversation', currentVar.name], ['conversation', chatVar.name]) @@ -183,7 +180,7 @@ const ChatVariablePanel = () => { // Revert local state on error updateChatVarList(varList) } - }, [currentVar, getEffectedNodes, store, updateChatVarList, varList, appId, invalidateConversationVarValues]) + }, [currentVar, getEffectedNodes, collaborativeWorkflow, updateChatVarList, varList, appId, invalidateConversationVarValues]) return (
{ const { t } = useTranslation() - const store = useStoreApi() + const collaborativeWorkflow = useCollaborativeWorkflow() const setShowEnvPanel = useStore(s => s.setShowEnvPanel) const envList = useStore(s => s.environmentVariables) as EnvironmentVariable[] const envSecrets = useStore(s => s.envSecrets) const updateEnvList = useStore(s => s.setEnvironmentVariables) const setEnvSecrets = useStore(s => s.setEnvSecrets) - const appId = useWorkflowStore(s => s.appId) + const appId = useWorkflowStore(s => s.appId) as string const [showVariableModal, setShowVariableModal] = useState(false) const [currentVar, setCurrentVar] = useState() @@ -42,25 +40,24 @@ const EnvPanel = () => { } const getEffectedNodes = useCallback((env: EnvironmentVariable) => { - const { getNodes } = store.getState() - const allNodes = getNodes() + const { nodes: allNodes } = collaborativeWorkflow.getState() return findUsedVarNodes( ['env', env.name], allNodes, ) - }, [store]) + }, [collaborativeWorkflow]) const removeUsedVarInNodes = useCallback((env: EnvironmentVariable) => { - const { getNodes, setNodes } = store.getState() + const { nodes, setNodes } = collaborativeWorkflow.getState() const effectedNodes = getEffectedNodes(env) - const newNodes = getNodes().map((node) => { + const newNodes = nodes.map((node) => { if (effectedNodes.find(n => n.id === node.id)) return updateNodeVars(node, ['env', env.name], []) return node }) setNodes(newNodes) - }, [getEffectedNodes, store]) + }, [getEffectedNodes, collaborativeWorkflow]) const handleEdit = (env: EnvironmentVariable) => { setCurrentVar(env) @@ -185,9 +182,9 @@ const EnvPanel = () => { // side effects of rename env if (currentVar.name !== env.name) { - const { getNodes, setNodes } = store.getState() + const { nodes, setNodes } = collaborativeWorkflow.getState() const effectedNodes = getEffectedNodes(currentVar) - const newNodes = getNodes().map((node) => { + const newNodes = nodes.map((node) => { if (effectedNodes.find(n => n.id === node.id)) return updateNodeVars(node, ['env', currentVar.name], ['env', env.name]) @@ -218,7 +215,7 @@ const EnvPanel = () => { // Revert local state on error updateEnvList(envList) } - }, [currentVar, envList, envSecrets, getEffectedNodes, setEnvSecrets, store, updateEnvList, appId]) + }, [currentVar, envList, envSecrets, getEffectedNodes, setEnvSecrets, collaborativeWorkflow, updateEnvList, appId]) return (
{ const selectionMenu = useStore(s => s.selectionMenu) // Access React Flow methods - const store = useStoreApi() const workflowStore = useWorkflowStore() + const collaborativeWorkflow = useCollaborativeWorkflow() // Get selected nodes for alignment logic const selectedNodes = useReactFlowStore(state => @@ -256,7 +257,7 @@ const SelectionContextmenu = () => { workflowStore.setState({ nodeAnimation: false }) // Get all current nodes - const nodes = store.getState().getNodes() + const { nodes, setNodes } = collaborativeWorkflow.getState() // Get all selected nodes const selectedNodeIds = selectedNodes.map(node => node.id) @@ -312,7 +313,7 @@ const SelectionContextmenu = () => { const distributeNodes = handleDistributeNodes(nodesToAlign, nodes, alignType) if (distributeNodes) { // Apply node distribution updates - store.getState().setNodes(distributeNodes) + setNodes(distributeNodes) handleSelectionContextmenuCancel() // Clear guide lines @@ -347,7 +348,7 @@ const SelectionContextmenu = () => { // Apply node position updates - consistent with handleNodeDrag and handleNodeDragStop try { // Directly use setNodes to update nodes - consistent with handleNodeDrag - store.getState().setNodes(newNodes) + setNodes(newNodes) // Close popup handleSelectionContextmenuCancel() @@ -366,7 +367,7 @@ const SelectionContextmenu = () => { catch (err) { console.error('Failed to update nodes:', err) } - }, [store, workflowStore, selectedNodes, getNodesReadOnly, handleSyncWorkflowDraft, saveStateToHistory, handleSelectionContextmenuCancel, handleAlignNode, handleDistributeNodes]) + }, [collaborativeWorkflow, workflowStore, selectedNodes, getNodesReadOnly, handleSyncWorkflowDraft, saveStateToHistory, handleSelectionContextmenuCancel, handleAlignNode, handleDistributeNodes]) if (!selectionMenu) return null