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
081baae8
Commit
081baae8
authored
Mar 04, 2024
by
StyleZhang
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
operator
parent
a3d4befa
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
281 additions
and
46 deletions
+281
-46
page.tsx
...onLayout)/app/(appDetailLayout)/[appId]/workflow/page.tsx
+9
-1
organize-grid.svg
...ts/base/icons/assets/vender/line/layout/organize-grid.svg
+10
-0
OrganizeGrid.json
...nents/base/icons/src/vender/line/layout/OrganizeGrid.json
+81
-0
OrganizeGrid.tsx
...onents/base/icons/src/vender/line/layout/OrganizeGrid.tsx
+16
-0
index.ts
...app/components/base/icons/src/vender/line/layout/index.ts
+1
-0
features.tsx
web/app/components/workflow/features.tsx
+23
-26
index.tsx
web/app/components/workflow/index.tsx
+10
-19
index.tsx
web/app/components/workflow/operator/index.tsx
+22
-0
zoom-in-out.tsx
web/app/components/workflow/operator/zoom-in-out.tsx
+109
-0
No files found.
web/app/(commonLayout)/app/(appDetailLayout)/[appId]/workflow/page.tsx
View file @
081baae8
...
@@ -4,9 +4,17 @@ import { memo } from 'react'
...
@@ -4,9 +4,17 @@ import { memo } from 'react'
import
Workflow
from
'@/app/components/workflow'
import
Workflow
from
'@/app/components/workflow'
const
Page
=
()
=>
{
const
Page
=
()
=>
{
const
nodes
=
[
{
id
:
'1'
,
type
:
'custom'
,
position
:
{
x
:
180
,
y
:
180
},
data
:
{
type
:
'start'
},
},
]
return
(
return
(
<
Workflow
<
Workflow
nodes=
{
[]
}
nodes=
{
nodes
}
edges=
{
[]
}
edges=
{
[]
}
/>
/>
)
)
...
...
web/app/components/base/icons/assets/vender/line/layout/organize-grid.svg
0 → 100644
View file @
081baae8
<svg
width=
"16"
height=
"16"
viewBox=
"0 0 16 16"
fill=
"none"
xmlns=
"http://www.w3.org/2000/svg"
>
<g
id=
"Icon"
>
<g
id=
"Vector"
>
<path
d=
"M9.33366 10.667C9.33366 9.93061 9.93061 9.33366 10.667 9.33366H12.0003C12.7367 9.33366 13.3337 9.93061 13.3337 10.667V12.0003C13.3337 12.7367 12.7367 13.3337 12.0003 13.3337H10.667C9.93061 13.3337 9.33366 12.7367 9.33366 12.0003V10.667Z"
stroke=
"#667085"
stroke-width=
"1.5"
stroke-linecap=
"round"
stroke-linejoin=
"round"
/>
<path
d=
"M2.66699 10.667C2.66699 9.93059 3.26395 9.33366 4.00033 9.33366H5.33366C6.07004 9.33366 6.66699 9.93059 6.66699 10.667V12.0003C6.66699 12.7367 6.07004 13.3337 5.33366 13.3337H4.00033C3.26395 13.3337 2.66699 12.7367 2.66699 12.0003V10.667Z"
stroke=
"#667085"
stroke-width=
"1.5"
stroke-linecap=
"round"
stroke-linejoin=
"round"
/>
<path
d=
"M2.66699 4.00033C2.66699 3.26395 3.26393 2.66699 4.00033 2.66699H5.33366C6.07006 2.66699 6.66699 3.26395 6.66699 4.00033V5.33366C6.66699 6.07004 6.07006 6.66699 5.33366 6.66699H4.00033C3.26393 6.66699 2.66699 6.07004 2.66699 5.33366V4.00033Z"
stroke=
"#667085"
stroke-width=
"1.5"
stroke-linecap=
"round"
stroke-linejoin=
"round"
/>
</g>
<path
id=
"Vector_2"
d=
"M11.6409 2.1899C11.5143 1.93674 11.153 1.93674 11.0265 2.1899L10.3544 3.53389C10.3213 3.60035 10.2673 3.65425 10.2008 3.68748L8.85684 4.35948C8.60371 4.48606 8.60371 4.84732 8.85684 4.97389L10.2008 5.64589C10.2673 5.67913 10.3213 5.73303 10.3544 5.7995L11.0265 7.14348C11.153 7.39664 11.5143 7.39664 11.6409 7.14348L12.3129 5.7995C12.3461 5.73303 12.4 5.67913 12.4665 5.64589L13.8105 4.97389C14.0636 4.84732 14.0636 4.48606 13.8105 4.35948L12.4665 3.68748C12.4 3.65425 12.3461 3.60035 12.3129 3.53389L11.6409 2.1899Z"
fill=
"#667085"
/>
</g>
</svg>
web/app/components/base/icons/src/vender/line/layout/OrganizeGrid.json
0 → 100644
View file @
081baae8
{
"icon"
:
{
"type"
:
"element"
,
"isRootNode"
:
true
,
"name"
:
"svg"
,
"attributes"
:
{
"width"
:
"16"
,
"height"
:
"16"
,
"viewBox"
:
"0 0 16 16"
,
"fill"
:
"none"
,
"xmlns"
:
"http://www.w3.org/2000/svg"
},
"children"
:
[
{
"type"
:
"element"
,
"name"
:
"g"
,
"attributes"
:
{
"id"
:
"Icon"
},
"children"
:
[
{
"type"
:
"element"
,
"name"
:
"g"
,
"attributes"
:
{
"id"
:
"Vector"
},
"children"
:
[
{
"type"
:
"element"
,
"name"
:
"path"
,
"attributes"
:
{
"d"
:
"M9.33366 10.667C9.33366 9.93061 9.93061 9.33366 10.667 9.33366H12.0003C12.7367 9.33366 13.3337 9.93061 13.3337 10.667V12.0003C13.3337 12.7367 12.7367 13.3337 12.0003 13.3337H10.667C9.93061 13.3337 9.33366 12.7367 9.33366 12.0003V10.667Z"
,
"stroke"
:
"currentColor"
,
"stroke-width"
:
"1.5"
,
"stroke-linecap"
:
"round"
,
"stroke-linejoin"
:
"round"
},
"children"
:
[]
},
{
"type"
:
"element"
,
"name"
:
"path"
,
"attributes"
:
{
"d"
:
"M2.66699 10.667C2.66699 9.93059 3.26395 9.33366 4.00033 9.33366H5.33366C6.07004 9.33366 6.66699 9.93059 6.66699 10.667V12.0003C6.66699 12.7367 6.07004 13.3337 5.33366 13.3337H4.00033C3.26395 13.3337 2.66699 12.7367 2.66699 12.0003V10.667Z"
,
"stroke"
:
"currentColor"
,
"stroke-width"
:
"1.5"
,
"stroke-linecap"
:
"round"
,
"stroke-linejoin"
:
"round"
},
"children"
:
[]
},
{
"type"
:
"element"
,
"name"
:
"path"
,
"attributes"
:
{
"d"
:
"M2.66699 4.00033C2.66699 3.26395 3.26393 2.66699 4.00033 2.66699H5.33366C6.07006 2.66699 6.66699 3.26395 6.66699 4.00033V5.33366C6.66699 6.07004 6.07006 6.66699 5.33366 6.66699H4.00033C3.26393 6.66699 2.66699 6.07004 2.66699 5.33366V4.00033Z"
,
"stroke"
:
"currentColor"
,
"stroke-width"
:
"1.5"
,
"stroke-linecap"
:
"round"
,
"stroke-linejoin"
:
"round"
},
"children"
:
[]
}
]
},
{
"type"
:
"element"
,
"name"
:
"path"
,
"attributes"
:
{
"id"
:
"Vector_2"
,
"d"
:
"M11.6409 2.1899C11.5143 1.93674 11.153 1.93674 11.0265 2.1899L10.3544 3.53389C10.3213 3.60035 10.2673 3.65425 10.2008 3.68748L8.85684 4.35948C8.60371 4.48606 8.60371 4.84732 8.85684 4.97389L10.2008 5.64589C10.2673 5.67913 10.3213 5.73303 10.3544 5.7995L11.0265 7.14348C11.153 7.39664 11.5143 7.39664 11.6409 7.14348L12.3129 5.7995C12.3461 5.73303 12.4 5.67913 12.4665 5.64589L13.8105 4.97389C14.0636 4.84732 14.0636 4.48606 13.8105 4.35948L12.4665 3.68748C12.4 3.65425 12.3461 3.60035 12.3129 3.53389L11.6409 2.1899Z"
,
"fill"
:
"currentColor"
},
"children"
:
[]
}
]
}
]
},
"name"
:
"OrganizeGrid"
}
\ No newline at end of file
web/app/components/base/icons/src/vender/line/layout/OrganizeGrid.tsx
0 → 100644
View file @
081baae8
// GENERATE BY script
// DON NOT EDIT IT MANUALLY
import
*
as
React
from
'react'
import
data
from
'./OrganizeGrid.json'
import
IconBase
from
'@/app/components/base/icons/IconBase'
import
type
{
IconBaseProps
,
IconData
}
from
'@/app/components/base/icons/IconBase'
const
Icon
=
React
.
forwardRef
<
React
.
MutableRefObject
<
SVGElement
>
,
Omit
<
IconBaseProps
,
'data'
>>
((
props
,
ref
,
)
=>
<
IconBase
{
...
props
}
ref=
{
ref
}
data=
{
data
as
IconData
}
/>)
Icon
.
displayName
=
'OrganizeGrid'
export
default
Icon
web/app/components/base/icons/src/vender/line/layout/index.ts
View file @
081baae8
...
@@ -2,3 +2,4 @@ export { default as AlignLeft01 } from './AlignLeft01'
...
@@ -2,3 +2,4 @@ export { default as AlignLeft01 } from './AlignLeft01'
export
{
default
as
AlignRight01
}
from
'./AlignRight01'
export
{
default
as
AlignRight01
}
from
'./AlignRight01'
export
{
default
as
Grid01
}
from
'./Grid01'
export
{
default
as
Grid01
}
from
'./Grid01'
export
{
default
as
LayoutGrid02
}
from
'./LayoutGrid02'
export
{
default
as
LayoutGrid02
}
from
'./LayoutGrid02'
export
{
default
as
OrganizeGrid
}
from
'./OrganizeGrid'
web/app/components/workflow/features.tsx
View file @
081baae8
...
@@ -4,41 +4,38 @@ import { XClose } from '@/app/components/base/icons/src/vender/line/general'
...
@@ -4,41 +4,38 @@ import { XClose } from '@/app/components/base/icons/src/vender/line/general'
import
{
import
{
FeaturesChoose
,
FeaturesChoose
,
FeaturesPanel
,
FeaturesPanel
,
FeaturesProvider
,
}
from
'@/app/components/base/features'
}
from
'@/app/components/base/features'
const
Features
=
()
=>
{
const
Features
=
()
=>
{
const
setShowFeaturesPanel
=
useStore
(
state
=>
state
.
setShowFeaturesPanel
)
const
setShowFeaturesPanel
=
useStore
(
state
=>
state
.
setShowFeaturesPanel
)
return
(
return
(
<
FeaturesProvider
>
<
div
className=
'absolute top-2 left-2 bottom-2 w-[600px] rounded-2xl border-[0.5px] border-gray-200 bg-white shadow-xl z-10'
>
<
div
className=
'absolute top-2 left-2 bottom-2 w-[600px] rounded-2xl border-[0.5px] border-gray-200 bg-white shadow-xl z-10'
>
<
div
className=
'flex items-center justify-between px-4 pt-3'
>
<
div
className=
'flex items-center justify-between px-4 pt-3'
>
Features
Features
<
div
className=
'flex items-center'
>
<
div
className=
'flex items-center'
>
<
FeaturesChoose
/>
<
FeaturesChoose
/>
<
div
className=
'mx-3 w-[1px] h-[14px] bg-gray-200'
></
div
>
<
div
className=
'mx-3 w-[1px] h-[14px] bg-gray-200'
></
div
>
<
div
<
div
className=
'flex items-center justify-center w-6 h-6 cursor-pointer'
className=
'flex items-center justify-center w-6 h-6 cursor-pointer'
onClick=
{
()
=>
setShowFeaturesPanel
(
false
)
}
onClick=
{
()
=>
setShowFeaturesPanel
(
false
)
}
>
>
<
XClose
className=
'w-4 h-4 text-gray-500'
/>
<
XClose
className=
'w-4 h-4 text-gray-500'
/>
</
div
>
</
div
>
</
div
>
</
div
>
</
div
>
<
div
className=
'p-4'
>
<
FeaturesPanel
openingStatementProps=
{
{
onAutoAddPromptVariable
:
()
=>
{},
}
}
annotationProps=
{
{
onEmbeddingChange
:
()
=>
{},
onScoreChange
:
()
=>
{},
}
}
/>
</
div
>
</
div
>
</
div
>
</
FeaturesProvider
>
<
div
className=
'p-4'
>
<
FeaturesPanel
openingStatementProps=
{
{
onAutoAddPromptVariable
:
()
=>
{},
}
}
annotationProps=
{
{
onEmbeddingChange
:
()
=>
{},
onScoreChange
:
()
=>
{},
}
}
/>
</
div
>
</
div
>
)
)
}
}
...
...
web/app/components/workflow/index.tsx
View file @
081baae8
import
type
{
FC
}
from
'react'
import
type
{
FC
}
from
'react'
import
{
import
{
memo
,
memo
,
// useEffect,
}
from
'react'
}
from
'react'
import
{
useParams
}
from
'next/navigation'
import
{
useParams
}
from
'next/navigation'
import
useSWR
from
'swr'
import
useSWR
from
'swr'
...
@@ -10,11 +9,9 @@ import ReactFlow, {
...
@@ -10,11 +9,9 @@ import ReactFlow, {
Background
,
Background
,
ReactFlowProvider
,
ReactFlowProvider
,
useEdgesState
,
useEdgesState
,
// useNodesInitialized,
useNodesState
,
useNodesState
,
}
from
'reactflow'
}
from
'reactflow'
import
'reactflow/dist/style.css'
import
'reactflow/dist/style.css'
// import './style.css'
import
type
{
import
type
{
Edge
,
Edge
,
Node
,
Node
,
...
@@ -22,7 +19,7 @@ import type {
...
@@ -22,7 +19,7 @@ import type {
import
{
useWorkflow
}
from
'./hooks'
import
{
useWorkflow
}
from
'./hooks'
import
Header
from
'./header'
import
Header
from
'./header'
import
CustomNode
from
'./nodes'
import
CustomNode
from
'./nodes'
import
ZoomInOut
from
'./zoom-in-out
'
import
Operator
from
'./operator
'
import
CustomEdge
from
'./custom-edge'
import
CustomEdge
from
'./custom-edge'
import
CustomConnectionLine
from
'./custom-connection-line'
import
CustomConnectionLine
from
'./custom-connection-line'
import
Panel
from
'./panel'
import
Panel
from
'./panel'
...
@@ -34,6 +31,7 @@ import {
...
@@ -34,6 +31,7 @@ import {
syncWorkflowDraft
,
syncWorkflowDraft
,
}
from
'@/service/workflow'
}
from
'@/service/workflow'
import
Loading
from
'@/app/components/base/loading'
import
Loading
from
'@/app/components/base/loading'
import
{
FeaturesProvider
}
from
'@/app/components/base/features'
const
nodeTypes
=
{
const
nodeTypes
=
{
custom
:
CustomNode
,
custom
:
CustomNode
,
...
@@ -53,11 +51,8 @@ const Workflow: FC<WorkflowProps> = memo(({
...
@@ -53,11 +51,8 @@ const Workflow: FC<WorkflowProps> = memo(({
const
showFeaturesPanel
=
useStore
(
state
=>
state
.
showFeaturesPanel
)
const
showFeaturesPanel
=
useStore
(
state
=>
state
.
showFeaturesPanel
)
const
[
nodes
]
=
useNodesState
(
initialNodes
)
const
[
nodes
]
=
useNodesState
(
initialNodes
)
const
[
edges
,
_
,
onEdgesChange
]
=
useEdgesState
(
initialEdges
)
const
[
edges
,
_
,
onEdgesChange
]
=
useEdgesState
(
initialEdges
)
// const nodesInitialized = useNodesInitialized()
const
{
const
{
// handleLayout,
handleNodeDragStart
,
handleNodeDragStart
,
handleNodeDrag
,
handleNodeDrag
,
handleNodeDragStop
,
handleNodeDragStop
,
...
@@ -71,18 +66,13 @@ const Workflow: FC<WorkflowProps> = memo(({
...
@@ -71,18 +66,13 @@ const Workflow: FC<WorkflowProps> = memo(({
handleEdgeDelete
,
handleEdgeDelete
,
}
=
useWorkflow
()
}
=
useWorkflow
()
// useEffect(() => {
// if (nodesInitialized)
// handleLayout()
// }, [nodesInitialized, handleLayout])
useKeyPress
(
'Backspace'
,
handleEdgeDelete
)
useKeyPress
(
'Backspace'
,
handleEdgeDelete
)
return
(
return
(
<
div
className=
'relative w-full h-full'
>
<
div
className=
'relative w-full h-full'
>
<
Header
/>
<
Header
/>
<
Panel
/>
<
Panel
/>
<
ZoomInOut
/>
<
Operator
/>
{
{
showFeaturesPanel
&&
<
Features
/>
showFeaturesPanel
&&
<
Features
/>
}
}
...
@@ -121,8 +111,7 @@ const WorkflowWrap: FC<WorkflowProps> = ({
...
@@ -121,8 +111,7 @@ const WorkflowWrap: FC<WorkflowProps> = ({
edges
,
edges
,
})
=>
{
})
=>
{
const
appId
=
useParams
().
appId
const
appId
=
useParams
().
appId
const
{
data
,
isLoading
,
error
}
=
useSWR
(
`/apps/
${
appId
}
/workflows/draft`
,
fetchWorkflowDraft
)
const
{
isLoading
,
error
}
=
useSWR
(
`/apps/
${
appId
}
/workflows/draft`
,
fetchWorkflowDraft
)
// const { data: configsData } = useSWR(`/apps/${appId}/workflows/default-workflow-block-configs`, fetchNodesDefaultConfigs)
if
(
error
)
{
if
(
error
)
{
syncWorkflowDraft
({
syncWorkflowDraft
({
...
@@ -152,10 +141,12 @@ const WorkflowWrap: FC<WorkflowProps> = ({
...
@@ -152,10 +141,12 @@ const WorkflowWrap: FC<WorkflowProps> = ({
return
(
return
(
<
ReactFlowProvider
>
<
ReactFlowProvider
>
<
Workflow
<
FeaturesProvider
>
nodes=
{
nodes
}
<
Workflow
edges=
{
edges
}
nodes=
{
nodes
}
/>
edges=
{
edges
}
/>
</
FeaturesProvider
>
</
ReactFlowProvider
>
</
ReactFlowProvider
>
)
)
}
}
...
...
web/app/components/workflow/operator/index.tsx
0 → 100644
View file @
081baae8
import
{
memo
}
from
'react'
import
ZoomInOut
from
'./zoom-in-out'
import
{
OrganizeGrid
}
from
'@/app/components/base/icons/src/vender/line/layout'
import
TooltipPlus
from
'@/app/components/base/tooltip-plus'
const
Operator
=
()
=>
{
return
(
<
div
className=
{
`
absolute left-6 bottom-6 flex items-center p-0.5
rounded-lg border-[0.5px] border-gray-100 bg-white shadow-lg text-gray-500 z-10
`
}
>
<
ZoomInOut
/>
<
TooltipPlus
popupContent=
'Organize blocks'
>
<
div
className=
'ml-[1px] flex items-center justify-center w-8 h-8 cursor-pointer hover:bg-black/5 rounded-lg'
>
<
OrganizeGrid
className=
'w-4 h-4'
/>
</
div
>
</
TooltipPlus
>
</
div
>
)
}
export
default
memo
(
Operator
)
web/app/components/workflow/operator/zoom-in-out.tsx
0 → 100644
View file @
081baae8
import
type
{
FC
}
from
'react'
import
{
Fragment
,
memo
,
useState
,
}
from
'react'
import
{
useReactFlow
}
from
'reactflow'
import
{
PortalToFollowElem
,
PortalToFollowElemContent
,
PortalToFollowElemTrigger
,
}
from
'@/app/components/base/portal-to-follow-elem'
import
{
SearchLg
}
from
'@/app/components/base/icons/src/vender/line/general'
import
{
ChevronDown
}
from
'@/app/components/base/icons/src/vender/line/arrows'
const
ZOOM_IN_OUT_OPTIONS
=
[
[
{
key
:
'in'
,
text
:
'Zoom In'
,
},
{
key
:
'out'
,
text
:
'Zoom Out'
,
},
],
[
{
key
:
'to50'
,
text
:
'Zoom to 50%'
,
},
{
key
:
'to100'
,
text
:
'Zoom to 100%'
,
},
],
[
{
key
:
'fit'
,
text
:
'Zoom to Fit'
,
},
],
]
const
ZoomInOut
:
FC
=
()
=>
{
const
reactFlow
=
useReactFlow
()
const
[
open
,
setOpen
]
=
useState
(
false
)
const
handleZoom
=
(
type
:
string
)
=>
{
if
(
type
===
'in'
)
reactFlow
.
zoomIn
()
if
(
type
===
'out'
)
reactFlow
.
zoomOut
()
if
(
type
===
'fit'
)
reactFlow
.
fitView
()
}
return
(
<
PortalToFollowElem
placement=
'top-start'
open=
{
open
}
onOpenChange=
{
setOpen
}
offset=
{
4
}
>
<
PortalToFollowElemTrigger
asChild
onClick=
{
()
=>
setOpen
(
v
=>
!
v
)
}
>
<
div
className=
{
`
flex items-center px-2 h-8 cursor-pointer text-[13px] hover:bg-gray-50 rounded-lg
${open && 'bg-gray-50'}
`
}
>
<
SearchLg
className=
'mr-1 w-4 h-4'
/>
100%
<
ChevronDown
className=
'ml-1 w-4 h-4'
/>
</
div
>
</
PortalToFollowElemTrigger
>
<
PortalToFollowElemContent
>
<
div
className=
'w-[168px] rounded-lg border-[0.5px] border-gray-200 bg-white shadow-lg'
>
{
ZOOM_IN_OUT_OPTIONS
.
map
((
options
,
i
)
=>
(
<
Fragment
key=
{
i
}
>
{
i
!==
0
&&
(
<
div
className=
'h-[1px] bg-gray-100'
/>
)
}
<
div
className=
'p-1'
>
{
options
.
map
(
option
=>
(
<
div
key=
{
option
.
key
}
className=
'flex items-center px-3 h-8 rounded-lg hover:bg-gray-50 cursor-pointer text-sm text-gray-700'
onClick=
{
()
=>
handleZoom
(
option
.
key
)
}
>
{
option
.
text
}
</
div
>
))
}
</
div
>
</
Fragment
>
))
}
</
div
>
</
PortalToFollowElemContent
>
</
PortalToFollowElem
>
)
}
export
default
memo
(
ZoomInOut
)
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