Feat: shortcut hook (#7385)

This commit is contained in:
Yi Xiao 2024-08-19 18:11:11 +08:00 committed by GitHub
parent 68dc6d5bc3
commit 8b06105fa1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 402 additions and 332 deletions

View File

@ -35,7 +35,9 @@ const RunMode = memo(() => {
'hover:bg-state-accent-hover cursor-pointer',
isRunning && 'bg-state-accent-hover !cursor-not-allowed',
)}
onClick={() => handleWorkflowStartRunInWorkflow()}
onClick={() => {
handleWorkflowStartRunInWorkflow()
}}
>
{
isRunning

View File

@ -17,7 +17,7 @@ import {
useWorkflowInteractions,
useWorkflowRun,
} from '../hooks'
import { WorkflowRunningStatus } from '../types'
import { ControlMode, WorkflowRunningStatus } from '../types'
import cn from '@/utils/classnames'
import {
PortalToFollowElem,
@ -58,6 +58,7 @@ const ViewHistory = ({
handleCancelDebugAndPreviewPanel,
} = useWorkflowInteractions()
const workflowStore = useWorkflowStore()
const setControlMode = useStore(s => s.setControlMode)
const { appDetail, setCurrentLogItem, setShowMessageLogModal } = useAppStore(useShallow(state => ({
appDetail: state.appDetail,
setCurrentLogItem: state.setCurrentLogItem,
@ -173,6 +174,7 @@ const ViewHistory = ({
setOpen(false)
handleNodesCancelSelected()
handleCancelDebugAndPreviewPanel()
setControlMode(ControlMode.Hand)
}}
>
{

View File

@ -7,11 +7,12 @@ export * from './use-workflow'
export * from './use-workflow-run'
export * from './use-workflow-template'
export * from './use-checklist'
export * from './use-workflow-mode'
export * from './use-workflow-interactions'
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-shortcuts'
export * from './use-workflow-interactions'
export * from './use-workflow-mode'

View File

@ -48,6 +48,7 @@ import { useHelpline } from './use-helpline'
import {
useNodesReadOnly,
useWorkflow,
useWorkflowReadOnly,
} from './use-workflow'
import { WorkflowHistoryEvent, useWorkflowHistory } from './use-workflow-history'
@ -62,6 +63,7 @@ export const useNodesInteractions = () => {
getAfterNodesInSameBranch,
} = useWorkflow()
const { getNodesReadOnly } = useNodesReadOnly()
const { getWorkflowReadOnly } = useWorkflowReadOnly()
const { handleSetHelpline } = useHelpline()
const {
handleNodeIterationChildDrag,
@ -1029,14 +1031,7 @@ export const useNodesInteractions = () => {
if (getNodesReadOnly())
return
const {
setClipboardElements,
shortcutsDisabled,
showFeaturesPanel,
} = workflowStore.getState()
if (shortcutsDisabled || showFeaturesPanel)
return
const { setClipboardElements } = workflowStore.getState()
const {
getNodes,
@ -1062,14 +1057,9 @@ export const useNodesInteractions = () => {
const {
clipboardElements,
shortcutsDisabled,
showFeaturesPanel,
mousePosition,
} = workflowStore.getState()
if (shortcutsDisabled || showFeaturesPanel)
return
const {
getNodes,
setNodes,
@ -1107,6 +1097,11 @@ export const useNodesInteractions = () => {
})
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[] = []
if (nodeToPaste.data.type === BlockEnum.Iteration) {
newNode.data._children = [];
@ -1145,14 +1140,6 @@ export const useNodesInteractions = () => {
if (getNodesReadOnly())
return
const {
shortcutsDisabled,
showFeaturesPanel,
} = workflowStore.getState()
if (shortcutsDisabled || showFeaturesPanel)
return
const {
getNodes,
edges,
@ -1175,7 +1162,7 @@ export const useNodesInteractions = () => {
if (selectedNode)
handleNodeDelete(selectedNode.id)
}, [store, workflowStore, getNodesReadOnly, handleNodeDelete])
}, [store, getNodesReadOnly, handleNodeDelete])
const handleNodeResize = useCallback((nodeId: string, params: ResizeParamsWithDirection) => {
if (getNodesReadOnly())
@ -1234,14 +1221,7 @@ export const useNodesInteractions = () => {
}, [getNodesReadOnly, store, handleSyncWorkflowDraft, saveStateToHistory])
const handleHistoryBack = useCallback(() => {
if (getNodesReadOnly())
return
const {
shortcutsDisabled,
} = workflowStore.getState()
if (shortcutsDisabled)
if (getNodesReadOnly() || getWorkflowReadOnly())
return
const { setEdges, setNodes } = store.getState()
@ -1253,17 +1233,10 @@ export const useNodesInteractions = () => {
setEdges(edges)
setNodes(nodes)
}, [store, undo, workflowHistoryStore, workflowStore, getNodesReadOnly])
}, [store, undo, workflowHistoryStore, getNodesReadOnly, getWorkflowReadOnly])
const handleHistoryForward = useCallback(() => {
if (getNodesReadOnly())
return
const {
shortcutsDisabled,
} = workflowStore.getState()
if (shortcutsDisabled)
if (getNodesReadOnly() || getWorkflowReadOnly())
return
const { setEdges, setNodes } = store.getState()
@ -1275,7 +1248,7 @@ export const useNodesInteractions = () => {
setEdges(edges)
setNodes(nodes)
}, [redo, store, workflowHistoryStore, workflowStore, getNodesReadOnly])
}, [redo, store, workflowHistoryStore, getNodesReadOnly, getWorkflowReadOnly])
return {
handleNodeDragStart,

View File

@ -8,7 +8,9 @@ import {
} from '../store'
import { BlockEnum } from '../types'
import { useWorkflowUpdate } from '../hooks'
import { useNodesReadOnly } from './use-workflow'
import {
useNodesReadOnly,
} from './use-workflow'
import { syncWorkflowDraft } from '@/service/workflow'
import { useFeaturesStore } from '@/app/components/base/features/hooks'
import { API_PREFIX } from '@/config'

View 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,
})
}

View File

@ -3,17 +3,29 @@ import {
useState,
} from 'react'
import { useTranslation } from 'react-i18next'
import { useReactFlow } from 'reactflow'
import { useWorkflowStore } from '../store'
import { DSL_EXPORT_CHECK, WORKFLOW_DATA_UPDATE } from '../constants'
import type { WorkflowDataUpdator } from '../types'
import { useReactFlow, useStoreApi } from 'reactflow'
import produce from 'immer'
import { useStore, useWorkflowStore } from '../store'
import {
CUSTOM_NODE, DSL_EXPORT_CHECK,
WORKFLOW_DATA_UPDATE,
} from '../constants'
import type { Node, WorkflowDataUpdator } from '../types'
import { ControlMode } from '../types'
import {
getLayoutByDagre,
initialEdges,
initialNodes,
} from '../utils'
import {
useNodesReadOnly,
useSelectionInteractions,
useWorkflowReadOnly,
} from '../hooks'
import { useEdgesInteractions } from './use-edges-interactions'
import { useNodesInteractions } from './use-nodes-interactions'
import { useNodesSyncDraft } from './use-nodes-sync-draft'
import { WorkflowHistoryEvent, useWorkflowHistory } from './use-workflow-history'
import { useEventEmitterContextContext } from '@/context/event-emitter'
import { fetchWorkflowDraft } from '@/service/workflow'
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 = () => {
const reactflow = useReactFlow()
const workflowStore = useWorkflowStore()

View File

@ -7,19 +7,14 @@ import {
import dayjs from 'dayjs'
import { uniqBy } from 'lodash-es'
import { useContext } from 'use-context-selector'
import produce from 'immer'
import {
getIncomers,
getOutgoers,
useReactFlow,
useStoreApi,
} from 'reactflow'
import type {
Connection,
} from 'reactflow'
import {
getLayoutByDagre,
} from '../utils'
import type {
Edge,
Node,
@ -34,15 +29,12 @@ import {
useWorkflowStore,
} from '../store'
import {
CUSTOM_NODE,
SUPPORT_OUTPUT_VARS_NODE,
} from '../constants'
import { CUSTOM_NOTE_NODE } from '../note-node/constants'
import { findUsedVarNodes, getNodeOutputVars, updateNodeVars } from '../nodes/_base/components/variable/utils'
import { useNodesExtraData } from './use-nodes-data'
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 {
fetchNodesDefaultConfigs,
@ -68,68 +60,13 @@ export const useIsChatMode = () => {
export const useWorkflow = () => {
const { locale } = useContext(I18n)
const store = useStoreApi()
const reactflow = useReactFlow()
const workflowStore = useWorkflowStore()
const nodesExtraData = useNodesExtraData()
const { handleSyncWorkflowDraft } = useNodesSyncDraft()
const { saveStateToHistory } = useWorkflowHistory()
const setPanelWidth = useCallback((width: number) => {
localStorage.setItem('workflow-node-panel-width', `${width}`)
workflowStore.setState({ panelWidth: width })
}, [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 {
getNodes,
@ -392,19 +329,8 @@ export const useWorkflow = () => {
return nodes.find(node => node.id === nodeId) || nodes.find(node => node.data.type === BlockEnum.Start)
}, [store])
const enableShortcuts = useCallback(() => {
const { setShortcutsDisabled } = workflowStore.getState()
setShortcutsDisabled(false)
}, [workflowStore])
const disableShortcuts = useCallback(() => {
const { setShortcutsDisabled } = workflowStore.getState()
setShortcutsDisabled(true)
}, [workflowStore])
return {
setPanelWidth,
handleLayout,
getTreeLeafNodes,
getBeforeNodesInSameBranch,
getBeforeNodesInSameBranchIncludeParent,
@ -418,8 +344,6 @@ export const useWorkflow = () => {
getNode,
getBeforeNodeById,
getIterationNodeChildren,
enableShortcuts,
disableShortcuts,
}
}

View File

@ -12,7 +12,6 @@ import {
import { setAutoFreeze } from 'immer'
import {
useEventListener,
useKeyPress,
} from 'ahooks'
import ReactFlow, {
Background,
@ -34,6 +33,9 @@ import type {
EnvironmentVariable,
Node,
} from './types'
import {
ControlMode,
} from './types'
import { WorkflowContextProvider } from './context'
import {
useDSL,
@ -43,10 +45,10 @@ import {
useNodesSyncDraft,
usePanelInteractions,
useSelectionInteractions,
useShortcuts,
useWorkflow,
useWorkflowInit,
useWorkflowReadOnly,
useWorkflowStartRun,
useWorkflowUpdate,
} from './hooks'
import Header from './header'
@ -70,10 +72,8 @@ import {
useWorkflowStore,
} from './store'
import {
getKeyboardKeyCodeBySystem,
initialEdges,
initialNodes,
isEventTargetInputArea,
} from './utils'
import {
CUSTOM_NODE,
@ -81,7 +81,7 @@ import {
ITERATION_CHILDREN_Z_INDEX,
WORKFLOW_DATA_UPDATE,
} from './constants'
import { WorkflowHistoryProvider, useWorkflowHistoryStore } from './workflow-history-store'
import { WorkflowHistoryProvider } from './workflow-history-store'
import Loading from '@/app/components/base/loading'
import { FeaturesProvider } from '@/app/components/base/features'
import type { Features as FeaturesData } from '@/app/components/base/features/types'
@ -225,17 +225,12 @@ const Workflow: FC<WorkflowProps> = memo(({
handleNodeConnectStart,
handleNodeConnectEnd,
handleNodeContextMenu,
handleNodesCopy,
handleNodesPaste,
handleNodesDuplicate,
handleNodesDelete,
handleHistoryBack,
handleHistoryForward,
} = useNodesInteractions()
const {
handleEdgeEnter,
handleEdgeLeave,
handleEdgeDelete,
handleEdgesChange,
} = useEdgesInteractions()
const {
@ -250,7 +245,6 @@ const Workflow: FC<WorkflowProps> = memo(({
const {
isValidConnection,
} = useWorkflow()
const { handleStartWorkflowRun } = useWorkflowStartRun()
const {
exportCheck,
handleExportDSL,
@ -262,41 +256,7 @@ const Workflow: FC<WorkflowProps> = memo(({
},
})
const { shortcutsEnabled: workflowHistoryShortcutsEnabled } = useWorkflowHistoryStore()
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 },
)
useShortcuts()
const store = useStoreApi()
if (process.env.NODE_ENV === 'development') {
@ -388,14 +348,14 @@ const Workflow: FC<WorkflowProps> = memo(({
nodesConnectable={!nodesReadOnly}
nodesFocusable={!nodesReadOnly}
edgesFocusable={!nodesReadOnly}
panOnDrag={controlMode === 'hand' && !workflowReadOnly}
panOnDrag={controlMode === ControlMode.Hand && !workflowReadOnly}
zoomOnPinch={!workflowReadOnly}
zoomOnScroll={!workflowReadOnly}
zoomOnDoubleClick={!workflowReadOnly}
isValidConnection={isValidConnection}
selectionKeyCode={null}
selectionMode={SelectionMode.Partial}
selectionOnDrag={controlMode === 'pointer' && !workflowReadOnly}
selectionOnDrag={controlMode === ControlMode.Pointer && !workflowReadOnly}
minZoom={0.25}
>
<Background

View File

@ -1,7 +1,6 @@
import type { MouseEvent } from 'react'
import {
memo,
useCallback,
} from 'react'
import { useTranslation } from 'react-i18next'
import {
@ -10,13 +9,14 @@ import {
RiHand,
RiStickyNoteAddLine,
} from '@remixicon/react'
import { useKeyPress } from 'ahooks'
import {
useNodesReadOnly,
useSelectionInteractions,
useWorkflow,
useWorkflowMoveMode,
useWorkflowOrganize,
} from '../hooks'
import { getKeyboardKeyCodeBySystem, isEventTargetInputArea } from '../utils'
import {
ControlMode,
} from '../types'
import { useStore } from '../store'
import AddBlock from './add-block'
import TipPopup from './tip-popup'
@ -26,62 +26,13 @@ import cn from '@/utils/classnames'
const Control = () => {
const { t } = useTranslation()
const controlMode = useStore(s => s.controlMode)
const setControlMode = useStore(s => s.setControlMode)
const { handleLayout } = useWorkflow()
const { handleModePointer, handleModeHand } = useWorkflowMoveMode()
const { handleLayout } = useWorkflowOrganize()
const { handleAddNote } = useOperator()
const {
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())
return
handleLayout()
}
useKeyPress(`${getKeyboardKeyCodeBySystem('ctrl')}.o`, (e) => {
e.preventDefault()
goLayout()
}, { exactMatch: true, useCapture: true })
const addNote = (e: MouseEvent<HTMLDivElement>) => {
if (getNodesReadOnly())
@ -110,7 +61,7 @@ const Control = () => {
<div
className={cn(
'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'}`,
)}
onClick={handleModePointer}
@ -122,7 +73,7 @@ const Control = () => {
<div
className={cn(
'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'}`,
)}
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',
`${nodesReadOnly && '!cursor-not-allowed opacity-50'}`,
)}
onClick={goLayout}
onClick={handleLayout}
>
<RiFunctionAddLine className='w-4 h-4' />
</div>

View File

@ -9,7 +9,6 @@ import {
RiZoomInLine,
RiZoomOutLine,
} from '@remixicon/react'
import { useKeyPress } from 'ahooks'
import { useTranslation } from 'react-i18next'
import {
useReactFlow,
@ -20,9 +19,7 @@ import {
useWorkflowReadOnly,
} from '../hooks'
import {
getKeyboardKeyCodeBySystem,
getKeyboardKeyNameBySystem,
isEventTargetInputArea,
} from '../utils'
import ShortcutsName from '../shortcuts-name'
import TipPopup from './tip-popup'
@ -116,87 +113,6 @@ const ZoomInOut: FC = () => {
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(() => {
if (getWorkflowReadOnly())
return
@ -289,11 +205,6 @@ const ZoomInOut: FC = () => {
<ShortcutsName keys={['shift', '1']} />
)
}
{
option.key === ZoomType.zoomTo200 && (
<ShortcutsName keys={['shift', '2']} />
)
}
</div>
))
}

View File

@ -7,7 +7,6 @@ import { Panel as NodePanel } from '../nodes'
import { useStore } from '../store'
import {
useIsChatMode,
useWorkflow,
} from '../hooks'
import DebugAndPreview from './debug-and-preview'
import Record from './record'
@ -28,10 +27,6 @@ const Panel: FC = () => {
const showEnvPanel = useStore(s => s.showEnvPanel)
const showChatVariablePanel = useStore(s => s.showChatVariablePanel)
const isRestoring = useStore(s => s.isRestoring)
const {
enableShortcuts,
disableShortcuts,
} = useWorkflow()
const { currentLogItem, setCurrentLogItem, showMessageLogModal, setShowMessageLogModal, currentLogModalActiveTab } = useAppStore(useShallow(state => ({
currentLogItem: state.currentLogItem,
setCurrentLogItem: state.setCurrentLogItem,
@ -44,8 +39,6 @@ const Panel: FC = () => {
<div
tabIndex={-1}
className={cn('absolute top-14 right-0 bottom-2 flex z-10 outline-none')}
onFocus={disableShortcuts}
onBlur={enableShortcuts}
key={`${isRestoring}`}
>
{

View File

@ -99,8 +99,6 @@ type Shape = {
setWorkflowTools: (tools: ToolWithProvider[]) => void
clipboardElements: Node[]
setClipboardElements: (clipboardElements: Node[]) => void
shortcutsDisabled: boolean
setShortcutsDisabled: (shortcutsDisabled: boolean) => void
showDebugAndPreviewPanel: boolean
setShowDebugAndPreviewPanel: (showDebugAndPreviewPanel: boolean) => void
showEnvPanel: boolean
@ -217,8 +215,6 @@ export const createWorkflowStore = () => {
setWorkflowTools: workflowTools => set(() => ({ workflowTools })),
clipboardElements: [],
setClipboardElements: clipboardElements => set(() => ({ clipboardElements })),
shortcutsDisabled: false,
setShortcutsDisabled: shortcutsDisabled => set(() => ({ shortcutsDisabled })),
showDebugAndPreviewPanel: false,
setShowDebugAndPreviewPanel: showDebugAndPreviewPanel => set(() => ({ showDebugAndPreviewPanel })),
showEnvPanel: false,

View File

@ -29,6 +29,11 @@ export enum BlockEnum {
Assigner = 'assigner', // is now named as VariableAssigner
}
export enum ControlMode {
Pointer = 'pointer',
Hand = 'hand',
}
export type Branch = {
id: string
name: string