Merge branch 'main' into feat/grouping-branching

# Conflicts:
#	web/app/components/workflow/block-icon.tsx
#	web/app/components/workflow/hooks/use-nodes-interactions.ts
#	web/app/components/workflow/index.tsx
#	web/app/components/workflow/nodes/components.ts
#	web/app/components/workflow/selection-contextmenu.tsx
#	web/app/components/workflow/utils/workflow-init.ts
This commit is contained in:
zhsama
2025-12-23 23:55:21 +08:00
3419 changed files with 86772 additions and 82082 deletions

View File

@ -1,26 +1,26 @@
export * from './use-auto-generate-webhook-url'
export * from './use-available-blocks'
export * from './use-checklist'
export * from './use-DSL'
export * from './use-edges-interactions'
export * from './use-inspect-vars-crud'
export * from './use-node-data-update'
export * from './use-nodes-interactions'
export * from './use-nodes-sync-draft'
export * from './use-workflow'
export * from './use-workflow-run'
export * from './use-checklist'
export * from './use-selection-interactions'
export * from './use-panel-interactions'
export * from './use-workflow-start-run'
export * from './use-nodes-layout'
export * from './use-workflow-history'
export * from './use-workflow-variables'
export * from './use-nodes-meta-data'
export * from './use-nodes-sync-draft'
export * from './use-panel-interactions'
export * from './use-selection-interactions'
export * from './use-serial-async-callback'
export * from './use-set-workflow-vars-with-value'
export * from './use-shortcuts'
export * from './use-tool-icon'
export * from './use-workflow'
export * from './use-workflow-history'
export * from './use-workflow-interactions'
export * from './use-workflow-mode'
export * from './use-nodes-meta-data'
export * from './use-available-blocks'
export * from './use-workflow-refresh-draft'
export * from './use-tool-icon'
export * from './use-DSL'
export * from './use-inspect-vars-crud'
export * from './use-set-workflow-vars-with-value'
export * from './use-workflow-run'
export * from './use-workflow-search'
export * from './use-auto-generate-webhook-url'
export * from './use-serial-async-callback'
export * from './use-workflow-start-run'
export * from './use-workflow-variables'

View File

@ -1,5 +1,5 @@
import { useCallback } from 'react'
import { produce } from 'immer'
import { useCallback } from 'react'
import { useStoreApi } from 'reactflow'
import { useStore as useAppStore } from '@/app/components/app/store'
import { BlockEnum } from '@/app/components/workflow/types'

View File

@ -23,8 +23,9 @@ export const useAvailableBlocks = (nodeType?: BlockEnum, inContainer?: boolean)
const availablePrevBlocks = useMemo(() => {
if (!nodeType || nodeType === BlockEnum.Start || nodeType === BlockEnum.DataSource
|| nodeType === BlockEnum.TriggerPlugin || nodeType === BlockEnum.TriggerWebhook
|| nodeType === BlockEnum.TriggerSchedule)
|| nodeType === BlockEnum.TriggerSchedule) {
return []
}
return availableNodesType
}, [availableNodesType, nodeType])

View File

@ -1,10 +1,9 @@
import {
useCallback,
useMemo,
useRef,
} from 'react'
import { useTranslation } from 'react-i18next'
import { useEdges, useStoreApi } from 'reactflow'
import type { AgentNodeType } from '../nodes/agent/types'
import type { DataSourceNodeType } from '../nodes/data-source/types'
import type { KnowledgeBaseNodeType } from '../nodes/knowledge-base/types'
import type { KnowledgeRetrievalNodeType } from '../nodes/knowledge-retrieval/types'
import type { ToolNodeType } from '../nodes/tool/types'
import type { PluginTriggerNodeType } from '../nodes/trigger-plugin/types'
import type {
CommonEdgeType,
CommonNodeType,
@ -12,51 +11,52 @@ import type {
Node,
ValueSelector,
} from '../types'
import { BlockEnum } from '../types'
import type { Emoji } from '@/app/components/tools/types'
import type { DataSet } from '@/models/datasets'
import {
useCallback,
useMemo,
useRef,
} from 'react'
import { useTranslation } from 'react-i18next'
import { useEdges, useStoreApi } from 'reactflow'
import { useStore as useAppStore } from '@/app/components/app/store'
import { useToastContext } from '@/app/components/base/toast'
import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
import { useModelList } from '@/app/components/header/account-setting/model-provider-page/hooks'
import useNodes from '@/app/components/workflow/store/workflow/use-nodes'
import { MAX_TREE_DEPTH } from '@/config'
import { useGetLanguage } from '@/context/i18n'
import { fetchDatasets } from '@/service/datasets'
import { useStrategyProviders } from '@/service/use-strategy'
import {
useAllBuiltInTools,
useAllCustomTools,
useAllWorkflowTools,
} from '@/service/use-tools'
import { useAllTriggerPlugins } from '@/service/use-triggers'
import { AppModeEnum } from '@/types/app'
import {
CUSTOM_NODE,
} from '../constants'
import { useDatasetsDetailStore } from '../datasets-detail-store/store'
import {
useGetToolIcon,
useNodesMetaData,
} from '../hooks'
import { getNodeUsedVars, isSpecialVar } from '../nodes/_base/components/variable/utils'
import {
useStore,
useWorkflowStore,
} from '../store'
import { BlockEnum } from '../types'
import {
getDataSourceCheckParams,
getToolCheckParams,
getValidTreeNodes,
} from '../utils'
import { getTriggerCheckParams } from '../utils/trigger'
import {
CUSTOM_NODE,
} from '../constants'
import {
useGetToolIcon,
useNodesMetaData,
} from '../hooks'
import type { ToolNodeType } from '../nodes/tool/types'
import type { DataSourceNodeType } from '../nodes/data-source/types'
import type { PluginTriggerNodeType } from '../nodes/trigger-plugin/types'
import { useToastContext } from '@/app/components/base/toast'
import { useGetLanguage } from '@/context/i18n'
import type { AgentNodeType } from '../nodes/agent/types'
import { useStrategyProviders } from '@/service/use-strategy'
import { useAllTriggerPlugins } from '@/service/use-triggers'
import { useDatasetsDetailStore } from '../datasets-detail-store/store'
import type { KnowledgeRetrievalNodeType } from '../nodes/knowledge-retrieval/types'
import type { DataSet } from '@/models/datasets'
import { fetchDatasets } from '@/service/datasets'
import { MAX_TREE_DEPTH } from '@/config'
import useNodesAvailableVarList, { useGetNodesAvailableVarList } from './use-nodes-available-var-list'
import { getNodeUsedVars, isSpecialVar } from '../nodes/_base/components/variable/utils'
import type { Emoji } from '@/app/components/tools/types'
import { useModelList } from '@/app/components/header/account-setting/model-provider-page/hooks'
import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
import type { KnowledgeBaseNodeType } from '../nodes/knowledge-base/types'
import {
useAllBuiltInTools,
useAllCustomTools,
useAllWorkflowTools,
} from '@/service/use-tools'
import { useStore as useAppStore } from '@/app/components/app/store'
import { AppModeEnum } from '@/types/app'
import useNodes from '@/app/components/workflow/store/workflow/use-nodes'
export type ChecklistItem = {
id: string

View File

@ -1,12 +1,12 @@
import type { ModelConfig, VisionSetting } from '@/app/components/workflow/types'
import { produce } from 'immer'
import { useCallback } from 'react'
import { useIsChatMode } from './use-workflow'
import type { ModelConfig, VisionSetting } from '@/app/components/workflow/types'
import { useTextGenerationCurrentProviderAndModelAndModelList } from '@/app/components/header/account-setting/model-provider-page/hooks'
import {
ModelFeatureEnum,
} from '@/app/components/header/account-setting/model-provider-page/declarations'
import { useTextGenerationCurrentProviderAndModelAndModelList } from '@/app/components/header/account-setting/model-provider-page/hooks'
import { Resolution } from '@/types/app'
import { useIsChatMode } from './use-workflow'
type Payload = {
enabled: boolean

View File

@ -1,13 +1,15 @@
import type { TestRunOptions, TriggerOption } from '../header/test-run-menu'
import type { CommonNodeType } from '../types'
import { useMemo } from 'react'
import useNodes from '@/app/components/workflow/store/workflow/use-nodes'
import { useTranslation } from 'react-i18next'
import { BlockEnum, type CommonNodeType } from '../types'
import { getWorkflowEntryNode } from '../utils/workflow-entry'
import { type TestRunOptions, type TriggerOption, TriggerType } from '../header/test-run-menu'
import { TriggerAll } from '@/app/components/base/icons/src/vender/workflow'
import BlockIcon from '../block-icon'
import { useStore } from '../store'
import useNodes from '@/app/components/workflow/store/workflow/use-nodes'
import { useAllTriggerPlugins } from '@/service/use-triggers'
import BlockIcon from '../block-icon'
import { TriggerType } from '../header/test-run-menu'
import { useStore } from '../store'
import { BlockEnum } from '../types'
import { getWorkflowEntryNode } from '../utils/workflow-entry'
export const useDynamicTestRunOptions = (): TestRunOptions => {
const { t } = useTranslation()
@ -25,7 +27,8 @@ export const useDynamicTestRunOptions = (): TestRunOptions => {
for (const node of nodes) {
const nodeData = node.data as CommonNodeType
if (!nodeData?.type) continue
if (!nodeData?.type)
continue
if (nodeData.type === BlockEnum.Start) {
userInput = {
@ -35,7 +38,7 @@ export const useDynamicTestRunOptions = (): TestRunOptions => {
icon: (
<BlockIcon
type={BlockEnum.Start}
size='md'
size="md"
/>
),
nodeId: node.id,
@ -50,7 +53,7 @@ export const useDynamicTestRunOptions = (): TestRunOptions => {
icon: (
<BlockIcon
type={BlockEnum.TriggerSchedule}
size='md'
size="md"
/>
),
nodeId: node.id,
@ -65,7 +68,7 @@ export const useDynamicTestRunOptions = (): TestRunOptions => {
icon: (
<BlockIcon
type={BlockEnum.TriggerWebhook}
size='md'
size="md"
/>
),
nodeId: node.id,
@ -83,7 +86,7 @@ export const useDynamicTestRunOptions = (): TestRunOptions => {
const icon = (
<BlockIcon
type={BlockEnum.TriggerPlugin}
size='md'
size="md"
toolIcon={triggerIcon}
/>
)
@ -109,7 +112,7 @@ export const useDynamicTestRunOptions = (): TestRunOptions => {
icon: (
<BlockIcon
type={BlockEnum.Start}
size='md'
size="md"
/>
),
nodeId: startNode.id,
@ -122,18 +125,20 @@ export const useDynamicTestRunOptions = (): TestRunOptions => {
.map(trigger => trigger.nodeId)
.filter((nodeId): nodeId is string => Boolean(nodeId))
const runAll: TriggerOption | undefined = triggerNodeIds.length > 1 ? {
id: 'run-all',
type: TriggerType.All,
name: t('workflow.common.runAllTriggers'),
icon: (
<div className="flex h-6 w-6 items-center justify-center rounded-lg border-[0.5px] border-white/2 bg-util-colors-purple-purple-500 text-white shadow-md">
<TriggerAll className="h-4.5 w-4.5" />
</div>
),
relatedNodeIds: triggerNodeIds,
enabled: true,
} : undefined
const runAll: TriggerOption | undefined = triggerNodeIds.length > 1
? {
id: 'run-all',
type: TriggerType.All,
name: t('workflow.common.runAllTriggers'),
icon: (
<div className="flex h-6 w-6 items-center justify-center rounded-lg border-[0.5px] border-white/2 bg-util-colors-purple-purple-500 text-white shadow-md">
<TriggerAll className="h-4.5 w-4.5" />
</div>
),
relatedNodeIds: triggerNodeIds,
enabled: true,
}
: undefined
return {
userInput,

View File

@ -1,5 +1,5 @@
import { useCallback } from 'react'
import { produce } from 'immer'
import { useCallback } from 'react'
import { useStoreApi } from 'reactflow'
export const useEdgesInteractionsWithoutSync = () => {

View File

@ -1,19 +1,19 @@
import { useCallback } from 'react'
import { produce } from 'immer'
import type {
EdgeMouseHandler,
OnEdgesChange,
} from 'reactflow'
import {
useStoreApi,
} from 'reactflow'
import type {
Node,
} from '../types'
import { produce } from 'immer'
import { useCallback } from 'react'
import {
useStoreApi,
} from 'reactflow'
import { getNodesConnectedSourceOrTargetHandleIdsMap } from '../utils'
import { useNodesSyncDraft } from './use-nodes-sync-draft'
import { useNodesReadOnly } from './use-workflow'
import { WorkflowHistoryEvent, useWorkflowHistory } from './use-workflow-history'
import { useWorkflowHistory, WorkflowHistoryEvent } from './use-workflow-history'
export const useEdgesInteractions = () => {
const store = useStoreApi()

View File

@ -1,22 +1,21 @@
import type { NodeWithVar, VarInInspect } from '@/types/workflow'
import { useStore, useWorkflowStore } from '@/app/components/workflow/store'
import { useStoreApi } from 'reactflow'
import type { ToolWithProvider } from '@/app/components/workflow/types'
import type { Node } from '@/app/components/workflow/types'
import { fetchAllInspectVars } from '@/service/workflow'
import { useInvalidateConversationVarValues, useInvalidateSysVarValues } from '@/service/use-workflow'
import { useNodesInteractionsWithoutSync } from '@/app/components/workflow/hooks/use-nodes-interactions-without-sync'
import type { FlowType } from '@/types/common'
import useMatchSchemaType, { getMatchedSchemaType } from '../nodes/_base/components/variable/use-match-schema-type'
import { toNodeOutputVars } from '../nodes/_base/components/variable/utils'
import type { Node, ToolWithProvider } from '@/app/components/workflow/types'
import type { SchemaTypeDefinition } from '@/service/use-common'
import type { FlowType } from '@/types/common'
import type { NodeWithVar, VarInInspect } from '@/types/workflow'
import { useCallback } from 'react'
import { useStoreApi } from 'reactflow'
import { useNodesInteractionsWithoutSync } from '@/app/components/workflow/hooks/use-nodes-interactions-without-sync'
import { useStore, useWorkflowStore } from '@/app/components/workflow/store'
import {
useAllBuiltInTools,
useAllCustomTools,
useAllMCPTools,
useAllWorkflowTools,
} from '@/service/use-tools'
import { useInvalidateConversationVarValues, useInvalidateSysVarValues } from '@/service/use-workflow'
import { fetchAllInspectVars } from '@/service/workflow'
import useMatchSchemaType, { getMatchedSchemaType } from '../nodes/_base/components/variable/use-match-schema-type'
import { toNodeOutputVars } from '../nodes/_base/components/variable/utils'
type Params = {
flowType: FlowType
@ -99,9 +98,9 @@ export const useSetWorkflowVarsWithValue = ({
}
const fetchInspectVars = useCallback(async (params: {
passInVars?: boolean,
vars?: VarInInspect[],
passedInAllPluginInfoList?: Record<string, ToolWithProvider[]>,
passInVars?: boolean
vars?: VarInInspect[]
passedInAllPluginInfoList?: Record<string, ToolWithProvider[]>
passedInSchemaTypeDefinitions?: SchemaTypeDefinition[]
}) => {
const { passInVars, vars, passedInAllPluginInfoList, passedInSchemaTypeDefinitions } = params

View File

@ -1,8 +1,8 @@
import type { Node } from '../types'
import { useCallback } from 'react'
import { useStoreApi } from 'reactflow'
import type { Node } from '../types'
import { BlockEnum, isTriggerNode } from '../types'
import { useWorkflowStore } from '../store'
import { BlockEnum, isTriggerNode } from '../types'
// Entry node (Start/Trigger) wrapper offsets
// The EntryNodeContainer adds a wrapper with status indicator above the actual node

View File

@ -1,29 +1,28 @@
import { fetchNodeInspectVars } from '@/service/workflow'
import { useWorkflowStore } from '@/app/components/workflow/store'
import type { ValueSelector } from '@/app/components/workflow/types'
import type { Node, ValueSelector } from '@/app/components/workflow/types'
import type { SchemaTypeDefinition } from '@/service/use-common'
import type { FlowType } from '@/types/common'
import type { VarInInspect } from '@/types/workflow'
import { VarInInspectType } from '@/types/workflow'
import { produce } from 'immer'
import { useCallback } from 'react'
import { useStoreApi } from 'reactflow'
import { useEdgesInteractionsWithoutSync } from '@/app/components/workflow/hooks/use-edges-interactions-without-sync'
import { useNodesInteractionsWithoutSync } from '@/app/components/workflow/hooks/use-nodes-interactions-without-sync'
import {
isConversationVar,
isENV,
isSystemVar,
toNodeOutputVars,
} from '@/app/components/workflow/nodes/_base/components/variable/utils'
import { produce } from 'immer'
import type { Node } from '@/app/components/workflow/types'
import { useNodesInteractionsWithoutSync } from '@/app/components/workflow/hooks/use-nodes-interactions-without-sync'
import { useEdgesInteractionsWithoutSync } from '@/app/components/workflow/hooks/use-edges-interactions-without-sync'
import type { FlowType } from '@/types/common'
import { useWorkflowStore } from '@/app/components/workflow/store'
import useFLow from '@/service/use-flow'
import { useStoreApi } from 'reactflow'
import type { SchemaTypeDefinition } from '@/service/use-common'
import {
useAllBuiltInTools,
useAllCustomTools,
useAllMCPTools,
useAllWorkflowTools,
} from '@/service/use-tools'
import { fetchNodeInspectVars } from '@/service/workflow'
import { VarInInspectType } from '@/types/workflow'
type Params = {
flowId: string

View File

@ -1,11 +1,11 @@
import { useStore } from '../store'
import { produce } from 'immer'
import { useHooksStore } from '@/app/components/workflow/hooks-store'
import {
useConversationVarValues,
useSysVarValues,
} from '@/service/use-workflow'
import { FlowType } from '@/types/common'
import { produce } from 'immer'
import { useStore } from '../store'
import { BlockEnum } from '../types'
const varsAppendStartNodeKeys = ['query', 'files']
@ -16,19 +16,19 @@ const useInspectVarsCrud = () => {
const { data: conversationVars } = useConversationVarValues(configsMap?.flowType, !isRagPipeline ? configsMap?.flowId : '')
const { data: allSystemVars } = useSysVarValues(configsMap?.flowType, !isRagPipeline ? configsMap?.flowId : '')
const { varsAppendStartNode, systemVars } = (() => {
if(allSystemVars?.length === 0)
if (allSystemVars?.length === 0)
return { varsAppendStartNode: [], systemVars: [] }
const varsAppendStartNode = allSystemVars?.filter(({ name }) => varsAppendStartNodeKeys.includes(name)) || []
const systemVars = allSystemVars?.filter(({ name }) => !varsAppendStartNodeKeys.includes(name)) || []
return { varsAppendStartNode, systemVars }
})()
const nodesWithInspectVars = (() => {
if(!partOfNodesWithInspectVars || partOfNodesWithInspectVars.length === 0)
if (!partOfNodesWithInspectVars || partOfNodesWithInspectVars.length === 0)
return []
const nodesWithInspectVars = produce(partOfNodesWithInspectVars, (draft) => {
draft.forEach((nodeWithVars) => {
if(nodeWithVars.nodeType === BlockEnum.Start)
if (nodeWithVars.nodeType === BlockEnum.Start)
nodeWithVars.vars = [...nodeWithVars.vars, ...varsAppendStartNode]
})
})

View File

@ -1,8 +1,8 @@
import type { PredecessorHandle } from '../utils'
import { useMemo } from 'react'
import { useStore as useReactFlowStore } from 'reactflow'
import { getCommonPredecessorHandles } from '../utils'
import type { PredecessorHandle } from '../utils'
import { shallow } from 'zustand/shallow'
import { getCommonPredecessorHandles } from '../utils'
export type MakeGroupAvailability = {
canMakeGroup: boolean

View File

@ -1,7 +1,7 @@
import { useCallback } from 'react'
import { produce } from 'immer'
import { useStoreApi } from 'reactflow'
import type { SyncCallback } from './use-nodes-sync-draft'
import { produce } from 'immer'
import { useCallback } from 'react'
import { useStoreApi } from 'reactflow'
import { useNodesSyncDraft } from './use-nodes-sync-draft'
import { useNodesReadOnly } from './use-workflow'

View File

@ -1,9 +1,10 @@
import { useCallback, useMemo } from 'react'
import { BlockEnum, type CommonNodeType } from '../types'
import type { DataSourceNodeType } from '../nodes/data-source/types'
import type { ToolNodeType } from '../nodes/tool/types'
import type { PluginTriggerNodeType } from '../nodes/trigger-plugin/types'
import type { DataSourceNodeType } from '../nodes/data-source/types'
import type { CommonNodeType } from '../types'
import { useCallback, useMemo } from 'react'
import { CollectionType } from '@/app/components/tools/types'
import { useInvalidDataSourceList } from '@/service/use-pipeline'
import {
useAllBuiltInTools,
useAllCustomTools,
@ -15,9 +16,9 @@ import {
useAllTriggerPlugins,
useInvalidateAllTriggerPlugins,
} from '@/service/use-triggers'
import { useInvalidDataSourceList } from '@/service/use-pipeline'
import { useStore } from '../store'
import { canFindTool } from '@/utils'
import { useStore } from '../store'
import { BlockEnum } from '../types'
type InstallationState = {
isChecking: boolean

View File

@ -1,10 +1,12 @@
import type { Node, NodeOutPutVar, ValueSelector, Var } from '@/app/components/workflow/types'
import { useCallback } from 'react'
import {
useIsChatMode,
useWorkflow,
useWorkflowVariables,
} from '@/app/components/workflow/hooks'
import { BlockEnum, type Node, type NodeOutPutVar, type ValueSelector, type Var } from '@/app/components/workflow/types'
import { BlockEnum } from '@/app/components/workflow/types'
type Params = {
onlyLeafNodeVar?: boolean
hideEnv?: boolean

View File

@ -1,5 +1,5 @@
import { useCallback } from 'react'
import { produce } from 'immer'
import { useCallback } from 'react'
import { useStoreApi } from 'reactflow'
import { NodeRunningStatus } from '../types'
@ -31,7 +31,7 @@ export const useNodesInteractionsWithoutSync = () => {
const nodes = getNodes()
const newNodes = produce(nodes, (draft) => {
draft.forEach((node) => {
if(node.data._runningStatus === NodeRunningStatus.Succeeded)
if (node.data._runningStatus === NodeRunningStatus.Succeeded)
node.data._runningStatus = undefined
})
})

View File

@ -1,7 +1,4 @@
import type { MouseEvent } from 'react'
import { useCallback, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { produce } from 'immer'
import type {
NodeDragHandler,
NodeMouseHandler,
@ -10,16 +7,22 @@ import type {
OnConnectStart,
ResizeParamsWithDirection,
} from 'reactflow'
import type { PluginDefaultValue } from '../block-selector/types'
import type { GroupHandler, GroupMember, GroupNodeData } from '../nodes/group/types'
import type { IterationNodeType } from '../nodes/iteration/types'
import type { LoopNodeType } from '../nodes/loop/types'
import type { VariableAssignerNodeType } from '../nodes/variable-assigner/types'
import type { Edge, Node, OnNodeAdd } from '../types'
import type { RAGPipelineVariables } from '@/models/pipeline'
import { produce } from 'immer'
import { useCallback, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import {
getConnectedEdges,
getOutgoers,
useReactFlow,
useStoreApi,
} from 'reactflow'
import type { PluginDefaultValue } from '../block-selector/types'
import type { Edge, Node, OnNodeAdd } from '../types'
import { BlockEnum, isTriggerNode } from '../types'
import { useWorkflowStore } from '../store'
import {
CUSTOM_EDGE,
ITERATION_CHILDREN_Z_INDEX,
@ -30,41 +33,38 @@ import {
X_OFFSET,
Y_OFFSET,
} from '../constants'
import { getNodeUsedVars } from '../nodes/_base/components/variable/utils'
import { CUSTOM_ITERATION_START_NODE } from '../nodes/iteration-start/constants'
import { useNodeIterationInteractions } from '../nodes/iteration/use-interactions'
import { CUSTOM_LOOP_START_NODE } from '../nodes/loop-start/constants'
import { useNodeLoopInteractions } from '../nodes/loop/use-interactions'
import { CUSTOM_NOTE_NODE } from '../note-node/constants'
import { useWorkflowStore } from '../store'
import { BlockEnum, isTriggerNode } from '../types'
import {
genNewNodeTitleFromOld,
generateNewNode,
genNewNodeTitleFromOld,
getNestedNodePosition,
getNodeCustomTypeByNodeDataType,
getNodesConnectedSourceOrTargetHandleIdsMap,
getTopLeftNodePosition,
} from '../utils'
import { CUSTOM_NOTE_NODE } from '../note-node/constants'
import type { IterationNodeType } from '../nodes/iteration/types'
import type { LoopNodeType } from '../nodes/loop/types'
import { CUSTOM_ITERATION_START_NODE } from '../nodes/iteration-start/constants'
import { CUSTOM_LOOP_START_NODE } from '../nodes/loop-start/constants'
import type { VariableAssignerNodeType } from '../nodes/variable-assigner/types'
import type { GroupHandler, GroupMember, GroupNodeData } from '../nodes/group/types'
import { useNodeIterationInteractions } from '../nodes/iteration/use-interactions'
import { useNodeLoopInteractions } from '../nodes/loop/use-interactions'
import { useWorkflowHistoryStore } from '../workflow-history-store'
import { useNodesSyncDraft } from './use-nodes-sync-draft'
import { useAutoGenerateWebhookUrl } from './use-auto-generate-webhook-url'
import { useHelpline } from './use-helpline'
import useInspectVarsCrud from './use-inspect-vars-crud'
import { checkMakeGroupAvailability } from './use-make-group'
import { useNodesMetaData } from './use-nodes-meta-data'
import { useNodesSyncDraft } from './use-nodes-sync-draft'
import {
useNodesReadOnly,
useWorkflow,
useWorkflowReadOnly,
} from './use-workflow'
import {
WorkflowHistoryEvent,
useWorkflowHistory,
WorkflowHistoryEvent,
} from './use-workflow-history'
import { useNodesMetaData } from './use-nodes-meta-data'
import { useAutoGenerateWebhookUrl } from './use-auto-generate-webhook-url'
import type { RAGPipelineVariables } from '@/models/pipeline'
import useInspectVarsCrud from './use-inspect-vars-crud'
import { getNodeUsedVars } from '../nodes/_base/components/variable/utils'
// Entry node deletion restriction has been removed to allow empty workflows
@ -164,8 +164,8 @@ export const useNodesInteractions = () => {
const { handleNodeLoopChildDrag, handleNodeLoopChildrenCopy }
= useNodeLoopInteractions()
const dragNodeStartPosition = useRef({ x: 0, y: 0 } as {
x: number;
y: number;
x: number
y: number
})
const { nodesMap: nodesMetaDataMap } = useNodesMetaData()
@ -176,19 +176,22 @@ export const useNodesInteractions = () => {
(_, node) => {
workflowStore.setState({ nodeAnimation: false })
if (getNodesReadOnly()) return
if (getNodesReadOnly())
return
if (
node.type === CUSTOM_ITERATION_START_NODE
|| node.type === CUSTOM_NOTE_NODE
)
) {
return
}
if (
node.type === CUSTOM_LOOP_START_NODE
|| node.type === CUSTOM_NOTE_NODE
)
) {
return
}
dragNodeStartPosition.current = {
x: node.position.x,
@ -200,11 +203,14 @@ export const useNodesInteractions = () => {
const handleNodeDrag = useCallback<NodeDragHandler>(
(e, node: Node) => {
if (getNodesReadOnly()) return
if (getNodesReadOnly())
return
if (node.type === CUSTOM_ITERATION_START_NODE) return
if (node.type === CUSTOM_ITERATION_START_NODE)
return
if (node.type === CUSTOM_LOOP_START_NODE) return
if (node.type === CUSTOM_LOOP_START_NODE)
return
const { getNodes, setNodes } = store.getState()
e.stopPropagation()
@ -286,7 +292,8 @@ export const useNodesInteractions = () => {
const { setHelpLineHorizontal, setHelpLineVertical }
= workflowStore.getState()
if (getNodesReadOnly()) return
if (getNodesReadOnly())
return
const { x, y } = dragNodeStartPosition.current
if (!(x === node.position.x && y === node.position.y)) {
@ -312,19 +319,22 @@ export const useNodesInteractions = () => {
const handleNodeEnter = useCallback<NodeMouseHandler>(
(_, node) => {
if (getNodesReadOnly()) return
if (getNodesReadOnly())
return
if (
node.type === CUSTOM_NOTE_NODE
|| node.type === CUSTOM_ITERATION_START_NODE
)
) {
return
}
if (
node.type === CUSTOM_LOOP_START_NODE
|| node.type === CUSTOM_NOTE_NODE
)
) {
return
}
const { getNodes, setNodes, edges, setEdges } = store.getState()
const nodes = getNodes()
@ -332,7 +342,8 @@ export const useNodesInteractions = () => {
= workflowStore.getState()
if (connectingNodePayload) {
if (connectingNodePayload.nodeId === node.id) return
if (connectingNodePayload.nodeId === node.id)
return
const connectingNode: Node = nodes.find(
n => n.id === connectingNodePayload.nodeId,
)!
@ -363,8 +374,9 @@ export const useNodesInteractions = () => {
|| connectingNode.data.type === BlockEnum.VariableAggregator)
&& node.data.type !== BlockEnum.IfElse
&& node.data.type !== BlockEnum.QuestionClassifier
)
) {
n.data._isEntering = true
}
})
})
setNodes(newNodes)
@ -375,7 +387,8 @@ export const useNodesInteractions = () => {
connectedEdges.forEach((edge) => {
const currentEdge = draft.find(e => e.id === edge.id)
if (currentEdge) currentEdge.data._connectedNodeIsHovering = true
if (currentEdge)
currentEdge.data._connectedNodeIsHovering = true
})
})
setEdges(newEdges)
@ -385,19 +398,22 @@ export const useNodesInteractions = () => {
const handleNodeLeave = useCallback<NodeMouseHandler>(
(_, node) => {
if (getNodesReadOnly()) return
if (getNodesReadOnly())
return
if (
node.type === CUSTOM_NOTE_NODE
|| node.type === CUSTOM_ITERATION_START_NODE
)
) {
return
}
if (
node.type === CUSTOM_NOTE_NODE
|| node.type === CUSTOM_LOOP_START_NODE
)
) {
return
}
const { setEnteringNodePayload } = workflowStore.getState()
setEnteringNodePayload(undefined)
@ -431,11 +447,13 @@ export const useNodesInteractions = () => {
const nodes = getNodes()
const selectedNode = nodes.find(node => node.data.selected)
if (!cancelSelection && selectedNode?.id === nodeId) return
if (!cancelSelection && selectedNode?.id === nodeId)
return
const newNodes = produce(nodes, (draft) => {
draft.forEach((node) => {
if (node.id === nodeId) node.data.selected = !cancelSelection
if (node.id === nodeId)
node.data.selected = !cancelSelection
else node.data.selected = false
})
})
@ -470,10 +488,14 @@ export const useNodesInteractions = () => {
const handleNodeClick = useCallback<NodeMouseHandler>(
(_, node) => {
if (node.type === CUSTOM_ITERATION_START_NODE) return
if (node.type === CUSTOM_LOOP_START_NODE) return
if (node.data.type === BlockEnum.DataSourceEmpty) return
if (node.data._pluginInstallLocked) return
if (node.type === CUSTOM_ITERATION_START_NODE)
return
if (node.type === CUSTOM_LOOP_START_NODE)
return
if (node.data.type === BlockEnum.DataSourceEmpty)
return
if (node.data._pluginInstallLocked)
return
handleNodeSelect(node.id)
},
[handleNodeSelect],
@ -481,21 +503,25 @@ export const useNodesInteractions = () => {
const handleNodeConnect = useCallback<OnConnect>(
({ source, sourceHandle, target, targetHandle }) => {
if (source === target) return
if (getNodesReadOnly()) return
if (source === target)
return
if (getNodesReadOnly())
return
const { getNodes, setNodes, edges, setEdges } = store.getState()
const nodes = getNodes()
const targetNode = nodes.find(node => node.id === target!)
const sourceNode = nodes.find(node => node.id === source!)
if (targetNode?.parentId !== sourceNode?.parentId) return
if (targetNode?.parentId !== sourceNode?.parentId)
return
if (
sourceNode?.type === CUSTOM_NOTE_NODE
|| targetNode?.type === CUSTOM_NOTE_NODE
)
) {
return
}
// Check if source is a group node - need special handling
const isSourceGroup = sourceNode?.data.type === BlockEnum.Group
@ -509,8 +535,9 @@ export const useNodesInteractions = () => {
&& edge.sourceHandle === originalSourceHandle
&& edge.target === target
&& edge.targetHandle === targetHandle,
))
)) {
return
}
const parentNode = nodes.find(node => node.id === targetNode?.parentId)
const isInIteration = parentNode && parentNode.data.type === BlockEnum.Iteration
@ -575,8 +602,9 @@ export const useNodesInteractions = () => {
&& edge.target === target
&& edge.targetHandle === targetHandle,
)
)
) {
return
}
const parendNode = nodes.find(node => node.id === targetNode?.parentId)
const isInIteration
@ -642,20 +670,24 @@ export const useNodesInteractions = () => {
const handleNodeConnectStart = useCallback<OnConnectStart>(
(_, { nodeId, handleType, handleId }) => {
if (getNodesReadOnly()) return
if (getNodesReadOnly())
return
if (nodeId && handleType) {
const { setConnectingNodePayload } = workflowStore.getState()
const { getNodes } = store.getState()
const node = getNodes().find(n => n.id === nodeId)!
if (node.type === CUSTOM_NOTE_NODE) return
if (node.type === CUSTOM_NOTE_NODE)
return
if (
node.data.type === BlockEnum.VariableAggregator
|| node.data.type === BlockEnum.VariableAssigner
)
if (handleType === 'target') return
) {
if (handleType === 'target')
return
}
setConnectingNodePayload({
nodeId,
@ -670,7 +702,8 @@ export const useNodesInteractions = () => {
const handleNodeConnectEnd = useCallback<OnConnectEnd>(
(e: any) => {
if (getNodesReadOnly()) return
if (getNodesReadOnly())
return
const {
connectingNodePayload,
@ -692,7 +725,8 @@ export const useNodesInteractions = () => {
const toNode = nodes.find(n => n.id === enteringNodePayload.nodeId)!
const toParentNode = nodes.find(n => n.id === toNode.parentId)
if (fromNode.parentId !== toNode.parentId) return
if (fromNode.parentId !== toNode.parentId)
return
const { x, y } = screenToFlowPosition({ x: e.x, y: e.y })
@ -747,7 +781,8 @@ export const useNodesInteractions = () => {
const handleNodeDelete = useCallback(
(nodeId: string) => {
if (getNodesReadOnly()) return
if (getNodesReadOnly())
return
const { getNodes, setNodes, edges, setEdges } = store.getState()
@ -755,13 +790,15 @@ export const useNodesInteractions = () => {
const currentNodeIndex = nodes.findIndex(node => node.id === nodeId)
const currentNode = nodes[currentNodeIndex]
if (!currentNode) return
if (!currentNode)
return
if (
nodesMetaDataMap?.[currentNode.data.type as BlockEnum]?.metaData
.isUndeletable
)
) {
return
}
deleteNodeInspectorVars(nodeId)
if (currentNode.data.type === BlockEnum.Iteration) {
@ -851,7 +888,8 @@ export const useNodesInteractions = () => {
if (ragPipelineVariables && setRagPipelineVariables) {
const newRagPipelineVariables: RAGPipelineVariables = []
ragPipelineVariables.forEach((variable) => {
if (variable.belong_to_node_id === id) return
if (variable.belong_to_node_id === id)
return
newRagPipelineVariables.push(variable)
})
setRagPipelineVariables(newRagPipelineVariables)
@ -926,7 +964,8 @@ export const useNodesInteractions = () => {
},
{ prevNodeId, prevNodeSourceHandle, nextNodeId, nextNodeTargetHandle },
) => {
if (getNodesReadOnly()) return
if (getNodesReadOnly())
return
const { getNodes, setNodes, edges, setEdges } = store.getState()
const nodes = getNodes()
@ -1107,9 +1146,11 @@ export const useNodesInteractions = () => {
})
draft.push(newNode)
if (newIterationStartNode) draft.push(newIterationStartNode)
if (newIterationStartNode)
draft.push(newIterationStartNode)
if (newLoopStartNode) draft.push(newLoopStartNode)
if (newLoopStartNode)
draft.push(newLoopStartNode)
})
if (
@ -1136,8 +1177,10 @@ export const useNodesInteractions = () => {
_connectedNodeIsSelected: false,
}
})
if (newEdge) draft.push(newEdge)
if (newUiEdge) draft.push(newUiEdge)
if (newEdge)
draft.push(newEdge)
if (newUiEdge)
draft.push(newUiEdge)
})
setNodes(newNodes)
@ -1149,8 +1192,9 @@ export const useNodesInteractions = () => {
if (
nodeType !== BlockEnum.IfElse
&& nodeType !== BlockEnum.QuestionClassifier
)
) {
newNode.data._connectedSourceHandleIds = [sourceHandle]
}
newNode.data._connectedTargetHandleIds = []
newNode.position = {
x: nextNode.position.x,
@ -1274,8 +1318,10 @@ export const useNodesInteractions = () => {
}
})
draft.push(newNode)
if (newIterationStartNode) draft.push(newIterationStartNode)
if (newLoopStartNode) draft.push(newLoopStartNode)
if (newIterationStartNode)
draft.push(newIterationStartNode)
if (newLoopStartNode)
draft.push(newLoopStartNode)
})
if (newEdge) {
const newEdges = produce(edges, (draft) => {
@ -1349,8 +1395,10 @@ export const useNodesInteractions = () => {
&& edge.sourceHandle === prevNodeSourceHandle
&& edge.target === nextNodeId,
)
if (hiddenEdge) edgesToRemove.push(hiddenEdge.id)
if (uiTempEdge) edgesToRemove.push(uiTempEdge.id)
if (hiddenEdge)
edgesToRemove.push(hiddenEdge.id)
if (uiTempEdge)
edgesToRemove.push(uiTempEdge.id)
const edgePair = createGroupEdgePair({
groupNodeId: prevNodeId,
@ -1377,7 +1425,8 @@ export const useNodesInteractions = () => {
const currentEdge = edges.find(
edge => edge.source === prevNodeId && edge.target === nextNodeId,
)
if (currentEdge) edgesToRemove.push(currentEdge.id)
if (currentEdge)
edgesToRemove.push(currentEdge.id)
if (nodeType !== BlockEnum.DataSource) {
newPrevEdge = {
@ -1411,10 +1460,10 @@ export const useNodesInteractions = () => {
= nodes.find(node => node.id === nextNode.parentId) || null
const isNextNodeInIteration
= !!nextNodeParentNode
&& nextNodeParentNode.data.type === BlockEnum.Iteration
&& nextNodeParentNode.data.type === BlockEnum.Iteration
const isNextNodeInLoop
= !!nextNodeParentNode
&& nextNodeParentNode.data.type === BlockEnum.Loop
&& nextNodeParentNode.data.type === BlockEnum.Loop
if (
nodeType !== BlockEnum.IfElse
@ -1495,8 +1544,10 @@ export const useNodesInteractions = () => {
}
})
draft.push(newNode)
if (newIterationStartNode) draft.push(newIterationStartNode)
if (newLoopStartNode) draft.push(newLoopStartNode)
if (newIterationStartNode)
draft.push(newIterationStartNode)
if (newLoopStartNode)
draft.push(newLoopStartNode)
})
setNodes(newNodes)
if (
@ -1528,9 +1579,12 @@ export const useNodesInteractions = () => {
_connectedNodeIsSelected: false,
}
})
if (newPrevEdge) draft.push(newPrevEdge)
if (newPrevUiEdge) draft.push(newPrevUiEdge)
if (newNextEdge) draft.push(newNextEdge)
if (newPrevEdge)
draft.push(newPrevEdge)
if (newPrevUiEdge)
draft.push(newPrevUiEdge)
if (newNextEdge)
draft.push(newNextEdge)
})
setEdges(newEdges)
}
@ -1555,7 +1609,8 @@ export const useNodesInteractions = () => {
sourceHandle: string,
pluginDefaultValue?: PluginDefaultValue,
) => {
if (getNodesReadOnly()) return
if (getNodesReadOnly())
return
const { getNodes, setNodes, edges, setEdges } = store.getState()
const nodes = getNodes()
@ -1613,8 +1668,10 @@ export const useNodesInteractions = () => {
const index = draft.findIndex(node => node.id === currentNodeId)
draft.splice(index, 1, newCurrentNode)
if (newIterationStartNode) draft.push(newIterationStartNode)
if (newLoopStartNode) draft.push(newLoopStartNode)
if (newIterationStartNode)
draft.push(newIterationStartNode)
if (newLoopStartNode)
draft.push(newLoopStartNode)
})
setNodes(newNodes)
const newEdges = produce(edges, (draft) => {
@ -1668,14 +1725,16 @@ export const useNodesInteractions = () => {
if (
node.type === CUSTOM_NOTE_NODE
|| node.type === CUSTOM_ITERATION_START_NODE
)
) {
return
}
if (
node.type === CUSTOM_NOTE_NODE
|| node.type === CUSTOM_LOOP_START_NODE
)
) {
return
}
e.preventDefault()
const container = document.querySelector('#workflow-container')
@ -1694,7 +1753,8 @@ export const useNodesInteractions = () => {
const handleNodesCopy = useCallback(
(nodeId?: string) => {
if (getNodesReadOnly()) return
if (getNodesReadOnly())
return
const { setClipboardElements } = workflowStore.getState()
@ -1714,15 +1774,19 @@ export const useNodesInteractions = () => {
&& node.data.type !== BlockEnum.KnowledgeBase
&& node.data.type !== BlockEnum.DataSourceEmpty,
)
if (nodeToCopy) setClipboardElements([nodeToCopy])
if (nodeToCopy)
setClipboardElements([nodeToCopy])
}
else {
// If no nodeId is provided, fall back to the current behavior
const bundledNodes = nodes.filter((node) => {
if (!node.data._isBundled) return false
if (node.type === CUSTOM_NOTE_NODE) return true
if (!node.data._isBundled)
return false
if (node.type === CUSTOM_NOTE_NODE)
return true
const { metaData } = nodesMetaDataMap![node.data.type as BlockEnum]
if (metaData.isSingleton) return false
if (metaData.isSingleton)
return false
return !node.data.isInIteration && !node.data.isInLoop
})
@ -1732,20 +1796,24 @@ export const useNodesInteractions = () => {
}
const selectedNode = nodes.find((node) => {
if (!node.data.selected) return false
if (node.type === CUSTOM_NOTE_NODE) return true
if (!node.data.selected)
return false
if (node.type === CUSTOM_NOTE_NODE)
return true
const { metaData } = nodesMetaDataMap![node.data.type as BlockEnum]
return !metaData.isSingleton
})
if (selectedNode) setClipboardElements([selectedNode])
if (selectedNode)
setClipboardElements([selectedNode])
}
},
[getNodesReadOnly, store, workflowStore],
)
const handleNodesPaste = useCallback(() => {
if (getNodesReadOnly()) return
if (getNodesReadOnly())
return
const { clipboardElements, mousePosition } = workflowStore.getState()
@ -1872,7 +1940,8 @@ export const useNodesInteractions = () => {
nodesToPaste.push(newNode)
if (newChildren.length) nodesToPaste.push(...newChildren)
if (newChildren.length)
nodesToPaste.push(...newChildren)
})
// only handle edge when paste nested block
@ -1916,7 +1985,8 @@ export const useNodesInteractions = () => {
const handleNodesDuplicate = useCallback(
(nodeId?: string) => {
if (getNodesReadOnly()) return
if (getNodesReadOnly())
return
handleNodesCopy(nodeId)
handleNodesPaste()
@ -1925,7 +1995,8 @@ export const useNodesInteractions = () => {
)
const handleNodesDelete = useCallback(() => {
if (getNodesReadOnly()) return
if (getNodesReadOnly())
return
const { getNodes, edges } = store.getState()
@ -1941,18 +2012,21 @@ export const useNodesInteractions = () => {
}
const edgeSelected = edges.some(edge => edge.selected)
if (edgeSelected) return
if (edgeSelected)
return
const selectedNode = nodes.find(
node => node.data.selected,
)
if (selectedNode) handleNodeDelete(selectedNode.id)
if (selectedNode)
handleNodeDelete(selectedNode.id)
}, [store, getNodesReadOnly, handleNodeDelete])
const handleNodeResize = useCallback(
(nodeId: string, params: ResizeParamsWithDirection) => {
if (getNodesReadOnly()) return
if (getNodesReadOnly())
return
const { getNodes, setNodes } = store.getState()
const { x, y, width, height } = params
@ -1977,8 +2051,9 @@ export const useNodesInteractions = () => {
if (
n.position.y + n.height!
> bottomNode.position.y + bottomNode.height!
)
) {
bottomNode = n
}
}
else {
bottomNode = n
@ -1997,8 +2072,9 @@ export const useNodesInteractions = () => {
if (
height
< bottomNode.position.y + bottomNode.height! + paddingMap.bottom
)
) {
return
}
}
const newNodes = produce(nodes, (draft) => {
draft.forEach((n) => {
@ -2021,7 +2097,8 @@ export const useNodesInteractions = () => {
const handleNodeDisconnect = useCallback(
(nodeId: string) => {
if (getNodesReadOnly()) return
if (getNodesReadOnly())
return
const { getNodes, setNodes, edges, setEdges } = store.getState()
const nodes = getNodes()
@ -2059,13 +2136,15 @@ export const useNodesInteractions = () => {
)
const handleHistoryBack = useCallback(() => {
if (getNodesReadOnly() || getWorkflowReadOnly()) return
if (getNodesReadOnly() || getWorkflowReadOnly())
return
const { setEdges, setNodes } = store.getState()
undo()
const { edges, nodes } = workflowHistoryStore.getState()
if (edges.length === 0 && nodes.length === 0) return
if (edges.length === 0 && nodes.length === 0)
return
setEdges(edges)
setNodes(nodes)
@ -2078,13 +2157,15 @@ export const useNodesInteractions = () => {
])
const handleHistoryForward = useCallback(() => {
if (getNodesReadOnly() || getWorkflowReadOnly()) return
if (getNodesReadOnly() || getWorkflowReadOnly())
return
const { setEdges, setNodes } = store.getState()
redo()
const { edges, nodes } = workflowHistoryStore.getState()
if (edges.length === 0 && nodes.length === 0) return
if (edges.length === 0 && nodes.length === 0)
return
setEdges(edges)
setNodes(nodes)
@ -2099,12 +2180,14 @@ export const useNodesInteractions = () => {
const [isDimming, setIsDimming] = useState(false)
/** Add opacity-30 to all nodes except the nodeId */
const dimOtherNodes = useCallback(() => {
if (isDimming) return
if (isDimming)
return
const { getNodes, setNodes, edges, setEdges } = store.getState()
const nodes = getNodes()
const selectedNode = nodes.find(n => n.data.selected)
if (!selectedNode) return
if (!selectedNode)
return
setIsDimming(true)
@ -2115,8 +2198,10 @@ export const useNodesInteractions = () => {
const dependencyNodes: Node[] = []
usedVars.forEach((valueSelector) => {
const node = workflowNodes.find(node => node.id === valueSelector?.[0])
if (node)
if (!dependencyNodes.includes(node)) dependencyNodes.push(node)
if (node) {
if (!dependencyNodes.includes(node))
dependencyNodes.push(node)
}
})
const outgoers = getOutgoers(selectedNode as Node, nodes as Node[], edges)
@ -2125,7 +2210,8 @@ export const useNodesInteractions = () => {
const outgoersForNode = getOutgoers(node, nodes as Node[], edges)
outgoersForNode.forEach((item) => {
const existed = outgoers.some(v => v.id === item.id)
if (!existed) outgoers.push(item)
if (!existed)
outgoers.push(item)
})
}
@ -2135,7 +2221,8 @@ export const useNodesInteractions = () => {
const used = usedVars.some(v => v?.[0] === selectedNode.id)
if (used) {
const existed = dependentNodes.some(v => v.id === node.id)
if (!existed) dependentNodes.push(node)
if (!existed)
dependentNodes.push(node)
}
})
@ -2144,7 +2231,8 @@ export const useNodesInteractions = () => {
const newNodes = produce(nodes, (draft) => {
draft.forEach((n) => {
const dimNode = dimNodes.find(v => v.id === n.id)
if (!dimNode) n.data._dimmed = true
if (!dimNode)
n.data._dimmed = true
})
})
@ -2299,7 +2387,7 @@ export const useNodesInteractions = () => {
const targetBranches = node.data._targetBranches
if (targetBranches && targetBranches.length > 0) {
// Multi-branch node: create handler for each branch
targetBranches.forEach((branch: { id: string; name?: string }) => {
targetBranches.forEach((branch: { id: string, name?: string }) => {
const handlerId = `${nodeId}-${branch.id}`
handlerMap.set(handlerId, {
id: handlerId,

View File

@ -1,16 +1,16 @@
import { useCallback } from 'react'
import ELK from 'elkjs/lib/elk.bundled.js'
import {
useReactFlow,
useStoreApi,
} from 'reactflow'
import { cloneDeep } from 'lodash-es'
import type {
Edge,
Node,
} from '../types'
import { useWorkflowStore } from '../store'
import ELK from 'elkjs/lib/elk.bundled.js'
import { cloneDeep } from 'lodash-es'
import { useCallback } from 'react'
import {
useReactFlow,
useStoreApi,
} from 'reactflow'
import { AUTO_LAYOUT_OFFSET } from '../constants'
import { useWorkflowStore } from '../store'
import { useNodesSyncDraft } from './use-nodes-sync-draft'
const layoutOptions = {

View File

@ -1,17 +1,17 @@
import { useMemo } from 'react'
import type { AvailableNodesMetaData } from '@/app/components/workflow/hooks-store'
import { useHooksStore } from '@/app/components/workflow/hooks-store'
import { BlockEnum } from '@/app/components/workflow/types'
import type { Node } from '@/app/components/workflow/types'
import { useMemo } from 'react'
import { CollectionType } from '@/app/components/tools/types'
import { useHooksStore } from '@/app/components/workflow/hooks-store'
import { useStore } from '@/app/components/workflow/store'
import { canFindTool } from '@/utils'
import { BlockEnum } from '@/app/components/workflow/types'
import { useGetLanguage } from '@/context/i18n'
import {
useAllBuiltInTools,
useAllCustomTools,
useAllWorkflowTools,
} from '@/service/use-tools'
import { canFindTool } from '@/utils'
export const useNodesMetaData = () => {
const availableNodesMetaData = useHooksStore(s => s.availableNodesMetaData)

View File

@ -1,7 +1,7 @@
import { useCallback } from 'react'
import { useHooksStore } from '@/app/components/workflow/hooks-store'
import { useStore } from '../store'
import { useNodesReadOnly } from './use-workflow'
import { useHooksStore } from '@/app/components/workflow/hooks-store'
export type SyncCallback = {
onSuccess?: () => void

View File

@ -1,14 +1,14 @@
import type { MouseEvent } from 'react'
import {
useCallback,
} from 'react'
import { produce } from 'immer'
import type {
OnSelectionChangeFunc,
} from 'reactflow'
import type { Node } from '../types'
import { produce } from 'immer'
import {
useCallback,
} from 'react'
import { useStoreApi } from 'reactflow'
import { useWorkflowStore } from '../store'
import type { Node } from '../types'
export const useSelectionInteractions = () => {
const store = useStoreApi()

View File

@ -1,13 +1,7 @@
import { useReactFlow } from 'reactflow'
import { useKeyPress } from 'ahooks'
import { useCallback, useEffect } from 'react'
import { useReactFlow } from 'reactflow'
import { ZEN_TOGGLE_EVENT } from '@/app/components/goto-anything/actions/commands/zen'
import {
getKeyboardKeyCodeBySystem,
isEventTargetInputArea,
} from '../utils'
import { useWorkflowHistoryStore } from '../workflow-history-store'
import { useWorkflowStore } from '../store'
import {
useEdgesInteractions,
useNodesInteractions,
@ -16,6 +10,12 @@ import {
useWorkflowMoveMode,
useWorkflowOrganize,
} from '.'
import { useWorkflowStore } from '../store'
import {
getKeyboardKeyCodeBySystem,
isEventTargetInputArea,
} from '../utils'
import { useWorkflowHistoryStore } from '../workflow-history-store'
export const useShortcuts = (): void => {
const {

View File

@ -1,9 +1,11 @@
import { useCallback, useMemo } from 'react'
import type { TriggerWithProvider } from '../block-selector/types'
import type { DataSourceNodeType } from '../nodes/data-source/types'
import type { ToolNodeType } from '../nodes/tool/types'
import type { PluginTriggerNodeType } from '../nodes/trigger-plugin/types'
import type { Node, ToolWithProvider } from '../types'
import { BlockEnum } from '../types'
import { useStore, useWorkflowStore } from '../store'
import { useCallback, useMemo } from 'react'
import { CollectionType } from '@/app/components/tools/types'
import { canFindTool } from '@/utils'
import useTheme from '@/hooks/use-theme'
import {
useAllBuiltInTools,
useAllCustomTools,
@ -11,11 +13,9 @@ import {
useAllWorkflowTools,
} from '@/service/use-tools'
import { useAllTriggerPlugins } from '@/service/use-triggers'
import type { PluginTriggerNodeType } from '../nodes/trigger-plugin/types'
import type { ToolNodeType } from '../nodes/tool/types'
import type { DataSourceNodeType } from '../nodes/data-source/types'
import type { TriggerWithProvider } from '../block-selector/types'
import useTheme from '@/hooks/use-theme'
import { canFindTool } from '@/utils'
import { useStore, useWorkflowStore } from '../store'
import { BlockEnum } from '../types'
const isTriggerPluginNode = (data: Node['data']): data is PluginTriggerNodeType => data.type === BlockEnum.TriggerPlugin

View File

@ -1,14 +1,15 @@
import type { WorkflowHistoryEventMeta } from '../workflow-history-store'
import { debounce } from 'lodash-es'
import {
useCallback,
useRef, useState,
useRef,
useState,
} from 'react'
import { debounce } from 'lodash-es'
import { useTranslation } from 'react-i18next'
import {
useStoreApi,
} from 'reactflow'
import { useTranslation } from 'react-i18next'
import { useWorkflowHistoryStore } from '../workflow-history-store'
import type { WorkflowHistoryEventMeta } from '../workflow-history-store'
/**
* All supported Events that create a new history state.

View File

@ -1,16 +1,23 @@
import type { WorkflowDataUpdater } from '../types'
import type { LayoutResult } from '../utils'
import { produce } from 'immer'
import {
useCallback,
} from 'react'
import { useReactFlow, useStoreApi } from 'reactflow'
import { produce } from 'immer'
import { useStore, useWorkflowStore } from '../store'
import { useEventEmitterContextContext } from '@/context/event-emitter'
import {
CUSTOM_NODE,
NODE_LAYOUT_HORIZONTAL_PADDING,
NODE_LAYOUT_VERTICAL_PADDING,
WORKFLOW_DATA_UPDATE,
} from '../constants'
import type { WorkflowDataUpdater } from '../types'
import {
useNodesReadOnly,
useSelectionInteractions,
useWorkflowReadOnly,
} from '../hooks'
import { useStore, useWorkflowStore } from '../store'
import { BlockEnum, ControlMode } from '../types'
import {
getLayoutByDagre,
@ -18,17 +25,10 @@ import {
initialEdges,
initialNodes,
} from '../utils'
import type { LayoutResult } from '../utils'
import {
useNodesReadOnly,
useSelectionInteractions,
useWorkflowReadOnly,
} from '../hooks'
import { useEdgesInteractionsWithoutSync } from './use-edges-interactions-without-sync'
import { useNodesInteractionsWithoutSync } from './use-nodes-interactions-without-sync'
import { useNodesSyncDraft } from './use-nodes-sync-draft'
import { WorkflowHistoryEvent, useWorkflowHistory } from './use-workflow-history'
import { useEventEmitterContextContext } from '@/context/event-emitter'
import { useWorkflowHistory, WorkflowHistoryEvent } from './use-workflow-history'
export const useWorkflowInteractions = () => {
const workflowStore = useWorkflowStore()
@ -99,8 +99,8 @@ export const useWorkflowOrganize = () => {
const loopAndIterationNodes = nodes.filter(
node => (node.data.type === BlockEnum.Loop || node.data.type === BlockEnum.Iteration)
&& !node.parentId
&& node.type === CUSTOM_NODE,
&& !node.parentId
&& node.type === CUSTOM_NODE,
)
const childLayoutEntries = await Promise.all(
@ -119,7 +119,8 @@ export const useWorkflowOrganize = () => {
loopAndIterationNodes.forEach((parentNode) => {
const childLayout = childLayoutsMap[parentNode.id]
if (!childLayout) return
if (!childLayout)
return
const {
bounds,
@ -141,7 +142,7 @@ export const useWorkflowOrganize = () => {
const nodesWithUpdatedSizes = produce(nodes, (draft) => {
draft.forEach((node) => {
if ((node.data.type === BlockEnum.Loop || node.data.type === BlockEnum.Iteration)
&& containerSizeChanges[node.id]) {
&& containerSizeChanges[node.id]) {
node.width = containerSizeChanges[node.id].width
node.height = containerSizeChanges[node.id].height
@ -160,7 +161,7 @@ export const useWorkflowOrganize = () => {
const layout = await getLayoutByDagre(nodesWithUpdatedSizes, edges)
// Build layer map for vertical alignment - nodes in the same layer should align
const layerMap = new Map<number, { minY: number; maxHeight: number }>()
const layerMap = new Map<number, { minY: number, maxHeight: number }>()
layout.nodes.forEach((layoutInfo) => {
if (layoutInfo.layer !== undefined) {
const existing = layerMap.get(layoutInfo.layer)

View File

@ -1,15 +1,15 @@
export * from './use-workflow-started'
export * from './use-workflow-finished'
export * from './use-workflow-agent-log'
export * from './use-workflow-failed'
export * from './use-workflow-node-started'
export * from './use-workflow-finished'
export * from './use-workflow-node-finished'
export * from './use-workflow-node-iteration-started'
export * from './use-workflow-node-iteration-next'
export * from './use-workflow-node-iteration-finished'
export * from './use-workflow-node-loop-started'
export * from './use-workflow-node-loop-next'
export * from './use-workflow-node-iteration-next'
export * from './use-workflow-node-iteration-started'
export * from './use-workflow-node-loop-finished'
export * from './use-workflow-node-loop-next'
export * from './use-workflow-node-loop-started'
export * from './use-workflow-node-retry'
export * from './use-workflow-node-started'
export * from './use-workflow-started'
export * from './use-workflow-text-chunk'
export * from './use-workflow-text-replace'
export * from './use-workflow-agent-log'

View File

@ -1,6 +1,6 @@
import { useCallback } from 'react'
import { produce } from 'immer'
import type { AgentLogResponse } from '@/types/workflow'
import { produce } from 'immer'
import { useCallback } from 'react'
import { useWorkflowStore } from '@/app/components/workflow/store'
export const useWorkflowAgentLog = () => {

View File

@ -1,5 +1,5 @@
import { useCallback } from 'react'
import { produce } from 'immer'
import { useCallback } from 'react'
import { useWorkflowStore } from '@/app/components/workflow/store'
import { WorkflowRunningStatus } from '@/app/components/workflow/types'

View File

@ -1,8 +1,8 @@
import { useCallback } from 'react'
import { produce } from 'immer'
import type { WorkflowFinishedResponse } from '@/types/workflow'
import { useWorkflowStore } from '@/app/components/workflow/store'
import { produce } from 'immer'
import { useCallback } from 'react'
import { getFilesInLogs } from '@/app/components/base/file-uploader/utils'
import { useWorkflowStore } from '@/app/components/workflow/store'
export const useWorkflowFinished = () => {
const workflowStore = useWorkflowStore()

View File

@ -1,13 +1,13 @@
import type { NodeFinishedResponse } from '@/types/workflow'
import { produce } from 'immer'
import { useCallback } from 'react'
import { useStoreApi } from 'reactflow'
import { produce } from 'immer'
import type { NodeFinishedResponse } from '@/types/workflow'
import { ErrorHandleTypeEnum } from '@/app/components/workflow/nodes/_base/components/error-handle/types'
import { useWorkflowStore } from '@/app/components/workflow/store'
import {
BlockEnum,
NodeRunningStatus,
} from '@/app/components/workflow/types'
import { ErrorHandleTypeEnum } from '@/app/components/workflow/nodes/_base/components/error-handle/types'
import { useWorkflowStore } from '@/app/components/workflow/store'
export const useWorkflowNodeFinished = () => {
const store = useStoreApi()

View File

@ -1,9 +1,9 @@
import type { IterationFinishedResponse } from '@/types/workflow'
import { produce } from 'immer'
import { useCallback } from 'react'
import { useStoreApi } from 'reactflow'
import { produce } from 'immer'
import type { IterationFinishedResponse } from '@/types/workflow'
import { useWorkflowStore } from '@/app/components/workflow/store'
import { DEFAULT_ITER_TIMES } from '@/app/components/workflow/constants'
import { useWorkflowStore } from '@/app/components/workflow/store'
export const useWorkflowNodeIterationFinished = () => {
const store = useStoreApi()

View File

@ -1,7 +1,7 @@
import type { IterationNextResponse } from '@/types/workflow'
import { produce } from 'immer'
import { useCallback } from 'react'
import { useStoreApi } from 'reactflow'
import { produce } from 'immer'
import type { IterationNextResponse } from '@/types/workflow'
import { useWorkflowStore } from '@/app/components/workflow/store'
export const useWorkflowNodeIterationNext = () => {

View File

@ -1,13 +1,13 @@
import type { IterationStartedResponse } from '@/types/workflow'
import { produce } from 'immer'
import { useCallback } from 'react'
import {
useReactFlow,
useStoreApi,
} from 'reactflow'
import { produce } from 'immer'
import { useWorkflowStore } from '@/app/components/workflow/store'
import type { IterationStartedResponse } from '@/types/workflow'
import { NodeRunningStatus } from '@/app/components/workflow/types'
import { DEFAULT_ITER_TIMES } from '@/app/components/workflow/constants'
import { useWorkflowStore } from '@/app/components/workflow/store'
import { NodeRunningStatus } from '@/app/components/workflow/types'
export const useWorkflowNodeIterationStarted = () => {
const store = useStoreApi()
@ -17,8 +17,8 @@ export const useWorkflowNodeIterationStarted = () => {
const handleWorkflowNodeIterationStarted = useCallback((
params: IterationStartedResponse,
containerParams: {
clientWidth: number,
clientHeight: number,
clientWidth: number
clientHeight: number
},
) => {
const { data } = params

View File

@ -1,7 +1,7 @@
import type { LoopFinishedResponse } from '@/types/workflow'
import { produce } from 'immer'
import { useCallback } from 'react'
import { useStoreApi } from 'reactflow'
import { produce } from 'immer'
import type { LoopFinishedResponse } from '@/types/workflow'
import { useWorkflowStore } from '@/app/components/workflow/store'
export const useWorkflowNodeLoopFinished = () => {

View File

@ -1,7 +1,7 @@
import type { LoopNextResponse } from '@/types/workflow'
import { produce } from 'immer'
import { useCallback } from 'react'
import { useStoreApi } from 'reactflow'
import { produce } from 'immer'
import type { LoopNextResponse } from '@/types/workflow'
import { NodeRunningStatus } from '@/app/components/workflow/types'
export const useWorkflowNodeLoopNext = () => {

View File

@ -1,11 +1,11 @@
import type { LoopStartedResponse } from '@/types/workflow'
import { produce } from 'immer'
import { useCallback } from 'react'
import {
useReactFlow,
useStoreApi,
} from 'reactflow'
import { produce } from 'immer'
import { useWorkflowStore } from '@/app/components/workflow/store'
import type { LoopStartedResponse } from '@/types/workflow'
import { NodeRunningStatus } from '@/app/components/workflow/types'
export const useWorkflowNodeLoopStarted = () => {
@ -16,8 +16,8 @@ export const useWorkflowNodeLoopStarted = () => {
const handleWorkflowNodeLoopStarted = useCallback((
params: LoopStartedResponse,
containerParams: {
clientWidth: number,
clientHeight: number,
clientWidth: number
clientHeight: number
},
) => {
const { data } = params

View File

@ -1,9 +1,9 @@
import { useCallback } from 'react'
import { useStoreApi } from 'reactflow'
import { produce } from 'immer'
import type {
NodeFinishedResponse,
} from '@/types/workflow'
import { produce } from 'immer'
import { useCallback } from 'react'
import { useStoreApi } from 'reactflow'
import { useWorkflowStore } from '@/app/components/workflow/store'
export const useWorkflowNodeRetry = () => {

View File

@ -1,12 +1,12 @@
import type { NodeStartedResponse } from '@/types/workflow'
import { produce } from 'immer'
import { useCallback } from 'react'
import {
useReactFlow,
useStoreApi,
} from 'reactflow'
import { produce } from 'immer'
import type { NodeStartedResponse } from '@/types/workflow'
import { NodeRunningStatus } from '@/app/components/workflow/types'
import { useWorkflowStore } from '@/app/components/workflow/store'
import { NodeRunningStatus } from '@/app/components/workflow/types'
export const useWorkflowNodeStarted = () => {
const store = useStoreApi()
@ -16,8 +16,8 @@ export const useWorkflowNodeStarted = () => {
const handleWorkflowNodeStarted = useCallback((
params: NodeStartedResponse,
containerParams: {
clientWidth: number,
clientHeight: number,
clientWidth: number
clientHeight: number
},
) => {
const { data } = params

View File

@ -1,9 +1,9 @@
import type { WorkflowStartedResponse } from '@/types/workflow'
import { produce } from 'immer'
import { useCallback } from 'react'
import { useStoreApi } from 'reactflow'
import { produce } from 'immer'
import type { WorkflowStartedResponse } from '@/types/workflow'
import { WorkflowRunningStatus } from '@/app/components/workflow/types'
import { useWorkflowStore } from '@/app/components/workflow/store'
import { WorkflowRunningStatus } from '@/app/components/workflow/types'
export const useWorkflowStarted = () => {
const store = useStoreApi()

View File

@ -1,6 +1,6 @@
import { useCallback } from 'react'
import { produce } from 'immer'
import type { TextChunkResponse } from '@/types/workflow'
import { produce } from 'immer'
import { useCallback } from 'react'
import { useWorkflowStore } from '@/app/components/workflow/store'
export const useWorkflowTextChunk = () => {

View File

@ -1,6 +1,6 @@
import { useCallback } from 'react'
import { produce } from 'immer'
import type { TextReplaceResponse } from '@/types/workflow'
import { produce } from 'immer'
import { useCallback } from 'react'
import { useWorkflowStore } from '@/app/components/workflow/store'
export const useWorkflowTextReplace = () => {

View File

@ -1,23 +1,23 @@
'use client'
import type { LLMNodeType } from '../nodes/llm/types'
import type { CommonNodeType } from '../types'
import type { Emoji } from '@/app/components/tools/types'
import { useCallback, useEffect, useMemo } from 'react'
import { useNodes } from 'reactflow'
import { useNodesInteractions } from './use-nodes-interactions'
import type { CommonNodeType } from '../types'
import { workflowNodesAction } from '@/app/components/goto-anything/actions/workflow-nodes'
import BlockIcon from '@/app/components/workflow/block-icon'
import { setupNodeSelectionListener } from '../utils/node-navigation'
import { BlockEnum } from '../types'
import type { Emoji } from '@/app/components/tools/types'
import { CollectionType } from '@/app/components/tools/types'
import { canFindTool } from '@/utils'
import type { LLMNodeType } from '../nodes/llm/types'
import BlockIcon from '@/app/components/workflow/block-icon'
import {
useAllBuiltInTools,
useAllCustomTools,
useAllMCPTools,
useAllWorkflowTools,
} from '@/service/use-tools'
import { canFindTool } from '@/utils'
import { BlockEnum } from '../types'
import { setupNodeSelectionListener } from '../utils/node-navigation'
import { useNodesInteractions } from './use-nodes-interactions'
/**
* Hook to register workflow nodes search functionality
@ -34,7 +34,8 @@ export const useWorkflowSearch = () => {
// Extract tool icon logic - clean separation of concerns
const getToolIcon = useCallback((nodeData: CommonNodeType): string | Emoji | undefined => {
if (nodeData?.type !== BlockEnum.Tool) return undefined
if (nodeData?.type !== BlockEnum.Tool)
return undefined
const toolCollections: Record<string, any[]> = {
[CollectionType.builtIn]: buildInTools || [],
@ -48,19 +49,23 @@ export const useWorkflowSearch = () => {
// Extract model info logic - clean extraction
const getModelInfo = useCallback((nodeData: CommonNodeType) => {
if (nodeData?.type !== BlockEnum.LLM) return {}
if (nodeData?.type !== BlockEnum.LLM)
return {}
const llmNodeData = nodeData as LLMNodeType
return llmNodeData.model ? {
provider: llmNodeData.model.provider,
name: llmNodeData.model.name,
mode: llmNodeData.model.mode,
} : {}
return llmNodeData.model
? {
provider: llmNodeData.model.provider,
name: llmNodeData.model.name,
mode: llmNodeData.model.mode,
}
: {}
}, [])
const searchableNodes = useMemo(() => {
const filteredNodes = nodes.filter((node) => {
if (!node.id || !node.data || node.type === 'sticky') return false
if (!node.id || !node.data || node.type === 'sticky')
return false
const nodeData = node.data as CommonNodeType
const nodeType = nodeData?.type
@ -87,12 +92,13 @@ export const useWorkflowSearch = () => {
// Calculate search score - clean scoring logic
const calculateScore = useCallback((node: {
title: string;
type: string;
desc: string;
modelInfo: { provider?: string; name?: string; mode?: string }
title: string
type: string
desc: string
modelInfo: { provider?: string, name?: string, mode?: string }
}, searchTerm: string): number => {
if (!searchTerm) return 1
if (!searchTerm)
return 1
const titleMatch = node.title.toLowerCase()
const typeMatch = node.type.toLowerCase()
@ -104,27 +110,36 @@ export const useWorkflowSearch = () => {
let score = 0
// Title matching (exact prefix > partial match)
if (titleMatch.startsWith(searchTerm)) score += 100
else if (titleMatch.includes(searchTerm)) score += 50
if (titleMatch.startsWith(searchTerm))
score += 100
else if (titleMatch.includes(searchTerm))
score += 50
// Type matching (exact > partial)
if (typeMatch === searchTerm) score += 80
else if (typeMatch.includes(searchTerm)) score += 30
if (typeMatch === searchTerm)
score += 80
else if (typeMatch.includes(searchTerm))
score += 30
// Description matching (additive)
if (descMatch.includes(searchTerm)) score += 20
if (descMatch.includes(searchTerm))
score += 20
// LLM model matching (additive - can combine multiple matches)
if (modelNameMatch && modelNameMatch.includes(searchTerm)) score += 60
if (modelProviderMatch && modelProviderMatch.includes(searchTerm)) score += 40
if (modelModeMatch && modelModeMatch.includes(searchTerm)) score += 30
if (modelNameMatch && modelNameMatch.includes(searchTerm))
score += 60
if (modelProviderMatch && modelProviderMatch.includes(searchTerm))
score += 40
if (modelModeMatch && modelModeMatch.includes(searchTerm))
score += 30
return score
}, [])
// Create search function for workflow nodes
const searchWorkflowNodes = useCallback((query: string) => {
if (!searchableNodes.length) return []
if (!searchableNodes.length)
return []
const searchTerm = query.toLowerCase().trim()
@ -132,32 +147,35 @@ export const useWorkflowSearch = () => {
.map((node) => {
const score = calculateScore(node, searchTerm)
return score > 0 ? {
id: node.id,
title: node.title,
description: node.desc || node.type,
type: 'workflow-node' as const,
path: `#${node.id}`,
icon: (
<BlockIcon
type={node.blockType}
className="shrink-0"
size="sm"
toolIcon={node.toolIcon}
/>
),
metadata: {
nodeId: node.id,
nodeData: node.nodeData,
},
data: node.nodeData,
score,
} : null
return score > 0
? {
id: node.id,
title: node.title,
description: node.desc || node.type,
type: 'workflow-node' as const,
path: `#${node.id}`,
icon: (
<BlockIcon
type={node.blockType}
className="shrink-0"
size="sm"
toolIcon={node.toolIcon}
/>
),
metadata: {
nodeId: node.id,
nodeData: node.nodeData,
},
data: node.nodeData,
score,
}
: null
})
.filter((node): node is NonNullable<typeof node> => node !== null)
.sort((a, b) => {
// If no search term, sort alphabetically
if (!searchTerm) return a.title.localeCompare(b.title)
if (!searchTerm)
return a.title.localeCompare(b.title)
// Sort by relevance score (higher score first)
return (b.score || 0) - (a.score || 0)
})

View File

@ -1,23 +1,23 @@
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import { useWorkflowStore } from '../store'
import { getVarType, toNodeAvailableVars } from '@/app/components/workflow/nodes/_base/components/variable/utils'
import type { Type } from '../nodes/llm/types'
import type {
Node,
NodeOutPutVar,
ValueSelector,
Var,
} from '@/app/components/workflow/types'
import { useIsChatMode } from './use-workflow'
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import { useStoreApi } from 'reactflow'
import type { Type } from '../nodes/llm/types'
import useMatchSchemaType from '../nodes/_base/components/variable/use-match-schema-type'
import { getVarType, toNodeAvailableVars } from '@/app/components/workflow/nodes/_base/components/variable/utils'
import {
useAllBuiltInTools,
useAllCustomTools,
useAllMCPTools,
useAllWorkflowTools,
} from '@/service/use-tools'
import useMatchSchemaType from '../nodes/_base/components/variable/use-match-schema-type'
import { useWorkflowStore } from '../store'
import { useIsChatMode } from './use-workflow'
export const useWorkflowVariables = () => {
const { t } = useTranslation()
@ -137,8 +137,8 @@ export const useWorkflowVariableType = () => {
nodeId,
valueSelector,
}: {
nodeId: string,
valueSelector: ValueSelector,
nodeId: string
valueSelector: ValueSelector
}) => {
const node = getNodes().find(n => n.id === nodeId)
const isInIteration = !!node?.data.isInIteration

View File

@ -1,46 +1,46 @@
import {
useCallback,
} from 'react'
import { uniqBy } from 'lodash-es'
import {
getIncomers,
getOutgoers,
useStoreApi,
} from 'reactflow'
import type {
Connection,
} from 'reactflow'
import type { IterationNodeType } from '../nodes/iteration/types'
import type { LoopNodeType } from '../nodes/loop/types'
import type {
BlockEnum,
Edge,
Node,
ValueSelector,
} from '../types'
import { uniqBy } from 'lodash-es'
import {
WorkflowRunningStatus,
} from '../types'
useCallback,
} from 'react'
import {
getIncomers,
getOutgoers,
useStoreApi,
} from 'reactflow'
import { useStore as useAppStore } from '@/app/components/app/store'
import { CUSTOM_ITERATION_START_NODE } from '@/app/components/workflow/nodes/iteration-start/constants'
import { CUSTOM_LOOP_START_NODE } from '@/app/components/workflow/nodes/loop-start/constants'
import { AppModeEnum } from '@/types/app'
import { useNodesMetaData } from '.'
import {
SUPPORT_OUTPUT_VARS_NODE,
} from '../constants'
import { findUsedVarNodes, getNodeOutputVars, updateNodeVars } from '../nodes/_base/components/variable/utils'
import { CUSTOM_NOTE_NODE } from '../note-node/constants'
import {
useStore,
useWorkflowStore,
} from '../store'
import {
WorkflowRunningStatus,
} from '../types'
import {
getWorkflowEntryNode,
isWorkflowEntryNode,
} from '../utils/workflow-entry'
import {
SUPPORT_OUTPUT_VARS_NODE,
} from '../constants'
import type { IterationNodeType } from '../nodes/iteration/types'
import type { LoopNodeType } from '../nodes/loop/types'
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 { CUSTOM_ITERATION_START_NODE } from '@/app/components/workflow/nodes/iteration-start/constants'
import { CUSTOM_LOOP_START_NODE } from '@/app/components/workflow/nodes/loop-start/constants'
import { useNodesMetaData } from '.'
import { AppModeEnum } from '@/types/app'
export const useIsChatMode = () => {
const appDetail = useAppStore(s => s.appDetail)