From 92c54d3c9db81f7ac4c42f623e8e84cffabb2c5b Mon Sep 17 00:00:00 2001 From: zhsama Date: Mon, 19 Jan 2026 23:56:15 +0800 Subject: [PATCH] feat: merge app and meta defaults when creating workflow nodes --- .../components/agent-node-list/index.tsx | 8 ++- .../variable/var-reference-vars.tsx | 8 ++- .../mixed-variable-text-input/index.tsx | 25 ++++++--- web/app/components/workflow/utils/node.ts | 56 +++++++++++++++++++ web/eslint-suppressions.json | 2 +- 5 files changed, 84 insertions(+), 15 deletions(-) diff --git a/web/app/components/workflow/nodes/_base/components/agent-node-list/index.tsx b/web/app/components/workflow/nodes/_base/components/agent-node-list/index.tsx index c21510483f..b01fab0aa2 100644 --- a/web/app/components/workflow/nodes/_base/components/agent-node-list/index.tsx +++ b/web/app/components/workflow/nodes/_base/components/agent-node-list/index.tsx @@ -199,9 +199,11 @@ const AgentNodeList: FC = ({ onSelect={onSelect} isHighlighted={enableKeyboardNavigation && index === activeIndex} onSetHighlight={enableKeyboardNavigation ? () => setActiveIndex(index) : undefined} - registerRef={enableKeyboardNavigation ? (element) => { - itemRefs.current[index] = element - } : undefined} + registerRef={enableKeyboardNavigation + ? (element) => { + itemRefs.current[index] = element + } + : undefined} /> ))} diff --git a/web/app/components/workflow/nodes/_base/components/variable/var-reference-vars.tsx b/web/app/components/workflow/nodes/_base/components/variable/var-reference-vars.tsx index 57b31094bd..9b257518a0 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/var-reference-vars.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/var-reference-vars.tsx @@ -531,9 +531,11 @@ const VarReferenceVars: FC = ({ preferSchemaType={preferSchemaType} isHighlighted={enableKeyboardNavigation && itemIndex === activeIndex} onSetHighlight={enableKeyboardNavigation ? () => setActiveIndex(itemIndex) : undefined} - registerRef={enableKeyboardNavigation ? (element) => { - itemRefs.current[itemIndex] = element - } : undefined} + registerRef={enableKeyboardNavigation + ? (element) => { + itemRefs.current[itemIndex] = element + } + : undefined} /> ) })} diff --git a/web/app/components/workflow/nodes/tool/components/mixed-variable-text-input/index.tsx b/web/app/components/workflow/nodes/tool/components/mixed-variable-text-input/index.tsx index 703559a2ee..cf3dcdef18 100644 --- a/web/app/components/workflow/nodes/tool/components/mixed-variable-text-input/index.tsx +++ b/web/app/components/workflow/nodes/tool/components/mixed-variable-text-input/index.tsx @@ -26,7 +26,7 @@ import { VarKindType as VarKindTypeEnum } from '@/app/components/workflow/nodes/ import { Type } from '@/app/components/workflow/nodes/llm/types' import { useStore } from '@/app/components/workflow/store' import { BlockEnum, EditionType, isPromptMessageContext, PromptRole, VarType } from '@/app/components/workflow/types' -import { generateNewNode, getNodeCustomTypeByNodeDataType } from '@/app/components/workflow/utils' +import { generateNewNode, getNodeCustomTypeByNodeDataType, mergeNodeDefaultData } from '@/app/components/workflow/utils' import { useGetLanguage } from '@/context/i18n' import { useStrategyProviders } from '@/service/use-strategy' import { cn } from '@/utils/classnames' @@ -169,6 +169,7 @@ const MixedVariableTextInput = ({ const nodes = useNodes() const controlPromptEditorRerenderKey = useStore(s => s.controlPromptEditorRerenderKey) const setControlPromptEditorRerenderKey = useStore(s => s.setControlPromptEditorRerenderKey) + const nodesDefaultConfigs = useStore(s => s.nodesDefaultConfigs) const { nodesMap: nodesMetaDataMap } = useNodesMetaData() const { handleSyncWorkflowDraft } = useNodesSyncDraft() const [isSubGraphModalOpen, setIsSubGraphModalOpen] = useState(false) @@ -219,8 +220,9 @@ const MixedVariableTextInput = ({ }) => { if (!toolNodeId) return null - const defaultValue = nodesMetaDataMap?.[payload.nodeType]?.defaultValue as Partial | undefined - if (!defaultValue) + const metaDefault = nodesMetaDataMap?.[payload.nodeType]?.defaultValue as Partial | undefined + const appDefault = nodesDefaultConfigs?.[payload.nodeType] as Partial | undefined + if (!metaDefault && !appDefault) return null const { getNodes, setNodes } = reactFlowStore.getState() @@ -231,15 +233,22 @@ const MixedVariableTextInput = ({ const nextNodes = shouldReplace ? currentNodes.filter(node => node.id !== payload.extractorNodeId) : currentNodes + const mergedData = mergeNodeDefaultData({ + nodeType: payload.nodeType, + metaDefault, + appDefault, + overrideData: payload.data, + }) + const resolvedTitle = mergedData.title ?? metaDefault?.title ?? appDefault?.title ?? '' + const resolvedDesc = mergedData.desc ?? metaDefault?.desc ?? appDefault?.desc ?? '' const { newNode } = generateNewNode({ id: payload.extractorNodeId, type: getNodeCustomTypeByNodeDataType(payload.nodeType), data: { - ...defaultValue, - ...payload.data, + ...mergedData, type: payload.nodeType, - title: defaultValue?.title ?? '', - desc: defaultValue.desc || '', + title: resolvedTitle, + desc: resolvedDesc, parent_node_id: toolNodeId, }, position: { @@ -254,7 +263,7 @@ const MixedVariableTextInput = ({ } return existingNode - }, [handleSyncWorkflowDraft, nodesMetaDataMap, reactFlowStore, toolNodeId]) + }, [handleSyncWorkflowDraft, nodesDefaultConfigs, nodesMetaDataMap, reactFlowStore, toolNodeId]) const ensureAssembleExtractorNode = useCallback(() => { if (!assembleExtractorNodeId) diff --git a/web/app/components/workflow/utils/node.ts b/web/app/components/workflow/utils/node.ts index 08ffb1ab1f..72f559835c 100644 --- a/web/app/components/workflow/utils/node.ts +++ b/web/app/components/workflow/utils/node.ts @@ -1,6 +1,8 @@ +import type { CodeNodeType, OutputVar } from '../nodes/code/types' import type { IterationNodeType } from '../nodes/iteration/types' import type { LoopNodeType } from '../nodes/loop/types' import type { + CommonNodeType, Node, } from '../types' import { @@ -20,6 +22,60 @@ import { BlockEnum, } from '../types' +type MergeNodeDefaultDataParams>> = { + nodeType: BlockEnum + metaDefault?: Partial + appDefault?: Partial + baseData?: Partial + overrideData?: Partial +} + +const pickNonEmptyArray = (value?: T[]) => { + return Array.isArray(value) && value.length > 0 ? value : undefined +} + +export const mergeNodeDefaultData = >>({ + nodeType, + metaDefault, + appDefault, + baseData, + overrideData, +}: MergeNodeDefaultDataParams) => { + const merged = { + ...(metaDefault || {}), + ...(appDefault || {}), + ...(baseData || {}), + ...(overrideData || {}), + } as Partial + + if (nodeType === BlockEnum.Code) { + const codeMetaDefault = (metaDefault || {}) as Partial + const codeAppDefault = (appDefault || {}) as Partial + const codeBase = (baseData || {}) as Partial + const codeOverride = (overrideData || {}) as Partial + const codeDefaults = { + ...codeMetaDefault, + ...codeAppDefault, + } + + const outputs: OutputVar = { + ...(codeDefaults.outputs || {}), + ...(codeBase.outputs || {}), + ...(codeOverride.outputs || {}), + } + if (Object.keys(outputs).length > 0) + (merged as Partial).outputs = outputs + + const resolvedVariables = pickNonEmptyArray(codeBase.variables) + ?? pickNonEmptyArray(codeOverride.variables) + ?? pickNonEmptyArray(codeDefaults.variables) + if (resolvedVariables) + (merged as Partial).variables = resolvedVariables + } + + return merged +} + export function generateNewNode({ data, position, id, zIndex, type, ...rest }: Omit, 'id'> & { id?: string }): { newNode: Node newIterationStartNode?: Node diff --git a/web/eslint-suppressions.json b/web/eslint-suppressions.json index 72b07644a9..6d8a20de38 100644 --- a/web/eslint-suppressions.json +++ b/web/eslint-suppressions.json @@ -4538,4 +4538,4 @@ "count": 2 } } -} +} \ No newline at end of file