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
1840d05a
Commit
1840d05a
authored
Feb 29, 2024
by
StyleZhang
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
record panel
parent
6d561844
Changes
11
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
158 additions
and
131 deletions
+158
-131
index.tsx
web/app/components/workflow/block-selector/index.tsx
+2
-2
index.tsx
web/app/components/workflow/header/index.tsx
+16
-7
run-and-history.tsx
web/app/components/workflow/header/run-and-history.tsx
+26
-6
hooks.ts
web/app/components/workflow/hooks.ts
+10
-69
node-handle.tsx
...omponents/workflow/nodes/_base/components/node-handle.tsx
+9
-6
index.tsx
web/app/components/workflow/panel/index.tsx
+7
-0
record.tsx
web/app/components/workflow/panel/record.tsx
+11
-0
run-history.tsx
web/app/components/workflow/panel/run-history.tsx
+5
-1
store.ts
web/app/components/workflow/store.ts
+4
-0
types.ts
web/app/components/workflow/types.ts
+2
-3
utils.ts
web/app/components/workflow/utils.ts
+66
-37
No files found.
web/app/components/workflow/block-selector/index.tsx
View file @
1840d05a
...
...
@@ -57,8 +57,8 @@ const NodeSelector: FC<NodeSelectorProps> = ({
},
[
onOpenChange
])
const
handleTrigger
=
useCallback
<
MouseEventHandler
<
HTMLDivElement
>>
((
e
)
=>
{
e
.
stopPropagation
()
handleOpenChange
(
!
open
)
},
[
open
,
handleOpenChange
])
setLocalOpen
(
v
=>
!
v
)
},
[])
const
handleSelect
=
useCallback
((
type
:
BlockEnum
)
=>
{
handleOpenChange
(
false
)
onSelect
(
type
)
...
...
web/app/components/workflow/header/index.tsx
View file @
1840d05a
...
...
@@ -14,6 +14,8 @@ import { ArrowNarrowLeft } from '@/app/components/base/icons/src/vender/line/arr
const
Header
:
FC
=
()
=>
{
const
mode
=
useStore
(
state
=>
state
.
mode
)
const
setShowFeatures
=
useStore
(
state
=>
state
.
setShowFeatures
)
const
runStaus
=
useStore
(
state
=>
state
.
runStaus
)
const
setRunStaus
=
useStore
(
state
=>
state
.
setRunStaus
)
const
handleShowFeatures
=
useCallback
(()
=>
{
setShowFeatures
(
true
)
...
...
@@ -36,13 +38,20 @@ const Header: FC = () => {
</
div
>
</
div
>
<
div
className=
'flex items-center'
>
<
Button
className=
{
`
mr-2 px-3 py-0 h-8 bg-white text-[13px] font-medium text-primary-600
border-[0.5px] border-gray-200 shadow-xs
`
}
>
<
ArrowNarrowLeft
className=
'mr-1 w-4 h-4'
/>
Go back to editor
</
Button
>
{
runStaus
&&
(
<
Button
className=
{
`
mr-2 px-3 py-0 h-8 bg-white text-[13px] font-medium text-primary-600
border-[0.5px] border-gray-200 shadow-xs
`
}
onClick=
{
()
=>
setRunStaus
(
''
)
}
>
<
ArrowNarrowLeft
className=
'mr-1 w-4 h-4'
/>
Go back to editor
</
Button
>
)
}
<
RunAndHistory
/>
<
div
className=
'mx-2 w-[1px] h-3.5 bg-gray-200'
></
div
>
{
...
...
web/app/components/workflow/header/run-and-history.tsx
View file @
1840d05a
...
...
@@ -4,19 +4,39 @@ import { useStore } from '../store'
import
{
Play
}
from
'@/app/components/base/icons/src/vender/line/mediaAndDevices'
import
{
ClockPlay
}
from
'@/app/components/base/icons/src/vender/line/time'
import
TooltipPlus
from
'@/app/components/base/tooltip-plus'
import
{
Loading02
}
from
'@/app/components/base/icons/src/vender/line/general'
const
RunAndHistory
:
FC
=
()
=>
{
const
showRunHistory
=
useStore
(
state
=>
state
.
showRunHistory
)
const
setShowRunHistory
=
useStore
(
state
=>
state
.
setShowRunHistory
)
const
runStaus
=
useStore
(
state
=>
state
.
runStaus
)
const
setRunStaus
=
useStore
(
state
=>
state
.
setRunStaus
)
return
(
<
div
className=
'flex items-center px-0.5 h-8 rounded-lg border-[0.5px] border-gray-200 bg-white shadow-xs'
>
<
div
className=
{
`
flex items-center px-1.5 h-7 rounded-md text-[13px] font-medium text-primary-600
hover:bg-primary-50 cursor-pointer
`
}
>
<
Play
className=
'mr-1 w-4 h-4'
/>
Run
<
div
className=
{
`
flex items-center px-1.5 h-7 rounded-md text-[13px] font-medium text-primary-600
hover:bg-primary-50 cursor-pointer
${runStaus === 'running' && 'bg-primary-50 !cursor-not-allowed'}
`
}
onClick=
{
()
=>
runStaus
!==
'running'
&&
setRunStaus
(
'running'
)
}
>
{
runStaus
===
'running'
?
(
<>
<
Loading02
className=
'mr-1 w-4 h-4 animate-spin'
/>
Running
</>
)
:
(
<>
<
Play
className=
'mr-1 w-4 h-4'
/>
Run
</>
)
}
</
div
>
<
div
className=
'mx-0.5 w-[0.5px] h-8 bg-gray-200'
></
div
>
<
TooltipPlus
...
...
web/app/components/workflow/hooks.ts
View file @
1840d05a
...
...
@@ -16,7 +16,6 @@ import type {
SelectedNode
,
}
from
'./types'
import
{
NodeInitialData
}
from
'./constants'
import
{
initialNodesPosition
}
from
'./utils'
export
const
useWorkflow
=
()
=>
{
const
store
=
useStoreApi
()
...
...
@@ -78,14 +77,11 @@ export const useWorkflow = () => {
}
=
store
.
getState
()
const
newNodes
=
produce
(
getNodes
(),
(
draft
)
=>
{
const
selectedNode
=
draft
.
find
(
node
=>
node
.
id
===
nodeId
)
draft
.
forEach
(
node
=>
node
.
selected
=
false
)
const
selectedNode
=
draft
.
find
(
node
=>
node
.
id
===
nodeId
)
!
if
(
selectedNode
)
{
if
(
cancelSelection
)
selectedNode
.
selected
=
false
else
selectedNode
.
selected
=
true
}
if
(
!
cancelSelection
)
selectedNode
.
selected
=
true
})
setNodes
(
newNodes
)
},
[
store
])
...
...
@@ -124,9 +120,9 @@ export const useWorkflow = () => {
setEdges
,
}
=
store
.
getState
()
const
newEdges
=
produce
(
edges
,
(
draft
)
=>
{
const
currentEdge
=
draft
.
find
(
e
=>
e
.
id
===
edge
.
id
)
if
(
currentEdge
)
currentEdge
.
data
=
{
...
currentEdge
.
data
,
hovering
:
true
}
const
currentEdge
=
draft
.
find
(
e
=>
e
.
id
===
edge
.
id
)
!
currentEdge
.
data
=
{
...
currentEdge
.
data
,
hovering
:
true
}
})
setEdges
(
newEdges
)
},
[
store
])
...
...
@@ -137,9 +133,9 @@ export const useWorkflow = () => {
setEdges
,
}
=
store
.
getState
()
const
newEdges
=
produce
(
edges
,
(
draft
)
=>
{
const
currentEdge
=
draft
.
find
(
e
=>
e
.
id
===
edge
.
id
)
if
(
currentEdge
)
currentEdge
.
data
=
{
...
currentEdge
.
data
,
hovering
:
false
}
const
currentEdge
=
draft
.
find
(
e
=>
e
.
id
===
edge
.
id
)
!
currentEdge
.
data
=
{
...
currentEdge
.
data
,
hovering
:
false
}
})
setEdges
(
newEdges
)
},
[
store
])
...
...
@@ -281,59 +277,6 @@ export const useWorkflow = () => {
setEdges
(
newEdges
)
},
[
store
])
const
handleInitialLayoutNodes
=
useCallback
(()
=>
{
const
{
getNodes
,
setNodes
,
edges
,
setEdges
,
}
=
store
.
getState
()
setNodes
(
initialNodesPosition
(
getNodes
(),
edges
))
setEdges
(
produce
(
edges
,
(
draft
)
=>
{
draft
.
forEach
((
edge
)
=>
{
edge
.
hidden
=
false
})
}))
},
[
store
])
const
handleUpdateNodesPosition
=
useCallback
(()
=>
{
const
{
getNodes
,
setNodes
,
}
=
store
.
getState
()
const
nodes
=
getNodes
()
const
groups
=
nodes
.
reduce
((
acc
,
cur
)
=>
{
const
x
=
cur
.
data
.
position
.
x
if
(
!
acc
[
x
])
acc
[
x
]
=
[
cur
]
else
acc
[
x
].
push
(
cur
)
return
acc
},
{}
as
Record
<
string
,
Node
[]
>
)
const
heightMap
:
Record
<
string
,
number
>
=
{}
Object
.
keys
(
groups
).
forEach
((
key
)
=>
{
let
baseHeight
=
0
groups
[
key
].
sort
((
a
,
b
)
=>
a
.
data
.
position
!
.
y
-
b
.
data
.
position
!
.
y
).
forEach
((
node
)
=>
{
heightMap
[
node
.
id
]
=
baseHeight
baseHeight
=
node
.
height
!
+
39
})
})
setNodes
(
produce
(
nodes
,
(
draft
)
=>
{
draft
.
forEach
((
node
)
=>
{
node
.
position
=
{
...
node
.
position
,
x
:
node
.
data
.
position
.
x
*
(
220
+
64
),
y
:
heightMap
[
node
.
id
],
}
})
}))
},
[
store
])
return
{
handleEnterNode
,
handleLeaveNode
,
...
...
@@ -346,7 +289,5 @@ export const useWorkflow = () => {
handleAddNextNode
,
handleChangeCurrentNode
,
handleDeleteNode
,
handleInitialLayoutNodes
,
handleUpdateNodesPosition
,
}
}
web/app/components/workflow/nodes/_base/components/node-handle.tsx
View file @
1840d05a
import
type
{
MouseEvent
}
from
'react'
import
{
useCallback
,
useState
,
...
...
@@ -35,10 +36,11 @@ export const NodeTargetHandle = ({
const
handleOpenChange
=
useCallback
((
v
:
boolean
)
=>
{
setOpen
(
v
)
},
[])
const
handleHandleClick
=
()
=>
{
const
handleHandleClick
=
useCallback
((
e
:
MouseEvent
)
=>
{
e
.
stopPropagation
()
if
(
!
connected
)
handleOpenChange
(
!
open
)
}
setOpen
(
v
=>
!
v
)
}
,
[
connected
])
return
(
<>
...
...
@@ -93,10 +95,11 @@ export const NodeSourceHandle = ({
const
handleOpenChange
=
useCallback
((
v
:
boolean
)
=>
{
setOpen
(
v
)
},
[])
const
handleHandleClick
=
()
=>
{
const
handleHandleClick
=
useCallback
((
e
:
MouseEvent
)
=>
{
e
.
stopPropagation
()
if
(
!
connected
)
handleOpenChange
(
!
open
)
}
setOpen
(
v
=>
!
v
)
}
,
[
connected
])
const
handleSelect
=
useCallback
((
type
:
BlockEnum
)
=>
{
handleAddNextNode
(
id
,
type
,
handleId
)
},
[
handleAddNextNode
,
id
,
handleId
])
...
...
web/app/components/workflow/panel/index.tsx
View file @
1840d05a
...
...
@@ -10,9 +10,11 @@ import { useStore } from '../store'
import
WorkflowInfo
from
'./workflow-info'
import
DebugAndPreview
from
'./debug-and-preview'
import
RunHistory
from
'./run-history'
import
Record
from
'./record'
const
Panel
:
FC
=
()
=>
{
const
mode
=
useStore
(
state
=>
state
.
mode
)
const
runStaus
=
useStore
(
state
=>
state
.
runStaus
)
const
nodes
=
useNodes
<
CommonNodeType
>
()
const
selectedNode
=
nodes
.
find
(
node
=>
node
.
selected
)
const
showRunHistory
=
useStore
(
state
=>
state
.
showRunHistory
)
...
...
@@ -30,6 +32,11 @@ const Panel: FC = () => {
return
(
<
div
className=
'absolute top-14 right-0 bottom-2 flex z-10'
>
{
runStaus
&&
(
<
Record
/>
)
}
{
showNodePanel
&&
(
<
NodePanel
{
...
selectedNode
!}
/>
...
...
web/app/components/workflow/panel/record.tsx
0 → 100644
View file @
1840d05a
const
Record
=
()
=>
{
return
(
<
div
className=
'w-[400px] h-full rounded-2xl border-[0.5px] border-gray-200 shadow-xl bg-white'
>
<
div
className=
'p-4 pb-1 text-base font-semibold text-gray-900'
>
Test Run#5
</
div
>
</
div
>
)
}
export
default
Record
web/app/components/workflow/panel/run-history.tsx
View file @
1840d05a
...
...
@@ -8,6 +8,7 @@ import { AlertCircle } from '@/app/components/base/icons/src/vender/line/alertsA
const
RunHistory
=
()
=>
{
const
mode
=
useStore
(
state
=>
state
.
mode
)
const
setShowRunHistory
=
useStore
(
state
=>
state
.
setShowRunHistory
)
const
setRunStaus
=
useStore
(
state
=>
state
.
setRunStaus
)
return
(
<
div
className=
'w-[200px] h-full bg-white border-[0.5px] border-gray-200 shadow-xl rounded-l-2xl'
>
...
...
@@ -34,7 +35,10 @@ const RunHistory = () => {
</
div
>
)
}
<
div
className=
'flex px-2 py-[7px] rounded-lg hover:bg-primary-50 cursor-pointer'
>
<
div
className=
'flex px-2 py-[7px] rounded-lg hover:bg-primary-50 cursor-pointer'
onClick=
{
()
=>
setRunStaus
(
'finished'
)
}
>
<
AlertCircle
className=
'mt-0.5 mr-1.5 w-3.5 h-3.5 text-[#F79009]'
/>
<
div
>
<
div
className=
'flex items-center text-[13px] font-medium text-primary-600 leading-[18px]'
>
Test Run#6
</
div
>
...
...
web/app/components/workflow/store.ts
View file @
1840d05a
...
...
@@ -4,11 +4,13 @@ type State = {
mode
:
string
showRunHistory
:
boolean
showFeatures
:
boolean
runStaus
:
string
}
type
Action
=
{
setShowRunHistory
:
(
showRunHistory
:
boolean
)
=>
void
setShowFeatures
:
(
showFeatures
:
boolean
)
=>
void
setRunStaus
:
(
runStaus
:
string
)
=>
void
}
export
const
useStore
=
create
<
State
&
Action
>
(
set
=>
({
...
...
@@ -17,4 +19,6 @@ export const useStore = create<State & Action>(set => ({
setShowRunHistory
:
showRunHistory
=>
set
(()
=>
({
showRunHistory
})),
showFeatures
:
false
,
setShowFeatures
:
showFeatures
=>
set
(()
=>
({
showFeatures
})),
runStaus
:
'finished'
,
setRunStaus
:
runStaus
=>
set
(()
=>
({
runStaus
})),
}))
web/app/components/workflow/types.ts
View file @
1840d05a
...
...
@@ -24,13 +24,12 @@ export type Branch = {
}
export
type
CommonNodeType
=
{
position
?:
{
index
?:
{
x
:
number
y
:
number
}
sortIndexInBranches
?:
number
hovering
?:
boolean
b
ranches
?:
Branch
[]
targetB
ranches
?:
Branch
[]
title
:
string
desc
:
string
type
:
BlockEnum
...
...
web/app/components/workflow/utils.ts
View file @
1840d05a
import
produce
from
'immer'
import
{
getConnectedEdges
,
getOutgoers
,
}
from
'reactflow'
import
{
cloneDeep
}
from
'lodash-es'
import
type
{
Edge
,
Node
,
}
from
'./types'
import
{
BlockEnum
}
from
'./types'
export
const
initialNodesPosition
=
(
oldNodes
:
Node
[],
edges
:
Edge
[])
=>
{
const
nodes
=
cloneDeep
(
oldNodes
)
const
start
=
nodes
.
find
(
node
=>
node
.
data
.
type
===
BlockEnum
.
Start
)
!
export
const
nodesLevelOrderTraverse
=
(
firstNode
:
Node
,
nodes
:
Node
[],
edges
:
Edge
[],
callback
:
(
n
:
Node
)
=>
void
,
)
=>
{
const
queue
=
[{
node
:
firstNode
,
}]
start
.
position
.
x
=
0
start
.
position
.
y
=
0
start
.
data
.
position
=
{
x
:
0
,
y
:
0
,
}
const
queue
=
[
start
]
let
depth
=
0
let
breadth
=
0
let
baseHeight
=
0
while
(
queue
.
length
)
{
const
node
=
queue
.
shift
()
!
const
{
node
}
=
queue
.
shift
()
!
callback
(
node
)
const
targetBranches
=
node
.
data
.
targetBranches
if
(
targetBranches
?.
length
)
{
const
targetEdges
=
getConnectedEdges
([
node
],
edges
)
if
(
node
.
data
.
position
?.
x
!==
depth
)
{
breadth
=
0
baseHeight
=
0
if
(
targetEdges
.
length
)
{
const
sortedTargetEdges
=
targetEdges
.
filter
(
edge
=>
edge
.
source
===
node
.
id
)
.
sort
((
a
,
b
)
=>
{
const
aIndex
=
targetBranches
.
findIndex
(
branch
=>
branch
.
id
===
a
.
sourceHandle
)
const
bIndex
=
targetBranches
.
findIndex
(
branch
=>
branch
.
id
===
b
.
sourceHandle
)
return
aIndex
-
bIndex
})
const
outgoers
=
getOutgoers
(
node
,
nodes
,
sortedTargetEdges
)
queue
.
push
(...
outgoers
.
map
((
outgoer
)
=>
{
return
{
node
:
outgoer
,
}
}))
}
}
else
{
const
outgoers
=
getOutgoers
(
node
,
nodes
,
edges
)
depth
=
node
.
data
.
position
?.
x
||
0
const
outgoers
=
getOutgoers
(
node
,
nodes
,
edges
).
sort
((
a
,
b
)
=>
(
a
.
data
.
sortIndexInBranches
||
0
)
-
(
b
.
data
.
sortIndexInBranches
||
0
))
if
(
outgoers
.
length
)
{
queue
.
push
(...
outgoers
.
map
((
outgoer
)
=>
{
outgoer
.
data
.
position
=
{
x
:
depth
+
1
,
y
:
breadth
,
}
outgoer
.
position
.
x
=
(
depth
+
1
)
*
(
220
+
64
)
outgoer
.
position
.
y
=
baseHeight
baseHeight
+=
((
outgoer
.
height
||
0
)
+
39
)
breadth
+=
1
return
outgoer
}))
if
(
outgoers
.
length
===
1
)
{
queue
.
push
({
node
:
outgoers
[
0
],
})
}
}
}
}
return
nodes
export
const
initialNodesAndEdges
=
(
nodes
:
Node
[],
edges
:
Edge
[])
=>
{
const
newNodes
=
produce
(
nodes
,
(
draft
)
=>
{
draft
.
forEach
((
node
)
=>
{
node
.
type
=
'custom'
})
})
const
newEdges
=
produce
(
edges
,
(
draft
)
=>
{
draft
.
forEach
((
edge
)
=>
{
edge
.
type
=
'custom'
})
})
return
[
newNodes
,
newEdges
]
}
export
type
PositionMap
=
{
position
:
{
x
:
number
y
:
number
}
index
:
{
x
:
number
y
:
number
}
}
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