diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/workflow/page.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/workflow/page.tsx index 467d5be3c0..2b9573e1dd 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/workflow/page.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/workflow/page.tsx @@ -2,52 +2,13 @@ import { memo } from 'react' import Workflow from '@/app/components/workflow' - -// export function createNodesAndEdges(xNodes = 10, yNodes = 10) { -// const nodes = [] -// const edges = [] -// let nodeId = 1 -// let recentNodeId = null - -// for (let y = 0; y < yNodes; y++) { -// for (let x = 0; x < xNodes; x++) { -// const position = { x: x * 200, y: y * 50 } -// const node = { -// id: `stress-${nodeId.toString()}`, -// type: 'custom', -// data: { type: 'start', title: '开始', variables: [] }, -// position, -// } -// nodes.push(node) - -// if (recentNodeId && nodeId <= xNodes * yNodes) { -// edges.push({ -// id: `${x}-${y}`, -// type: 'custom', -// source: `stress-${recentNodeId.toString()}`, -// target: `stress-${nodeId.toString()}`, -// }) -// } - -// recentNodeId = nodeId -// nodeId++ -// } -// } - -// return { nodes, edges } -// } +import { useStore } from '@/app/components/app/store' const Page = () => { - // const { - // nodes, - // edges, - // } = createNodesAndEdges() + const appDetail = useStore(s => s.appDetail)! return ( -
- +
+
) } diff --git a/web/app/components/workflow/context.tsx b/web/app/components/workflow/context.tsx new file mode 100644 index 0000000000..77c8291389 --- /dev/null +++ b/web/app/components/workflow/context.tsx @@ -0,0 +1,24 @@ +import { + createContext, + useRef, +} from 'react' +import { createWorkflowStore } from './store' + +type WorkflowStore = ReturnType +export const WorkflowContext = createContext(null) + +type WorkflowProviderProps = { + children: React.ReactNode +} +export const WorkflowContextProvider = ({ children }: WorkflowProviderProps) => { + const storeRef = useRef() + + if (!storeRef.current) + storeRef.current = createWorkflowStore() + + return ( + + {children} + + ) +} diff --git a/web/app/components/workflow/header/index.tsx b/web/app/components/workflow/header/index.tsx index 9ad43692be..84abc4a6ad 100644 --- a/web/app/components/workflow/header/index.tsx +++ b/web/app/components/workflow/header/index.tsx @@ -4,7 +4,10 @@ import { useCallback, } from 'react' import { useTranslation } from 'react-i18next' -import { useStore } from '../store' +import { + useStore, + useWorkflowStore, +} from '../store' import { useIsChatMode, useWorkflowRun, @@ -20,6 +23,7 @@ import { useStore as useAppStore } from '@/app/components/app/store' const Header: FC = () => { const { t } = useTranslation() + const workflowStore = useWorkflowStore() const appDetail = useAppStore(s => s.appDetail) const appSidebarExpand = useAppStore(s => s.appSidebarExpand) const isChatMode = useIsChatMode() @@ -30,8 +34,8 @@ const Header: FC = () => { if (runningStatus) return - useStore.setState({ showFeaturesPanel: true }) - }, [runningStatus]) + workflowStore.setState({ showFeaturesPanel: true }) + }, [runningStatus, workflowStore]) const handleGoBackToEdit = useCallback(() => { handleRunSetting(true) diff --git a/web/app/components/workflow/header/run-and-history.tsx b/web/app/components/workflow/header/run-and-history.tsx index 299d20cc87..73036e5fe1 100644 --- a/web/app/components/workflow/header/run-and-history.tsx +++ b/web/app/components/workflow/header/run-and-history.tsx @@ -1,7 +1,10 @@ import type { FC } from 'react' import { memo } from 'react' import { useTranslation } from 'react-i18next' -import { useStore } from '../store' +import { + useStore, + useWorkflowStore, +} from '../store' import { useIsChatMode, useWorkflowRun, @@ -14,12 +17,13 @@ import { Loading02 } from '@/app/components/base/icons/src/vender/line/general' const RunMode = memo(() => { const { t } = useTranslation() + const workflowStore = useWorkflowStore() const runningStatus = useStore(s => s.runningStatus) const showInputsPanel = useStore(s => s.showInputsPanel) const isRunning = runningStatus === WorkflowRunningStatus.Running const handleClick = () => { - useStore.setState({ showInputsPanel: true }) + workflowStore.setState({ showInputsPanel: true }) } return ( @@ -91,6 +95,7 @@ PreviewMode.displayName = 'PreviewMode' const RunAndHistory: FC = () => { const { t } = useTranslation() + const workflowStore = useWorkflowStore() const isChatMode = useIsChatMode() const showRunHistory = useStore(state => state.showRunHistory) @@ -111,7 +116,7 @@ const RunAndHistory: FC = () => { flex items-center justify-center w-7 h-7 rounded-md hover:bg-black/5 cursor-pointer ${showRunHistory && 'bg-primary-50'} `} - onClick={() => useStore.setState({ showRunHistory: true })} + onClick={() => workflowStore.setState({ showRunHistory: true })} >
diff --git a/web/app/components/workflow/hooks/use-edges-interactions.ts b/web/app/components/workflow/hooks/use-edges-interactions.ts index b4f3afd8ad..01b96a3c42 100644 --- a/web/app/components/workflow/hooks/use-edges-interactions.ts +++ b/web/app/components/workflow/hooks/use-edges-interactions.ts @@ -8,7 +8,7 @@ import { getConnectedEdges, useStoreApi, } from 'reactflow' -import { useStore } from '../store' +import { useWorkflowStore } from '../store' import type { Edge, Node, @@ -18,10 +18,11 @@ import { useNodesSyncDraft } from './use-nodes-sync-draft' export const useEdgesInteractions = () => { const store = useStoreApi() + const workflowStore = useWorkflowStore() const { handleSyncWorkflowDraft } = useNodesSyncDraft() const handleEdgeEnter = useCallback((_, edge) => { - const { runningStatus } = useStore.getState() + const { runningStatus } = workflowStore.getState() if (runningStatus) return @@ -36,10 +37,10 @@ export const useEdgesInteractions = () => { currentEdge.data = { ...currentEdge.data, _hovering: true } }) setEdges(newEdges) - }, [store]) + }, [store, workflowStore]) const handleEdgeLeave = useCallback((_, edge) => { - const { runningStatus } = useStore.getState() + const { runningStatus } = workflowStore.getState() if (runningStatus) return @@ -54,10 +55,10 @@ export const useEdgesInteractions = () => { currentEdge.data = { ...currentEdge.data, _hovering: false } }) setEdges(newEdges) - }, [store]) + }, [store, workflowStore]) const handleEdgeDeleteByDeleteBranch = useCallback((nodeId: string, branchId: string) => { - const { runningStatus } = useStore.getState() + const { runningStatus } = workflowStore.getState() if (runningStatus) return @@ -90,10 +91,10 @@ export const useEdgesInteractions = () => { }) setEdges(newEdges) handleSyncWorkflowDraft() - }, [store, handleSyncWorkflowDraft]) + }, [store, handleSyncWorkflowDraft, workflowStore]) const handleEdgeDelete = useCallback(() => { - const { runningStatus } = useStore.getState() + const { runningStatus } = workflowStore.getState() if (runningStatus) return @@ -125,10 +126,10 @@ export const useEdgesInteractions = () => { }) setEdges(newEdges) handleSyncWorkflowDraft() - }, [store, handleSyncWorkflowDraft]) + }, [store, workflowStore, handleSyncWorkflowDraft]) const handleEdgesChange = useCallback((changes) => { - const { runningStatus } = useStore.getState() + const { runningStatus } = workflowStore.getState() if (runningStatus) return @@ -145,7 +146,7 @@ export const useEdgesInteractions = () => { }) }) setEdges(newEdges) - }, [store]) + }, [store, workflowStore]) const handleVariableAssignerEdgesChange = useCallback((nodeId: string, variables: any) => { const { diff --git a/web/app/components/workflow/hooks/use-node-data-update.ts b/web/app/components/workflow/hooks/use-node-data-update.ts index 3f8175683c..d46fbb4c47 100644 --- a/web/app/components/workflow/hooks/use-node-data-update.ts +++ b/web/app/components/workflow/hooks/use-node-data-update.ts @@ -1,7 +1,7 @@ import { useCallback } from 'react' import produce from 'immer' import { useStoreApi } from 'reactflow' -import { useStore } from '../store' +import { useWorkflowStore } from '../store' import { useNodesSyncDraft } from './use-nodes-sync-draft' type NodeDataUpdatePayload = { @@ -11,6 +11,7 @@ type NodeDataUpdatePayload = { export const useNodeDataUpdate = () => { const store = useStoreApi() + const workflowStore = useWorkflowStore() const { handleSyncWorkflowDraft } = useNodesSyncDraft() const handleNodeDataUpdate = useCallback(({ id, data }: NodeDataUpdatePayload) => { @@ -27,14 +28,14 @@ export const useNodeDataUpdate = () => { }, [store]) const handleNodeDataUpdateWithSyncDraft = useCallback((payload: NodeDataUpdatePayload) => { - const { runningStatus } = useStore.getState() + const { runningStatus } = workflowStore.getState() if (runningStatus) return handleNodeDataUpdate(payload) handleSyncWorkflowDraft(true) - }, [handleSyncWorkflowDraft, handleNodeDataUpdate]) + }, [handleSyncWorkflowDraft, handleNodeDataUpdate, workflowStore]) return { handleNodeDataUpdate, diff --git a/web/app/components/workflow/hooks/use-nodes-interactions.ts b/web/app/components/workflow/hooks/use-nodes-interactions.ts index d9bfff1110..a5c7f00da7 100644 --- a/web/app/components/workflow/hooks/use-nodes-interactions.ts +++ b/web/app/components/workflow/hooks/use-nodes-interactions.ts @@ -18,7 +18,7 @@ import type { OnNodeAdd, } from '../types' import { BlockEnum } from '../types' -import { useStore } from '../store' +import { useWorkflowStore } from '../store' import { NODE_WIDTH_X_OFFSET, Y_OFFSET, @@ -36,6 +36,7 @@ import { useWorkflow } from './use-workflow' export const useNodesInteractions = () => { const store = useStoreApi() + const workflowStore = useWorkflowStore() const nodesInitialData = useNodesInitialData() const nodesExtraData = useNodesExtraData() const { handleSyncWorkflowDraft } = useNodesSyncDraft() @@ -46,16 +47,16 @@ export const useNodesInteractions = () => { const handleNodeDragStart = useCallback((_, node) => { const { runningStatus, - } = useStore.getState() + } = workflowStore.getState() if (runningStatus) return dragNodeStartPosition.current = { x: node.position.x, y: node.position.y } - }, []) + }, [workflowStore]) const handleNodeDrag = useCallback((e, node: Node) => { - const { runningStatus } = useStore.getState() + const { runningStatus } = workflowStore.getState() if (runningStatus) return @@ -67,7 +68,7 @@ export const useNodesInteractions = () => { const { setHelpLineHorizontal, setHelpLineVertical, - } = useStore.getState() + } = workflowStore.getState() e.stopPropagation() const nodes = getNodes() @@ -157,14 +158,14 @@ export const useNodesInteractions = () => { }) setNodes(newNodes) - }, [store]) + }, [store, workflowStore]) const handleNodeDragStop = useCallback((_, node) => { const { runningStatus, setHelpLineHorizontal, setHelpLineVertical, - } = useStore.getState() + } = workflowStore.getState() if (runningStatus) return @@ -175,10 +176,10 @@ export const useNodesInteractions = () => { setHelpLineVertical() handleSyncWorkflowDraft() } - }, [handleSyncWorkflowDraft]) + }, [handleSyncWorkflowDraft, workflowStore]) const handleNodeEnter = useCallback((_, node) => { - const { runningStatus } = useStore.getState() + const { runningStatus } = workflowStore.getState() if (runningStatus) return @@ -215,10 +216,10 @@ export const useNodesInteractions = () => { }) }) setEdges(newEdges) - }, [store, nodesExtraData]) + }, [store, nodesExtraData, workflowStore]) const handleNodeLeave = useCallback(() => { - const { runningStatus } = useStore.getState() + const { runningStatus } = workflowStore.getState() if (runningStatus) return @@ -241,10 +242,10 @@ export const useNodesInteractions = () => { }) }) setEdges(newEdges) - }, [store]) + }, [store, workflowStore]) const handleNodeSelect = useCallback((nodeId: string, cancelSelection?: boolean) => { - const { runningStatus } = useStore.getState() + const { runningStatus } = workflowStore.getState() if (runningStatus) return @@ -270,18 +271,18 @@ export const useNodesInteractions = () => { }) setNodes(newNodes) handleSyncWorkflowDraft() - }, [store, handleSyncWorkflowDraft]) + }, [store, handleSyncWorkflowDraft, workflowStore]) const handleNodeClick = useCallback((_, node) => { const { runningStatus, - } = useStore.getState() + } = workflowStore.getState() if (runningStatus) return handleNodeSelect(node.id) - }, [handleNodeSelect]) + }, [handleNodeSelect, workflowStore]) const handleNodeConnect = useCallback(({ source, @@ -289,7 +290,7 @@ export const useNodesInteractions = () => { target, targetHandle, }) => { - const { runningStatus } = useStore.getState() + const { runningStatus } = workflowStore.getState() if (runningStatus) return @@ -338,7 +339,7 @@ export const useNodesInteractions = () => { }) setEdges(newEdges) handleSyncWorkflowDraft() - }, [store, handleSyncWorkflowDraft]) + }, [store, handleSyncWorkflowDraft, workflowStore]) const handleNodeConnectStart = useCallback((_, { nodeId, handleType }) => { if (nodeId && handleType) { @@ -354,7 +355,7 @@ export const useNodesInteractions = () => { }, []) const handleNodeDelete = useCallback((nodeId: string) => { - const { runningStatus } = useStore.getState() + const { runningStatus } = workflowStore.getState() if (runningStatus) return @@ -387,7 +388,7 @@ export const useNodesInteractions = () => { }) setEdges(newEdges) handleSyncWorkflowDraft() - }, [store, handleSyncWorkflowDraft]) + }, [store, handleSyncWorkflowDraft, workflowStore]) const handleNodeAdd = useCallback(( { @@ -403,7 +404,7 @@ export const useNodesInteractions = () => { nextNodeTargetHandle, }, ) => { - const { runningStatus } = useStore.getState() + const { runningStatus } = workflowStore.getState() if (runningStatus) return @@ -565,7 +566,7 @@ export const useNodesInteractions = () => { setEdges(newEdges) } handleSyncWorkflowDraft() - }, [store, nodesInitialData, handleSyncWorkflowDraft, getAfterNodesInSameBranch]) + }, [store, nodesInitialData, handleSyncWorkflowDraft, getAfterNodesInSameBranch, workflowStore]) const handleNodeChange = useCallback(( currentNodeId: string, @@ -573,7 +574,7 @@ export const useNodesInteractions = () => { sourceHandle: string, toolDefaultValue?: ToolDefaultValue, ) => { - const { runningStatus } = useStore.getState() + const { runningStatus } = workflowStore.getState() if (runningStatus) return @@ -631,7 +632,7 @@ export const useNodesInteractions = () => { }) setEdges(newEdges) handleSyncWorkflowDraft() - }, [store, nodesInitialData, handleSyncWorkflowDraft]) + }, [store, nodesInitialData, handleSyncWorkflowDraft, workflowStore]) return { handleNodeDragStart, diff --git a/web/app/components/workflow/hooks/use-nodes-sync-draft.ts b/web/app/components/workflow/hooks/use-nodes-sync-draft.ts index c96a451182..a695a4598f 100644 --- a/web/app/components/workflow/hooks/use-nodes-sync-draft.ts +++ b/web/app/components/workflow/hooks/use-nodes-sync-draft.ts @@ -5,13 +5,14 @@ import { useReactFlow, useStoreApi, } from 'reactflow' -import { useStore } from '../store' +import { useWorkflowStore } from '../store' import { syncWorkflowDraft } from '@/service/workflow' import { useFeaturesStore } from '@/app/components/base/features/hooks' import { useStore as useAppStore } from '@/app/components/app/store' export const useNodesSyncDraft = () => { const store = useStoreApi() + const workflowStore = useWorkflowStore() const reactFlow = useReactFlow() const featuresStore = useFeaturesStore() @@ -57,10 +58,10 @@ export const useNodesSyncDraft = () => { }, }, }).then((res) => { - useStore.setState({ draftUpdatedAt: res.updated_at }) + workflowStore.setState({ draftUpdatedAt: res.updated_at }) }) } - }, [store, reactFlow, featuresStore]) + }, [store, reactFlow, featuresStore, workflowStore]) const { run: debouncedSyncWorkflowDraft } = useDebounceFn(shouldDebouncedSyncWorkflowDraft, { wait: 2000, @@ -68,7 +69,7 @@ export const useNodesSyncDraft = () => { }) const handleSyncWorkflowDraft = useCallback((shouldDelay?: boolean) => { - const { runningStatus } = useStore.getState() + const { runningStatus } = workflowStore.getState() if (runningStatus) return @@ -77,7 +78,7 @@ export const useNodesSyncDraft = () => { debouncedSyncWorkflowDraft() else shouldDebouncedSyncWorkflowDraft() - }, [debouncedSyncWorkflowDraft, shouldDebouncedSyncWorkflowDraft]) + }, [debouncedSyncWorkflowDraft, shouldDebouncedSyncWorkflowDraft, workflowStore]) return { handleSyncWorkflowDraft, diff --git a/web/app/components/workflow/hooks/use-workflow-run.ts b/web/app/components/workflow/hooks/use-workflow-run.ts index 76c568c067..e89fd7d61f 100644 --- a/web/app/components/workflow/hooks/use-workflow-run.ts +++ b/web/app/components/workflow/hooks/use-workflow-run.ts @@ -7,7 +7,7 @@ import { useStoreApi, } from 'reactflow' import produce from 'immer' -import { useStore } from '../store' +import { useWorkflowStore } from '../store' import { NodeRunningStatus, WorkflowRunningStatus, @@ -19,6 +19,7 @@ import { ssePost } from '@/service/base' export const useWorkflowRun = () => { const store = useStoreApi() + const workflowStore = useWorkflowStore() const reactflow = useReactFlow() const workflowContainerRef = useRef(null) @@ -30,14 +31,14 @@ export const useWorkflowRun = () => { } = reactflow const { setBackupDraft, - } = useStore.getState() + } = workflowStore.getState() setBackupDraft({ nodes: getNodes(), edges: getEdges(), viewport: getViewport(), }) - }, [reactflow]) + }, [reactflow, workflowStore]) const handleLoadBackupDraft = useCallback(() => { const { @@ -45,7 +46,7 @@ export const useWorkflowRun = () => { setEdges, } = store.getState() const { setViewport } = reactflow - const { backupDraft } = useStore.getState() + const { backupDraft } = workflowStore.getState() if (backupDraft) { const { @@ -57,10 +58,10 @@ export const useWorkflowRun = () => { setEdges(edges) setViewport(viewport) } - }, [store, reactflow]) + }, [store, reactflow, workflowStore]) const handleRunSetting = useCallback((shouldClear?: boolean) => { - useStore.setState({ runningStatus: shouldClear ? undefined : WorkflowRunningStatus.Waiting }) + workflowStore.setState({ runningStatus: shouldClear ? undefined : WorkflowRunningStatus.Waiting }) const { setNodes, getNodes, @@ -86,7 +87,7 @@ export const useWorkflowRun = () => { }) setEdges(newEdges) } - }, [store, handleLoadBackupDraft, handleBackupDraft]) + }, [store, handleLoadBackupDraft, handleBackupDraft, workflowStore]) const handleRun = useCallback((params: any, callback?: IOtherOptions) => { const { @@ -117,10 +118,10 @@ export const useWorkflowRun = () => { }, { onWorkflowStarted: ({ task_id, workflow_run_id, data }) => { - useStore.setState({ runningStatus: WorkflowRunningStatus.Running }) - useStore.setState({ taskId: task_id }) - useStore.setState({ currentSequenceNumber: data.sequence_number }) - useStore.setState({ workflowRunId: workflow_run_id }) + workflowStore.setState({ runningStatus: WorkflowRunningStatus.Running }) + workflowStore.setState({ taskId: task_id }) + workflowStore.setState({ currentSequenceNumber: data.sequence_number }) + workflowStore.setState({ workflowRunId: workflow_run_id }) const newNodes = produce(getNodes(), (draft) => { draft.forEach((node) => { node.data._runningStatus = NodeRunningStatus.Waiting @@ -129,7 +130,7 @@ export const useWorkflowRun = () => { setNodes(newNodes) }, onWorkflowFinished: ({ data }) => { - useStore.setState({ runningStatus: data.status as WorkflowRunningStatus }) + workflowStore.setState({ runningStatus: data.status as WorkflowRunningStatus }) }, onNodeStarted: ({ data }) => { const nodes = getNodes() @@ -171,7 +172,7 @@ export const useWorkflowRun = () => { ...callback, }, ) - }, [store, reactflow]) + }, [store, reactflow, workflowStore]) return { handleBackupDraft, diff --git a/web/app/components/workflow/hooks/use-workflow.ts b/web/app/components/workflow/hooks/use-workflow.ts index 4f1e855d30..3a81343c4d 100644 --- a/web/app/components/workflow/hooks/use-workflow.ts +++ b/web/app/components/workflow/hooks/use-workflow.ts @@ -17,7 +17,7 @@ import { } from '../utils' import type { Node } from '../types' import { BlockEnum } from '../types' -import { useStore } from '../store' +import { useWorkflowStore } from '../store' import { START_INITIAL_POSITION, SUPPORT_OUTPUT_VARS_NODE, @@ -203,6 +203,7 @@ export const useWorkflow = () => { } export const useWorkflowInit = () => { + const workflowStore = useWorkflowStore() const nodesInitialData = useNodesInitialData() const appDetail = useAppStore(state => state.appDetail)! const { data, error, mutate } = useSWR(`/apps/${appDetail.id}/workflows/draft`, fetchWorkflowDraft) @@ -212,14 +213,14 @@ export const useWorkflowInit = () => { const toolsets = await fetchCollectionList() const nodesDefaultConfigsData = await fetchNodesDefaultConfigs(`/apps/${appDetail?.id}/workflows/default-workflow-block-configs`) - useStore.setState({ + workflowStore.setState({ toolsets, toolsMap: toolsets.reduce((acc, toolset) => { acc[toolset.id] = [] return acc }, {} as ToolsMap), }) - useStore.setState({ + workflowStore.setState({ nodesDefaultConfigs: nodesDefaultConfigsData.reduce((acc, block) => { if (!acc[block.type]) acc[block.type] = block.config @@ -238,13 +239,13 @@ export const useWorkflowInit = () => { useEffect(() => { if (data) - useStore.setState({ draftUpdatedAt: data.updated_at }) - }, [data]) + workflowStore.setState({ draftUpdatedAt: data.updated_at }) + }, [data, workflowStore]) if (error && error.json && !error.bodyUsed && appDetail) { error.json().then((err: any) => { if (err.code === 'draft_workflow_not_exist') { - useStore.setState({ notInitialWorkflow: true }) + workflowStore.setState({ notInitialWorkflow: true }) syncWorkflowDraft({ url: `/apps/${appDetail.id}/workflows/draft`, params: { @@ -261,7 +262,7 @@ export const useWorkflowInit = () => { features: {}, }, }).then((res) => { - useStore.setState({ draftUpdatedAt: res.updated_at }) + workflowStore.setState({ draftUpdatedAt: res.updated_at }) mutate() }) } diff --git a/web/app/components/workflow/index.tsx b/web/app/components/workflow/index.tsx index 6c827fd407..26da3b90ae 100644 --- a/web/app/components/workflow/index.tsx +++ b/web/app/components/workflow/index.tsx @@ -17,6 +17,7 @@ import type { Edge, Node, } from './types' +import { WorkflowContextProvider } from './context' import { useEdgesInteractions, useNodesInteractions, @@ -151,30 +152,21 @@ const Workflow: FC = memo(({ Workflow.displayName = 'Workflow' -const WorkflowWrap: FC = ({ - nodes, - edges, -}) => { +const WorkflowWrap = memo(() => { const data = useWorkflowInit() const nodesData = useMemo(() => { - if (nodes) - return nodes - if (data) return initialNodes(data.graph.nodes, data.graph.edges) return [] - }, [data, nodes]) + }, [data]) const edgesData = useMemo(() => { - if (edges) - return edges - if (data) return initialEdges(data.graph.edges) return [] - }, [data, edges]) + }, [data]) if (!data) { return ( @@ -209,6 +201,15 @@ const WorkflowWrap: FC = ({ ) +}) +WorkflowWrap.displayName = 'WorkflowWrap' + +const WorkflowContainer = () => { + return ( + + + + ) } -export default memo(WorkflowWrap) +export default memo(WorkflowContainer) diff --git a/web/app/components/workflow/panel/debug-and-preview/chat-wrapper.tsx b/web/app/components/workflow/panel/debug-and-preview/chat-wrapper.tsx index 0fb1ea973a..e5d6dc82ba 100644 --- a/web/app/components/workflow/panel/debug-and-preview/chat-wrapper.tsx +++ b/web/app/components/workflow/panel/debug-and-preview/chat-wrapper.tsx @@ -3,7 +3,7 @@ import { useCallback, useMemo, } from 'react' -import { useStore } from '../../store' +import { useWorkflowStore } from '../../store' import UserInput from './user-input' import { useChat } from './hooks' import Chat from '@/app/components/base/chat/chat' @@ -19,6 +19,7 @@ const ChatWrapper = () => { suggestedQuestions, handleSend, } = useChat() + const workflowStore = useWorkflowStore() const featuresStore = useFeaturesStore() const features = featuresStore!.getState().features @@ -38,10 +39,10 @@ const ChatWrapper = () => { handleSend({ query, files, - inputs: useStore.getState().inputs, + inputs: workflowStore.getState().inputs, conversationId, }) - }, [conversationId, handleSend]) + }, [conversationId, handleSend, workflowStore]) return ( { const { t } = useTranslation() + const workflowStore = useWorkflowStore() const [expanded, setExpanded] = useState(true) const inputs = useStore(s => s.inputs) const nodes = useNodes() @@ -19,7 +23,7 @@ const UserInput = () => { const variables = startNode?.data.variables || [] const handleValueChange = (variable: string, v: string) => { - useStore.getState().setInputs({ + workflowStore.getState().setInputs({ ...inputs, [variable]: v, }) diff --git a/web/app/components/workflow/panel/inputs-panel.tsx b/web/app/components/workflow/panel/inputs-panel.tsx index 91a717a8e8..430a072891 100644 --- a/web/app/components/workflow/panel/inputs-panel.tsx +++ b/web/app/components/workflow/panel/inputs-panel.tsx @@ -6,13 +6,17 @@ import { useTranslation } from 'react-i18next' import { useNodes } from 'reactflow' import FormItem from '../nodes/_base/components/before-run-form/form-item' import { BlockEnum } from '../types' -import { useStore } from '../store' +import { + useStore, + useWorkflowStore, +} from '../store' import { useWorkflowRun } from '../hooks' import type { StartNodeType } from '../nodes/start/types' import Button from '@/app/components/base/button' const InputsPanel = () => { const { t } = useTranslation() + const workflowStore = useWorkflowStore() const nodes = useNodes() const inputs = useStore(s => s.inputs) const { @@ -23,15 +27,15 @@ const InputsPanel = () => { const variables = startNode?.data.variables || [] const handleValueChange = (variable: string, v: string) => { - useStore.getState().setInputs({ + workflowStore.getState().setInputs({ ...inputs, [variable]: v, }) } const handleCancel = useCallback(() => { - useStore.setState({ showInputsPanel: false }) - }, []) + workflowStore.setState({ showInputsPanel: false }) + }, [workflowStore]) const doRun = () => { handleCancel() diff --git a/web/app/components/workflow/panel/run-history.tsx b/web/app/components/workflow/panel/run-history.tsx index 263d2354e7..1ea4a7fd8a 100644 --- a/web/app/components/workflow/panel/run-history.tsx +++ b/web/app/components/workflow/panel/run-history.tsx @@ -6,13 +6,17 @@ import useSWR from 'swr' import { WorkflowRunningStatus } from '../types' import { CheckCircle, XClose } from '@/app/components/base/icons/src/vender/line/general' import { AlertCircle } from '@/app/components/base/icons/src/vender/line/alertsAndFeedback' -import { useStore as useRunHistoryStore } from '@/app/components/workflow/store' +import { + useStore as useRunHistoryStore, + useWorkflowStore, +} from '@/app/components/workflow/store' import { useStore as useAppStore } from '@/app/components/app/store' import { fetchWorkflowRunHistory } from '@/service/workflow' import Loading from '@/app/components/base/loading' const RunHistory = () => { const { t } = useTranslation() + const workflowStore = useWorkflowStore() const appDetail = useAppStore(state => state.appDetail) const workflowRunId = useRunHistoryStore(state => state.workflowRunId) const { data, isLoading } = useSWR(appDetail ? `/apps/${appDetail.id}/workflow-runs` : null, fetchWorkflowRunHistory) @@ -26,7 +30,7 @@ const RunHistory = () => { {t('workflow.common.runHistory')}
useRunHistoryStore.setState({ showRunHistory: false })} + onClick={() => workflowStore.setState({ showRunHistory: false })} >
@@ -47,7 +51,7 @@ const RunHistory = () => { 'flex mb-0.5 px-2 py-[7px] rounded-lg hover:bg-primary-50 cursor-pointer', item.id === workflowRunId && 'bg-primary-50', )} - onClick={() => useRunHistoryStore.setState({ + onClick={() => workflowStore.setState({ currentSequenceNumber: item.sequence_number, workflowRunId: item.id, runningStatus: item.status as WorkflowRunningStatus, diff --git a/web/app/components/workflow/store.ts b/web/app/components/workflow/store.ts index 18fd1224b9..4b10dc372e 100644 --- a/web/app/components/workflow/store.ts +++ b/web/app/components/workflow/store.ts @@ -1,5 +1,9 @@ -import { create } from 'zustand' +import { + create, + useStore as useZustandStore, +} from 'zustand' import type { Viewport } from 'reactflow' +import { useContext } from 'react' import type { HelpLineHorizontalPosition, HelpLineVerticalPosition, @@ -15,6 +19,7 @@ import type { Node, WorkflowRunningStatus, } from './types' +import { WorkflowContext } from './context' type State = { mode: Mode @@ -62,41 +67,55 @@ type Action = { setNodesDefaultConfigs: (nodesDefaultConfigs: Record) => void } -export const useStore = create(set => ({ - mode: Mode.Editing, - taskId: '', - setTaskId: taskId => set(() => ({ taskId })), - currentSequenceNumber: 0, - setCurrentSequenceNumber: currentSequenceNumber => set(() => ({ currentSequenceNumber })), - workflowRunId: '', - setWorkflowRunId: workflowRunId => set(() => ({ workflowRunId })), - setMode: mode => set(() => ({ mode })), - showRunHistory: false, - setShowRunHistory: showRunHistory => set(() => ({ showRunHistory })), - showFeaturesPanel: false, - setShowFeaturesPanel: showFeaturesPanel => set(() => ({ showFeaturesPanel })), - helpLineHorizontal: undefined, - setHelpLineHorizontal: helpLineHorizontal => set(() => ({ helpLineHorizontal })), - helpLineVertical: undefined, - setHelpLineVertical: helpLineVertical => set(() => ({ helpLineVertical })), - toolsets: [], - setToolsets: toolsets => set(() => ({ toolsets })), - toolsMap: {}, - setToolsMap: toolsMap => set(() => ({ toolsMap })), - draftUpdatedAt: 0, - setDraftUpdatedAt: draftUpdatedAt => set(() => ({ draftUpdatedAt })), - publishedAt: 0, - setPublishedAt: publishedAt => set(() => ({ publishedAt })), - runningStatus: undefined, - setRunningStatus: runningStatus => set(() => ({ runningStatus })), - showInputsPanel: false, - setShowInputsPanel: showInputsPanel => set(() => ({ showInputsPanel })), - inputs: {}, - setInputs: inputs => set(() => ({ inputs })), - backupDraft: undefined, - setBackupDraft: backupDraft => set(() => ({ backupDraft })), - notInitialWorkflow: false, - setNotInitialWorkflow: notInitialWorkflow => set(() => ({ notInitialWorkflow })), - nodesDefaultConfigs: {}, - setNodesDefaultConfigs: nodesDefaultConfigs => set(() => ({ nodesDefaultConfigs })), -})) +export const createWorkflowStore = () => { + return create(set => ({ + mode: Mode.Editing, + taskId: '', + setTaskId: taskId => set(() => ({ taskId })), + currentSequenceNumber: 0, + setCurrentSequenceNumber: currentSequenceNumber => set(() => ({ currentSequenceNumber })), + workflowRunId: '', + setWorkflowRunId: workflowRunId => set(() => ({ workflowRunId })), + setMode: mode => set(() => ({ mode })), + showRunHistory: false, + setShowRunHistory: showRunHistory => set(() => ({ showRunHistory })), + showFeaturesPanel: false, + setShowFeaturesPanel: showFeaturesPanel => set(() => ({ showFeaturesPanel })), + helpLineHorizontal: undefined, + setHelpLineHorizontal: helpLineHorizontal => set(() => ({ helpLineHorizontal })), + helpLineVertical: undefined, + setHelpLineVertical: helpLineVertical => set(() => ({ helpLineVertical })), + toolsets: [], + setToolsets: toolsets => set(() => ({ toolsets })), + toolsMap: {}, + setToolsMap: toolsMap => set(() => ({ toolsMap })), + draftUpdatedAt: 0, + setDraftUpdatedAt: draftUpdatedAt => set(() => ({ draftUpdatedAt })), + publishedAt: 0, + setPublishedAt: publishedAt => set(() => ({ publishedAt })), + runningStatus: undefined, + setRunningStatus: runningStatus => set(() => ({ runningStatus })), + showInputsPanel: false, + setShowInputsPanel: showInputsPanel => set(() => ({ showInputsPanel })), + inputs: {}, + setInputs: inputs => set(() => ({ inputs })), + backupDraft: undefined, + setBackupDraft: backupDraft => set(() => ({ backupDraft })), + notInitialWorkflow: false, + setNotInitialWorkflow: notInitialWorkflow => set(() => ({ notInitialWorkflow })), + nodesDefaultConfigs: {}, + setNodesDefaultConfigs: nodesDefaultConfigs => set(() => ({ nodesDefaultConfigs })), + })) +} + +export function useStore(selector: (state: State & Action) => T): T { + const store = useContext(WorkflowContext) + if (!store) + throw new Error('Missing WorkflowContext.Provider in the tree') + + return useZustandStore(store, selector) +} + +export const useWorkflowStore = () => { + return useContext(WorkflowContext)! +}