diff --git a/web/app/components/workflow/hooks/use-nodes-interactions.ts b/web/app/components/workflow/hooks/use-nodes-interactions.ts
index e0e4f69c78..a1951e46c2 100644
--- a/web/app/components/workflow/hooks/use-nodes-interactions.ts
+++ b/web/app/components/workflow/hooks/use-nodes-interactions.ts
@@ -466,8 +466,7 @@ export const useNodesInteractions = () => {
const prevNode = nodes[prevNodeIndex]
const outgoers = getOutgoers(prevNode, nodes, edges).sort((a, b) => a.position.y - b.position.y)
const lastOutgoer = outgoers[outgoers.length - 1]
- if (prevNode.data.type === BlockEnum.KnowledgeRetrieval)
- targetHandle = prevNodeId
+
newNode.data._connectedTargetHandleIds = [targetHandle]
newNode.data._connectedSourceHandleIds = []
newNode.position = {
@@ -593,8 +592,7 @@ export const useNodesInteractions = () => {
if (prevNodeId && nextNodeId) {
const prevNode = nodes.find(node => node.id === prevNodeId)!
const nextNode = nodes.find(node => node.id === nextNodeId)!
- if (prevNode.data.type === BlockEnum.KnowledgeRetrieval)
- targetHandle = prevNodeId
+
newNode.data._connectedTargetHandleIds = [targetHandle]
newNode.data._connectedSourceHandleIds = [sourceHandle]
newNode.position = {
@@ -925,10 +923,6 @@ export const useNodesInteractions = () => {
edges,
} = store.getState()
- const edgeSelected = edges.some(edge => edge.selected)
- if (edgeSelected)
- return
-
const nodes = getNodes()
const bundledNodes = nodes.filter(node => node.data._isBundled && node.data.type !== BlockEnum.Start)
@@ -937,6 +931,10 @@ export const useNodesInteractions = () => {
return
}
+ const edgeSelected = edges.some(edge => edge.selected)
+ if (edgeSelected)
+ return
+
const selectedNode = nodes.find(node => node.data.selected && node.data.type !== BlockEnum.Start)
if (selectedNode)
diff --git a/web/app/components/workflow/hooks/use-selection-interactions.ts b/web/app/components/workflow/hooks/use-selection-interactions.ts
index 38fd4f497b..36aa0485ae 100644
--- a/web/app/components/workflow/hooks/use-selection-interactions.ts
+++ b/web/app/components/workflow/hooks/use-selection-interactions.ts
@@ -101,9 +101,40 @@ export const useSelectionInteractions = () => {
setNodes(newNodes)
}, [store, workflowStore])
+ const handleSelectionCancel = useCallback(() => {
+ const {
+ getNodes,
+ setNodes,
+ edges,
+ setEdges,
+ } = store.getState()
+
+ store.setState({
+ userSelectionRect: null,
+ userSelectionActive: true,
+ })
+
+ const nodes = getNodes()
+ const newNodes = produce(nodes, (draft) => {
+ draft.forEach((node) => {
+ if (node.data._isBundled)
+ node.data._isBundled = false
+ })
+ })
+ setNodes(newNodes)
+ const newEdges = produce(edges, (draft) => {
+ draft.forEach((edge) => {
+ if (edge.data._isBundled)
+ edge.data._isBundled = false
+ })
+ })
+ setEdges(newEdges)
+ }, [store])
+
return {
handleSelectionStart,
handleSelectionChange,
handleSelectionDrag,
+ handleSelectionCancel,
}
}
diff --git a/web/app/components/workflow/operator/control.tsx b/web/app/components/workflow/operator/control.tsx
index 94d47129c5..8903239e90 100644
--- a/web/app/components/workflow/operator/control.tsx
+++ b/web/app/components/workflow/operator/control.tsx
@@ -1,10 +1,13 @@
-import { memo } from 'react'
+import { memo, useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import cn from 'classnames'
+import { useKeyPress } from 'ahooks'
import {
useNodesReadOnly,
+ useSelectionInteractions,
useWorkflow,
} from '../hooks'
+import { isEventTargetInputArea } from '../utils'
import { useStore } from '../store'
import AddBlock from './add-block'
import TipPopup from './tip-popup'
@@ -27,6 +30,44 @@ const Control = () => {
nodesReadOnly,
getNodesReadOnly,
} = useNodesReadOnly()
+ const { handleSelectionCancel } = useSelectionInteractions()
+
+ const handleModePointer = useCallback(() => {
+ if (getNodesReadOnly())
+ return
+ setControlMode('pointer')
+ }, [getNodesReadOnly, setControlMode])
+ const handleModeHand = useCallback(() => {
+ if (getNodesReadOnly())
+ return
+ setControlMode('hand')
+ handleSelectionCancel()
+ }, [getNodesReadOnly, setControlMode, handleSelectionCancel])
+
+ useKeyPress('h', (e) => {
+ if (getNodesReadOnly())
+ return
+
+ if (isEventTargetInputArea(e.target as HTMLElement))
+ return
+
+ e.preventDefault()
+ handleModeHand()
+ }, {
+ exactMatch: true,
+ useCapture: true,
+ })
+
+ useKeyPress('v', (e) => {
+ if (isEventTargetInputArea(e.target as HTMLElement))
+ return
+
+ e.preventDefault()
+ handleModePointer()
+ }, {
+ exactMatch: true,
+ useCapture: true,
+ })
const goLayout = () => {
if (getNodesReadOnly())
@@ -45,7 +86,7 @@ const Control = () => {
controlMode === 'pointer' ? 'bg-primary-50 text-primary-600' : 'hover:bg-black/5 hover:text-gray-700',
`${nodesReadOnly && '!cursor-not-allowed opacity-50'}`,
)}
- onClick={() => setControlMode('pointer')}
+ onClick={handleModePointer}
>
{
controlMode === 'pointer' ? :
@@ -59,7 +100,7 @@ const Control = () => {
controlMode === 'hand' ? 'bg-primary-50 text-primary-600' : 'hover:bg-black/5 hover:text-gray-700',
`${nodesReadOnly && '!cursor-not-allowed opacity-50'}`,
)}
- onClick={() => setControlMode('hand')}
+ onClick={handleModeHand}
>
{
controlMode === 'hand' ? :