Co-authored-by: crazywoola <427733928@qq.com> Co-authored-by: crazywoola <100913391+crazywoola@users.noreply.github.com>tags/1.7.2
| @@ -134,7 +134,8 @@ const CustomEdge = ({ | |||
| style={{ | |||
| stroke, | |||
| strokeWidth: 2, | |||
| opacity: data._waitingRun ? 0.7 : 1, | |||
| opacity: data._dimmed ? 0.3 : (data._waitingRun ? 0.7 : 1), | |||
| strokeDasharray: data._isTemp ? '8 8' : undefined, | |||
| }} | |||
| /> | |||
| <EdgeLabelRenderer> | |||
| @@ -1,5 +1,5 @@ | |||
| import type { MouseEvent } from 'react' | |||
| import { useCallback, useRef } from 'react' | |||
| import { useCallback, useRef, useState } from 'react' | |||
| import { useTranslation } from 'react-i18next' | |||
| import produce from 'immer' | |||
| import type { | |||
| @@ -61,6 +61,7 @@ import { | |||
| } from './use-workflow' | |||
| import { WorkflowHistoryEvent, useWorkflowHistory } from './use-workflow-history' | |||
| import useInspectVarsCrud from './use-inspect-vars-crud' | |||
| import { getNodeUsedVars } from '../nodes/_base/components/variable/utils' | |||
| export const useNodesInteractions = () => { | |||
| const { t } = useTranslation() | |||
| @@ -1530,6 +1531,135 @@ export const useNodesInteractions = () => { | |||
| setNodes(nodes) | |||
| }, [redo, store, workflowHistoryStore, getNodesReadOnly, getWorkflowReadOnly]) | |||
| const [isDimming, setIsDimming] = useState(false) | |||
| /** Add opacity-30 to all nodes except the nodeId */ | |||
| const dimOtherNodes = useCallback(() => { | |||
| if (isDimming) | |||
| return | |||
| const { getNodes, setNodes, edges, setEdges } = store.getState() | |||
| const nodes = getNodes() | |||
| const selectedNode = nodes.find(n => n.data.selected) | |||
| if (!selectedNode) | |||
| return | |||
| setIsDimming(true) | |||
| // const workflowNodes = useStore(s => s.getNodes()) | |||
| const workflowNodes = nodes | |||
| const usedVars = getNodeUsedVars(selectedNode) | |||
| const dependencyNodes: Node[] = [] | |||
| usedVars.forEach((valueSelector) => { | |||
| const node = workflowNodes.find(node => node.id === valueSelector?.[0]) | |||
| if (node) { | |||
| if (!dependencyNodes.includes(node)) | |||
| dependencyNodes.push(node) | |||
| } | |||
| }) | |||
| const outgoers = getOutgoers(selectedNode as Node, nodes as Node[], edges) | |||
| for (let currIdx = 0; currIdx < outgoers.length; currIdx++) { | |||
| const node = outgoers[currIdx] | |||
| const outgoersForNode = getOutgoers(node, nodes as Node[], edges) | |||
| outgoersForNode.forEach((item) => { | |||
| const existed = outgoers.some(v => v.id === item.id) | |||
| if (!existed) | |||
| outgoers.push(item) | |||
| }) | |||
| } | |||
| const dependentNodes: Node[] = [] | |||
| outgoers.forEach((node) => { | |||
| const usedVars = getNodeUsedVars(node) | |||
| const used = usedVars.some(v => v?.[0] === selectedNode.id) | |||
| if (used) { | |||
| const existed = dependentNodes.some(v => v.id === node.id) | |||
| if (!existed) | |||
| dependentNodes.push(node) | |||
| } | |||
| }) | |||
| const dimNodes = [...dependencyNodes, ...dependentNodes, selectedNode] | |||
| const newNodes = produce(nodes, (draft) => { | |||
| draft.forEach((n) => { | |||
| const dimNode = dimNodes.find(v => v.id === n.id) | |||
| if (!dimNode) | |||
| n.data._dimmed = true | |||
| }) | |||
| }) | |||
| setNodes(newNodes) | |||
| const tempEdges: Edge[] = [] | |||
| dependencyNodes.forEach((n) => { | |||
| tempEdges.push({ | |||
| id: `tmp_${n.id}-source-${selectedNode.id}-target`, | |||
| type: CUSTOM_EDGE, | |||
| source: n.id, | |||
| sourceHandle: 'source_tmp', | |||
| target: selectedNode.id, | |||
| targetHandle: 'target_tmp', | |||
| animated: true, | |||
| data: { | |||
| sourceType: n.data.type, | |||
| targetType: selectedNode.data.type, | |||
| _isTemp: true, | |||
| _connectedNodeIsHovering: true, | |||
| }, | |||
| }) | |||
| }) | |||
| dependentNodes.forEach((n) => { | |||
| tempEdges.push({ | |||
| id: `tmp_${selectedNode.id}-source-${n.id}-target`, | |||
| type: CUSTOM_EDGE, | |||
| source: selectedNode.id, | |||
| sourceHandle: 'source_tmp', | |||
| target: n.id, | |||
| targetHandle: 'target_tmp', | |||
| animated: true, | |||
| data: { | |||
| sourceType: selectedNode.data.type, | |||
| targetType: n.data.type, | |||
| _isTemp: true, | |||
| _connectedNodeIsHovering: true, | |||
| }, | |||
| }) | |||
| }) | |||
| const newEdges = produce(edges, (draft) => { | |||
| draft.forEach((e) => { | |||
| e.data._dimmed = true | |||
| }) | |||
| draft.push(...tempEdges) | |||
| }) | |||
| setEdges(newEdges) | |||
| }, [isDimming, store]) | |||
| /** Restore all nodes to full opacity */ | |||
| const undimAllNodes = useCallback(() => { | |||
| const { getNodes, setNodes, edges, setEdges } = store.getState() | |||
| const nodes = getNodes() | |||
| setIsDimming(false) | |||
| const newNodes = produce(nodes, (draft) => { | |||
| draft.forEach((n) => { | |||
| n.data._dimmed = false | |||
| }) | |||
| }) | |||
| setNodes(newNodes) | |||
| const newEdges = produce(edges.filter(e => !e.data._isTemp), (draft) => { | |||
| draft.forEach((e) => { | |||
| e.data._dimmed = false | |||
| }) | |||
| }) | |||
| setEdges(newEdges) | |||
| }, [store]) | |||
| return { | |||
| handleNodeDragStart, | |||
| handleNodeDrag, | |||
| @@ -1554,5 +1684,7 @@ export const useNodesInteractions = () => { | |||
| handleNodeDisconnect, | |||
| handleHistoryBack, | |||
| handleHistoryForward, | |||
| dimOtherNodes, | |||
| undimAllNodes, | |||
| } | |||
| } | |||
| @@ -25,6 +25,8 @@ export const useShortcuts = (): void => { | |||
| handleNodesDelete, | |||
| handleHistoryBack, | |||
| handleHistoryForward, | |||
| dimOtherNodes, | |||
| undimAllNodes, | |||
| } = useNodesInteractions() | |||
| const { handleStartWorkflowRun } = useWorkflowStartRun() | |||
| const { shortcutsEnabled: workflowHistoryShortcutsEnabled } = useWorkflowHistoryStore() | |||
| @@ -211,4 +213,35 @@ export const useShortcuts = (): void => { | |||
| exactMatch: true, | |||
| useCapture: true, | |||
| }) | |||
| // Shift ↓ | |||
| useKeyPress( | |||
| 'shift', | |||
| (e) => { | |||
| console.log('Shift down', e) | |||
| if (shouldHandleShortcut(e)) | |||
| dimOtherNodes() | |||
| }, | |||
| { | |||
| exactMatch: true, | |||
| useCapture: true, | |||
| events: ['keydown'], | |||
| }, | |||
| ) | |||
| // Shift ↑ | |||
| useKeyPress( | |||
| (e) => { | |||
| return e.key === 'Shift' | |||
| }, | |||
| (e) => { | |||
| if (shouldHandleShortcut(e)) | |||
| undimAllNodes() | |||
| }, | |||
| { | |||
| exactMatch: true, | |||
| useCapture: true, | |||
| events: ['keyup'], | |||
| }, | |||
| ) | |||
| } | |||
| @@ -7,6 +7,7 @@ import { useTranslation } from 'react-i18next' | |||
| export enum TabType { | |||
| settings = 'settings', | |||
| lastRun = 'lastRun', | |||
| relations = 'relations', | |||
| } | |||
| type Props = { | |||
| @@ -143,6 +143,7 @@ const BaseNode: FC<BaseNodeProps> = ({ | |||
| showSelectedBorder ? 'border-components-option-card-option-selected-border' : 'border-transparent', | |||
| !showSelectedBorder && data._inParallelHovering && 'border-workflow-block-border-highlight', | |||
| data._waitingRun && 'opacity-70', | |||
| data._dimmed && 'opacity-30', | |||
| )} | |||
| ref={nodeRef} | |||
| style={{ | |||
| @@ -94,6 +94,7 @@ export type CommonNodeType<T = {}> = { | |||
| retry_config?: WorkflowRetryConfig | |||
| default_value?: DefaultValueForm[] | |||
| credential_id?: string | |||
| _dimmed?: boolean | |||
| } & T & Partial<Pick<ToolDefaultValue, 'provider_id' | 'provider_type' | 'provider_name' | 'tool_name'>> | |||
| export type CommonEdgeType = { | |||
| @@ -109,7 +110,8 @@ export type CommonEdgeType = { | |||
| isInLoop?: boolean | |||
| loop_id?: string | |||
| sourceType: BlockEnum | |||
| targetType: BlockEnum | |||
| targetType: BlockEnum, | |||
| _isTemp?: boolean, | |||
| } | |||
| export type Node<T = {}> = ReactFlowNode<CommonNodeType<T>> | |||
| @@ -943,6 +943,7 @@ const translation = { | |||
| debug: { | |||
| settingsTab: 'Settings', | |||
| lastRunTab: 'Last Run', | |||
| relationsTab: 'Relations', | |||
| noData: { | |||
| description: 'The results of the last run will be displayed here', | |||
| runThisNode: 'Run this node', | |||
| @@ -968,6 +969,14 @@ const translation = { | |||
| chatNode: 'Conversation', | |||
| systemNode: 'System', | |||
| }, | |||
| relations: { | |||
| dependencies: 'Dependencies', | |||
| dependents: 'Dependents', | |||
| dependenciesDescription: 'Nodes that this node relies on', | |||
| dependentsDescription: 'Nodes that rely on this node', | |||
| noDependencies: 'No dependencies', | |||
| noDependents: 'No dependents', | |||
| }, | |||
| }, | |||
| } | |||
| @@ -968,6 +968,15 @@ const translation = { | |||
| }, | |||
| settingsTab: '設定', | |||
| lastRunTab: '最後の実行', | |||
| relationsTab: '関係', | |||
| relations: { | |||
| dependencies: '依存元', | |||
| dependents: '依存先', | |||
| dependenciesDescription: 'このノードが依存している他のノード', | |||
| dependentsDescription: 'このノードに依存している他のノード', | |||
| noDependencies: '依存元なし', | |||
| noDependents: '依存先なし', | |||
| }, | |||
| }, | |||
| } | |||
| @@ -943,6 +943,7 @@ const translation = { | |||
| debug: { | |||
| settingsTab: '设置', | |||
| lastRunTab: '上次运行', | |||
| relationsTab: '关系', | |||
| noData: { | |||
| description: '上次运行的结果将显示在这里', | |||
| runThisNode: '运行此节点', | |||
| @@ -968,6 +969,14 @@ const translation = { | |||
| chatNode: '会话变量', | |||
| systemNode: '系统变量', | |||
| }, | |||
| relations: { | |||
| dependencies: '依赖', | |||
| dependents: '被依赖', | |||
| dependenciesDescription: '本节点依赖的其他节点', | |||
| dependentsDescription: '依赖于本节点的其他节点', | |||
| noDependencies: '无依赖', | |||
| noDependents: '无被依赖', | |||
| }, | |||
| }, | |||
| } | |||
| @@ -941,6 +941,9 @@ const translation = { | |||
| copyId: '複製ID', | |||
| }, | |||
| debug: { | |||
| settingsTab: '設定', | |||
| lastRunTab: '最後一次運行', | |||
| relationsTab: '關係', | |||
| noData: { | |||
| runThisNode: '運行此節點', | |||
| description: '上次運行的結果將顯示在這裡', | |||
| @@ -966,8 +969,14 @@ const translation = { | |||
| emptyTip: '在畫布上逐步執行節點或逐步運行節點後,您可以在變數檢視中查看節點變數的當前值。', | |||
| resetConversationVar: '將對話變數重置為默認值', | |||
| }, | |||
| settingsTab: '設定', | |||
| lastRunTab: '最後一次運行', | |||
| relations: { | |||
| dependencies: '依賴', | |||
| dependents: '被依賴', | |||
| dependenciesDescription: '此節點所依賴的其他節點', | |||
| dependentsDescription: '依賴此節點的其他節點', | |||
| noDependencies: '無依賴', | |||
| noDependents: '無被依賴', | |||
| }, | |||
| }, | |||
| } | |||