Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
D
dify
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
ai-tech
dify
Commits
e3a3e07e
Commit
e3a3e07e
authored
Mar 06, 2024
by
StyleZhang
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
tool
parent
8a906e29
Changes
22
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
22 changed files
with
1042 additions
and
836 deletions
+1042
-836
constants.tsx
web/app/components/workflow/block-selector/constants.tsx
+1
-26
hooks.ts
web/app/components/workflow/block-selector/hooks.ts
+20
-0
index.tsx
web/app/components/workflow/block-selector/index.tsx
+4
-4
tabs.tsx
web/app/components/workflow/block-selector/tabs.tsx
+64
-46
index.tsx
web/app/components/workflow/block-selector/tools/index.tsx
+82
-0
item.tsx
web/app/components/workflow/block-selector/tools/item.tsx
+128
-0
types.ts
web/app/components/workflow/block-selector/types.ts
+34
-0
hooks.ts
web/app/components/workflow/hooks.ts
+22
-3
index.tsx
web/app/components/workflow/index.tsx
+19
-0
add.tsx
...ponents/workflow/nodes/_base/components/next-step/add.tsx
+5
-5
index.tsx
...nents/workflow/nodes/_base/components/next-step/index.tsx
+1
-0
item.tsx
...onents/workflow/nodes/_base/components/next-step/item.tsx
+8
-8
node-handle.tsx
...omponents/workflow/nodes/_base/components/node-handle.tsx
+3
-2
change-block.tsx
...ow/nodes/_base/components/panel-operator/change-block.tsx
+8
-6
index.tsx
.../workflow/nodes/_base/components/panel-operator/index.tsx
+9
-2
node.tsx
web/app/components/workflow/nodes/_base/node.tsx
+40
-7
panel.tsx
web/app/components/workflow/nodes/_base/panel.tsx
+41
-10
store.ts
web/app/components/workflow/store.ts
+13
-0
types.ts
web/app/components/workflow/types.ts
+5
-0
workflow.ts
web/i18n/en-US/workflow.ts
+5
-0
workflow.ts
web/i18n/zh-Hans/workflow.ts
+5
-0
yarn.lock
web/yarn.lock
+525
-717
No files found.
web/app/components/workflow/block-selector/constants.tsx
View file @
e3a3e07e
import
{
groupBy
}
from
'lodash-es'
import
type
{
Block
}
from
'../types'
import
{
BlockEnum
}
from
'../types'
export
const
TABS
=
[
{
key
:
'blocks'
,
name
:
'Blocks'
,
},
{
key
:
'built-in-tool'
,
name
:
'Built-in Tool'
,
},
{
key
:
'custom-tool'
,
name
:
'Custom Tool'
,
},
]
export
enum
BlockClassificationEnum
{
Default
=
'-'
,
QuestionUnderstand
=
'question-understand'
,
Logic
=
'logic'
,
Transform
=
'transform'
,
Utilities
=
'utilities'
,
}
import
{
BlockClassificationEnum
}
from
'./types'
export
const
BLOCKS
:
Block
[]
=
[
{
...
...
@@ -91,5 +68,3 @@ export const BLOCK_CLASSIFICATIONS: string[] = [
BlockClassificationEnum
.
Transform
,
BlockClassificationEnum
.
Utilities
,
]
export
const
BLOCK_GROUP_BY_CLASSIFICATION
=
groupBy
(
BLOCKS
,
'classification'
)
web/app/components/workflow/block-selector/hooks.ts
View file @
e3a3e07e
import
{
useTranslation
}
from
'react-i18next'
import
{
BLOCKS
}
from
'./constants'
import
{
TabsEnum
}
from
'./types'
export
const
useBlocks
=
()
=>
{
const
{
t
}
=
useTranslation
()
...
...
@@ -11,3 +12,22 @@ export const useBlocks = () => {
}
})
}
export
const
useTabs
=
()
=>
{
const
{
t
}
=
useTranslation
()
return
[
{
key
:
TabsEnum
.
Blocks
,
name
:
t
(
'workflow.tabs.blocks'
),
},
{
key
:
TabsEnum
.
BuiltInTool
,
name
:
t
(
'workflow.tabs.builtInTool'
),
},
{
key
:
TabsEnum
.
CustomTool
,
name
:
t
(
'workflow.tabs.customTool'
),
},
]
}
web/app/components/workflow/block-selector/index.tsx
View file @
e3a3e07e
...
...
@@ -11,7 +11,6 @@ import type {
OffsetOptions
,
Placement
,
}
from
'@floating-ui/react'
import
type
{
BlockEnum
}
from
'../types'
import
Tabs
from
'./tabs'
import
{
PortalToFollowElem
,
...
...
@@ -22,11 +21,12 @@ import {
Plus02
,
SearchLg
,
}
from
'@/app/components/base/icons/src/vender/line/general'
import
type
{
OnSelectBlock
}
from
'@/app/components/workflow/types'
type
NodeSelectorProps
=
{
open
?:
boolean
onOpenChange
?:
(
open
:
boolean
)
=>
void
onSelect
:
(
type
:
BlockEnum
)
=>
void
onSelect
:
OnSelectBlock
trigger
?:
(
open
:
boolean
)
=>
React
.
ReactNode
placement
?:
Placement
offset
?:
OffsetOptions
...
...
@@ -59,9 +59,9 @@ const NodeSelector: FC<NodeSelectorProps> = ({
e
.
stopPropagation
()
setLocalOpen
(
v
=>
!
v
)
},
[])
const
handleSelect
=
useCallback
((
type
:
BlockEnum
)
=>
{
const
handleSelect
=
useCallback
<
OnSelectBlock
>
((
type
,
toolDefaultValue
)
=>
{
handleOpenChange
(
false
)
onSelect
(
type
)
onSelect
(
type
,
toolDefaultValue
)
},
[
handleOpenChange
,
onSelect
])
return
(
...
...
web/app/components/workflow/block-selector/tabs.tsx
View file @
e3a3e07e
...
...
@@ -6,30 +6,34 @@ import {
import
{
groupBy
}
from
'lodash-es'
import
BlockIcon
from
'../block-icon'
import
type
{
BlockEnum
}
from
'../types'
import
{
BLOCK_CLASSIFICATIONS
}
from
'./constants'
import
{
BLOCK_CLASSIFICATIONS
,
TABS
,
}
from
'./constants'
import
{
useBlocks
}
from
'./hooks'
useBlocks
,
useTabs
,
}
from
'./hooks'
import
type
{
ToolDefaultValue
}
from
'./types'
import
{
TabsEnum
}
from
'./types'
import
Tools
from
'./tools'
export
type
TabsProps
=
{
onSelect
:
(
type
:
BlockEnum
)
=>
void
onSelect
:
(
type
:
BlockEnum
,
tool
?:
ToolDefaultValue
)
=>
void
}
const
Tabs
:
FC
<
TabsProps
>
=
({
onSelect
,
})
=>
{
const
[
activeTab
,
setActiveTab
]
=
useState
(
TABS
[
0
].
key
)
const
blocks
=
useBlocks
()
const
tabs
=
useTabs
()
const
[
activeTab
,
setActiveTab
]
=
useState
(
tabs
[
0
].
key
)
return
(
<
div
>
<
div
className=
'flex items-center
justify-between
px-3 h-[34px] border-b-[0.5px] border-b-black/5'
>
<
div
onClick=
{
e
=>
e
.
stopPropagation
()
}
>
<
div
className=
'flex items-center px-3 h-[34px] border-b-[0.5px] border-b-black/5'
>
{
TABS
.
map
(
tab
=>
(
tabs
.
map
(
tab
=>
(
<
div
key=
{
tab
.
key
}
className=
{
`
text-[13px] font-medium cursor-pointer
mr-4
text-[13px] font-medium cursor-pointer
${activeTab === tab.key ? 'text-gray-700' : 'text-gray-500'}
`
}
onClick=
{
()
=>
setActiveTab
(
tab
.
key
)
}
...
...
@@ -39,6 +43,8 @@ const Tabs: FC<TabsProps> = ({
))
}
</
div
>
{
activeTab
===
TabsEnum
.
Blocks
&&
(
<
div
className=
'p-1'
>
{
BLOCK_CLASSIFICATIONS
.
map
(
classification
=>
(
...
...
@@ -58,10 +64,7 @@ const Tabs: FC<TabsProps> = ({
<
div
key=
{
block
.
type
}
className=
'flex items-center px-3 h-8 rounded-lg hover:bg-gray-50 cursor-pointer'
onClick=
{
(
e
)
=>
{
e
.
stopPropagation
()
onSelect
(
block
.
type
)
}
}
onClick=
{
()
=>
onSelect
(
block
.
type
)
}
>
<
BlockIcon
className=
'mr-2'
...
...
@@ -75,6 +78,21 @@ const Tabs: FC<TabsProps> = ({
))
}
</
div
>
)
}
{
activeTab
===
TabsEnum
.
BuiltInTool
&&
(
<
Tools
onSelect=
{
onSelect
}
/>
)
}
{
activeTab
===
TabsEnum
.
CustomTool
&&
(
<
Tools
isCustom
onSelect=
{
onSelect
}
/>
)
}
</
div
>
)
}
...
...
web/app/components/workflow/block-selector/tools/index.tsx
0 → 100644
View file @
e3a3e07e
import
{
memo
,
useCallback
,
}
from
'react'
import
produce
from
'immer'
import
{
useStore
}
from
'../../store'
import
type
{
BlockEnum
}
from
'../../types'
import
type
{
ToolDefaultValue
,
ToolInWorkflow
,
}
from
'../types'
import
Item
from
'./item'
type
ToolsProps
=
{
isCustom
?:
boolean
onSelect
:
(
type
:
BlockEnum
,
tool
?:
ToolDefaultValue
)
=>
void
}
const
Tools
=
({
isCustom
,
onSelect
,
}:
ToolsProps
)
=>
{
const
toolsets
=
useStore
(
state
=>
state
.
toolsets
).
filter
(
toolset
=>
toolset
.
type
===
(
isCustom
?
'api'
:
'builtin'
))
const
setToolsets
=
useStore
(
state
=>
state
.
setToolsets
)
const
toolsMap
=
useStore
(
state
=>
state
.
toolsMap
)
const
setToolsMap
=
useStore
(
state
=>
state
.
setToolsMap
)
const
handleExpand
=
useCallback
((
toolId
:
string
)
=>
{
const
currentToolset
=
toolsets
.
find
(
toolset
=>
toolset
.
id
===
toolId
)
!
if
(
currentToolset
.
expanded
)
{
setToolsets
(
produce
(
toolsets
,
(
draft
)
=>
{
const
index
=
draft
.
findIndex
(
toolset
=>
toolset
.
id
===
toolId
)
draft
[
index
].
expanded
=
false
}))
return
}
if
(
!
currentToolset
.
expanded
)
{
setToolsets
(
produce
(
toolsets
,
(
draft
)
=>
{
const
index
=
draft
.
findIndex
(
toolset
=>
toolset
.
id
===
toolId
)
if
(
!
toolsMap
[
toolId
].
length
&&
!
currentToolset
.
fetching
)
draft
[
index
].
fetching
=
true
draft
[
index
].
expanded
=
true
}))
}
},
[
setToolsets
,
toolsets
,
toolsMap
])
const
handleAddTools
=
useCallback
((
toolsetId
:
string
,
tools
:
ToolInWorkflow
[])
=>
{
setToolsMap
(
produce
(
toolsMap
,
(
draft
)
=>
{
draft
[
toolsetId
]
=
tools
}))
},
[
setToolsMap
,
toolsMap
])
const
handleFetched
=
useCallback
((
toolsetId
:
string
)
=>
{
setToolsets
(
produce
(
toolsets
,
(
draft
)
=>
{
const
index
=
draft
.
findIndex
(
toolset
=>
toolset
.
id
===
toolsetId
)
draft
[
index
].
fetching
=
false
}))
},
[
setToolsets
,
toolsets
])
return
(
<
div
className=
'p-1 max-h-[464px] overflow-y-auto'
>
{
toolsets
.
map
(
toolset
=>
(
<
Item
key=
{
toolset
.
id
}
data=
{
toolset
}
tools=
{
toolsMap
[
toolset
.
id
]
}
onExpand=
{
handleExpand
}
onAddTools=
{
handleAddTools
}
onFetched=
{
handleFetched
}
onSelect=
{
onSelect
}
/>
))
}
</
div
>
)
}
export
default
memo
(
Tools
)
web/app/components/workflow/block-selector/tools/item.tsx
0 → 100644
View file @
e3a3e07e
import
{
memo
,
useCallback
,
useEffect
,
useMemo
,
}
from
'react'
import
{
useContext
}
from
'use-context-selector'
import
{
BlockEnum
}
from
'../../types'
import
type
{
CollectionWithExpanded
,
ToolDefaultValue
,
ToolInWorkflow
,
}
from
'../types'
import
AppIcon
from
'@/app/components/base/app-icon'
import
{
ChevronDown
}
from
'@/app/components/base/icons/src/vender/line/arrows'
import
{
fetchBuiltInToolList
,
fetchCustomToolList
,
}
from
'@/service/tools'
import
I18n
from
'@/context/i18n'
import
{
getLanguage
}
from
'@/i18n/language'
import
Loading
from
'@/app/components/base/loading'
type
ItemProps
=
{
data
:
CollectionWithExpanded
tools
:
ToolInWorkflow
[]
onExpand
:
(
toolsetId
:
string
)
=>
void
onAddTools
:
(
toolsetId
:
string
,
tools
:
ToolInWorkflow
[])
=>
void
onFetched
:
(
toolsetId
:
string
)
=>
void
onSelect
:
(
type
:
BlockEnum
,
tool
?:
ToolDefaultValue
)
=>
void
}
const
Item
=
({
data
,
tools
,
onExpand
,
onAddTools
,
onFetched
,
onSelect
,
}:
ItemProps
)
=>
{
const
{
locale
}
=
useContext
(
I18n
)
const
language
=
getLanguage
(
locale
)
const
fetchToolList
=
useMemo
(()
=>
{
return
data
.
type
===
'api'
?
fetchCustomToolList
:
fetchBuiltInToolList
},
[
data
.
type
])
const
handleFetchToolList
=
useCallback
(()
=>
{
fetchToolList
(
data
.
name
).
then
((
list
)
=>
{
onAddTools
(
data
.
id
,
list
)
}).
finally
(()
=>
{
onFetched
(
data
.
id
)
})
},
[
data
.
id
,
data
.
name
,
fetchToolList
,
onAddTools
,
onFetched
])
useEffect
(()
=>
{
if
(
data
.
fetching
)
handleFetchToolList
()
},
[
data
.
fetching
,
handleFetchToolList
])
return
(
<>
<
div
className=
'flex items-center pl-3 pr-2.5 h-8 cursor-pointer'
key=
{
data
.
id
}
onClick=
{
()
=>
onExpand
(
data
.
id
)
}
>
{
typeof
data
.
icon
===
'string'
?
(
<
div
className=
'shrink-0 mr-2 w-5 h-5 bg-cover bg-center rounded-md'
style=
{
{
backgroundImage
:
`url(${data.icon})`
,
}
}
></
div
>
)
:
(
<
AppIcon
className=
'shrink-0 mr-2 !w-5 !h-5 !text-sm'
size=
'tiny'
icon=
{
data
.
icon
.
content
}
background=
{
data
.
icon
.
background
}
/>
)
}
<
div
className=
'grow mr-2 truncate text-sm text-gray-900'
title=
{
data
.
name
}
>
{
data
.
name
}
</
div
>
{
data
.
expanded
?
<
ChevronDown
className=
'shrink-0 w-3 h-3 text-gray-500'
/>
:
<
ChevronDown
className=
'shrink-0 w-3 h-3 text-gray-500 -rotate-90'
/>
}
</
div
>
{
data
.
expanded
&&
!
data
.
fetching
&&
tools
.
map
(
tool
=>
(
<
div
key=
{
tool
.
name
}
className=
'relative flex items-center pl-10 pr-3 h-8 rounded-lg truncate cursor-pointer text-sm text-gray-900 hover:bg-black/5'
title=
{
tool
.
label
[
language
]
}
onClick=
{
()
=>
onSelect
(
BlockEnum
.
Tool
,
{
provider_id
:
data
.
id
,
provider_type
:
data
.
type
,
tool_name
:
tool
.
name
,
_icon
:
data
.
icon
,
title
:
tool
.
label
[
language
],
})
}
>
<
div
className=
'absolute left-[22px] w-[1px] h-8 bg-black/5'
/>
{
tool
.
label
[
language
]
}
</
div
>
))
}
{
data
.
expanded
&&
data
.
fetching
&&
(
<
div
className=
'felx items-center justify-center h-8'
>
<
Loading
/>
</
div
>
)
}
</>
)
}
export
default
memo
(
Item
)
web/app/components/workflow/block-selector/types.ts
0 → 100644
View file @
e3a3e07e
import
type
{
Collection
,
Tool
,
}
from
'@/app/components/tools/types'
export
enum
TabsEnum
{
Blocks
=
'blocks'
,
BuiltInTool
=
'built-in-tool'
,
CustomTool
=
'custom-tool'
,
}
export
enum
BlockClassificationEnum
{
Default
=
'-'
,
QuestionUnderstand
=
'question-understand'
,
Logic
=
'logic'
,
Transform
=
'transform'
,
Utilities
=
'utilities'
,
}
export
type
CollectionWithExpanded
=
Collection
&
{
expanded
?:
boolean
fetching
?:
boolean
}
export
type
ToolInWorkflow
=
Tool
export
type
ToolsMap
=
Record
<
string
,
ToolInWorkflow
[]
>
export
type
ToolDefaultValue
=
{
provider_id
:
string
provider_type
:
string
tool_name
:
string
title
:
string
_icon
:
Collection
[
'icon'
]
}
web/app/components/workflow/hooks.ts
View file @
e3a3e07e
...
...
@@ -22,6 +22,7 @@ import type {
import
{
NODES_INITIAL_DATA
}
from
'./constants'
import
{
getLayoutByDagre
}
from
'./utils'
import
{
useStore
}
from
'./store'
import
type
{
ToolDefaultValue
}
from
'./block-selector/types'
import
{
syncWorkflowDraft
}
from
'@/service/workflow'
import
{
useFeaturesStore
}
from
'@/app/components/base/features/hooks'
import
{
useStore
as
useAppStore
}
from
'@/app/components/app/store'
...
...
@@ -197,6 +198,12 @@ export const useWorkflow = () => {
setNodes
,
}
=
store
.
getState
()
const
nodes
=
getNodes
()
const
selectedNode
=
nodes
.
find
(
node
=>
node
.
data
.
_selected
)
if
(
!
cancelSelection
&&
selectedNode
?.
id
===
nodeId
)
return
const
newNodes
=
produce
(
getNodes
(),
(
draft
)
=>
{
draft
.
forEach
(
node
=>
node
.
data
.
_selected
=
false
)
const
selectedNode
=
draft
.
find
(
node
=>
node
.
id
===
nodeId
)
!
...
...
@@ -280,7 +287,12 @@ export const useWorkflow = () => {
setNodes
(
newNodes
)
},
[
store
])
const
handleNodeAddNext
=
useCallback
((
currentNodeId
:
string
,
nodeType
:
BlockEnum
,
sourceHandle
:
string
)
=>
{
const
handleNodeAddNext
=
useCallback
((
currentNodeId
:
string
,
nodeType
:
BlockEnum
,
sourceHandle
:
string
,
toolDefaultValue
?:
ToolDefaultValue
,
)
=>
{
const
{
getNodes
,
setNodes
,
...
...
@@ -294,6 +306,7 @@ export const useWorkflow = () => {
type
:
'custom'
,
data
:
{
...
nodesInitialData
[
nodeType
],
...(
toolDefaultValue
||
{}),
_selected
:
true
,
},
position
:
{
...
...
@@ -323,7 +336,12 @@ export const useWorkflow = () => {
handleSyncWorkflowDraft
()
},
[
store
,
nodesInitialData
,
handleSyncWorkflowDraft
])
const
handleNodeChange
=
useCallback
((
currentNodeId
:
string
,
nodeType
:
BlockEnum
,
sourceHandle
?:
string
)
=>
{
const
handleNodeChange
=
useCallback
((
currentNodeId
:
string
,
nodeType
:
BlockEnum
,
sourceHandle
:
string
,
toolDefaultValue
?:
ToolDefaultValue
,
)
=>
{
const
{
getNodes
,
setNodes
,
...
...
@@ -339,6 +357,7 @@ export const useWorkflow = () => {
type
:
'custom'
,
data
:
{
...
nodesInitialData
[
nodeType
],
...(
toolDefaultValue
||
{}),
_selected
:
currentNode
.
data
.
_selected
,
},
position
:
{
...
...
@@ -359,7 +378,7 @@ export const useWorkflow = () => {
id
:
`
${
parentNodeId
}
-
${
newCurrentNode
.
id
}
`
,
type
:
'custom'
,
source
:
parentNodeId
,
sourceHandle
:
sourceHandle
||
'source'
,
sourceHandle
,
target
:
newCurrentNode
.
id
,
targetHandle
:
'target'
,
}
...
...
web/app/components/workflow/index.tsx
View file @
e3a3e07e
import
type
{
FC
}
from
'react'
import
{
memo
,
useEffect
,
useMemo
,
}
from
'react'
import
useSWR
from
'swr'
...
...
@@ -14,6 +15,7 @@ import ReactFlow, {
}
from
'reactflow'
import
type
{
Viewport
}
from
'reactflow'
import
'reactflow/dist/style.css'
import
type
{
ToolsMap
}
from
'./block-selector/types'
import
type
{
Edge
,
Node
,
...
...
@@ -38,6 +40,7 @@ import {
import
{
useStore
as
useAppStore
}
from
'@/app/components/app/store'
import
Loading
from
'@/app/components/base/loading'
import
{
FeaturesProvider
}
from
'@/app/components/base/features'
import
{
fetchCollectionList
}
from
'@/service/tools'
const
nodeTypes
=
{
custom
:
CustomNode
,
...
...
@@ -162,6 +165,22 @@ const WorkflowWrap: FC<WorkflowProps> = ({
return
[]
},
[
data
,
nodes
])
const
handleFetchCollectionList
=
async
()
=>
{
const
toolsets
=
await
fetchCollectionList
()
useStore
.
setState
({
toolsets
,
toolsMap
:
toolsets
.
reduce
((
acc
,
toolset
)
=>
{
acc
[
toolset
.
id
]
=
[]
return
acc
},
{}
as
ToolsMap
),
})
}
useEffect
(()
=>
{
handleFetchCollectionList
()
},
[])
if
(
error
&&
appDetail
)
{
syncWorkflowDraft
({
url
:
`/apps/
${
appDetail
.
id
}
/workflows/draft`
,
...
...
web/app/components/workflow/nodes/_base/components/next-step/add.tsx
View file @
e3a3e07e
...
...
@@ -2,10 +2,10 @@ import {
memo
,
useCallback
,
}
from
'react'
import
BlockSelector
from
'../../../../block-selector'
import
{
useWorkflow
}
from
'../../../../hooks'
import
type
{
BlockEnum
}
from
'../../../../types'
import
{
useWorkflow
}
from
'@/app/components/workflow/hooks'
import
BlockSelector
from
'@/app/components/workflow/block-selector'
import
{
Plus
}
from
'@/app/components/base/icons/src/vender/line/general'
import
type
{
OnSelectBlock
}
from
'@/app/components/workflow/types'
type
AddProps
=
{
nodeId
:
string
...
...
@@ -19,8 +19,8 @@ const Add = ({
}:
AddProps
)
=>
{
const
{
handleNodeAddNext
}
=
useWorkflow
()
const
handleSelect
=
useCallback
((
type
:
BlockEnum
)
=>
{
handleNodeAddNext
(
nodeId
,
type
,
sourceHandle
)
const
handleSelect
=
useCallback
<
OnSelectBlock
>
((
type
,
toolDefaultValue
)
=>
{
handleNodeAddNext
(
nodeId
,
type
,
sourceHandle
,
toolDefaultValue
)
},
[
nodeId
,
sourceHandle
,
handleNodeAddNext
])
const
renderTrigger
=
useCallback
((
open
:
boolean
)
=>
{
...
...
web/app/components/workflow/nodes/_base/components/next-step/index.tsx
View file @
e3a3e07e
...
...
@@ -35,6 +35,7 @@ const NextStep = ({
<
Item
nodeId=
{
outgoers
[
0
].
id
}
data=
{
outgoers
[
0
].
data
}
sourceHandle=
'source'
/>
)
}
...
...
web/app/components/workflow/nodes/_base/components/next-step/item.tsx
View file @
e3a3e07e
...
...
@@ -3,17 +3,17 @@ import {
useCallback
,
}
from
'react'
import
type
{
BlockEnum
,
CommonNodeType
,
}
from
'../../../../types'
import
BlockIcon
from
'../../../../block-icon'
import
BlockSelector
from
'../../../../block-selector'
import
{
useWorkflow
}
from
'../../../../hooks'
OnSelectBlock
,
}
from
'@/app/components/workflow/types'
import
BlockIcon
from
'@/app/components/workflow/block-icon'
import
BlockSelector
from
'@/app/components/workflow/block-selector'
import
{
useWorkflow
}
from
'@/app/components/workflow/hooks'
import
Button
from
'@/app/components/base/button'
type
ItemProps
=
{
nodeId
:
string
sourceHandle
?
:
string
sourceHandle
:
string
branchName
?:
string
data
:
CommonNodeType
}
...
...
@@ -24,8 +24,8 @@ const Item = ({
data
,
}:
ItemProps
)
=>
{
const
{
handleNodeChange
}
=
useWorkflow
()
const
handleSelect
=
useCallback
((
type
:
BlockEnum
)
=>
{
handleNodeChange
(
nodeId
,
type
,
sourceHandle
)
const
handleSelect
=
useCallback
<
OnSelectBlock
>
((
type
,
toolDefaultValue
)
=>
{
handleNodeChange
(
nodeId
,
type
,
sourceHandle
,
toolDefaultValue
)
},
[
nodeId
,
sourceHandle
,
handleNodeChange
])
const
renderTrigger
=
useCallback
((
open
:
boolean
)
=>
{
return
(
...
...
web/app/components/workflow/nodes/_base/components/node-handle.tsx
View file @
e3a3e07e
...
...
@@ -13,6 +13,7 @@ import {
import
{
BlockEnum
}
from
'../../../types'
import
type
{
Node
}
from
'../../../types'
import
BlockSelector
from
'../../../block-selector'
import
type
{
ToolDefaultValue
}
from
'../../../block-selector/types'
import
{
useWorkflow
}
from
'../../../hooks'
type
NodeHandleProps
=
{
...
...
@@ -100,8 +101,8 @@ export const NodeSourceHandle = ({
if
(
!
connected
)
setOpen
(
v
=>
!
v
)
},
[
connected
])
const
handleSelect
=
useCallback
((
type
:
BlockEnum
)
=>
{
handleNodeAddNext
(
id
,
type
,
handleId
)
const
handleSelect
=
useCallback
((
type
:
BlockEnum
,
toolDefaultValue
?:
ToolDefaultValue
)
=>
{
handleNodeAddNext
(
id
,
type
,
handleId
,
toolDefaultValue
)
},
[
handleNodeAddNext
,
id
,
handleId
])
return
(
...
...
web/app/components/workflow/nodes/_base/components/panel-operator/change-block.tsx
View file @
e3a3e07e
...
...
@@ -2,21 +2,23 @@ import {
memo
,
useCallback
,
}
from
'react'
import
BlockSelector
from
'
../../../..
/block-selector'
import
{
useWorkflow
}
from
'
../../../..
/hooks'
import
type
{
BlockEnum
}
from
'../../../..
/types'
import
BlockSelector
from
'
@/app/components/workflow
/block-selector'
import
{
useWorkflow
}
from
'
@/app/components/workflow
/hooks'
import
type
{
OnSelectBlock
}
from
'@/app/components/workflow
/types'
type
ChangeBlockProps
=
{
nodeId
:
string
sourceHandle
:
string
}
const
ChangeBlock
=
({
nodeId
,
sourceHandle
,
}:
ChangeBlockProps
)
=>
{
const
{
handleNodeChange
}
=
useWorkflow
()
const
handleSelect
=
useCallback
((
type
:
BlockEnum
)
=>
{
handleNodeChange
(
nodeId
,
type
)
},
[
handleNodeChange
,
nodeId
])
const
handleSelect
=
useCallback
<
OnSelectBlock
>
((
type
,
toolDefaultValue
)
=>
{
handleNodeChange
(
nodeId
,
type
,
sourceHandle
,
toolDefaultValue
)
},
[
handleNodeChange
,
nodeId
,
sourceHandle
])
const
renderTrigger
=
useCallback
(()
=>
{
return
(
...
...
web/app/components/workflow/nodes/_base/components/panel-operator/index.tsx
View file @
e3a3e07e
...
...
@@ -2,8 +2,9 @@ import {
memo
,
useState
,
}
from
'react'
import
{
use
Workflow
}
from
'../../../../hooks
'
import
{
use
Edges
}
from
'reactflow
'
import
ChangeBlock
from
'./change-block'
import
{
useWorkflow
}
from
'@/app/components/workflow/hooks'
import
{
DotsHorizontal
}
from
'@/app/components/base/icons/src/vender/line/general'
import
{
PortalToFollowElem
,
...
...
@@ -17,9 +18,12 @@ type PanelOperatorProps = {
const
PanelOperator
=
({
nodeId
,
}:
PanelOperatorProps
)
=>
{
const
edges
=
useEdges
()
const
{
handleNodeDelete
}
=
useWorkflow
()
const
[
open
,
setOpen
]
=
useState
(
false
)
const
edge
=
edges
.
find
(
edge
=>
edge
.
target
===
nodeId
)
return
(
<
PortalToFollowElem
placement=
'bottom-end'
...
...
@@ -44,7 +48,10 @@ const PanelOperator = ({
<
PortalToFollowElemContent
className=
'z-[11]'
>
<
div
className=
'w-[240px] border-[0.5px] border-gray-200 rounded-2xl shadow-xl bg-white'
>
<
div
className=
'p-1'
>
<
ChangeBlock
nodeId=
{
nodeId
}
/>
<
ChangeBlock
nodeId=
{
nodeId
}
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'
>
Help Link
</
div
>
</
div
>
<
div
className=
'h-[1px] bg-gray-100'
></
div
>
...
...
web/app/components/workflow/nodes/_base/node.tsx
View file @
e3a3e07e
...
...
@@ -7,7 +7,9 @@ import {
memo
,
}
from
'react'
import
type
{
NodeProps
}
from
'../../types'
import
BlockIcon
from
'../../block-icon'
import
{
BlockEnum
}
from
'@/app/components/workflow/types'
import
BlockIcon
from
'@/app/components/workflow/block-icon'
import
AppIcon
from
'@/app/components/base/app-icon'
type
BaseNodeProps
=
{
children
:
ReactElement
...
...
@@ -18,6 +20,8 @@ const BaseNode: FC<BaseNodeProps> = ({
data
,
children
,
})
=>
{
const
type
=
data
.
type
return
(
<
div
className=
{
`
...
...
@@ -27,14 +31,43 @@ const BaseNode: FC<BaseNodeProps> = ({
`
}
>
<
div
className=
'flex items-center px-3 pt-3 pb-2'
>
{
type
!==
BlockEnum
.
Tool
&&
(
<
BlockIcon
className=
'shrink-0 mr-2'
type=
{
data
.
type
}
size=
'md'
/>
)
}
{
type
===
BlockEnum
.
Tool
&&
(
<>
{
typeof
data
.
_icon
===
'string'
?
(
<
div
className=
'shrink-0 mr-2 w-6 h-6 bg-cover bg-center rounded-md'
style=
{
{
backgroundImage
:
`url(${data._icon})`
,
}
}
></
div
>
)
:
(
<
AppIcon
className=
'shrink-0 mr-2'
size=
'tiny'
icon=
{
data
.
_icon
?.
content
}
background=
{
data
.
_icon
?.
background
}
/>
)
}
</>
)
}
<
div
title=
{
data
.
title
}
className=
'text-[13px] font-semibold text-gray-700 truncate'
className=
'
grow
text-[13px] font-semibold text-gray-700 truncate'
>
{
data
.
title
}
</
div
>
...
...
web/app/components/workflow/nodes/_base/panel.tsx
View file @
e3a3e07e
...
...
@@ -7,11 +7,6 @@ import {
memo
,
useCallback
,
}
from
'react'
import
{
type
Node
}
from
'../../types'
import
{
BlockEnum
}
from
'../../types'
import
BlockIcon
from
'../../block-icon'
import
{
useWorkflow
}
from
'../../hooks'
import
{
canRunBySingle
}
from
'../../utils'
import
NextStep
from
'./components/next-step'
import
PanelOperator
from
'./components/panel-operator'
import
{
...
...
@@ -21,9 +16,15 @@ import {
import
{
XClose
,
}
from
'@/app/components/base/icons/src/vender/line/general'
import
BlockIcon
from
'@/app/components/workflow/block-icon'
import
{
useWorkflow
}
from
'@/app/components/workflow/hooks'
import
{
canRunBySingle
}
from
'@/app/components/workflow/utils'
import
{
GitBranch01
}
from
'@/app/components/base/icons/src/vender/line/development'
import
{
Play
}
from
'@/app/components/base/icons/src/vender/line/mediaAndDevices'
import
TooltipPlus
from
'@/app/components/base/tooltip-plus'
import
type
{
Node
}
from
'@/app/components/workflow/types'
import
{
BlockEnum
}
from
'@/app/components/workflow/types'
import
AppIcon
from
'@/app/components/base/app-icon'
type
BasePanelProps
=
{
children
:
ReactElement
...
...
@@ -34,6 +35,7 @@ const BasePanel: FC<BasePanelProps> = ({
data
,
children
,
})
=>
{
const
type
=
data
.
type
const
{
handleNodeSelect
,
handleNodeDataUpdate
,
...
...
@@ -49,11 +51,40 @@ const BasePanel: FC<BasePanelProps> = ({
<
div
className=
'mr-2 w-[420px] h-full bg-white shadow-lg border-[0.5px] border-gray-200 rounded-2xl overflow-y-auto'
>
<
div
className=
'sticky top-0 bg-white border-b-[0.5px] border-black/5 z-10'
>
<
div
className=
'flex items-center px-4 pt-4 pb-1'
>
{
type
!==
BlockEnum
.
Tool
&&
(
<
BlockIcon
className=
'shrink-0 mr-1'
type=
{
data
.
type
}
size=
'md'
/>
)
}
{
type
===
BlockEnum
.
Tool
&&
(
<>
{
typeof
data
.
_icon
===
'string'
?
(
<
div
className=
'shrink-0 mr-2 w-6 h-6 bg-cover bg-center rounded-md'
style=
{
{
backgroundImage
:
`url(${data._icon})`
,
}
}
></
div
>
)
:
(
<
AppIcon
className=
'shrink-0 mr-2'
size=
'tiny'
icon=
{
data
.
_icon
?.
content
}
background=
{
data
.
_icon
?.
background
}
/>
)
}
</>
)
}
<
TitleInput
value=
{
data
.
title
||
''
}
onChange=
{
handleTitleChange
}
...
...
web/app/components/workflow/store.ts
View file @
e3a3e07e
import
{
create
}
from
'zustand'
import
type
{
HelpLinePosition
}
from
'./help-line/types'
import
type
{
CollectionWithExpanded
,
ToolInWorkflow
,
ToolsMap
,
}
from
'./block-selector/types'
type
State
=
{
mode
:
string
...
...
@@ -8,6 +13,8 @@ type State = {
runStaus
:
string
isDragging
:
boolean
helpLine
?:
HelpLinePosition
toolsets
:
CollectionWithExpanded
[]
toolsMap
:
ToolsMap
}
type
Action
=
{
...
...
@@ -16,6 +23,8 @@ type Action = {
setRunStaus
:
(
runStaus
:
string
)
=>
void
setIsDragging
:
(
isDragging
:
boolean
)
=>
void
setHelpLine
:
(
helpLine
?:
HelpLinePosition
)
=>
void
setToolsets
:
(
toolsets
:
CollectionWithExpanded
[])
=>
void
setToolsMap
:
(
toolsMap
:
Record
<
string
,
ToolInWorkflow
[]
>
)
=>
void
}
export
const
useStore
=
create
<
State
&
Action
>
(
set
=>
({
...
...
@@ -30,4 +39,8 @@ export const useStore = create<State & Action>(set => ({
setIsDragging
:
isDragging
=>
set
(()
=>
({
isDragging
})),
helpLine
:
undefined
,
setHelpLine
:
helpLine
=>
set
(()
=>
({
helpLine
})),
toolsets
:
[],
setToolsets
:
toolsets
=>
set
(()
=>
({
toolsets
})),
toolsMap
:
{},
setToolsMap
:
toolsMap
=>
set
(()
=>
({
toolsMap
})),
}))
web/app/components/workflow/types.ts
View file @
e3a3e07e
...
...
@@ -2,6 +2,8 @@ import type {
Edge
as
ReactFlowEdge
,
Node
as
ReactFlowNode
,
}
from
'reactflow'
import
type
{
Collection
}
from
'@/app/components/tools/types'
import
type
{
ToolDefaultValue
}
from
'@/app/components/workflow/block-selector/types'
export
enum
BlockEnum
{
Start
=
'start'
,
...
...
@@ -28,6 +30,7 @@ export type CommonNodeType<T = {}> = {
_hovering
?:
boolean
_targetBranches
?:
Branch
[]
_isSingleRun
?:
boolean
_icon
?:
Collection
[
'icon'
]
title
:
string
desc
:
string
type
:
BlockEnum
...
...
@@ -134,3 +137,5 @@ export type NodeDefault<T> = {
getAvailablePrevNodes
:
()
=>
BlockEnum
[]
getAvailableNextNodes
:
()
=>
BlockEnum
[]
}
export
type
OnSelectBlock
=
(
type
:
BlockEnum
,
toolDefaultValue
?:
ToolDefaultValue
)
=>
void
web/i18n/en-US/workflow.ts
View file @
e3a3e07e
const
translation
=
{
tabs
:
{
blocks
:
'Blocks'
,
builtInTool
:
'Built-in Tool'
,
customTool
:
'Custom Tool'
,
},
blocks
:
{
'start'
:
'Start'
,
'end'
:
'End'
,
...
...
web/i18n/zh-Hans/workflow.ts
View file @
e3a3e07e
const
translation
=
{
tabs
:
{
blocks
:
'Blocks'
,
builtInTool
:
'内置工具'
,
customTool
:
'自定义工具'
,
},
blocks
:
{
'start'
:
'开始'
,
'end'
:
'结束'
,
...
...
web/yarn.lock
View file @
e3a3e07e
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment