| @@ -2,11 +2,11 @@ import { | |||
| useCallback, | |||
| useEffect, | |||
| useMemo, | |||
| useState, | |||
| } from 'react' | |||
| import dayjs from 'dayjs' | |||
| import { uniqBy } from 'lodash-es' | |||
| import { useContext } from 'use-context-selector' | |||
| import useSWR from 'swr' | |||
| import produce from 'immer' | |||
| import { | |||
| getIncomers, | |||
| @@ -52,6 +52,7 @@ import { | |||
| fetchWorkflowDraft, | |||
| syncWorkflowDraft, | |||
| } from '@/service/workflow' | |||
| import type { FetchWorkflowDraftResponse } from '@/types/workflow' | |||
| import { | |||
| fetchAllBuiltInTools, | |||
| fetchAllCustomTools, | |||
| @@ -403,9 +404,45 @@ export const useWorkflowInit = () => { | |||
| } = useWorkflowTemplate() | |||
| const { handleFetchAllTools } = useFetchToolsData() | |||
| const appDetail = useAppStore(state => state.appDetail)! | |||
| const { data, isLoading, error, mutate } = useSWR(`/apps/${appDetail.id}/workflows/draft`, fetchWorkflowDraft) | |||
| const [data, setData] = useState<FetchWorkflowDraftResponse>() | |||
| const [isLoading, setIsLoading] = useState(true) | |||
| workflowStore.setState({ appId: appDetail.id }) | |||
| const handleGetInitialWorkflowData = useCallback(async () => { | |||
| try { | |||
| const res = await fetchWorkflowDraft(`/apps/${appDetail.id}/workflows/draft`) | |||
| setData(res) | |||
| setIsLoading(false) | |||
| } | |||
| catch (error: any) { | |||
| if (error && error.json && !error.bodyUsed && appDetail) { | |||
| error.json().then((err: any) => { | |||
| if (err.code === 'draft_workflow_not_exist') { | |||
| workflowStore.setState({ notInitialWorkflow: true }) | |||
| syncWorkflowDraft({ | |||
| url: `/apps/${appDetail.id}/workflows/draft`, | |||
| params: { | |||
| graph: { | |||
| nodes: nodesTemplate, | |||
| edges: edgesTemplate, | |||
| }, | |||
| features: {}, | |||
| }, | |||
| }).then((res) => { | |||
| workflowStore.getState().setDraftUpdatedAt(res.updated_at) | |||
| handleGetInitialWorkflowData() | |||
| }) | |||
| } | |||
| }) | |||
| } | |||
| } | |||
| }, [appDetail, nodesTemplate, edgesTemplate, workflowStore]) | |||
| useEffect(() => { | |||
| handleGetInitialWorkflowData() | |||
| }, []) | |||
| const handleFetchPreloadData = useCallback(async () => { | |||
| try { | |||
| const nodesDefaultConfigsData = await fetchNodesDefaultConfigs(`/apps/${appDetail?.id}/workflows/default-workflow-block-configs`) | |||
| @@ -435,27 +472,6 @@ export const useWorkflowInit = () => { | |||
| workflowStore.getState().setDraftUpdatedAt(data.updated_at) | |||
| }, [data, workflowStore]) | |||
| if (error && error.json && !error.bodyUsed && appDetail) { | |||
| error.json().then((err: any) => { | |||
| if (err.code === 'draft_workflow_not_exist') { | |||
| workflowStore.setState({ notInitialWorkflow: true }) | |||
| syncWorkflowDraft({ | |||
| url: `/apps/${appDetail.id}/workflows/draft`, | |||
| params: { | |||
| graph: { | |||
| nodes: nodesTemplate, | |||
| edges: edgesTemplate, | |||
| }, | |||
| features: {}, | |||
| }, | |||
| }).then((res) => { | |||
| workflowStore.getState().setDraftUpdatedAt(res.updated_at) | |||
| mutate() | |||
| }) | |||
| } | |||
| }) | |||
| } | |||
| return { | |||
| data, | |||
| isLoading, | |||
| @@ -17,7 +17,6 @@ import ReactFlow, { | |||
| useEdgesState, | |||
| useNodesState, | |||
| useOnViewportChange, | |||
| useReactFlow, | |||
| } from 'reactflow' | |||
| import type { Viewport } from 'reactflow' | |||
| import 'reactflow/dist/style.css' | |||
| @@ -72,7 +71,6 @@ const Workflow: FC<WorkflowProps> = memo(({ | |||
| edges: originalEdges, | |||
| viewport, | |||
| }) => { | |||
| const reactflow = useReactFlow() | |||
| const [nodes, setNodes] = useNodesState(originalNodes) | |||
| const [edges, setEdges] = useEdgesState(originalEdges) | |||
| const showFeaturesPanel = useStore(state => state.showFeaturesPanel) | |||
| @@ -93,17 +91,6 @@ const Workflow: FC<WorkflowProps> = memo(({ | |||
| } | |||
| }) | |||
| useEffect(() => { | |||
| setNodes(originalNodes) | |||
| }, [originalNodes, setNodes]) | |||
| useEffect(() => { | |||
| setEdges(originalEdges) | |||
| }, [originalEdges, setEdges]) | |||
| useEffect(() => { | |||
| if (viewport) | |||
| reactflow.setViewport(viewport) | |||
| }, [reactflow, viewport]) | |||
| useEffect(() => { | |||
| setAutoFreeze(false) | |||
| @@ -155,8 +142,6 @@ const Workflow: FC<WorkflowProps> = memo(({ | |||
| } = useEdgesInteractions() | |||
| const { | |||
| isValidConnection, | |||
| enableShortcuts, | |||
| disableShortcuts, | |||
| } = useWorkflow() | |||
| useOnViewportChange({ | |||
| @@ -193,8 +178,6 @@ const Workflow: FC<WorkflowProps> = memo(({ | |||
| edgeTypes={edgeTypes} | |||
| nodes={nodes} | |||
| edges={edges} | |||
| onPointerDown={enableShortcuts} | |||
| onMouseLeave={disableShortcuts} | |||
| onNodeDragStart={handleNodeDragStart} | |||
| onNodeDrag={handleNodeDrag} | |||
| onNodeDragStop={handleNodeDragStop} | |||
| @@ -8,7 +8,10 @@ import { useShallow } from 'zustand/react/shallow' | |||
| import type { CommonNodeType } from '../types' | |||
| import { Panel as NodePanel } from '../nodes' | |||
| import { useStore } from '../store' | |||
| import { useIsChatMode } from '../hooks' | |||
| import { | |||
| useIsChatMode, | |||
| useWorkflow, | |||
| } from '../hooks' | |||
| import DebugAndPreview from './debug-and-preview' | |||
| import Record from './record' | |||
| import WorkflowPreview from './workflow-preview' | |||
| @@ -24,6 +27,10 @@ const Panel: FC = () => { | |||
| const workflowRunningData = useStore(s => s.workflowRunningData) | |||
| const historyWorkflowData = useStore(s => s.historyWorkflowData) | |||
| const isRestoring = useStore(s => s.isRestoring) | |||
| const { | |||
| enableShortcuts, | |||
| disableShortcuts, | |||
| } = useWorkflow() | |||
| const { currentLogItem, setCurrentLogItem, showMessageLogModal, setShowMessageLogModal } = useAppStore(useShallow(state => ({ | |||
| currentLogItem: state.currentLogItem, | |||
| setCurrentLogItem: state.setCurrentLogItem, | |||
| @@ -49,7 +56,13 @@ const Panel: FC = () => { | |||
| ]) | |||
| return ( | |||
| <div className='absolute top-14 right-0 bottom-2 flex z-10' key={`${isRestoring}`}> | |||
| <div | |||
| tabIndex={-1} | |||
| className='absolute top-14 right-0 bottom-2 flex z-10 outline-none' | |||
| onFocus={disableShortcuts} | |||
| onBlur={enableShortcuts} | |||
| key={`${isRestoring}`} | |||
| > | |||
| { | |||
| showMessageLogModal && ( | |||
| <MessageLogModal | |||