mirror of
https://github.com/langgenius/dify.git
synced 2026-04-27 02:36:29 +08:00
node control
This commit is contained in:
parent
04ad1eef79
commit
e307947dd8
@ -19,7 +19,8 @@ const Tools = ({
|
|||||||
isCustom,
|
isCustom,
|
||||||
onSelect,
|
onSelect,
|
||||||
}: ToolsProps) => {
|
}: ToolsProps) => {
|
||||||
const toolsets = useStore(state => state.toolsets).filter(toolset => toolset.type === (isCustom ? 'api' : 'builtin'))
|
const totalToolsets = useStore(state => state.toolsets)
|
||||||
|
const toolsets = totalToolsets.filter(toolset => toolset.type === (isCustom ? 'api' : 'builtin'))
|
||||||
const setToolsets = useStore(state => state.setToolsets)
|
const setToolsets = useStore(state => state.setToolsets)
|
||||||
const toolsMap = useStore(state => state.toolsMap)
|
const toolsMap = useStore(state => state.toolsMap)
|
||||||
const setToolsMap = useStore(state => state.setToolsMap)
|
const setToolsMap = useStore(state => state.setToolsMap)
|
||||||
|
|||||||
@ -1,52 +1,77 @@
|
|||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
import { memo } from 'react'
|
|
||||||
import { useWorkflow } from '../../../hooks'
|
|
||||||
import {
|
import {
|
||||||
DotsHorizontal,
|
memo,
|
||||||
Loading02,
|
useCallback,
|
||||||
} from '@/app/components/base/icons/src/vender/line/general'
|
useState,
|
||||||
|
} from 'react'
|
||||||
|
import { useWorkflow } from '../../../hooks'
|
||||||
|
import type { Node } from '../../../types'
|
||||||
|
import { canRunBySingle } from '../../../utils'
|
||||||
|
import PanelOperator from './panel-operator'
|
||||||
|
import { Loading02 } from '@/app/components/base/icons/src/vender/line/general'
|
||||||
import {
|
import {
|
||||||
Play,
|
Play,
|
||||||
Stop,
|
Stop,
|
||||||
} from '@/app/components/base/icons/src/vender/line/mediaAndDevices'
|
} from '@/app/components/base/icons/src/vender/line/mediaAndDevices'
|
||||||
|
|
||||||
type NodeControlProps = {
|
type NodeControlProps = Pick<Node, 'id' | 'data'>
|
||||||
isRunning?: boolean
|
|
||||||
nodeId: string
|
|
||||||
}
|
|
||||||
const NodeControl: FC<NodeControlProps> = ({
|
const NodeControl: FC<NodeControlProps> = ({
|
||||||
isRunning,
|
id,
|
||||||
nodeId,
|
data,
|
||||||
}) => {
|
}) => {
|
||||||
|
const [open, setOpen] = useState(false)
|
||||||
const { handleNodeDataUpdate } = useWorkflow()
|
const { handleNodeDataUpdate } = useWorkflow()
|
||||||
|
|
||||||
|
const handleOpenChange = useCallback((newOpen: boolean) => {
|
||||||
|
setOpen(newOpen)
|
||||||
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex items-center px-0.5 h-6 bg-white rounded-lg border-[0.5px] border-gray-100 shadow-xs text-gray-500'>
|
<div
|
||||||
{
|
className={`
|
||||||
isRunning && (
|
hidden group-hover:flex pb-1 absolute right-0 -top-7 h-7
|
||||||
<div className='flex items-center px-1 h-5 rounded-md bg-primary-50 text-xs font-medium text-primary-600'>
|
${data.selected && '!flex'}
|
||||||
<Loading02 className='mr-1 w-3 h-3 animate-spin' />
|
${open && '!flex'}
|
||||||
RUNNING
|
`}
|
||||||
</div>
|
>
|
||||||
)
|
|
||||||
}
|
|
||||||
<div
|
<div
|
||||||
className='flex items-center justify-center w-5 h-5 rounded-md cursor-pointer hover:bg-black/5'
|
className='flex items-center px-0.5 h-6 bg-white rounded-lg border-[0.5px] border-gray-100 shadow-xs text-gray-500'
|
||||||
onClick={() => {
|
onClick={e => e.stopPropagation()}
|
||||||
handleNodeDataUpdate({
|
|
||||||
id: nodeId,
|
|
||||||
data: { _isSingleRun: !isRunning },
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
isRunning
|
data._isSingleRun && (
|
||||||
? <Stop className='w-3 h-3' />
|
<div className='flex items-center mr-1 px-1 h-5 rounded-md bg-primary-50 text-xs font-medium text-primary-600'>
|
||||||
: <Play className='w-3 h-3' />
|
<Loading02 className='mr-1 w-3 h-3 animate-spin' />
|
||||||
|
RUNNING
|
||||||
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
</div>
|
{
|
||||||
<div className='flex items-center justify-center w-5 h-5 rounded-md cursor-pointer hover:bg-black/5'>
|
canRunBySingle(data.type) && (
|
||||||
<DotsHorizontal className='w-3 h-3' />
|
<div
|
||||||
|
className='flex items-center justify-center w-5 h-5 rounded-md cursor-pointer hover:bg-black/5'
|
||||||
|
onClick={() => {
|
||||||
|
handleNodeDataUpdate({
|
||||||
|
id,
|
||||||
|
data: { _isSingleRun: !data._isSingleRun },
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
data._isSingleRun
|
||||||
|
? <Stop className='w-3 h-3' />
|
||||||
|
: <Play className='w-3 h-3' />
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
<PanelOperator
|
||||||
|
id={id}
|
||||||
|
data={data}
|
||||||
|
offset={0}
|
||||||
|
onOpenChange={handleOpenChange}
|
||||||
|
triggerClassName='!w-5 !h-5'
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import produce from 'immer'
|
|||||||
import { useContext } from 'use-context-selector'
|
import { useContext } from 'use-context-selector'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useEdges } from 'reactflow'
|
import { useEdges } from 'reactflow'
|
||||||
|
import type { OffsetOptions } from '@floating-ui/react'
|
||||||
import ChangeBlock from './change-block'
|
import ChangeBlock from './change-block'
|
||||||
import { useStore } from '@/app/components/workflow/store'
|
import { useStore } from '@/app/components/workflow/store'
|
||||||
import {
|
import {
|
||||||
@ -33,10 +34,19 @@ import {
|
|||||||
type PanelOperatorProps = {
|
type PanelOperatorProps = {
|
||||||
id: string
|
id: string
|
||||||
data: Node['data']
|
data: Node['data']
|
||||||
|
triggerClassName?: string
|
||||||
|
offset?: OffsetOptions
|
||||||
|
onOpenChange?: (open: boolean) => void
|
||||||
}
|
}
|
||||||
const PanelOperator = ({
|
const PanelOperator = ({
|
||||||
id,
|
id,
|
||||||
data,
|
data,
|
||||||
|
triggerClassName,
|
||||||
|
offset = {
|
||||||
|
mainAxis: 4,
|
||||||
|
crossAxis: 53,
|
||||||
|
},
|
||||||
|
onOpenChange,
|
||||||
}: PanelOperatorProps) => {
|
}: PanelOperatorProps) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { locale } = useContext(I18n)
|
const { locale } = useContext(I18n)
|
||||||
@ -86,22 +96,27 @@ const PanelOperator = ({
|
|||||||
return tool?.description[language] || ''
|
return tool?.description[language] || ''
|
||||||
}, [data, nodesExtraData, toolsMap, language])
|
}, [data, nodesExtraData, toolsMap, language])
|
||||||
|
|
||||||
|
const handleOpenChange = useCallback((newOpen: boolean) => {
|
||||||
|
setOpen(newOpen)
|
||||||
|
|
||||||
|
if (onOpenChange)
|
||||||
|
onOpenChange(newOpen)
|
||||||
|
}, [onOpenChange])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PortalToFollowElem
|
<PortalToFollowElem
|
||||||
placement='bottom-end'
|
placement='bottom-end'
|
||||||
offset={{
|
offset={offset}
|
||||||
mainAxis: 4,
|
|
||||||
crossAxis: 53,
|
|
||||||
}}
|
|
||||||
open={open}
|
open={open}
|
||||||
onOpenChange={setOpen}
|
onOpenChange={handleOpenChange}
|
||||||
>
|
>
|
||||||
<PortalToFollowElemTrigger onClick={() => setOpen(v => !v)}>
|
<PortalToFollowElemTrigger onClick={() => handleOpenChange(!open)}>
|
||||||
<div
|
<div
|
||||||
className={`
|
className={`
|
||||||
flex items-center justify-center w-6 h-6 rounded-md cursor-pointer
|
flex items-center justify-center w-6 h-6 rounded-md cursor-pointer
|
||||||
hover:bg-black/5
|
hover:bg-black/5
|
||||||
${open && 'bg-black/5'}
|
${open && 'bg-black/5'}
|
||||||
|
${triggerClassName}
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
<DotsHorizontal className='w-4 h-4 text-gray-700' />
|
<DotsHorizontal className='w-4 h-4 text-gray-700' />
|
||||||
@ -110,10 +125,14 @@ const PanelOperator = ({
|
|||||||
<PortalToFollowElemContent className='z-[11]'>
|
<PortalToFollowElemContent className='z-[11]'>
|
||||||
<div className='w-[240px] border-[0.5px] border-gray-200 rounded-2xl shadow-xl bg-white'>
|
<div className='w-[240px] border-[0.5px] border-gray-200 rounded-2xl shadow-xl bg-white'>
|
||||||
<div className='p-1'>
|
<div className='p-1'>
|
||||||
<ChangeBlock
|
{
|
||||||
nodeId={id}
|
data.type !== BlockEnum.Start && (
|
||||||
sourceHandle={edge?.sourceHandle || 'source'}
|
<ChangeBlock
|
||||||
/>
|
nodeId={id}
|
||||||
|
sourceHandle={edge?.sourceHandle || 'source'}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
<div className='flex items-center px-3 h-8 text-sm text-gray-700 rounded-lg cursor-pointer hover:bg-gray-50'>
|
<div className='flex items-center px-3 h-8 text-sm text-gray-700 rounded-lg cursor-pointer hover:bg-gray-50'>
|
||||||
{t('workflow.panel.helpLink')}
|
{t('workflow.panel.helpLink')}
|
||||||
</div>
|
</div>
|
||||||
@ -124,7 +143,10 @@ const PanelOperator = ({
|
|||||||
<div className='h-[1px] bg-gray-100'></div>
|
<div className='h-[1px] bg-gray-100'></div>
|
||||||
<div className='p-1'>
|
<div className='p-1'>
|
||||||
<div
|
<div
|
||||||
className='flex items-center px-3 h-8 text-sm text-gray-700 rounded-lg cursor-pointer hover:bg-gray-50'
|
className={`
|
||||||
|
flex items-center px-3 h-8 text-sm text-gray-700 rounded-lg cursor-pointer
|
||||||
|
hover:bg-rose-50 hover:text-red-500
|
||||||
|
`}
|
||||||
onClick={() => handleNodeDelete(id)}
|
onClick={() => handleNodeDelete(id)}
|
||||||
>
|
>
|
||||||
{t('common.operation.delete')}
|
{t('common.operation.delete')}
|
||||||
|
|||||||
@ -8,7 +8,6 @@ import {
|
|||||||
} from 'react'
|
} from 'react'
|
||||||
import type { NodeProps } from '../../types'
|
import type { NodeProps } from '../../types'
|
||||||
import { BlockEnum } from '../../types'
|
import { BlockEnum } from '../../types'
|
||||||
import { canRunBySingle } from '../../utils'
|
|
||||||
import {
|
import {
|
||||||
NodeSourceHandle,
|
NodeSourceHandle,
|
||||||
NodeTargetHandle,
|
NodeTargetHandle,
|
||||||
@ -59,17 +58,10 @@ const BaseNode: FC<BaseNodeProps> = ({
|
|||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
{
|
<NodeControl
|
||||||
canRunBySingle(data.type)
|
id={id}
|
||||||
&& (
|
data={data}
|
||||||
<div className='hidden group-hover:flex pb-1 absolute right-0 -top-7 h-7'>
|
/>
|
||||||
<NodeControl
|
|
||||||
nodeId={id}
|
|
||||||
isRunning={data._isSingleRun}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
<div className='flex items-center px-3 pt-3 pb-2'>
|
<div className='flex items-center px-3 pt-3 pb-2'>
|
||||||
<BlockIcon
|
<BlockIcon
|
||||||
className='shrink-0 mr-2'
|
className='shrink-0 mr-2'
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user