mirror of
https://github.com/langgenius/dify.git
synced 2026-05-04 01:18:05 +08:00
Merge remote-tracking branch 'origin/main' into feat/model-plugins-implementing
This commit is contained in:
@ -332,8 +332,7 @@ const Chat: FC<ChatProps> = ({
|
||||
!noStopResponding && isResponding && (
|
||||
<div data-testid="stop-responding-container" className="mb-2 flex justify-center">
|
||||
<Button className="border-components-panel-border bg-components-panel-bg text-components-button-secondary-text" onClick={onStopResponding}>
|
||||
{/* eslint-disable-next-line tailwindcss/no-unknown-classes */}
|
||||
<div className="i-custom-vender-solid-mediaanddevices-stop-circle mr-[5px] h-3.5 w-3.5" />
|
||||
<div className="i-custom-vender-solid-mediaAndDevices-stop-circle mr-[5px] h-3.5 w-3.5" />
|
||||
<span className="text-xs font-normal">{t('operation.stopResponding', { ns: 'appDebug' })}</span>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@ -111,11 +111,11 @@ const ToolItem: FC<Props> = ({
|
||||
})
|
||||
}}
|
||||
>
|
||||
<div className={cn('system-sm-medium h-8 truncate border-l-2 border-divider-subtle pl-4 leading-8 text-text-secondary')}>
|
||||
<div className={cn('truncate border-l-2 border-divider-subtle py-2 pl-4 text-text-secondary system-sm-medium')}>
|
||||
<span className={cn(disabled && 'opacity-30')}>{payload.label[language]}</span>
|
||||
</div>
|
||||
{isAdded && (
|
||||
<div className="system-xs-regular mr-4 text-text-tertiary">{t('addToolModal.added', { ns: 'tools' })}</div>
|
||||
<div className="mr-4 text-text-tertiary system-xs-regular">{t('addToolModal.added', { ns: 'tools' })}</div>
|
||||
)}
|
||||
</div>
|
||||
</Tooltip>
|
||||
|
||||
@ -77,11 +77,11 @@ const TriggerPluginActionItem: FC<Props> = ({
|
||||
})
|
||||
}}
|
||||
>
|
||||
<div className={cn('system-sm-medium h-8 truncate border-l-2 border-divider-subtle pl-4 leading-8 text-text-secondary')}>
|
||||
<div className={cn('truncate border-l-2 border-divider-subtle py-2 pl-4 text-text-secondary system-sm-medium')}>
|
||||
<span className={cn(disabled && 'opacity-30')}>{payload.label[language]}</span>
|
||||
</div>
|
||||
{isAdded && (
|
||||
<div className="system-xs-regular mr-4 text-text-tertiary">{t('addToolModal.added', { ns: 'tools' })}</div>
|
||||
<div className="mr-4 text-text-tertiary system-xs-regular">{t('addToolModal.added', { ns: 'tools' })}</div>
|
||||
)}
|
||||
</div>
|
||||
</Tooltip>
|
||||
|
||||
@ -1422,21 +1422,136 @@ export const useNodesInteractions = () => {
|
||||
extent: currentNode.extent,
|
||||
zIndex: currentNode.zIndex,
|
||||
})
|
||||
const nodesConnectedSourceOrTargetHandleIdsMap
|
||||
= getNodesConnectedSourceOrTargetHandleIdsMap(
|
||||
connectedEdges.map(edge => ({ type: 'remove', edge })),
|
||||
nodes,
|
||||
)
|
||||
const newNodes = produce(nodes, (draft) => {
|
||||
const parentNode = nodes.find(node => node.id === currentNode.parentId)
|
||||
const newNodeIsInIteration
|
||||
= !!parentNode && parentNode.data.type === BlockEnum.Iteration
|
||||
const newNodeIsInLoop
|
||||
= !!parentNode && parentNode.data.type === BlockEnum.Loop
|
||||
const outgoingEdges = connectedEdges.filter(
|
||||
edge => edge.source === currentNodeId,
|
||||
)
|
||||
const normalizedSourceHandle = sourceHandle || 'source'
|
||||
const outgoingHandles = new Set(
|
||||
outgoingEdges.map(edge => edge.sourceHandle || 'source'),
|
||||
)
|
||||
const branchSourceHandle = currentNode.data._targetBranches?.[0]?.id
|
||||
let outgoingHandleToPreserve = normalizedSourceHandle
|
||||
if (!outgoingHandles.has(outgoingHandleToPreserve)) {
|
||||
if (branchSourceHandle && outgoingHandles.has(branchSourceHandle))
|
||||
outgoingHandleToPreserve = branchSourceHandle
|
||||
else if (outgoingHandles.has('source'))
|
||||
outgoingHandleToPreserve = 'source'
|
||||
else
|
||||
outgoingHandleToPreserve = outgoingEdges[0]?.sourceHandle || 'source'
|
||||
}
|
||||
const outgoingEdgesToPreserve = outgoingEdges.filter(
|
||||
edge => (edge.sourceHandle || 'source') === outgoingHandleToPreserve,
|
||||
)
|
||||
const outgoingEdgeIds = new Set(
|
||||
outgoingEdgesToPreserve.map(edge => edge.id),
|
||||
)
|
||||
const newNodeSourceHandle = newCurrentNode.data._targetBranches?.[0]?.id || 'source'
|
||||
const reconnectedEdges = connectedEdges.reduce<Edge[]>(
|
||||
(acc, edge) => {
|
||||
if (outgoingEdgeIds.has(edge.id)) {
|
||||
const originalTargetNode = nodes.find(
|
||||
node => node.id === edge.target,
|
||||
)
|
||||
const targetNodeForEdge
|
||||
= originalTargetNode && originalTargetNode.id !== currentNodeId
|
||||
? originalTargetNode
|
||||
: newCurrentNode
|
||||
if (!targetNodeForEdge)
|
||||
return acc
|
||||
|
||||
const targetHandle = edge.targetHandle || 'target'
|
||||
const targetParentNode
|
||||
= targetNodeForEdge.id === newCurrentNode.id
|
||||
? parentNode || null
|
||||
: nodes.find(node => node.id === targetNodeForEdge.parentId)
|
||||
|| null
|
||||
const isInIteration
|
||||
= !!targetParentNode
|
||||
&& targetParentNode.data.type === BlockEnum.Iteration
|
||||
const isInLoop
|
||||
= !!targetParentNode
|
||||
&& targetParentNode.data.type === BlockEnum.Loop
|
||||
|
||||
acc.push({
|
||||
...edge,
|
||||
id: `${newCurrentNode.id}-${newNodeSourceHandle}-${targetNodeForEdge.id}-${targetHandle}`,
|
||||
source: newCurrentNode.id,
|
||||
sourceHandle: newNodeSourceHandle,
|
||||
target: targetNodeForEdge.id,
|
||||
targetHandle,
|
||||
type: CUSTOM_EDGE,
|
||||
data: {
|
||||
...(edge.data || {}),
|
||||
sourceType: newCurrentNode.data.type,
|
||||
targetType: targetNodeForEdge.data.type,
|
||||
isInIteration,
|
||||
iteration_id: isInIteration
|
||||
? targetNodeForEdge.parentId
|
||||
: undefined,
|
||||
isInLoop,
|
||||
loop_id: isInLoop ? targetNodeForEdge.parentId : undefined,
|
||||
_connectedNodeIsSelected: false,
|
||||
},
|
||||
zIndex: targetNodeForEdge.parentId
|
||||
? isInIteration
|
||||
? ITERATION_CHILDREN_Z_INDEX
|
||||
: LOOP_CHILDREN_Z_INDEX
|
||||
: 0,
|
||||
})
|
||||
}
|
||||
|
||||
if (
|
||||
edge.target === currentNodeId
|
||||
&& edge.source !== currentNodeId
|
||||
&& !outgoingEdgeIds.has(edge.id)
|
||||
) {
|
||||
const sourceNode = nodes.find(node => node.id === edge.source)
|
||||
if (!sourceNode)
|
||||
return acc
|
||||
|
||||
const targetHandle = edge.targetHandle || 'target'
|
||||
const sourceHandle = edge.sourceHandle || 'source'
|
||||
|
||||
acc.push({
|
||||
...edge,
|
||||
id: `${sourceNode.id}-${sourceHandle}-${newCurrentNode.id}-${targetHandle}`,
|
||||
source: sourceNode.id,
|
||||
sourceHandle,
|
||||
target: newCurrentNode.id,
|
||||
targetHandle,
|
||||
type: CUSTOM_EDGE,
|
||||
data: {
|
||||
...(edge.data || {}),
|
||||
sourceType: sourceNode.data.type,
|
||||
targetType: newCurrentNode.data.type,
|
||||
isInIteration: newNodeIsInIteration,
|
||||
iteration_id: newNodeIsInIteration
|
||||
? newCurrentNode.parentId
|
||||
: undefined,
|
||||
isInLoop: newNodeIsInLoop,
|
||||
loop_id: newNodeIsInLoop ? newCurrentNode.parentId : undefined,
|
||||
_connectedNodeIsSelected: false,
|
||||
},
|
||||
zIndex: newCurrentNode.parentId
|
||||
? newNodeIsInIteration
|
||||
? ITERATION_CHILDREN_Z_INDEX
|
||||
: LOOP_CHILDREN_Z_INDEX
|
||||
: 0,
|
||||
})
|
||||
}
|
||||
|
||||
return acc
|
||||
},
|
||||
[],
|
||||
)
|
||||
const nodesWithNewNode = produce(nodes, (draft) => {
|
||||
draft.forEach((node) => {
|
||||
node.data.selected = false
|
||||
|
||||
if (nodesConnectedSourceOrTargetHandleIdsMap[node.id]) {
|
||||
node.data = {
|
||||
...node.data,
|
||||
...nodesConnectedSourceOrTargetHandleIdsMap[node.id],
|
||||
}
|
||||
}
|
||||
})
|
||||
const index = draft.findIndex(node => node.id === currentNodeId)
|
||||
|
||||
@ -1446,18 +1561,32 @@ export const useNodesInteractions = () => {
|
||||
if (newLoopStartNode)
|
||||
draft.push(newLoopStartNode)
|
||||
})
|
||||
setNodes(newNodes)
|
||||
const newEdges = produce(edges, (draft) => {
|
||||
const filtered = draft.filter(
|
||||
edge =>
|
||||
!connectedEdges.find(
|
||||
connectedEdge => connectedEdge.id === edge.id,
|
||||
),
|
||||
const nodesConnectedSourceOrTargetHandleIdsMap
|
||||
= getNodesConnectedSourceOrTargetHandleIdsMap(
|
||||
[
|
||||
...connectedEdges.map(edge => ({ type: 'remove', edge })),
|
||||
...reconnectedEdges.map(edge => ({ type: 'add', edge })),
|
||||
],
|
||||
nodesWithNewNode,
|
||||
)
|
||||
|
||||
return filtered
|
||||
const newNodes = produce(nodesWithNewNode, (draft) => {
|
||||
draft.forEach((node) => {
|
||||
if (nodesConnectedSourceOrTargetHandleIdsMap[node.id]) {
|
||||
node.data = {
|
||||
...node.data,
|
||||
...nodesConnectedSourceOrTargetHandleIdsMap[node.id],
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
setEdges(newEdges)
|
||||
setNodes(newNodes)
|
||||
const remainingEdges = edges.filter(
|
||||
edge =>
|
||||
!connectedEdges.find(
|
||||
connectedEdge => connectedEdge.id === edge.id,
|
||||
),
|
||||
)
|
||||
setEdges([...remainingEdges, ...reconnectedEdges])
|
||||
if (nodeType === BlockEnum.TriggerWebhook) {
|
||||
handleSyncWorkflowDraft(true, true, {
|
||||
onSuccess: () => autoGenerateWebhookUrl(newCurrentNode.id),
|
||||
@ -1606,6 +1735,7 @@ export const useNodesInteractions = () => {
|
||||
const offsetX = currentPosition.x - x
|
||||
const offsetY = currentPosition.y - y
|
||||
let idMapping: Record<string, string> = {}
|
||||
const pastedNodesMap: Record<string, Node> = {}
|
||||
const parentChildrenToAppend: { parentId: string, childId: string, childType: BlockEnum }[] = []
|
||||
clipboardElements.forEach((nodeToPaste, index) => {
|
||||
const nodeType = nodeToPaste.data.type
|
||||
@ -1665,7 +1795,21 @@ export const useNodesInteractions = () => {
|
||||
newLoopStartNode!.parentId = newNode.id;
|
||||
(newNode.data as LoopNodeType).start_node_id = newLoopStartNode!.id
|
||||
|
||||
newChildren = handleNodeLoopChildrenCopy(nodeToPaste.id, newNode.id)
|
||||
const oldLoopStartNode = nodes.find(
|
||||
n =>
|
||||
n.parentId === nodeToPaste.id
|
||||
&& n.type === CUSTOM_LOOP_START_NODE,
|
||||
)
|
||||
idMapping[oldLoopStartNode!.id] = newLoopStartNode!.id
|
||||
|
||||
const { copyChildren, newIdMapping }
|
||||
= handleNodeLoopChildrenCopy(
|
||||
nodeToPaste.id,
|
||||
newNode.id,
|
||||
idMapping,
|
||||
)
|
||||
newChildren = copyChildren
|
||||
idMapping = newIdMapping
|
||||
newChildren.forEach((child) => {
|
||||
newNode.data._children?.push({
|
||||
nodeId: child.id,
|
||||
@ -1710,18 +1854,31 @@ export const useNodesInteractions = () => {
|
||||
}
|
||||
}
|
||||
|
||||
idMapping[nodeToPaste.id] = newNode.id
|
||||
nodesToPaste.push(newNode)
|
||||
pastedNodesMap[newNode.id] = newNode
|
||||
|
||||
if (newChildren.length)
|
||||
if (newChildren.length) {
|
||||
newChildren.forEach((child) => {
|
||||
pastedNodesMap[child.id] = child
|
||||
})
|
||||
nodesToPaste.push(...newChildren)
|
||||
}
|
||||
})
|
||||
|
||||
// only handle edge when paste nested block
|
||||
// Rebuild edges where both endpoints are part of the pasted set.
|
||||
edges.forEach((edge) => {
|
||||
const sourceId = idMapping[edge.source]
|
||||
const targetId = idMapping[edge.target]
|
||||
|
||||
if (sourceId && targetId) {
|
||||
const sourceNode = pastedNodesMap[sourceId]
|
||||
const targetNode = pastedNodesMap[targetId]
|
||||
const parentNode = sourceNode?.parentId && sourceNode.parentId === targetNode?.parentId
|
||||
? pastedNodesMap[sourceNode.parentId] ?? nodes.find(n => n.id === sourceNode.parentId)
|
||||
: null
|
||||
const isInIteration = parentNode?.data.type === BlockEnum.Iteration
|
||||
const isInLoop = parentNode?.data.type === BlockEnum.Loop
|
||||
const newEdge: Edge = {
|
||||
...edge,
|
||||
id: `${sourceId}-${edge.sourceHandle}-${targetId}-${edge.targetHandle}`,
|
||||
@ -1729,8 +1886,19 @@ export const useNodesInteractions = () => {
|
||||
target: targetId,
|
||||
data: {
|
||||
...edge.data,
|
||||
isInIteration,
|
||||
iteration_id: isInIteration ? parentNode?.id : undefined,
|
||||
isInLoop,
|
||||
loop_id: isInLoop ? parentNode?.id : undefined,
|
||||
_connectedNodeIsSelected: false,
|
||||
},
|
||||
zIndex: parentNode
|
||||
? isInIteration
|
||||
? ITERATION_CHILDREN_Z_INDEX
|
||||
: isInLoop
|
||||
? LOOP_CHILDREN_Z_INDEX
|
||||
: 0
|
||||
: 0,
|
||||
}
|
||||
edgesToPaste.push(newEdge)
|
||||
}
|
||||
|
||||
@ -108,12 +108,13 @@ export const useNodeLoopInteractions = () => {
|
||||
handleNodeLoopRerender(parentId)
|
||||
}, [store, handleNodeLoopRerender])
|
||||
|
||||
const handleNodeLoopChildrenCopy = useCallback((nodeId: string, newNodeId: string) => {
|
||||
const handleNodeLoopChildrenCopy = useCallback((nodeId: string, newNodeId: string, idMapping: Record<string, string>) => {
|
||||
const { getNodes } = store.getState()
|
||||
const nodes = getNodes()
|
||||
const childrenNodes = nodes.filter(n => n.parentId === nodeId && n.type !== CUSTOM_LOOP_START_NODE)
|
||||
const newIdMapping = { ...idMapping }
|
||||
|
||||
return childrenNodes.map((child, index) => {
|
||||
const copyChildren = childrenNodes.map((child, index) => {
|
||||
const childNodeType = child.data.type as BlockEnum
|
||||
const { defaultValue } = nodesMetaDataMap![childNodeType]
|
||||
const nodesWithSameType = nodes.filter(node => node.data.type === childNodeType)
|
||||
@ -139,8 +140,14 @@ export const useNodeLoopInteractions = () => {
|
||||
zIndex: LOOP_CHILDREN_Z_INDEX,
|
||||
})
|
||||
newNode.id = `${newNodeId}${newNode.id + index}`
|
||||
newIdMapping[child.id] = newNode.id
|
||||
return newNode
|
||||
})
|
||||
|
||||
return {
|
||||
copyChildren,
|
||||
newIdMapping,
|
||||
}
|
||||
}, [store, nodesMetaDataMap])
|
||||
|
||||
return {
|
||||
|
||||
@ -6319,9 +6319,6 @@
|
||||
"app/components/workflow/block-selector/tool/action-item.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 1
|
||||
},
|
||||
"tailwindcss/enforce-consistent-class-order": {
|
||||
"count": 2
|
||||
}
|
||||
},
|
||||
"app/components/workflow/block-selector/tool/tool-list-flat-view/list.tsx": {
|
||||
@ -6341,9 +6338,6 @@
|
||||
"no-restricted-imports": {
|
||||
"count": 1
|
||||
},
|
||||
"tailwindcss/enforce-consistent-class-order": {
|
||||
"count": 2
|
||||
},
|
||||
"ts/no-explicit-any": {
|
||||
"count": 1
|
||||
}
|
||||
|
||||
@ -228,7 +228,7 @@
|
||||
"eslint-plugin-sonarjs": "4.0.0",
|
||||
"eslint-plugin-storybook": "10.2.13",
|
||||
"husky": "9.1.7",
|
||||
"iconify-import-svg": "0.1.1",
|
||||
"iconify-import-svg": "0.1.2",
|
||||
"jsdom": "27.3.0",
|
||||
"jsdom-testing-mocks": "1.16.0",
|
||||
"knip": "5.78.0",
|
||||
|
||||
10
web/pnpm-lock.yaml
generated
10
web/pnpm-lock.yaml
generated
@ -552,8 +552,8 @@ importers:
|
||||
specifier: 9.1.7
|
||||
version: 9.1.7
|
||||
iconify-import-svg:
|
||||
specifier: 0.1.1
|
||||
version: 0.1.1
|
||||
specifier: 0.1.2
|
||||
version: 0.1.2
|
||||
jsdom:
|
||||
specifier: 27.3.0
|
||||
version: 27.3.0(canvas@3.2.1)
|
||||
@ -5263,8 +5263,8 @@ packages:
|
||||
typescript:
|
||||
optional: true
|
||||
|
||||
iconify-import-svg@0.1.1:
|
||||
resolution: {integrity: sha512-8HwZIe3ZqCfZ68NZUCnHN264fwHWhE+O5hWDfBtOEY7u1V97yOogHaoXGRLOx17M0c8+z65xYqJXA16ieCYIwA==}
|
||||
iconify-import-svg@0.1.2:
|
||||
resolution: {integrity: sha512-8dwxdGK1a7oPDQhLQOPTbx51tpkxYB6HZvf4fxWz2QVYqEtgop0FWE7OXQ+4zqnrTVUpMIGnOsvqIHtPBK9Isw==}
|
||||
|
||||
iconv-lite@0.6.3:
|
||||
resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
|
||||
@ -13145,7 +13145,7 @@ snapshots:
|
||||
optionalDependencies:
|
||||
typescript: 5.9.3
|
||||
|
||||
iconify-import-svg@0.1.1:
|
||||
iconify-import-svg@0.1.2:
|
||||
dependencies:
|
||||
'@iconify/tools': 4.2.0
|
||||
'@iconify/types': 2.0.0
|
||||
|
||||
@ -14,11 +14,16 @@ const _dirname = typeof __dirname !== 'undefined'
|
||||
: path.dirname(fileURLToPath(import.meta.url))
|
||||
|
||||
const disableSVGOptimize = process.env.TAILWIND_MODE === 'ESLINT'
|
||||
const parseColorOptions = {
|
||||
fallback: () => 'currentColor',
|
||||
}
|
||||
const svgOptimizeConfig = {
|
||||
cleanupSVG: !disableSVGOptimize,
|
||||
deOptimisePaths: !disableSVGOptimize,
|
||||
runSVGO: !disableSVGOptimize,
|
||||
parseColors: !disableSVGOptimize,
|
||||
parseColors: !disableSVGOptimize
|
||||
? parseColorOptions
|
||||
: false,
|
||||
}
|
||||
|
||||
const config = {
|
||||
|
||||
Reference in New Issue
Block a user