mirror of
https://git.mirrors.martin98.com/https://github.com/langgenius/dify.git
synced 2025-08-15 08:56:07 +08:00
Feat: shortcut hook (#7385)
This commit is contained in:
parent
68dc6d5bc3
commit
8b06105fa1
@ -35,7 +35,9 @@ const RunMode = memo(() => {
|
|||||||
'hover:bg-state-accent-hover cursor-pointer',
|
'hover:bg-state-accent-hover cursor-pointer',
|
||||||
isRunning && 'bg-state-accent-hover !cursor-not-allowed',
|
isRunning && 'bg-state-accent-hover !cursor-not-allowed',
|
||||||
)}
|
)}
|
||||||
onClick={() => handleWorkflowStartRunInWorkflow()}
|
onClick={() => {
|
||||||
|
handleWorkflowStartRunInWorkflow()
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
isRunning
|
isRunning
|
||||||
|
@ -17,7 +17,7 @@ import {
|
|||||||
useWorkflowInteractions,
|
useWorkflowInteractions,
|
||||||
useWorkflowRun,
|
useWorkflowRun,
|
||||||
} from '../hooks'
|
} from '../hooks'
|
||||||
import { WorkflowRunningStatus } from '../types'
|
import { ControlMode, WorkflowRunningStatus } from '../types'
|
||||||
import cn from '@/utils/classnames'
|
import cn from '@/utils/classnames'
|
||||||
import {
|
import {
|
||||||
PortalToFollowElem,
|
PortalToFollowElem,
|
||||||
@ -58,6 +58,7 @@ const ViewHistory = ({
|
|||||||
handleCancelDebugAndPreviewPanel,
|
handleCancelDebugAndPreviewPanel,
|
||||||
} = useWorkflowInteractions()
|
} = useWorkflowInteractions()
|
||||||
const workflowStore = useWorkflowStore()
|
const workflowStore = useWorkflowStore()
|
||||||
|
const setControlMode = useStore(s => s.setControlMode)
|
||||||
const { appDetail, setCurrentLogItem, setShowMessageLogModal } = useAppStore(useShallow(state => ({
|
const { appDetail, setCurrentLogItem, setShowMessageLogModal } = useAppStore(useShallow(state => ({
|
||||||
appDetail: state.appDetail,
|
appDetail: state.appDetail,
|
||||||
setCurrentLogItem: state.setCurrentLogItem,
|
setCurrentLogItem: state.setCurrentLogItem,
|
||||||
@ -173,6 +174,7 @@ const ViewHistory = ({
|
|||||||
setOpen(false)
|
setOpen(false)
|
||||||
handleNodesCancelSelected()
|
handleNodesCancelSelected()
|
||||||
handleCancelDebugAndPreviewPanel()
|
handleCancelDebugAndPreviewPanel()
|
||||||
|
setControlMode(ControlMode.Hand)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
|
@ -7,11 +7,12 @@ export * from './use-workflow'
|
|||||||
export * from './use-workflow-run'
|
export * from './use-workflow-run'
|
||||||
export * from './use-workflow-template'
|
export * from './use-workflow-template'
|
||||||
export * from './use-checklist'
|
export * from './use-checklist'
|
||||||
export * from './use-workflow-mode'
|
|
||||||
export * from './use-workflow-interactions'
|
|
||||||
export * from './use-selection-interactions'
|
export * from './use-selection-interactions'
|
||||||
export * from './use-panel-interactions'
|
export * from './use-panel-interactions'
|
||||||
export * from './use-workflow-start-run'
|
export * from './use-workflow-start-run'
|
||||||
export * from './use-nodes-layout'
|
export * from './use-nodes-layout'
|
||||||
export * from './use-workflow-history'
|
export * from './use-workflow-history'
|
||||||
export * from './use-workflow-variables'
|
export * from './use-workflow-variables'
|
||||||
|
export * from './use-shortcuts'
|
||||||
|
export * from './use-workflow-interactions'
|
||||||
|
export * from './use-workflow-mode'
|
||||||
|
@ -48,6 +48,7 @@ import { useHelpline } from './use-helpline'
|
|||||||
import {
|
import {
|
||||||
useNodesReadOnly,
|
useNodesReadOnly,
|
||||||
useWorkflow,
|
useWorkflow,
|
||||||
|
useWorkflowReadOnly,
|
||||||
} from './use-workflow'
|
} from './use-workflow'
|
||||||
import { WorkflowHistoryEvent, useWorkflowHistory } from './use-workflow-history'
|
import { WorkflowHistoryEvent, useWorkflowHistory } from './use-workflow-history'
|
||||||
|
|
||||||
@ -62,6 +63,7 @@ export const useNodesInteractions = () => {
|
|||||||
getAfterNodesInSameBranch,
|
getAfterNodesInSameBranch,
|
||||||
} = useWorkflow()
|
} = useWorkflow()
|
||||||
const { getNodesReadOnly } = useNodesReadOnly()
|
const { getNodesReadOnly } = useNodesReadOnly()
|
||||||
|
const { getWorkflowReadOnly } = useWorkflowReadOnly()
|
||||||
const { handleSetHelpline } = useHelpline()
|
const { handleSetHelpline } = useHelpline()
|
||||||
const {
|
const {
|
||||||
handleNodeIterationChildDrag,
|
handleNodeIterationChildDrag,
|
||||||
@ -1029,14 +1031,7 @@ export const useNodesInteractions = () => {
|
|||||||
if (getNodesReadOnly())
|
if (getNodesReadOnly())
|
||||||
return
|
return
|
||||||
|
|
||||||
const {
|
const { setClipboardElements } = workflowStore.getState()
|
||||||
setClipboardElements,
|
|
||||||
shortcutsDisabled,
|
|
||||||
showFeaturesPanel,
|
|
||||||
} = workflowStore.getState()
|
|
||||||
|
|
||||||
if (shortcutsDisabled || showFeaturesPanel)
|
|
||||||
return
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
getNodes,
|
getNodes,
|
||||||
@ -1062,14 +1057,9 @@ export const useNodesInteractions = () => {
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
clipboardElements,
|
clipboardElements,
|
||||||
shortcutsDisabled,
|
|
||||||
showFeaturesPanel,
|
|
||||||
mousePosition,
|
mousePosition,
|
||||||
} = workflowStore.getState()
|
} = workflowStore.getState()
|
||||||
|
|
||||||
if (shortcutsDisabled || showFeaturesPanel)
|
|
||||||
return
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
getNodes,
|
getNodes,
|
||||||
setNodes,
|
setNodes,
|
||||||
@ -1107,6 +1097,11 @@ export const useNodesInteractions = () => {
|
|||||||
})
|
})
|
||||||
newNode.id = newNode.id + index
|
newNode.id = newNode.id + index
|
||||||
|
|
||||||
|
// If only the iteration start node is copied, remove the isIterationStart flag
|
||||||
|
// This new node is movable and can be placed anywhere
|
||||||
|
if (clipboardElements.length === 1 && newNode.data.isIterationStart)
|
||||||
|
newNode.data.isIterationStart = false
|
||||||
|
|
||||||
let newChildren: Node[] = []
|
let newChildren: Node[] = []
|
||||||
if (nodeToPaste.data.type === BlockEnum.Iteration) {
|
if (nodeToPaste.data.type === BlockEnum.Iteration) {
|
||||||
newNode.data._children = [];
|
newNode.data._children = [];
|
||||||
@ -1145,14 +1140,6 @@ export const useNodesInteractions = () => {
|
|||||||
if (getNodesReadOnly())
|
if (getNodesReadOnly())
|
||||||
return
|
return
|
||||||
|
|
||||||
const {
|
|
||||||
shortcutsDisabled,
|
|
||||||
showFeaturesPanel,
|
|
||||||
} = workflowStore.getState()
|
|
||||||
|
|
||||||
if (shortcutsDisabled || showFeaturesPanel)
|
|
||||||
return
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
getNodes,
|
getNodes,
|
||||||
edges,
|
edges,
|
||||||
@ -1175,7 +1162,7 @@ export const useNodesInteractions = () => {
|
|||||||
|
|
||||||
if (selectedNode)
|
if (selectedNode)
|
||||||
handleNodeDelete(selectedNode.id)
|
handleNodeDelete(selectedNode.id)
|
||||||
}, [store, workflowStore, getNodesReadOnly, handleNodeDelete])
|
}, [store, getNodesReadOnly, handleNodeDelete])
|
||||||
|
|
||||||
const handleNodeResize = useCallback((nodeId: string, params: ResizeParamsWithDirection) => {
|
const handleNodeResize = useCallback((nodeId: string, params: ResizeParamsWithDirection) => {
|
||||||
if (getNodesReadOnly())
|
if (getNodesReadOnly())
|
||||||
@ -1234,14 +1221,7 @@ export const useNodesInteractions = () => {
|
|||||||
}, [getNodesReadOnly, store, handleSyncWorkflowDraft, saveStateToHistory])
|
}, [getNodesReadOnly, store, handleSyncWorkflowDraft, saveStateToHistory])
|
||||||
|
|
||||||
const handleHistoryBack = useCallback(() => {
|
const handleHistoryBack = useCallback(() => {
|
||||||
if (getNodesReadOnly())
|
if (getNodesReadOnly() || getWorkflowReadOnly())
|
||||||
return
|
|
||||||
|
|
||||||
const {
|
|
||||||
shortcutsDisabled,
|
|
||||||
} = workflowStore.getState()
|
|
||||||
|
|
||||||
if (shortcutsDisabled)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
const { setEdges, setNodes } = store.getState()
|
const { setEdges, setNodes } = store.getState()
|
||||||
@ -1253,17 +1233,10 @@ export const useNodesInteractions = () => {
|
|||||||
|
|
||||||
setEdges(edges)
|
setEdges(edges)
|
||||||
setNodes(nodes)
|
setNodes(nodes)
|
||||||
}, [store, undo, workflowHistoryStore, workflowStore, getNodesReadOnly])
|
}, [store, undo, workflowHistoryStore, getNodesReadOnly, getWorkflowReadOnly])
|
||||||
|
|
||||||
const handleHistoryForward = useCallback(() => {
|
const handleHistoryForward = useCallback(() => {
|
||||||
if (getNodesReadOnly())
|
if (getNodesReadOnly() || getWorkflowReadOnly())
|
||||||
return
|
|
||||||
|
|
||||||
const {
|
|
||||||
shortcutsDisabled,
|
|
||||||
} = workflowStore.getState()
|
|
||||||
|
|
||||||
if (shortcutsDisabled)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
const { setEdges, setNodes } = store.getState()
|
const { setEdges, setNodes } = store.getState()
|
||||||
@ -1275,7 +1248,7 @@ export const useNodesInteractions = () => {
|
|||||||
|
|
||||||
setEdges(edges)
|
setEdges(edges)
|
||||||
setNodes(nodes)
|
setNodes(nodes)
|
||||||
}, [redo, store, workflowHistoryStore, workflowStore, getNodesReadOnly])
|
}, [redo, store, workflowHistoryStore, getNodesReadOnly, getWorkflowReadOnly])
|
||||||
|
|
||||||
return {
|
return {
|
||||||
handleNodeDragStart,
|
handleNodeDragStart,
|
||||||
|
@ -8,7 +8,9 @@ import {
|
|||||||
} from '../store'
|
} from '../store'
|
||||||
import { BlockEnum } from '../types'
|
import { BlockEnum } from '../types'
|
||||||
import { useWorkflowUpdate } from '../hooks'
|
import { useWorkflowUpdate } from '../hooks'
|
||||||
import { useNodesReadOnly } from './use-workflow'
|
import {
|
||||||
|
useNodesReadOnly,
|
||||||
|
} from './use-workflow'
|
||||||
import { syncWorkflowDraft } from '@/service/workflow'
|
import { syncWorkflowDraft } from '@/service/workflow'
|
||||||
import { useFeaturesStore } from '@/app/components/base/features/hooks'
|
import { useFeaturesStore } from '@/app/components/base/features/hooks'
|
||||||
import { API_PREFIX } from '@/config'
|
import { API_PREFIX } from '@/config'
|
||||||
|
186
web/app/components/workflow/hooks/use-shortcuts.ts
Normal file
186
web/app/components/workflow/hooks/use-shortcuts.ts
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
import { useReactFlow } from 'reactflow'
|
||||||
|
import { useKeyPress } from 'ahooks'
|
||||||
|
import { useCallback } from 'react'
|
||||||
|
import {
|
||||||
|
getKeyboardKeyCodeBySystem,
|
||||||
|
isEventTargetInputArea,
|
||||||
|
} from '../utils'
|
||||||
|
import { useWorkflowHistoryStore } from '../workflow-history-store'
|
||||||
|
import { useWorkflowStore } from '../store'
|
||||||
|
import {
|
||||||
|
useEdgesInteractions,
|
||||||
|
useNodesInteractions,
|
||||||
|
useNodesSyncDraft,
|
||||||
|
useWorkflowMoveMode,
|
||||||
|
useWorkflowOrganize,
|
||||||
|
useWorkflowStartRun,
|
||||||
|
} from '.'
|
||||||
|
|
||||||
|
export const useShortcuts = (): void => {
|
||||||
|
const {
|
||||||
|
handleNodesCopy,
|
||||||
|
handleNodesPaste,
|
||||||
|
handleNodesDuplicate,
|
||||||
|
handleNodesDelete,
|
||||||
|
handleHistoryBack,
|
||||||
|
handleHistoryForward,
|
||||||
|
} = useNodesInteractions()
|
||||||
|
const { handleStartWorkflowRun } = useWorkflowStartRun()
|
||||||
|
const { shortcutsEnabled: workflowHistoryShortcutsEnabled } = useWorkflowHistoryStore()
|
||||||
|
const { handleSyncWorkflowDraft } = useNodesSyncDraft()
|
||||||
|
const { handleEdgeDelete } = useEdgesInteractions()
|
||||||
|
const workflowStore = useWorkflowStore()
|
||||||
|
const {
|
||||||
|
handleModeHand,
|
||||||
|
handleModePointer,
|
||||||
|
} = useWorkflowMoveMode()
|
||||||
|
const { handleLayout } = useWorkflowOrganize()
|
||||||
|
|
||||||
|
const {
|
||||||
|
zoomIn,
|
||||||
|
zoomOut,
|
||||||
|
zoomTo,
|
||||||
|
fitView,
|
||||||
|
} = useReactFlow()
|
||||||
|
|
||||||
|
const shouldHandleShortcut = useCallback((e: KeyboardEvent) => {
|
||||||
|
const { showFeaturesPanel } = workflowStore.getState()
|
||||||
|
return !showFeaturesPanel && !isEventTargetInputArea(e.target as HTMLElement)
|
||||||
|
}, [workflowStore])
|
||||||
|
|
||||||
|
useKeyPress(['delete', 'backspace'], (e) => {
|
||||||
|
if (shouldHandleShortcut(e)) {
|
||||||
|
e.preventDefault()
|
||||||
|
handleNodesDelete()
|
||||||
|
handleEdgeDelete()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
useKeyPress(`${getKeyboardKeyCodeBySystem('ctrl')}.c`, (e) => {
|
||||||
|
if (shouldHandleShortcut(e)) {
|
||||||
|
e.preventDefault()
|
||||||
|
handleNodesCopy()
|
||||||
|
}
|
||||||
|
}, { exactMatch: true, useCapture: true })
|
||||||
|
|
||||||
|
useKeyPress(`${getKeyboardKeyCodeBySystem('ctrl')}.v`, (e) => {
|
||||||
|
if (shouldHandleShortcut(e)) {
|
||||||
|
e.preventDefault()
|
||||||
|
handleNodesPaste()
|
||||||
|
}
|
||||||
|
}, { exactMatch: true, useCapture: true })
|
||||||
|
|
||||||
|
useKeyPress(`${getKeyboardKeyCodeBySystem('ctrl')}.d`, (e) => {
|
||||||
|
if (shouldHandleShortcut(e)) {
|
||||||
|
e.preventDefault()
|
||||||
|
handleNodesDuplicate()
|
||||||
|
}
|
||||||
|
}, { exactMatch: true, useCapture: true })
|
||||||
|
|
||||||
|
useKeyPress(`${getKeyboardKeyCodeBySystem('alt')}.r`, (e) => {
|
||||||
|
if (shouldHandleShortcut(e)) {
|
||||||
|
e.preventDefault()
|
||||||
|
handleStartWorkflowRun()
|
||||||
|
}
|
||||||
|
}, { exactMatch: true, useCapture: true })
|
||||||
|
|
||||||
|
useKeyPress(`${getKeyboardKeyCodeBySystem('ctrl')}.z`, (e) => {
|
||||||
|
if (shouldHandleShortcut(e)) {
|
||||||
|
e.preventDefault()
|
||||||
|
workflowHistoryShortcutsEnabled && handleHistoryBack()
|
||||||
|
}
|
||||||
|
}, { exactMatch: true, useCapture: true })
|
||||||
|
|
||||||
|
useKeyPress(
|
||||||
|
[`${getKeyboardKeyCodeBySystem('ctrl')}.y`, `${getKeyboardKeyCodeBySystem('ctrl')}.shift.z`],
|
||||||
|
(e) => {
|
||||||
|
if (shouldHandleShortcut(e)) {
|
||||||
|
e.preventDefault()
|
||||||
|
workflowHistoryShortcutsEnabled && handleHistoryForward()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ exactMatch: true, useCapture: true },
|
||||||
|
)
|
||||||
|
|
||||||
|
useKeyPress('h', (e) => {
|
||||||
|
if (shouldHandleShortcut(e)) {
|
||||||
|
e.preventDefault()
|
||||||
|
handleModeHand()
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
exactMatch: true,
|
||||||
|
useCapture: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
useKeyPress('v', (e) => {
|
||||||
|
if (shouldHandleShortcut(e)) {
|
||||||
|
e.preventDefault()
|
||||||
|
handleModePointer()
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
exactMatch: true,
|
||||||
|
useCapture: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
useKeyPress(`${getKeyboardKeyCodeBySystem('ctrl')}.o`, (e) => {
|
||||||
|
if (shouldHandleShortcut(e)) {
|
||||||
|
e.preventDefault()
|
||||||
|
handleLayout()
|
||||||
|
}
|
||||||
|
}, { exactMatch: true, useCapture: true })
|
||||||
|
|
||||||
|
useKeyPress(`${getKeyboardKeyCodeBySystem('ctrl')}.1`, (e) => {
|
||||||
|
if (shouldHandleShortcut(e)) {
|
||||||
|
e.preventDefault()
|
||||||
|
fitView()
|
||||||
|
handleSyncWorkflowDraft()
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
exactMatch: true,
|
||||||
|
useCapture: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
useKeyPress('shift.1', (e) => {
|
||||||
|
if (shouldHandleShortcut(e)) {
|
||||||
|
e.preventDefault()
|
||||||
|
zoomTo(1)
|
||||||
|
handleSyncWorkflowDraft()
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
exactMatch: true,
|
||||||
|
useCapture: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
useKeyPress('shift.5', (e) => {
|
||||||
|
if (shouldHandleShortcut(e)) {
|
||||||
|
e.preventDefault()
|
||||||
|
zoomTo(0.5)
|
||||||
|
handleSyncWorkflowDraft()
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
exactMatch: true,
|
||||||
|
useCapture: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
useKeyPress(`${getKeyboardKeyCodeBySystem('ctrl')}.dash`, (e) => {
|
||||||
|
if (shouldHandleShortcut(e)) {
|
||||||
|
e.preventDefault()
|
||||||
|
zoomOut()
|
||||||
|
handleSyncWorkflowDraft()
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
exactMatch: true,
|
||||||
|
useCapture: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
useKeyPress(`${getKeyboardKeyCodeBySystem('ctrl')}.equalsign`, (e) => {
|
||||||
|
if (shouldHandleShortcut(e)) {
|
||||||
|
e.preventDefault()
|
||||||
|
zoomIn()
|
||||||
|
handleSyncWorkflowDraft()
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
exactMatch: true,
|
||||||
|
useCapture: true,
|
||||||
|
})
|
||||||
|
}
|
@ -3,17 +3,29 @@ import {
|
|||||||
useState,
|
useState,
|
||||||
} from 'react'
|
} from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useReactFlow } from 'reactflow'
|
import { useReactFlow, useStoreApi } from 'reactflow'
|
||||||
import { useWorkflowStore } from '../store'
|
import produce from 'immer'
|
||||||
import { DSL_EXPORT_CHECK, WORKFLOW_DATA_UPDATE } from '../constants'
|
import { useStore, useWorkflowStore } from '../store'
|
||||||
import type { WorkflowDataUpdator } from '../types'
|
|
||||||
import {
|
import {
|
||||||
|
CUSTOM_NODE, DSL_EXPORT_CHECK,
|
||||||
|
WORKFLOW_DATA_UPDATE,
|
||||||
|
} from '../constants'
|
||||||
|
import type { Node, WorkflowDataUpdator } from '../types'
|
||||||
|
import { ControlMode } from '../types'
|
||||||
|
import {
|
||||||
|
getLayoutByDagre,
|
||||||
initialEdges,
|
initialEdges,
|
||||||
initialNodes,
|
initialNodes,
|
||||||
} from '../utils'
|
} from '../utils'
|
||||||
|
import {
|
||||||
|
useNodesReadOnly,
|
||||||
|
useSelectionInteractions,
|
||||||
|
useWorkflowReadOnly,
|
||||||
|
} from '../hooks'
|
||||||
import { useEdgesInteractions } from './use-edges-interactions'
|
import { useEdgesInteractions } from './use-edges-interactions'
|
||||||
import { useNodesInteractions } from './use-nodes-interactions'
|
import { useNodesInteractions } from './use-nodes-interactions'
|
||||||
import { useNodesSyncDraft } from './use-nodes-sync-draft'
|
import { useNodesSyncDraft } from './use-nodes-sync-draft'
|
||||||
|
import { WorkflowHistoryEvent, useWorkflowHistory } from './use-workflow-history'
|
||||||
import { useEventEmitterContextContext } from '@/context/event-emitter'
|
import { useEventEmitterContextContext } from '@/context/event-emitter'
|
||||||
import { fetchWorkflowDraft } from '@/service/workflow'
|
import { fetchWorkflowDraft } from '@/service/workflow'
|
||||||
import { exportAppConfig } from '@/service/apps'
|
import { exportAppConfig } from '@/service/apps'
|
||||||
@ -39,6 +51,158 @@ export const useWorkflowInteractions = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const useWorkflowMoveMode = () => {
|
||||||
|
const setControlMode = useStore(s => s.setControlMode)
|
||||||
|
const {
|
||||||
|
getNodesReadOnly,
|
||||||
|
} = useNodesReadOnly()
|
||||||
|
const { handleSelectionCancel } = useSelectionInteractions()
|
||||||
|
|
||||||
|
const handleModePointer = useCallback(() => {
|
||||||
|
if (getNodesReadOnly())
|
||||||
|
return
|
||||||
|
|
||||||
|
setControlMode(ControlMode.Pointer)
|
||||||
|
}, [getNodesReadOnly, setControlMode])
|
||||||
|
|
||||||
|
const handleModeHand = useCallback(() => {
|
||||||
|
if (getNodesReadOnly())
|
||||||
|
return
|
||||||
|
|
||||||
|
setControlMode(ControlMode.Hand)
|
||||||
|
handleSelectionCancel()
|
||||||
|
}, [getNodesReadOnly, setControlMode, handleSelectionCancel])
|
||||||
|
|
||||||
|
return {
|
||||||
|
handleModePointer,
|
||||||
|
handleModeHand,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useWorkflowOrganize = () => {
|
||||||
|
const workflowStore = useWorkflowStore()
|
||||||
|
const store = useStoreApi()
|
||||||
|
const reactflow = useReactFlow()
|
||||||
|
const { getNodesReadOnly } = useNodesReadOnly()
|
||||||
|
const { saveStateToHistory } = useWorkflowHistory()
|
||||||
|
const { handleSyncWorkflowDraft } = useNodesSyncDraft()
|
||||||
|
|
||||||
|
const handleLayout = useCallback(async () => {
|
||||||
|
if (getNodesReadOnly())
|
||||||
|
return
|
||||||
|
workflowStore.setState({ nodeAnimation: true })
|
||||||
|
const {
|
||||||
|
getNodes,
|
||||||
|
edges,
|
||||||
|
setNodes,
|
||||||
|
} = store.getState()
|
||||||
|
const { setViewport } = reactflow
|
||||||
|
const nodes = getNodes()
|
||||||
|
const layout = getLayoutByDagre(nodes, edges)
|
||||||
|
const rankMap = {} as Record<string, Node>
|
||||||
|
|
||||||
|
nodes.forEach((node) => {
|
||||||
|
if (!node.parentId && node.type === CUSTOM_NODE) {
|
||||||
|
const rank = layout.node(node.id).rank!
|
||||||
|
|
||||||
|
if (!rankMap[rank]) {
|
||||||
|
rankMap[rank] = node
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (rankMap[rank].position.y > node.position.y)
|
||||||
|
rankMap[rank] = node
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const newNodes = produce(nodes, (draft) => {
|
||||||
|
draft.forEach((node) => {
|
||||||
|
if (!node.parentId && node.type === CUSTOM_NODE) {
|
||||||
|
const nodeWithPosition = layout.node(node.id)
|
||||||
|
|
||||||
|
node.position = {
|
||||||
|
x: nodeWithPosition.x - node.width! / 2,
|
||||||
|
y: nodeWithPosition.y - node.height! / 2 + rankMap[nodeWithPosition.rank!].height! / 2,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
setNodes(newNodes)
|
||||||
|
const zoom = 0.7
|
||||||
|
setViewport({
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
zoom,
|
||||||
|
})
|
||||||
|
saveStateToHistory(WorkflowHistoryEvent.LayoutOrganize)
|
||||||
|
setTimeout(() => {
|
||||||
|
handleSyncWorkflowDraft()
|
||||||
|
})
|
||||||
|
}, [getNodesReadOnly, store, reactflow, workflowStore, handleSyncWorkflowDraft, saveStateToHistory])
|
||||||
|
return {
|
||||||
|
handleLayout,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useWorkflowZoom = () => {
|
||||||
|
const { handleSyncWorkflowDraft } = useNodesSyncDraft()
|
||||||
|
const { getWorkflowReadOnly } = useWorkflowReadOnly()
|
||||||
|
const {
|
||||||
|
zoomIn,
|
||||||
|
zoomOut,
|
||||||
|
zoomTo,
|
||||||
|
fitView,
|
||||||
|
} = useReactFlow()
|
||||||
|
|
||||||
|
const handleFitView = useCallback(() => {
|
||||||
|
if (getWorkflowReadOnly())
|
||||||
|
return
|
||||||
|
|
||||||
|
fitView()
|
||||||
|
handleSyncWorkflowDraft()
|
||||||
|
}, [getWorkflowReadOnly, fitView, handleSyncWorkflowDraft])
|
||||||
|
|
||||||
|
const handleBackToOriginalSize = useCallback(() => {
|
||||||
|
if (getWorkflowReadOnly())
|
||||||
|
return
|
||||||
|
|
||||||
|
zoomTo(1)
|
||||||
|
handleSyncWorkflowDraft()
|
||||||
|
}, [getWorkflowReadOnly, zoomTo, handleSyncWorkflowDraft])
|
||||||
|
|
||||||
|
const handleSizeToHalf = useCallback(() => {
|
||||||
|
if (getWorkflowReadOnly())
|
||||||
|
return
|
||||||
|
|
||||||
|
zoomTo(0.5)
|
||||||
|
handleSyncWorkflowDraft()
|
||||||
|
}, [getWorkflowReadOnly, zoomTo, handleSyncWorkflowDraft])
|
||||||
|
|
||||||
|
const handleZoomOut = useCallback(() => {
|
||||||
|
if (getWorkflowReadOnly())
|
||||||
|
return
|
||||||
|
|
||||||
|
zoomOut()
|
||||||
|
handleSyncWorkflowDraft()
|
||||||
|
}, [getWorkflowReadOnly, zoomOut, handleSyncWorkflowDraft])
|
||||||
|
|
||||||
|
const handleZoomIn = useCallback(() => {
|
||||||
|
if (getWorkflowReadOnly())
|
||||||
|
return
|
||||||
|
|
||||||
|
zoomIn()
|
||||||
|
handleSyncWorkflowDraft()
|
||||||
|
}, [getWorkflowReadOnly, zoomIn, handleSyncWorkflowDraft])
|
||||||
|
|
||||||
|
return {
|
||||||
|
handleFitView,
|
||||||
|
handleBackToOriginalSize,
|
||||||
|
handleSizeToHalf,
|
||||||
|
handleZoomOut,
|
||||||
|
handleZoomIn,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const useWorkflowUpdate = () => {
|
export const useWorkflowUpdate = () => {
|
||||||
const reactflow = useReactFlow()
|
const reactflow = useReactFlow()
|
||||||
const workflowStore = useWorkflowStore()
|
const workflowStore = useWorkflowStore()
|
||||||
|
@ -7,19 +7,14 @@ import {
|
|||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import { uniqBy } from 'lodash-es'
|
import { uniqBy } from 'lodash-es'
|
||||||
import { useContext } from 'use-context-selector'
|
import { useContext } from 'use-context-selector'
|
||||||
import produce from 'immer'
|
|
||||||
import {
|
import {
|
||||||
getIncomers,
|
getIncomers,
|
||||||
getOutgoers,
|
getOutgoers,
|
||||||
useReactFlow,
|
|
||||||
useStoreApi,
|
useStoreApi,
|
||||||
} from 'reactflow'
|
} from 'reactflow'
|
||||||
import type {
|
import type {
|
||||||
Connection,
|
Connection,
|
||||||
} from 'reactflow'
|
} from 'reactflow'
|
||||||
import {
|
|
||||||
getLayoutByDagre,
|
|
||||||
} from '../utils'
|
|
||||||
import type {
|
import type {
|
||||||
Edge,
|
Edge,
|
||||||
Node,
|
Node,
|
||||||
@ -34,15 +29,12 @@ import {
|
|||||||
useWorkflowStore,
|
useWorkflowStore,
|
||||||
} from '../store'
|
} from '../store'
|
||||||
import {
|
import {
|
||||||
CUSTOM_NODE,
|
|
||||||
SUPPORT_OUTPUT_VARS_NODE,
|
SUPPORT_OUTPUT_VARS_NODE,
|
||||||
} from '../constants'
|
} from '../constants'
|
||||||
import { CUSTOM_NOTE_NODE } from '../note-node/constants'
|
import { CUSTOM_NOTE_NODE } from '../note-node/constants'
|
||||||
import { findUsedVarNodes, getNodeOutputVars, updateNodeVars } from '../nodes/_base/components/variable/utils'
|
import { findUsedVarNodes, getNodeOutputVars, updateNodeVars } from '../nodes/_base/components/variable/utils'
|
||||||
import { useNodesExtraData } from './use-nodes-data'
|
import { useNodesExtraData } from './use-nodes-data'
|
||||||
import { useWorkflowTemplate } from './use-workflow-template'
|
import { useWorkflowTemplate } from './use-workflow-template'
|
||||||
import { useNodesSyncDraft } from './use-nodes-sync-draft'
|
|
||||||
import { WorkflowHistoryEvent, useWorkflowHistory } from './use-workflow-history'
|
|
||||||
import { useStore as useAppStore } from '@/app/components/app/store'
|
import { useStore as useAppStore } from '@/app/components/app/store'
|
||||||
import {
|
import {
|
||||||
fetchNodesDefaultConfigs,
|
fetchNodesDefaultConfigs,
|
||||||
@ -68,68 +60,13 @@ export const useIsChatMode = () => {
|
|||||||
export const useWorkflow = () => {
|
export const useWorkflow = () => {
|
||||||
const { locale } = useContext(I18n)
|
const { locale } = useContext(I18n)
|
||||||
const store = useStoreApi()
|
const store = useStoreApi()
|
||||||
const reactflow = useReactFlow()
|
|
||||||
const workflowStore = useWorkflowStore()
|
const workflowStore = useWorkflowStore()
|
||||||
const nodesExtraData = useNodesExtraData()
|
const nodesExtraData = useNodesExtraData()
|
||||||
const { handleSyncWorkflowDraft } = useNodesSyncDraft()
|
|
||||||
const { saveStateToHistory } = useWorkflowHistory()
|
|
||||||
|
|
||||||
const setPanelWidth = useCallback((width: number) => {
|
const setPanelWidth = useCallback((width: number) => {
|
||||||
localStorage.setItem('workflow-node-panel-width', `${width}`)
|
localStorage.setItem('workflow-node-panel-width', `${width}`)
|
||||||
workflowStore.setState({ panelWidth: width })
|
workflowStore.setState({ panelWidth: width })
|
||||||
}, [workflowStore])
|
}, [workflowStore])
|
||||||
|
|
||||||
const handleLayout = useCallback(async () => {
|
|
||||||
workflowStore.setState({ nodeAnimation: true })
|
|
||||||
const {
|
|
||||||
getNodes,
|
|
||||||
edges,
|
|
||||||
setNodes,
|
|
||||||
} = store.getState()
|
|
||||||
const { setViewport } = reactflow
|
|
||||||
const nodes = getNodes()
|
|
||||||
const layout = getLayoutByDagre(nodes, edges)
|
|
||||||
const rankMap = {} as Record<string, Node>
|
|
||||||
|
|
||||||
nodes.forEach((node) => {
|
|
||||||
if (!node.parentId && node.type === CUSTOM_NODE) {
|
|
||||||
const rank = layout.node(node.id).rank!
|
|
||||||
|
|
||||||
if (!rankMap[rank]) {
|
|
||||||
rankMap[rank] = node
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (rankMap[rank].position.y > node.position.y)
|
|
||||||
rankMap[rank] = node
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const newNodes = produce(nodes, (draft) => {
|
|
||||||
draft.forEach((node) => {
|
|
||||||
if (!node.parentId && node.type === CUSTOM_NODE) {
|
|
||||||
const nodeWithPosition = layout.node(node.id)
|
|
||||||
|
|
||||||
node.position = {
|
|
||||||
x: nodeWithPosition.x - node.width! / 2,
|
|
||||||
y: nodeWithPosition.y - node.height! / 2 + rankMap[nodeWithPosition.rank!].height! / 2,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
setNodes(newNodes)
|
|
||||||
const zoom = 0.7
|
|
||||||
setViewport({
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
zoom,
|
|
||||||
})
|
|
||||||
saveStateToHistory(WorkflowHistoryEvent.LayoutOrganize)
|
|
||||||
setTimeout(() => {
|
|
||||||
handleSyncWorkflowDraft()
|
|
||||||
})
|
|
||||||
}, [workflowStore, store, reactflow, saveStateToHistory, handleSyncWorkflowDraft])
|
|
||||||
|
|
||||||
const getTreeLeafNodes = useCallback((nodeId: string) => {
|
const getTreeLeafNodes = useCallback((nodeId: string) => {
|
||||||
const {
|
const {
|
||||||
getNodes,
|
getNodes,
|
||||||
@ -392,19 +329,8 @@ export const useWorkflow = () => {
|
|||||||
return nodes.find(node => node.id === nodeId) || nodes.find(node => node.data.type === BlockEnum.Start)
|
return nodes.find(node => node.id === nodeId) || nodes.find(node => node.data.type === BlockEnum.Start)
|
||||||
}, [store])
|
}, [store])
|
||||||
|
|
||||||
const enableShortcuts = useCallback(() => {
|
|
||||||
const { setShortcutsDisabled } = workflowStore.getState()
|
|
||||||
setShortcutsDisabled(false)
|
|
||||||
}, [workflowStore])
|
|
||||||
|
|
||||||
const disableShortcuts = useCallback(() => {
|
|
||||||
const { setShortcutsDisabled } = workflowStore.getState()
|
|
||||||
setShortcutsDisabled(true)
|
|
||||||
}, [workflowStore])
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
setPanelWidth,
|
setPanelWidth,
|
||||||
handleLayout,
|
|
||||||
getTreeLeafNodes,
|
getTreeLeafNodes,
|
||||||
getBeforeNodesInSameBranch,
|
getBeforeNodesInSameBranch,
|
||||||
getBeforeNodesInSameBranchIncludeParent,
|
getBeforeNodesInSameBranchIncludeParent,
|
||||||
@ -418,8 +344,6 @@ export const useWorkflow = () => {
|
|||||||
getNode,
|
getNode,
|
||||||
getBeforeNodeById,
|
getBeforeNodeById,
|
||||||
getIterationNodeChildren,
|
getIterationNodeChildren,
|
||||||
enableShortcuts,
|
|
||||||
disableShortcuts,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,6 @@ import {
|
|||||||
import { setAutoFreeze } from 'immer'
|
import { setAutoFreeze } from 'immer'
|
||||||
import {
|
import {
|
||||||
useEventListener,
|
useEventListener,
|
||||||
useKeyPress,
|
|
||||||
} from 'ahooks'
|
} from 'ahooks'
|
||||||
import ReactFlow, {
|
import ReactFlow, {
|
||||||
Background,
|
Background,
|
||||||
@ -34,6 +33,9 @@ import type {
|
|||||||
EnvironmentVariable,
|
EnvironmentVariable,
|
||||||
Node,
|
Node,
|
||||||
} from './types'
|
} from './types'
|
||||||
|
import {
|
||||||
|
ControlMode,
|
||||||
|
} from './types'
|
||||||
import { WorkflowContextProvider } from './context'
|
import { WorkflowContextProvider } from './context'
|
||||||
import {
|
import {
|
||||||
useDSL,
|
useDSL,
|
||||||
@ -43,10 +45,10 @@ import {
|
|||||||
useNodesSyncDraft,
|
useNodesSyncDraft,
|
||||||
usePanelInteractions,
|
usePanelInteractions,
|
||||||
useSelectionInteractions,
|
useSelectionInteractions,
|
||||||
|
useShortcuts,
|
||||||
useWorkflow,
|
useWorkflow,
|
||||||
useWorkflowInit,
|
useWorkflowInit,
|
||||||
useWorkflowReadOnly,
|
useWorkflowReadOnly,
|
||||||
useWorkflowStartRun,
|
|
||||||
useWorkflowUpdate,
|
useWorkflowUpdate,
|
||||||
} from './hooks'
|
} from './hooks'
|
||||||
import Header from './header'
|
import Header from './header'
|
||||||
@ -70,10 +72,8 @@ import {
|
|||||||
useWorkflowStore,
|
useWorkflowStore,
|
||||||
} from './store'
|
} from './store'
|
||||||
import {
|
import {
|
||||||
getKeyboardKeyCodeBySystem,
|
|
||||||
initialEdges,
|
initialEdges,
|
||||||
initialNodes,
|
initialNodes,
|
||||||
isEventTargetInputArea,
|
|
||||||
} from './utils'
|
} from './utils'
|
||||||
import {
|
import {
|
||||||
CUSTOM_NODE,
|
CUSTOM_NODE,
|
||||||
@ -81,7 +81,7 @@ import {
|
|||||||
ITERATION_CHILDREN_Z_INDEX,
|
ITERATION_CHILDREN_Z_INDEX,
|
||||||
WORKFLOW_DATA_UPDATE,
|
WORKFLOW_DATA_UPDATE,
|
||||||
} from './constants'
|
} from './constants'
|
||||||
import { WorkflowHistoryProvider, useWorkflowHistoryStore } from './workflow-history-store'
|
import { WorkflowHistoryProvider } from './workflow-history-store'
|
||||||
import Loading from '@/app/components/base/loading'
|
import Loading from '@/app/components/base/loading'
|
||||||
import { FeaturesProvider } from '@/app/components/base/features'
|
import { FeaturesProvider } from '@/app/components/base/features'
|
||||||
import type { Features as FeaturesData } from '@/app/components/base/features/types'
|
import type { Features as FeaturesData } from '@/app/components/base/features/types'
|
||||||
@ -225,17 +225,12 @@ const Workflow: FC<WorkflowProps> = memo(({
|
|||||||
handleNodeConnectStart,
|
handleNodeConnectStart,
|
||||||
handleNodeConnectEnd,
|
handleNodeConnectEnd,
|
||||||
handleNodeContextMenu,
|
handleNodeContextMenu,
|
||||||
handleNodesCopy,
|
|
||||||
handleNodesPaste,
|
|
||||||
handleNodesDuplicate,
|
|
||||||
handleNodesDelete,
|
|
||||||
handleHistoryBack,
|
handleHistoryBack,
|
||||||
handleHistoryForward,
|
handleHistoryForward,
|
||||||
} = useNodesInteractions()
|
} = useNodesInteractions()
|
||||||
const {
|
const {
|
||||||
handleEdgeEnter,
|
handleEdgeEnter,
|
||||||
handleEdgeLeave,
|
handleEdgeLeave,
|
||||||
handleEdgeDelete,
|
|
||||||
handleEdgesChange,
|
handleEdgesChange,
|
||||||
} = useEdgesInteractions()
|
} = useEdgesInteractions()
|
||||||
const {
|
const {
|
||||||
@ -250,7 +245,6 @@ const Workflow: FC<WorkflowProps> = memo(({
|
|||||||
const {
|
const {
|
||||||
isValidConnection,
|
isValidConnection,
|
||||||
} = useWorkflow()
|
} = useWorkflow()
|
||||||
const { handleStartWorkflowRun } = useWorkflowStartRun()
|
|
||||||
const {
|
const {
|
||||||
exportCheck,
|
exportCheck,
|
||||||
handleExportDSL,
|
handleExportDSL,
|
||||||
@ -262,41 +256,7 @@ const Workflow: FC<WorkflowProps> = memo(({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const { shortcutsEnabled: workflowHistoryShortcutsEnabled } = useWorkflowHistoryStore()
|
useShortcuts()
|
||||||
|
|
||||||
useKeyPress(['delete', 'backspace'], (e) => {
|
|
||||||
if (isEventTargetInputArea(e.target as HTMLElement))
|
|
||||||
return
|
|
||||||
|
|
||||||
handleNodesDelete()
|
|
||||||
})
|
|
||||||
useKeyPress(['delete', 'backspace'], handleEdgeDelete)
|
|
||||||
useKeyPress(`${getKeyboardKeyCodeBySystem('ctrl')}.c`, (e) => {
|
|
||||||
if (isEventTargetInputArea(e.target as HTMLElement))
|
|
||||||
return
|
|
||||||
|
|
||||||
handleNodesCopy()
|
|
||||||
}, { exactMatch: true, useCapture: true })
|
|
||||||
useKeyPress(`${getKeyboardKeyCodeBySystem('ctrl')}.v`, (e) => {
|
|
||||||
if (isEventTargetInputArea(e.target as HTMLElement))
|
|
||||||
return
|
|
||||||
|
|
||||||
handleNodesPaste()
|
|
||||||
}, { exactMatch: true, useCapture: true })
|
|
||||||
useKeyPress(`${getKeyboardKeyCodeBySystem('ctrl')}.d`, handleNodesDuplicate, { exactMatch: true, useCapture: true })
|
|
||||||
useKeyPress(`${getKeyboardKeyCodeBySystem('alt')}.r`, handleStartWorkflowRun, { exactMatch: true, useCapture: true })
|
|
||||||
useKeyPress(`${getKeyboardKeyCodeBySystem('alt')}.r`, handleStartWorkflowRun, { exactMatch: true, useCapture: true })
|
|
||||||
useKeyPress(
|
|
||||||
`${getKeyboardKeyCodeBySystem('ctrl')}.z`,
|
|
||||||
() => workflowHistoryShortcutsEnabled && handleHistoryBack(),
|
|
||||||
{ exactMatch: true, useCapture: true },
|
|
||||||
)
|
|
||||||
|
|
||||||
useKeyPress(
|
|
||||||
[`${getKeyboardKeyCodeBySystem('ctrl')}.y`, `${getKeyboardKeyCodeBySystem('ctrl')}.shift.z`],
|
|
||||||
() => workflowHistoryShortcutsEnabled && handleHistoryForward(),
|
|
||||||
{ exactMatch: true, useCapture: true },
|
|
||||||
)
|
|
||||||
|
|
||||||
const store = useStoreApi()
|
const store = useStoreApi()
|
||||||
if (process.env.NODE_ENV === 'development') {
|
if (process.env.NODE_ENV === 'development') {
|
||||||
@ -388,14 +348,14 @@ const Workflow: FC<WorkflowProps> = memo(({
|
|||||||
nodesConnectable={!nodesReadOnly}
|
nodesConnectable={!nodesReadOnly}
|
||||||
nodesFocusable={!nodesReadOnly}
|
nodesFocusable={!nodesReadOnly}
|
||||||
edgesFocusable={!nodesReadOnly}
|
edgesFocusable={!nodesReadOnly}
|
||||||
panOnDrag={controlMode === 'hand' && !workflowReadOnly}
|
panOnDrag={controlMode === ControlMode.Hand && !workflowReadOnly}
|
||||||
zoomOnPinch={!workflowReadOnly}
|
zoomOnPinch={!workflowReadOnly}
|
||||||
zoomOnScroll={!workflowReadOnly}
|
zoomOnScroll={!workflowReadOnly}
|
||||||
zoomOnDoubleClick={!workflowReadOnly}
|
zoomOnDoubleClick={!workflowReadOnly}
|
||||||
isValidConnection={isValidConnection}
|
isValidConnection={isValidConnection}
|
||||||
selectionKeyCode={null}
|
selectionKeyCode={null}
|
||||||
selectionMode={SelectionMode.Partial}
|
selectionMode={SelectionMode.Partial}
|
||||||
selectionOnDrag={controlMode === 'pointer' && !workflowReadOnly}
|
selectionOnDrag={controlMode === ControlMode.Pointer && !workflowReadOnly}
|
||||||
minZoom={0.25}
|
minZoom={0.25}
|
||||||
>
|
>
|
||||||
<Background
|
<Background
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import type { MouseEvent } from 'react'
|
import type { MouseEvent } from 'react'
|
||||||
import {
|
import {
|
||||||
memo,
|
memo,
|
||||||
useCallback,
|
|
||||||
} from 'react'
|
} from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import {
|
import {
|
||||||
@ -10,13 +9,14 @@ import {
|
|||||||
RiHand,
|
RiHand,
|
||||||
RiStickyNoteAddLine,
|
RiStickyNoteAddLine,
|
||||||
} from '@remixicon/react'
|
} from '@remixicon/react'
|
||||||
import { useKeyPress } from 'ahooks'
|
|
||||||
import {
|
import {
|
||||||
useNodesReadOnly,
|
useNodesReadOnly,
|
||||||
useSelectionInteractions,
|
useWorkflowMoveMode,
|
||||||
useWorkflow,
|
useWorkflowOrganize,
|
||||||
} from '../hooks'
|
} from '../hooks'
|
||||||
import { getKeyboardKeyCodeBySystem, isEventTargetInputArea } from '../utils'
|
import {
|
||||||
|
ControlMode,
|
||||||
|
} from '../types'
|
||||||
import { useStore } from '../store'
|
import { useStore } from '../store'
|
||||||
import AddBlock from './add-block'
|
import AddBlock from './add-block'
|
||||||
import TipPopup from './tip-popup'
|
import TipPopup from './tip-popup'
|
||||||
@ -26,62 +26,13 @@ import cn from '@/utils/classnames'
|
|||||||
const Control = () => {
|
const Control = () => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const controlMode = useStore(s => s.controlMode)
|
const controlMode = useStore(s => s.controlMode)
|
||||||
const setControlMode = useStore(s => s.setControlMode)
|
const { handleModePointer, handleModeHand } = useWorkflowMoveMode()
|
||||||
const { handleLayout } = useWorkflow()
|
const { handleLayout } = useWorkflowOrganize()
|
||||||
const { handleAddNote } = useOperator()
|
const { handleAddNote } = useOperator()
|
||||||
const {
|
const {
|
||||||
nodesReadOnly,
|
nodesReadOnly,
|
||||||
getNodesReadOnly,
|
getNodesReadOnly,
|
||||||
} = useNodesReadOnly()
|
} = 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())
|
|
||||||
return
|
|
||||||
handleLayout()
|
|
||||||
}
|
|
||||||
|
|
||||||
useKeyPress(`${getKeyboardKeyCodeBySystem('ctrl')}.o`, (e) => {
|
|
||||||
e.preventDefault()
|
|
||||||
goLayout()
|
|
||||||
}, { exactMatch: true, useCapture: true })
|
|
||||||
|
|
||||||
const addNote = (e: MouseEvent<HTMLDivElement>) => {
|
const addNote = (e: MouseEvent<HTMLDivElement>) => {
|
||||||
if (getNodesReadOnly())
|
if (getNodesReadOnly())
|
||||||
@ -110,7 +61,7 @@ const Control = () => {
|
|||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'flex items-center justify-center mr-[1px] w-8 h-8 rounded-lg cursor-pointer',
|
'flex items-center justify-center mr-[1px] w-8 h-8 rounded-lg cursor-pointer',
|
||||||
controlMode === 'pointer' ? 'bg-primary-50 text-primary-600' : 'hover:bg-black/5 hover:text-gray-700',
|
controlMode === ControlMode.Pointer ? 'bg-primary-50 text-primary-600' : 'hover:bg-black/5 hover:text-gray-700',
|
||||||
`${nodesReadOnly && '!cursor-not-allowed opacity-50'}`,
|
`${nodesReadOnly && '!cursor-not-allowed opacity-50'}`,
|
||||||
)}
|
)}
|
||||||
onClick={handleModePointer}
|
onClick={handleModePointer}
|
||||||
@ -122,7 +73,7 @@ const Control = () => {
|
|||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'flex items-center justify-center w-8 h-8 rounded-lg cursor-pointer',
|
'flex items-center justify-center w-8 h-8 rounded-lg cursor-pointer',
|
||||||
controlMode === 'hand' ? 'bg-primary-50 text-primary-600' : 'hover:bg-black/5 hover:text-gray-700',
|
controlMode === ControlMode.Hand ? 'bg-primary-50 text-primary-600' : 'hover:bg-black/5 hover:text-gray-700',
|
||||||
`${nodesReadOnly && '!cursor-not-allowed opacity-50'}`,
|
`${nodesReadOnly && '!cursor-not-allowed opacity-50'}`,
|
||||||
)}
|
)}
|
||||||
onClick={handleModeHand}
|
onClick={handleModeHand}
|
||||||
@ -137,7 +88,7 @@ const Control = () => {
|
|||||||
'flex items-center justify-center w-8 h-8 rounded-lg hover:bg-black/5 hover:text-gray-700 cursor-pointer',
|
'flex items-center justify-center w-8 h-8 rounded-lg hover:bg-black/5 hover:text-gray-700 cursor-pointer',
|
||||||
`${nodesReadOnly && '!cursor-not-allowed opacity-50'}`,
|
`${nodesReadOnly && '!cursor-not-allowed opacity-50'}`,
|
||||||
)}
|
)}
|
||||||
onClick={goLayout}
|
onClick={handleLayout}
|
||||||
>
|
>
|
||||||
<RiFunctionAddLine className='w-4 h-4' />
|
<RiFunctionAddLine className='w-4 h-4' />
|
||||||
</div>
|
</div>
|
||||||
|
@ -9,7 +9,6 @@ import {
|
|||||||
RiZoomInLine,
|
RiZoomInLine,
|
||||||
RiZoomOutLine,
|
RiZoomOutLine,
|
||||||
} from '@remixicon/react'
|
} from '@remixicon/react'
|
||||||
import { useKeyPress } from 'ahooks'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import {
|
import {
|
||||||
useReactFlow,
|
useReactFlow,
|
||||||
@ -20,9 +19,7 @@ import {
|
|||||||
useWorkflowReadOnly,
|
useWorkflowReadOnly,
|
||||||
} from '../hooks'
|
} from '../hooks'
|
||||||
import {
|
import {
|
||||||
getKeyboardKeyCodeBySystem,
|
|
||||||
getKeyboardKeyNameBySystem,
|
getKeyboardKeyNameBySystem,
|
||||||
isEventTargetInputArea,
|
|
||||||
} from '../utils'
|
} from '../utils'
|
||||||
import ShortcutsName from '../shortcuts-name'
|
import ShortcutsName from '../shortcuts-name'
|
||||||
import TipPopup from './tip-popup'
|
import TipPopup from './tip-popup'
|
||||||
@ -116,87 +113,6 @@ const ZoomInOut: FC = () => {
|
|||||||
handleSyncWorkflowDraft()
|
handleSyncWorkflowDraft()
|
||||||
}
|
}
|
||||||
|
|
||||||
useKeyPress(`${getKeyboardKeyCodeBySystem('ctrl')}.1`, (e) => {
|
|
||||||
e.preventDefault()
|
|
||||||
if (workflowReadOnly)
|
|
||||||
return
|
|
||||||
|
|
||||||
fitView()
|
|
||||||
handleSyncWorkflowDraft()
|
|
||||||
}, {
|
|
||||||
exactMatch: true,
|
|
||||||
useCapture: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
useKeyPress('shift.1', (e) => {
|
|
||||||
if (workflowReadOnly)
|
|
||||||
return
|
|
||||||
|
|
||||||
if (isEventTargetInputArea(e.target as HTMLElement))
|
|
||||||
return
|
|
||||||
|
|
||||||
e.preventDefault()
|
|
||||||
zoomTo(1)
|
|
||||||
handleSyncWorkflowDraft()
|
|
||||||
}, {
|
|
||||||
exactMatch: true,
|
|
||||||
useCapture: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
useKeyPress('shift.2', (e) => {
|
|
||||||
if (workflowReadOnly)
|
|
||||||
return
|
|
||||||
|
|
||||||
if (isEventTargetInputArea(e.target as HTMLElement))
|
|
||||||
return
|
|
||||||
|
|
||||||
e.preventDefault()
|
|
||||||
zoomTo(2)
|
|
||||||
handleSyncWorkflowDraft()
|
|
||||||
}, {
|
|
||||||
exactMatch: true,
|
|
||||||
useCapture: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
useKeyPress('shift.5', (e) => {
|
|
||||||
if (workflowReadOnly)
|
|
||||||
return
|
|
||||||
|
|
||||||
if (isEventTargetInputArea(e.target as HTMLElement))
|
|
||||||
return
|
|
||||||
|
|
||||||
e.preventDefault()
|
|
||||||
zoomTo(0.5)
|
|
||||||
handleSyncWorkflowDraft()
|
|
||||||
}, {
|
|
||||||
exactMatch: true,
|
|
||||||
useCapture: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
useKeyPress(`${getKeyboardKeyCodeBySystem('ctrl')}.dash`, (e) => {
|
|
||||||
e.preventDefault()
|
|
||||||
if (workflowReadOnly)
|
|
||||||
return
|
|
||||||
|
|
||||||
zoomOut()
|
|
||||||
handleSyncWorkflowDraft()
|
|
||||||
}, {
|
|
||||||
exactMatch: true,
|
|
||||||
useCapture: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
useKeyPress(`${getKeyboardKeyCodeBySystem('ctrl')}.equalsign`, (e) => {
|
|
||||||
e.preventDefault()
|
|
||||||
if (workflowReadOnly)
|
|
||||||
return
|
|
||||||
|
|
||||||
zoomIn()
|
|
||||||
handleSyncWorkflowDraft()
|
|
||||||
}, {
|
|
||||||
exactMatch: true,
|
|
||||||
useCapture: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
const handleTrigger = useCallback(() => {
|
const handleTrigger = useCallback(() => {
|
||||||
if (getWorkflowReadOnly())
|
if (getWorkflowReadOnly())
|
||||||
return
|
return
|
||||||
@ -289,11 +205,6 @@ const ZoomInOut: FC = () => {
|
|||||||
<ShortcutsName keys={['shift', '1']} />
|
<ShortcutsName keys={['shift', '1']} />
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
{
|
|
||||||
option.key === ZoomType.zoomTo200 && (
|
|
||||||
<ShortcutsName keys={['shift', '2']} />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ import { Panel as NodePanel } from '../nodes'
|
|||||||
import { useStore } from '../store'
|
import { useStore } from '../store'
|
||||||
import {
|
import {
|
||||||
useIsChatMode,
|
useIsChatMode,
|
||||||
useWorkflow,
|
|
||||||
} from '../hooks'
|
} from '../hooks'
|
||||||
import DebugAndPreview from './debug-and-preview'
|
import DebugAndPreview from './debug-and-preview'
|
||||||
import Record from './record'
|
import Record from './record'
|
||||||
@ -28,10 +27,6 @@ const Panel: FC = () => {
|
|||||||
const showEnvPanel = useStore(s => s.showEnvPanel)
|
const showEnvPanel = useStore(s => s.showEnvPanel)
|
||||||
const showChatVariablePanel = useStore(s => s.showChatVariablePanel)
|
const showChatVariablePanel = useStore(s => s.showChatVariablePanel)
|
||||||
const isRestoring = useStore(s => s.isRestoring)
|
const isRestoring = useStore(s => s.isRestoring)
|
||||||
const {
|
|
||||||
enableShortcuts,
|
|
||||||
disableShortcuts,
|
|
||||||
} = useWorkflow()
|
|
||||||
const { currentLogItem, setCurrentLogItem, showMessageLogModal, setShowMessageLogModal, currentLogModalActiveTab } = useAppStore(useShallow(state => ({
|
const { currentLogItem, setCurrentLogItem, showMessageLogModal, setShowMessageLogModal, currentLogModalActiveTab } = useAppStore(useShallow(state => ({
|
||||||
currentLogItem: state.currentLogItem,
|
currentLogItem: state.currentLogItem,
|
||||||
setCurrentLogItem: state.setCurrentLogItem,
|
setCurrentLogItem: state.setCurrentLogItem,
|
||||||
@ -44,8 +39,6 @@ const Panel: FC = () => {
|
|||||||
<div
|
<div
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
className={cn('absolute top-14 right-0 bottom-2 flex z-10 outline-none')}
|
className={cn('absolute top-14 right-0 bottom-2 flex z-10 outline-none')}
|
||||||
onFocus={disableShortcuts}
|
|
||||||
onBlur={enableShortcuts}
|
|
||||||
key={`${isRestoring}`}
|
key={`${isRestoring}`}
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
|
@ -99,8 +99,6 @@ type Shape = {
|
|||||||
setWorkflowTools: (tools: ToolWithProvider[]) => void
|
setWorkflowTools: (tools: ToolWithProvider[]) => void
|
||||||
clipboardElements: Node[]
|
clipboardElements: Node[]
|
||||||
setClipboardElements: (clipboardElements: Node[]) => void
|
setClipboardElements: (clipboardElements: Node[]) => void
|
||||||
shortcutsDisabled: boolean
|
|
||||||
setShortcutsDisabled: (shortcutsDisabled: boolean) => void
|
|
||||||
showDebugAndPreviewPanel: boolean
|
showDebugAndPreviewPanel: boolean
|
||||||
setShowDebugAndPreviewPanel: (showDebugAndPreviewPanel: boolean) => void
|
setShowDebugAndPreviewPanel: (showDebugAndPreviewPanel: boolean) => void
|
||||||
showEnvPanel: boolean
|
showEnvPanel: boolean
|
||||||
@ -217,8 +215,6 @@ export const createWorkflowStore = () => {
|
|||||||
setWorkflowTools: workflowTools => set(() => ({ workflowTools })),
|
setWorkflowTools: workflowTools => set(() => ({ workflowTools })),
|
||||||
clipboardElements: [],
|
clipboardElements: [],
|
||||||
setClipboardElements: clipboardElements => set(() => ({ clipboardElements })),
|
setClipboardElements: clipboardElements => set(() => ({ clipboardElements })),
|
||||||
shortcutsDisabled: false,
|
|
||||||
setShortcutsDisabled: shortcutsDisabled => set(() => ({ shortcutsDisabled })),
|
|
||||||
showDebugAndPreviewPanel: false,
|
showDebugAndPreviewPanel: false,
|
||||||
setShowDebugAndPreviewPanel: showDebugAndPreviewPanel => set(() => ({ showDebugAndPreviewPanel })),
|
setShowDebugAndPreviewPanel: showDebugAndPreviewPanel => set(() => ({ showDebugAndPreviewPanel })),
|
||||||
showEnvPanel: false,
|
showEnvPanel: false,
|
||||||
|
@ -29,6 +29,11 @@ export enum BlockEnum {
|
|||||||
Assigner = 'assigner', // is now named as VariableAssigner
|
Assigner = 'assigner', // is now named as VariableAssigner
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum ControlMode {
|
||||||
|
Pointer = 'pointer',
|
||||||
|
Hand = 'hand',
|
||||||
|
}
|
||||||
|
|
||||||
export type Branch = {
|
export type Branch = {
|
||||||
id: string
|
id: string
|
||||||
name: string
|
name: string
|
||||||
|
Loading…
x
Reference in New Issue
Block a user